UNPKG

@burglekitt/gmt

Version:

Temporal-based date and time utilities with timezone support and polyfill integration

63 lines (62 loc) 2.39 kB
import { Temporal } from "@js-temporal/polyfill"; import { isValidTimeZone } from "../validate/index.js"; const MAX_TRANSITIONS_PER_YEAR = 20; /** * List every DST transition instant for an IANA timezone within a given year. * * - A "transition" is any UTC offset change: a spring-forward gap (nonexistent * local time) or a fall-back overlap (ambiguous local time) — see * `docs/dst-disambiguation.md` for the gap/overlap terminology. This is * distinct from `hasDaylightSaving` (whether a zone observes DST at all) * and from the `disambiguation`/`offset` options (what to do when * constructing a single instant that lands in a gap/overlap). * - Most zones have 0 or 2 transitions per year; some (e.g. `Africa/Casablanca`, * which pauses DST for Ramadan) can have more. * - Returns `[]` for an invalid timeZone, a non-integer year, or a valid zone * with zero transitions in that year (not an error case). * * @param timeZone IANA timeZone identifier * @param year calendar year to scan (must be an integer) * @returns array of `{ instant, offsetBefore, offsetAfter }`, in chronological order * * @example getDstTransitions("America/New_York", 2024) * // [ * // { instant: "2024-03-10T07:00:00Z", offsetBefore: "-05:00", offsetAfter: "-04:00" }, * // { instant: "2024-11-03T06:00:00Z", offsetBefore: "-04:00", offsetAfter: "-05:00" }, * // ] * @example getDstTransitions("Asia/Tokyo", 2024) // [] * @example getDstTransitions("Invalid/Zone", 2024) // [] */ export function getDstTransitions(timeZone, year) { if (!isValidTimeZone(timeZone) || !Number.isInteger(year)) { return []; } try { let cur = Temporal.ZonedDateTime.from({ year, month: 1, day: 1, hour: 0, minute: 0, second: 0, timeZone, }); const transitions = []; for (let i = 0; i < MAX_TRANSITIONS_PER_YEAR; i++) { const next = cur.getTimeZoneTransition("next"); if (!next || next.year > year) { break; } transitions.push({ instant: next.toInstant().toString(), offsetBefore: cur.offset, offsetAfter: next.offset, }); cur = next; } return transitions; } catch { return []; } }