@burglekitt/gmt
Version:
Temporal-based date and time utilities with timezone support and polyfill integration
63 lines (62 loc) • 2.39 kB
JavaScript
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 [];
}
}