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.

Related references