Correct at the Edges
This is the detailed evidence behind the summary on Why GMT?. Date bugs hide at the extremes: the last nanosecond of a day, the first and last instant a clock can name, a daylight-saving change nobody tests because it is hundreds of millennia away. GMT tests those edges directly, and each paragraph below links the tests that prove it.
The last nanosecond of a day stays on its day
Section titled “The last nanosecond of a day stays on its day”A PlainTime has no date, so in
Temporal adding one
nanosecond to 23:59:59.999999999 wraps to 00:00:00. intervalAbutsTime and
intervalXorAllTime compare the endpoints they are given and never step one nanosecond from
them, so an interval ending on the last nanosecond of the day is never mistaken for one that
starts at midnight.
Tests: intervalAbutsTime · intervalXorAllTime · halfOpenIntervals
The first and last instant give exact answers
Section titled “The first and last instant give exact answers”Temporal represents instants from
-271821-04-20T00:00:00Z to +275760-09-13T00:00:00Z, 108 days either side of the
epoch (TC39 Temporal).
Nothing can be added to the last one, and intervalAbuts* and intervalXorAll* for Date,
DateTime, Utc and Zoned never compute a boundary past it. They return the exact answer
for intervals that end there, in UTC and in New York. At the other end, the first month is partial: its start
lies before the range, but its end does not. endOfDate("-271821-04-19", "month") returns
"-271821-04-30", and endOfDateTime, endOfUtc, endOfZoned and endOfUnix return the
matching day, week, month and year ends.
Tests: intervalAbutsUtc · intervalAbutsZoned · intervalXorAllUtc · intervalXorAllZoned · intervalAbutsDate · intervalXorAllDateTime · endOfDate · endOfUtc · endOfZoned · endOfUnix
Daylight-saving rules hold to the end of the range
Section titled “Daylight-saving rules hold to the end of the range”The last instant falls on 12 September
+275760 in Santiago, a few days after a daylight-saving change on 7 September. startOfZoned
and floorToZone start that month at local midnight on the offset then in force, -04:00.
getLocaleZonedStartOfWeek starts an en-US week at 01:00, because that Sunday’s midnight
never happened (TC39 Temporal startOfDay,
RFC 9557 time-zone annotations).
startOfQuarterForZoned, startOfUnix, intervalCountZoned and intervalCountUnix agree at
the same instant.
Tests: startOfZoned · floorToZone · getLocaleZonedStartOfWeek · startOfQuarterForZoned · startOfUnix · intervalCountZoned · intervalCountUnix
Zone conversions are exact at the very end of the representable range, too
Section titled “Zone conversions are exact at the very end of the representable range, too”In zones ahead
of UTC — Sydney and Kiritimati among them — GMT returns the spec-correct ZonedDateTime for a
wall-clock value at or near the last representable instant, which the underlying Temporal
polyfill release cannot yet parse that close to the edge (fixed on its main; see
Helping fix Temporal). It still rejects a value one nanosecond past
the maximum, and any offset the spec would reject. The same helper resolves America/Santiago’s
last representable day, which opens with a daylight-saving change, to the instant the
transition actually allows rather than to a midnight that was skipped.
TC39 Temporal treats
a ZonedDateTime as valid whenever its epoch nanoseconds lie within the Instant range,
whatever its wall-clock fields read.
Tests: zonedWallClock · zonedWallClockOperations
Spans and totals are exact across the whole range
Section titled “Spans and totals are exact across the whole range”spanNs returns the full distance from
the first instant to the last as a bigint. sumIntervals totals covered time exactly past
253 nanoseconds, about 104 days, which is where a JavaScript number stops holding
every nanosecond (ECMA-262 Number.MAX_SAFE_INTEGER).
It stays exact up to the whole range, PT4800000000H, with hours as the largest unit, as
Temporal.Instant.until allows.
Tests: spanNs · sumIntervals
Unix epochs are whole units
Section titled “Unix epochs are whole units”The Unix interval functions that compare, cut or combine
intervals reject a fractional epoch, or one past Number.MAX_SAFE_INTEGER, with their
documented sentinel. Past that point 2 ** 53 + 1 === 2 ** 53, so consecutive epoch values
can no longer be told apart. The functions stay exact on Number.MAX_SAFE_INTEGER itself.
Tests: intervalAbutsUnix ·
intervalXorUnix ·
the rest of unix/interval
One documented boundary standard
Section titled “One documented boundary standard”Every interval function is half-open, [start, end): the
start is inside and the end is not. This is the convention of SQL:2011’s application-time
PERIOD, RFC 5545’s non-inclusive
DTEND, and EWD831.
Touching intervals share no instant, so they do not overlap or intersect. Merging joins them,
and subtracting one from the other removes nothing. Take an interval apart with
subtractIntervals and put it back with mergeIntervals and intersectIntervals, and not a
nanosecond is lost. The positional functions in plain/, utc/, zoned/ and unix/ follow
the same rule, and the instant-typed ones are property-tested against interval/ at every
boundary; Interval Basics compares the two families.
Tests: intervalsOverlap · intersectIntervals · mergeIntervals · subtractIntervals · interval algebra round trip · positional UTC against interval/
Zero known bugs
Section titled “Zero known bugs”A defect GMT finds is fixed in the same change, together with every
function that shares its cause. It is never deferred, skipped or written up as a known issue.
pnpm run validate enforces this with
scripts/test-markers.mjs,
which fails the build on any skipped, focused, to-do or expected-to-fail test, and on any
known-defect note. None of the 56,006 tests is switched off.