Temporal: JavaScript dates, finally fixed

By

Published Updated 2 min read

Date has been a source of bugs for decades: it is mutable, months are zero-based, parsing is inconsistent, and time zones are implicit. Temporal replaces it with a set of explicit, immutable types.

Support today

Chrome and Edge 144 (January 2026) and Firefox 139 support Temporal, and recent Node.js versions enable it too. Safari has not shipped it, which also affects every browser on iOS. If you need Safari, use the official polyfill and budget for its bundle size. Check current support before relying on it.

Pick the right type

  • Temporal.Instant: an exact point in time (like a UTC timestamp).
  • Temporal.PlainDate: a calendar date with no time or zone (a birthday).
  • Temporal.PlainDateTime: date and time with no zone.
  • Temporal.ZonedDateTime: a moment in a specific time zone, with DST handled correctly.
  • Temporal.Duration: an amount of time.
JavaScript
const meeting = Temporal.ZonedDateTime.from(
  '2026-10-25T09:00[Europe/Kyiv]',
);


// Same instant, shown in another zone
const inNewYork = meeting.withTimeZone('America/New_York');
console.log(inNewYork.toString());

Arithmetic without surprises

Every operation returns a new object, so shared values are never mutated by accident.

JavaScript
const start = Temporal.PlainDate.from('2026-01-31');


const next = start.add({ months: 1 });
console.log(next.toString()); // 2026-02-28 (clamped, no overflow into March)


const until = Temporal.PlainDate.from('2026-12-31').since(start, {
  largestUnit: 'months',
});
console.log(until.months, until.days);

With Date, new Date(2026, 0, 31) plus one month silently becomes March 3. Temporal clamps to the last valid day by default.

Comparing and sorting

JavaScript
const dates = [
  Temporal.PlainDate.from('2026-05-01'),
  Temporal.PlainDate.from('2026-03-15'),
];
dates.sort(Temporal.PlainDate.compare);

Using it in TypeScript

TypeScript 6.0 includes Temporal types through esnext or the more specific esnext.temporal library. Add "esnext.temporal" to your lib setting alongside the libraries your project already uses. Types do not add runtime support; browsers without Temporal still need a polyfill.

TypeScript
// Include "esnext.temporal" in compilerOptions.lib
export function daysUntil(deadline: Temporal.PlainDate): number {
  const today = Temporal.Now.plainDateISO();
  return today.until(deadline, { largestUnit: 'days' }).days;
}

When to migrate

Use Temporal for new code where you control the browsers or can ship the polyfill. Keep Date at API boundaries until your support matrix allows more. Convert at the edge with Temporal.Instant.fromEpochMilliseconds(date.getTime()).

Reference

TypeScript 6.0 release notes.

Sources and further reading