Temporal reached Stage 4 in March 2026 and joins ES2027. It ships in Chrome 144 1 , Firefox 139 555 , Deno 2.7 60,577 and Node.js 26 2,131 ; Safari 10 has it only in Technology Preview, so load temporal-polyfill 775 (github.com/fullcalendar/temporal-polyfill (https://github.com/fullcalendar/temporal-polyfill 775 )) there. Its objects are immutable, months count from 1, parsing accepts only ISO 8601 (plus RFC 9557 zone annotations such as [Europe/Paris]), and separate types say what a value means: Instant (an exact moment), ZonedDateTime (moment plus time zone), PlainDate, PlainTime and PlainDateTime (wall-clock values with no zone) and Duration. Node.js 25 lacks Temporal, so this listing ran in headless Chrome 152.
const jan31 = Temporal.PlainDate.from('2026-01-31');
console.log(`${jan31.add({ months: 1 })}`, jan31.dayOfWeek, jan31.daysInMonth); // clamps to Feb 28
const call = Temporal.ZonedDateTime.from('2026-03-07T09:00[America/New_York]');
const nextDay = call.add({ days: 1 }); // crosses the DST change on 8 March
console.log(`${nextDay}`, `${nextDay.since(call, { largestUnit: 'hours' })}`);
console.log(`${call.withTimeZone('Asia/Kuala_Lumpur').toPlainDateTime()}`);
const wait = Temporal.PlainDate.from('2026-09-15').until('2026-12-25', { largestUnit: 'months' });
console.log(`${wait}`, wait.months, wait.days, `${new Date(0).toTemporalInstant()}`);2026-02-28 6 31 2026-03-08T09:00:00-04:00[America/New_York] PT23H 2026-03-07T22:00:00 P3M10D 3 10 1970-01-01T00:00:00Z
Adding one day kept 09:00 local time even though only 23 hours passed, which Date arithmetic gets wrong. Bridge old code with date.toTemporalInstant() and new Date(instant.epochMilliseconds).