Dates Done Right: An Introduction to the Temporal API
Date mixes up dates, times and time zones, months start at 0, and objects are mutable. Temporal is a new built-in API that fixes these problems. It has started shipping in browsers; elsewhere you can use the official polyfill.
npm install @js-temporal/polyfill # where Temporal isn't built in yet
Pick the type that matches your data
Temporal.PlainDate- a calendar date with no time: birthdays, due dates.Temporal.PlainTime- a wall-clock time: "opens at 09:30".Temporal.PlainDateTime- date and time without a zone.Temporal.ZonedDateTime- an exact moment in a specific time zone: meetings, flights.Temporal.Instant- an exact moment in UTC: timestamps, logs.Temporal.Duration- an amount of time: "2 hours 30 minutes".
Examples
const today = Temporal.Now.plainDateISO();
const due = today.add({ days: 30 }); // months are 1-based, objects immutable
console.log(due.toString()); // '2026-11-10'
const deadline = Temporal.PlainDate.from('2026-12-31');
const left = today.until(deadline, { largestUnit: 'months' });
console.log(`${left.months} months ${left.days} days left`);
const meeting = Temporal.ZonedDateTime.from('2026-10-20T15:00[Asia/Kolkata]');
const inLondon = meeting.withTimeZone('Europe/London');
console.log(inLondon.toLocaleString('en-GB'));
Daylight saving time handled correctly
const start = Temporal.ZonedDateTime.from('2026-03-07T10:00[America/New_York]');
start.add({ days: 1 }).toString(); // still 10:00 local time, across the DST change
start.add({ hours: 24 }).toString(); // exactly 24 hours later, which is 11:00 local
Comparing and sorting
Temporal.PlainDate.compare(a, b); // -1, 0 or 1
dates.toSorted(Temporal.PlainDate.compare);
a.equals(b);
Until Temporal is available everywhere you support, date-fns or Luxon remain good choices, and the concepts (separate types for dates, zoned times and instants) transfer directly.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.