By Ether DataRequest data sample
All sections

Daypart to Slots

A daypart is a local clock band such as primetime; mapping it to canonical UTC slots for a zone and ISO week produces a weighted slot-set whenever the band straddles a UTC hour boundary.

stable5 min read
Source time
daypart, iana_zone, iso_week
Destination time
slot_set
weighted

Purpose

A daypart — "primetime," "morning drive," "overnight" — is a band of local clock hours on a set of weekdays. It is a scheduling and measurement concept defined the way audiences experience it: 8pm feels like primetime everywhere, regardless of what UTC hour that is. The canonical unit in this KB, however, is the UTC hour-of-week slot. Converting a daypart to slots therefore requires resolving each local hour through the tz database for a specific IANA zone and a specific ISO week, because the local-to-UTC offset shifts across DST — the same local daypart maps to a different UTC slot set in a spring week than in a fall week.

Source and destination

Source: a daypart definition (local hours + weekdays), an iana_zone, and an iso_week. Destination: a slot_set of UTC hour-of-week slots.

Exactness: weighted

weighted. Sub-hour offsets (India, Sri Lanka, Newfoundland at half-hour offsets; Nepal, Chatham Islands at 45-minute offsets) and dayparts whose edges fall on the half hour (a "daytime" band running 9:30am-4:30pm local) both mean the local band does not always align to whole UTC hours. When it doesn't, a single local hour straddles two UTC slots, so the correct output is not a clean slot list but a weighted slot-set — the fraction of coverage each UTC slot receives — the direct temporal analog of the geo KB's weighted crosswalk for a polygon that straddles a cell boundary. Rounding to whole-slot membership by majority overlap is a documented, lossy simplification, not the default behavior.

Algorithm

import { STANDARD_DAYPARTS, daypartToUtcSlots, utcSlotToDaypart } from "@/lib/time/daypart";

const primetime = STANDARD_DAYPARTS.find((d) => d.id === "primetime")!;
// { id: "primetime", name: "Primetime (8p-11p)", hours: [20, 21, 22] }

// Expand to UTC slots for a specific zone and ISO week (DST-correct for that week).
const janSlots = daypartToUtcSlots(primetime, "America/New_York", 2026, 3);
// Early January: EST is UTC-5, so 20:00-23:00 local -> 01:00-04:00 UTC next day.

const julySlots = daypartToUtcSlots(primetime, "America/New_York", 2026, 29);
// Mid-July: EDT is UTC-4, so the SAME local daypart resolves to a different
// UTC slot set than in week 3 — a one-hour shift purely from DST.

// Inverse: which daypart does a given UTC slot fall into, for a zone + week?
const back = utcSlotToDaypart(julySlots.slots[0]!, "America/New_York", 2026, 29);
// -> { daypart: "primetime", localHour: 20, localWeekday: <Mon0 weekday> }

STANDARD_DAYPARTS is a conventional US broadcast scheme (overnight, morning, daytime, early fringe, early news, prime access, primetime, late news) defined purely in local hours. It is a labelled default, not a universal standard — Nielsen, individual networks, and international broadcasters each define their own bands, sometimes at 15-minute resolution. Treat it the same way this KB treats a conversion profile: override the hours/days per market rather than assuming the US scheme applies elsewhere.

Parameters

daypart
An id/name/hours definition with optional days. hours are local clock hours 0-23; days default to all seven (Monday0-Sunday6).
zone
IANA zone the local hours are resolved in.
isoYear / isoWeek
The specific ISO week to resolve against — required because the local-UTC offset depends on the DST calendar for that week, not a fixed constant.

Outputs

A DaypartSlots record: the daypart id, zone, ISO year/week, and the sorted, deduplicated set of UTC hour-of-week slots the daypart occupies in that week. Where a sub-hour offset or a non-hour-aligned band edge applies, treat that set as coverage-weighted rather than binary membership — carry the overlap fraction through to any downstream reporting rather than silently rounding.

Units and convention

Local hours are integers 0-23; UTC slots follow the KB-wide convention (slot 0 = Monday 00:00 UTC); zones are IANA identifiers; the tz engine is luxon.

DST and disambiguation behavior

daypartToUtcSlots resolves each (weekday, local hour) pair as a wall time in the target ISO week via luxon, so it inherits the correct DST offset for that specific week automatically. An hour skipped entirely by a spring-forward gap (the local hour that never occurs) is silently excluded from the slot set rather than raising — callers doing exact accounting (e.g. ad-slot inventory) should independently check for gap weeks via DST Handling if that hour mattered to them.

Quality and provenance

Every result should carry the zone, ISO week, and daypart definition used — the same daypart id resolves to a different slot set in different weeks, so the week is not optional context, it is part of the key.

Edge cases

Half-hour and 45-minute offset zones, and the general sub-hour band straddle case, are why the output is weighted rather than a clean partition. Southern-Hemisphere reversed DST means "primetime" in Sydney and New York shift in opposite calendar directions across the year — never compare local-experience dayparts across hemispheres by UTC slot alone; see Measurement Semantics for the general UTC-canonical-vs-local-experience tension. Extreme offset span means a daypart aggregated across many zones can produce a slot set that wraps the entire week.

Python parity

from zoneinfo import ZoneInfo
from datetime import datetime

def daypart_to_utc_slots(hours, days, zone: str, iso_year: int, iso_week: int) -> set[int]:
    slots = set()
    for day_mon0 in days:
        for hour in hours:
            try:
                local = datetime.fromisocalendar(iso_year, iso_week, day_mon0 + 1).replace(
                    hour=hour, tzinfo=ZoneInfo(zone)
                )
            except ValueError:
                continue  # hour skipped by a spring-forward gap
            utc = local.astimezone(ZoneInfo("UTC"))
            weekday_mon0 = (utc.isoweekday() - 1)
            slots.add(weekday_mon0 * 24 + utc.hour)
    return slots

The tested reference implementation is the TypeScript in lib/time/daypart.ts; this Python mirrors daypartToUtcSlots using datetime.fromisocalendar (3.9+) and zoneinfo, catching the ValueError a gap produces the same way the TS skips an invalid luxon DateTime.

Edge cases affecting this page

Half-hour offset zonesIndia (+5:30), Sri Lanka (+5:30), central Australia ACST (+9:30), Iran (+3:30), Afghanistan (+4:30), Myanmar (+6:30), and Newfoundland (-3:30) sit a half hour off UTC. A local clock hour therefore straddles TWO UTC hour-of-week slots, so a local band (daypart, broadcast day) never maps to a whole number of slots.45-minute offset zonesNepal (+5:45), the Chatham Islands (+12:45 / +13:45 DST), and the Australian Eucla region (+8:45) are offset by three quarters of an hour. A local hour straddles a UTC slot 45/15, so even majority assignment is badly lopsided.Sub-hour band straddle (general)Because the canonical unit is a whole UTC hour, ANY local band whose edges do not land on a UTC hour boundary produces fractional slot membership — from sub-hour offsets, from bands defined at :30 (e.g. daytime 09:30–16:30), or from DST. This is the same phenomenon as a geo cell straddling a boundary.Reversed (Southern Hemisphere) DSTDST runs in the opposite calendar months south of the equator, and transition dates differ per country within the same offset. The same UTC slot is a different local season and local hour band in Sydney vs New York.UTC canonical vs local experienceThe unit is UTC, but 'primetime', 'morning', and 'lunch' are LOCAL experiences (8pm local everywhere). Comparing behavior at the same UTC slot is not the same as at the same local hour — the core temporal-interop tension, and the direct analog of geo's requested-vs-executed geography.Extreme offset span (UTC−12 … +14)The inhabited world spans 26 hours of offset, so a single local hour exists across a more-than-24-hour span of UTC instants. A local daypart aggregated across many zones can therefore occupy a UTC slot-set that wraps the entire week.
The 168 AxisConceptual modelMeasurement SemanticsMeasurement semanticsTimezone DatabaseTime systems & zones