JavaScript Temporal API: The Modern Replacement for Date

Photo by Chirayu Trivedi on Unsplash
Temporal is a new built-in JavaScript global that replaces the flawed Date object. It offers immutable, purpose-built types such as PlainDate, ZonedDateTime, Instant, and Duration, along with first-class IANA time-zone support and DST-safe arithmetic. It reached TC39 Stage 4, making it an accepted part of the ECMAScript standard.
Date crams a timestamp and calendar fields into one object, its setters mutate in place, and it only understands UTC and the local zone. Parsing behaviour has historically varied between browsers and it supports only the Gregorian calendar. These structural flaws make time-zone and mutation bugs common in production code.
The core types are Temporal.Instant for exact machine timestamps, Temporal.ZonedDateTime for real zoned events, Temporal.PlainDate, Temporal.PlainTime, and Temporal.PlainDateTime for zone-free values, Temporal.PlainYearMonth and Temporal.PlainMonthDay for partial dates, and Temporal.Duration for lengths of time. Temporal.Now reads the current time.
Firefox shipped Temporal by default in Firefox 139 on 27 May 2025, Chrome added it in Chrome 144 on 13 January 2026, and Node.js in Node 26. Safari support is still in development, so Temporal is not yet a Baseline feature you can assume is available everywhere.
Yes, with care. You can call Temporal natively in Firefox, Chrome, and recent Node. For engines that lack it, install the official @js-temporal/polyfill package, which exposes the same API. Write against Temporal now and remove the polyfill later once Safari ships, with no code changes required.

Photo by Chirayu Trivedi on Unsplash
Key Takeaway
The JavaScript Temporal API is the modern, immutable replacement for the flawed Date object. It provides dedicated types like PlainDate, ZonedDateTime, Instant, and Duration, plus first-class time zones and DST-safe arithmetic. Temporal reached TC39 Stage 4 and now ships in Firefox and Chrome, with the js-temporal polyfill covering older engines.
Working with dates in JavaScript has always felt like fighting the language. The built-in Date object was ported almost verbatim from early Java, and it has carried the same design mistakes for three decades. Temporal is the standards-track answer — a brand-new global object built to make date and time handling predictable instead of perilous.
In this guide I walk through why Date is broken, the new Temporal types you will actually use, how immutability and first-class time zones change the way you reason about time, and exactly where Temporal stands today in browsers and in the language standard.
Date tries to be too many things at once. A single Date instance is simultaneously a timestamp measured in milliseconds since the Unix epoch and a bag of calendar fields, so the API is confusing and easy to misuse. Its worst traits are structural, not cosmetic:
These are not edge cases. Time-zone confusion and accidental mutation are two of the most common sources of date bugs in production JavaScript, and no amount of wrapper libraries fully hides them.
Temporal replaces the single overloaded Date with a small family of purpose-built, immutable types. Every method that looks like it changes a value — add, subtract, with, round — instead returns a brand-new object and leaves the original untouched. That single rule eliminates an entire class of aliasing bugs.
import { Temporal } from '@js-temporal/polyfill';
// A calendar date — no time, no time zone attached
const releaseDay = Temporal.PlainDate.from('2026-08-01');
// Immutable: add() returns a NEW instance, the original is untouched
const nextRelease = releaseDay.add({ months: 1 });
releaseDay.toString(); // '2026-08-01' (unchanged)
nextRelease.toString(); // '2026-09-01'
// A real event carries its IANA time zone
const meeting = Temporal.ZonedDateTime.from(
'2026-03-07T12:00[America/New_York]'
);
// DST-safe arithmetic: adding one calendar day keeps 12:00 wall-clock,
// even though 8 Mar 2026 is only 23 hours long (spring-forward)
meeting.add({ days: 1 }).toString();
// '2026-03-08T12:00:00-04:00[America/New_York]'
// A timestamp on the global timeline: nanosecond precision, no calendar
const now = Temporal.Now.instant();Notice how each value in the sample above declares exactly what it is. A PlainDate has no time zone, so it cannot accidentally shift a day when a server runs in a different region. A ZonedDateTime carries its IANA zone, so arithmetic across daylight-saving boundaries stays correct. There is no ambiguity about what a value means.
Prefer the most specific type your use case allows. Store a birthday as a PlainDate, a daily alarm as a PlainTime, and a real scheduled event as a ZonedDateTime. Reaching for ZonedDateTime everywhere reintroduces the very ambiguity Temporal is designed to remove.
Temporal exposes a handful of types under one global namespace, each modelling one clear concept. Most application code touches only the first three or four:
The feature developers miss most in Date is honest time-zone support. Temporal treats IANA zones as a first-class input: you can construct a ZonedDateTime in Asia/Jakarta or America/New_York directly, convert between zones without losing information, and compare instants across the planet safely.
Arithmetic is daylight-saving aware. In the code sample, adding one calendar day to a New York meeting keeps the 12:00 wall-clock time even though the spring-forward day is only 23 hours long — the offset shifts from minus five to minus four automatically. With Date you would have had to special-case that yourself, and most code never does.
Temporal is not a drop-in rename of Date. Its methods, property names, and rounding behaviour differ deliberately. Budget time to learn the model rather than search-and-replacing Date calls, and keep Instant for machine timestamps separate from the plain types used for human-facing calendar values.
The table below sums up why teams are migrating. Each row is a concrete pain point that Temporal removes by design rather than by convention.
| Aspect | Legacy Date | Temporal |
|---|---|---|
| Mutability | Setters mutate the object in place | Immutable — every operation returns a new value |
| Time zones | UTC or local machine only | Any IANA time zone, first-class |
| Types | One object represents everything | Distinct types for date, time, instant, and duration |
| Calendars | Gregorian calendar only | Multiple calendar systems supported |
| Parsing | Inconsistent across engines | Strict, predictable ISO 8601 parsing |
Temporal has finished the TC39 process. It reached Stage 4 — the final stage, meaning it is an accepted addition to the ECMAScript standard — after nearly a decade of design work. The specification is being merged into ECMA-262 and ECMA-402, so the API surface is now stable.
Shipping in engines is well underway. Firefox was first, enabling Temporal by default in Firefox 139 on 27 May 2025. Chrome followed in Chrome 144 on 13 January 2026, and Node.js added it in Node 26 on 5 May 2026. Safari support is still in development, so Temporal is not yet a Baseline feature you can assume everywhere.
Yes, carefully. In Firefox, Chrome, and recent Node you can call Temporal natively. For everything else, install the official js-temporal polyfill package from the proposal champions, which exposes the exact same API so your code stays forward-compatible. Write against Temporal now, keep the polyfill until Safari ships, and drop it later with zero code changes.