Time Zones Are a Type Problem: What Temporal Actually Fixes
Temporal splits JavaScript's one vague Date into precise types. Measured DST behaviour in Node 26, and where the adoption claims in this year's blog posts get the browser versions wrong.
Every date bug I have had to debug in production has the same shape: one value was really two, and they got stored as one. JavaScript's Date is a single mutable millisecond count asked to mean both a wall-clock reading ("the standup is at 09:00") and an absolute point on the timeline ("this token expires at epoch X").[5] Those are different kinds of thing, and the space between them is where daylight saving bugs live.[1] MDN now says so plainly: "With the introduction of the Temporal API, the Date object is considered a legacy feature."[5]
Temporal's headline is usually "a better Date"; the more useful framing is that it splits one vague type into several precise ones, each forcing a decision Date used to make silently on your behalf.[1] The proposal summarises the fix as "first-class support for all time zones, including DST-safe arithmetic", and MDN calls the result "a full replacement for the Date object".[1][4]
The gap that `Date` hides
In a zone that observes daylight saving, one day per year is 23 hours and another is 25, because "the length of a day may vary due to offset changes".[9] Legacy Date has no way to distinguish "one calendar day" from "24 hours" — it only has the second concept.
I ran this in Node v26.10.0 with TZ=America/New_York on the 2026 spring-forward date. Constructing the local wall clock new Date(2026, 2, 8, 2, 30) — 02:30, a time that does not exist that day — returns a Date whose getHours() is 3; the runtime moved the value forward an hour and said nothing. Adding 24 hours of milliseconds to noon on March 7 gives 13:00 on March 8, because the arithmetic was on an instant while the intent was a calendar day.
Temporal keeps the two apart: Temporal.ZonedDateTime.from("2026-03-07T12:00[America/New_York]").add({ days: 1 }) lands on 12:00 the next day, while since() reports the elapsed time as 23 hours — both correct, because they answer different questions.[9] For the non-existent hour it makes the resolution an explicit argument: ZonedDateTime.from accepts a disambiguation option, and "reject" throws a RangeError where the wall clock is ambiguous or falls in a gap.[7] The default, compatible, is documented as "same behavior as Date : use later for gaps and earlier for ambiguities" — right for migration, wrong wherever a silently shifted timestamp is a correctness failure.[6]
A duration is not a number of milliseconds
The same ambiguity runs through duration arithmetic. Temporal.Duration refuses to treat calendar units as fixed: days-to-hours conversion is "technically ambiguous because the length of a day may vary due to offset changes, such as daylight saving time", and you must supply a zoned relativeTo for the conversion to account for them.[9] In my probe, { days: 1 } before the transition totals 23 hours and { hours: 24 } totals 1.0417 days — not a rounding artifact, but the correct answer to a differently-phrased question.
A function that adds a month must now say whether "one month" means a calendar month, so 31 January clamps to 28 February, or a fixed 30-day span: add takes an overflow option where "constrain" clamps the date component to the valid range and "reject" throws a RangeError.[8] Temporal's design goal is to deal "only with objects representing fixed dates and times", which is why nothing here mutates in place.[1]
What the type names buy you
The API surface reads as a list of decisions made out loud.[4] Temporal.Instant is a point on the timeline, stored as nanoseconds since the epoch "without any time zone or calendar system", while Temporal.ZonedDateTime is "a date-time component combination paired with a time zone".[10][4] Temporal.PlainDate and PlainDateTime are "time-zone-unaware" values, for things like "the release is on the 15th", and Temporal.Duration is "a difference between two time points" that knows calendar units depend on context.[4] Instant also closes a precision gap: Date stores milliseconds and cannot round-trip a nanosecond timestamp without loss, which my probe confirms — Instant.from("...123456789Z") keeps all nine digits while the equivalent Date truncates to .123Z.[10]
What it deliberately does not do
Two absences matter before you plan a migration. There is no token formatting: format("YYYY-MM-DD") has no equivalent, PlainDate has no .format method at all in my probe, and output goes through toLocaleString and Intl — the correct design, but it means rewriting format strings.[4] And the API "does not support year-week dates", so ISO week-numbering workflows are not covered.[4]
The adoption story is not what the blog posts say
This is where the secondary sources are wrong, and it matters if you are deciding when to adopt. One widely-shared 2026 article calls Temporal "the ES2026 replacement for Date" and says it is "now part of the ES2026 specification".[14] The TC39 finished-proposals table — the primary source — lists Temporal's expected publication year as 2027.[2] The proposal is Stage 4, and its README says it "will be merged into the ECMA-262 and ECMA-402 standards and this repository will be archived": standardised, but not yet in a published edition.[3]
The same article claims it "ships natively in Chrome 144+, Firefox 145+, Node 24", while MDN's browser-compat-data records Chrome 144, Firefox 139, and Node.js 26.0.0, with Safari at "preview" and Safari on iOS as false.[14][13] Chrome 144 introduced it as "a modern date and time API to the web".[11] Firefox shipped it earlier, in 139 on 2025-05-27, which the proposal README and release notes confirm.[3][12][15] MDN still labels the API "Limited availability" rather than Baseline, "because it does not work in some of the most widely-used browsers".[4]
So the practical strategy is the unglamorous one: feature-detect globalThis.Temporal, ship a polyfill where it is missing, and write application code against one API — on Node 26 the cost is zero, which is how this article's measurements were taken.[3][13] The reason to do that work now is not that the API is new; it is that every Date in your codebase is an unstated decision about whether a value is a wall clock or an instant, and Temporal is the first version of this platform where you can write that decision down and have the runtime enforce it.[1]
Sources
[1] https://tc39.es/proposal-temporal/docs — Temporal proposal documentation (TC39)
[2] https://github.com/tc39/proposals/blob/main/finished-proposals.md — TC39 Finished Proposals (stage 4 list)
[3] https://github.com/tc39/proposal-temporal — tc39/proposal-temporal README
[4] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal — Temporal - MDN Web Docs
[5] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date — Date - MDN Web Docs
[6] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime — Temporal.ZonedDateTime - MDN Web Docs
[7] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime/from — Temporal.ZonedDateTime.from() - MDN Web Docs
[8] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/PlainDate/add — Temporal.PlainDate.prototype.add() - MDN Web Docs
[9] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration — Temporal.Duration - MDN Web Docs
[10] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant — Temporal.Instant - MDN Web Docs
[11] https://developer.chrome.com/blog/new-in-chrome-144 — New in Chrome 144 - Chrome for Developers
[12] https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/139 — Firefox 139 release notes - MDN
[13] https://github.com/mdn/browser-compat-data/blob/main/javascript/builtins/Temporal.json — MDN browser-compat-data: Temporal API support
[14] https://w3tweaks.com/javascript/javascript-temporal-api — The Temporal API: Finally Fixing JavaScript Dates (w3tweaks)
[15] https://bryntum.com/blog/javascript-temporal-is-it-finally-here — JavaScript Temporal in 2026 - is it finally here? (Bryntum)