The JavaScript Temporal API: A Modern Replacement for Date
JavaScript Date has been broken in well-known ways for decades. TC39 Temporal — Stage 4 since early 2025, shipping in modern browsers and Node.js — cleanly separates instants, wall-clock dates, and zoned date-times. This guide explains the new types, how they map to Unix timestamps, and how to migrate.
Why Temporal exists
JavaScript Date conflates several distinct concepts: an exact instant, a wall-clock time, and a zoned date-time. Date also has well-known parsing quirks, a single timezone (the user’s), and millisecond precision. Temporal exposes the underlying concepts as separate types and supports nanosecond precision, arbitrary IANA timezones, and explicit calendar systems.
- Stage 4 since early 2025; first shipped in Firefox 139 by default
- Now available in Chrome 134+, Safari 18+, Node.js 26+
- Polyfills (@js-temporal/polyfill) work in any modern environment
- Designed to coexist with Date — interop methods exist in both directions
Core Temporal types
Five types cover the date-time problem cleanly:
- Temporal.Instant — an exact moment, like a Unix timestamp but with nanosecond precision
- Temporal.ZonedDateTime — an Instant + IANA timezone + calendar; the type Date should have been
- Temporal.PlainDate — a calendar date with no time and no timezone (e.g. a birthday)
- Temporal.PlainTime — a wall-clock time with no date and no timezone
- Temporal.PlainDateTime — a date + time without a timezone (a "naive" datetime)
- Temporal.Duration — a length of time (P3DT4H10M format), unit-aware
Working with Unix timestamps
Temporal.Instant exposes Unix-epoch conversions in seconds, milliseconds, microseconds, and nanoseconds. Use Instant for storage/transport and ZonedDateTime when you need wall-clock context.
- Temporal.Now.instant() — current exact moment
- Temporal.Instant.fromEpochSeconds(1700000000)
- Temporal.Instant.fromEpochMilliseconds(Date.now())
- instant.epochSeconds, instant.epochMilliseconds, instant.epochNanoseconds
- instant.toZonedDateTimeISO("America/New_York") to get a wall-clock view
Timezones and DST handled correctly
ZonedDateTime carries an IANA timezone, so DST transitions, gap times, and overlap times are all handled explicitly. The disambiguation option lets you pick a policy for ambiguous local times.
- zdt.timeZoneId — the IANA name (e.g. America/Los_Angeles)
- zdt.add({ hours: 1 }) — DST-aware arithmetic in wall-clock terms
- zdt.until(zdt2) — distance between two zoned date-times
- Disambiguation options: "earlier", "later", "compatible" (default — earlier for gap, later for overlap), "reject"
- Round-trip: instant ↔ zoned date-time is loss-free
Migration patterns from Date
The simplest migration strategy is to introduce Temporal at the boundaries first (parsing input, formatting output) and gradually move internal logic over.
- Date → Instant: Temporal.Instant.fromEpochMilliseconds(date.getTime())
- Instant → Date: new Date(instant.epochMilliseconds)
- For new code, prefer Temporal everywhere and convert to Date only at API edges
- date-fns and Luxon both have Temporal-aware modes / adapters
- JSON: Temporal.Instant.prototype.toJSON returns an ISO 8601 string
Browser and runtime support
Temporal shipped in 2025; native support is now broad. The polyfill is the safest path while older browsers and Node versions are still in service.
- Firefox 139+: shipped enabled by default (May 2025)
- Chrome 134+: enabled by default (early 2026)
- Safari 18+: enabled by default
- Node.js 26+: enabled by default (May 2026 release)
- Polyfill: @js-temporal/polyfill on npm — same API, runs in any modern environment
Temporal API FAQ
- Is Temporal a replacement for Date?
- Yes — Temporal is designed to fully replace Date for new code. Date remains in the language for backward compatibility, and the two interoperate via epoch milliseconds.
- Does Temporal support nanoseconds?
- Yes. Temporal.Instant has nanosecond precision internally and exposes epochNanoseconds as a BigInt.
- Can I use Temporal in Node.js today?
- In Node.js 26+ Temporal is enabled by default. In older Node versions, install @js-temporal/polyfill — the API surface is identical.
- How does Temporal handle the DST gap?
- ZonedDateTime constructors and arithmetic accept a disambiguation option. "compatible" (the default) picks the later instant for gaps and the earlier for overlaps; "reject" throws an error if the local time is ambiguous.