Diego Betto's Blog
Foto di Lukas Blazek su Unsplash

Diego Betto · September 11, 2026 · 5 min di lettura

Temporal: the new JavaScript API that (finally) replaces Date

What's wrong with the legacy Date object, how Temporal fixes it, the core types (PlainDate, ZonedDateTime, Duration...), and where support stands in 2026.

Condividi:XLinkedInFacebookWhatsApp

Date is one of the oldest, most-complained-about parts of JavaScript — mutable, full of parsing footguns, and unreliable across time zones. After years in TC39’s proposal pipeline, Temporal is JavaScript’s answer: a new, purpose-built API for working with dates, times, and durations, designed from scratch instead of patched onto the original 1995 Date design.

ℹ️ Note on availability

Temporal has been at TC39 Stage 3 for a while, with native support rolling out gradually across engines rather than landing everywhere at once — check current browser/Node support before relying on it in production, and use the official @js-temporal/polyfill where it isn’t natively available yet. Everything below describes the API as specified, which is what the polyfill also implements.

What’s actually wrong with Date

A quick list, because it’s worth remembering why this needed a full replacement instead of incremental fixes:

  • Date is mutable. date.setMonth(5) changes the object in place — pass a Date into a function and that function can silently mutate your original value.
  • Parsing is inconsistent. new Date("2026-01-15") and new Date("01/15/2026") can behave differently across engines, and some formats are outright ambiguous (is 01/02/2026 January 2nd or February 1st?).
  • Time zone handling is bolted on. Date internally always stores a UTC timestamp; anything you do with a specific time zone requires manual offset math or a third-party library like date-fns-tz or Luxon.
  • There’s no first-class “duration” or “plain date” concept. Wanting “just a calendar date with no time” (a birthday) or “just a duration” (45 minutes) means either hacking Date into representing it or building it yourself.

The core Temporal types

Instead of one do-everything object, Temporal splits the domain into distinct, purpose-built types:

Type Represents
Temporal.PlainDate A calendar date with no time or time zone (e.g. a birthday)
Temporal.PlainTime A time of day with no date (e.g. “office opens at 09:00”)
Temporal.PlainDateTime A date and time with no time zone
Temporal.ZonedDateTime A specific instant, tied to a specific time zone — what you usually want for “when did this happen”
Temporal.Instant A precise point on the UTC timeline, with no calendar or time zone attached
Temporal.Duration A length of time (e.g. “2 hours 30 minutes”)
// A calendar date, no time attached
const birthday = Temporal.PlainDate.from("2026-03-14");

// A specific moment, in a specific time zone
const meeting = Temporal.ZonedDateTime.from("2026-09-10T15:00:00[Europe/Rome]");

// A duration you can do arithmetic with
const break_ = Temporal.Duration.from({ minutes: 15 });

Every one of these objects is immutable — any operation returns a new instance instead of mutating the original, the same model as Date’s friendlier modern alternatives (Luxon, date-fns) already popularized.

A practical example: converting between time zones

This is the exact kind of thing that required a library before Temporal:

const meetingInRome = Temporal.ZonedDateTime.from("2026-09-10T15:00:00[Europe/Rome]");

const meetingInNewYork = meetingInRome.withTimeZone("America/New_York");

console.log(meetingInNewYork.toString());
// 2026-09-10T09:00:00-04:00[America/New_York]

No manual offset math, no risk of forgetting daylight saving time — withTimeZone handles the conversion correctly, including DST transitions, because ZonedDateTime carries the IANA time zone identifier (Europe/Rome), not just a numeric UTC offset.

Arithmetic with durations

const start = Temporal.PlainDateTime.from("2026-09-10T09:00:00");
const end = start.add({ hours: 2, minutes: 30 });

console.log(end.toString()); // 2026-09-10T11:30:00

const duration = start.until(end);
console.log(duration.toString()); // PT2H30M

add, subtract, and until are available on every Temporal type that makes sense for them, and they correctly handle edge cases Date arithmetic gets wrong by hand — month-end rollovers, leap years, and DST transitions for ZonedDateTime.

Date vs Temporal at a glance

Date Temporal
Mutable Yes No
Plain date without time Not natively Temporal.PlainDate
Reliable time zone support No (manual/library) Yes, built in (ZonedDateTime)
Parsing Inconsistent across engines Strict, spec-defined format
Duration as a first-class type No Temporal.Duration
Arithmetic (add/subtract) Manual, error-prone Built-in methods

Should you migrate existing code today?

Not wholesale, and not yet everywhere. Given the uneven native support described above, the practical path for most projects in 2026 is:

  • Use the polyfill for new code where date/time correctness genuinely matters — anything dealing with multiple time zones, recurring events, or calendar math.
  • Leave Date in place where it’s just being used to log a timestamp or measure elapsed time with Date.now() — there’s no correctness win from migrating that.
  • Keep an eye on your target runtimes’ support tables before dropping the polyfill dependency.

FAQ

❓ Does Temporal replace Date entirely?

Date isn’t being removed from the language — it stays for backward compatibility. Temporal is the new, recommended API for anything beyond the simplest timestamp use cases.

❓ Do I need a library like Luxon or date-fns if I use Temporal?

For most use cases, no — Temporal covers parsing, arithmetic, formatting, and time zone conversion natively. Libraries built around the old Date object become largely unnecessary once Temporal is available in your target environments.

❓ Can I convert an existing Date to Temporal?

Yes — Temporal.Instant.fromEpochMilliseconds(date.getTime()) gives you a Temporal.Instant from a legacy Date, which you can then convert to a ZonedDateTime with toZonedDateTimeISO() for a given time zone.

Conclusion

Temporal fixes the structural problems Date couldn’t outgrow: mutability, ambiguous parsing, and time zone handling that always required extra tooling. Splitting the domain into PlainDate, ZonedDateTime, Duration, and friends makes code that handles dates correctly read like it’s handling dates correctly, instead of hiding intent behind generic Date math. Check your target runtimes’ support before dropping the polyfill, but it’s worth learning the API now — this is where date handling in JavaScript is headed.

References

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Written by

Diego Betto

Co-Founder & CTO at PAPION. Senior full-stack engineer specializing in React, TypeScript, Node.js, and application security.