Modern Temporal API: Immutable Calendar Math vs Mutable Date
- Last updated
- Prerequisites:
- JavaScript Date basics
- Understanding of timezones and UTC
- temporal
- date
- intl
- timezone
- calendar
- immutable
Read at your depth
The practical view
Temporal provides new date/time types: Temporal.Instant (a precise UTC moment), Temporal.PlainDate, PlainTime, PlainDateTime (wall-clock values without timezone), Temporal.ZonedDateTime (moment + timezone), and Temporal.Duration. They are immutable: every operation returns a NEW instance — d.add({ days: 1 }) returns a new date, leaving d unchanged (unlike Date.setDate which mutates). Temporal.Now.zonedDateTimeISO() gets the current moment. Math like .add, .subtract, .until, .since works with calendar-aware durations. Dates compare with plain <, >, and .equals. This replaces the error-prone mutable Date API for real applications.
The same idea in other frameworks
React apps still use Date + libraries like date-fns; Temporal offers a future native alternative for scheduling UIs.Angular pipes and forms use Date; Temporal types need adapters but provide safe calendar math for booking/calendar features.Vue date pickers can consume Temporal values directly via its ISO string methods.SvelteKit load functions can return Temporal strings; client components parse with Temporal for safe math.Legacy vs modern
Adding a day: mutable Date.setDate vs immutable Temporal
Date mutates in place and is easy to corrupt with DST/overflow; Temporal returns a new immutable value with calendar-safe arithmetic.
const d = new Date(); // ❌ OLD: mutable
const t = d.setDate(d.getDate() + 1); // mutates d in place — original lostconst t = Temporal.Now.zonedDateTimeISO(); // ✅ NEW: immutable
const next = t.add({ days: 1 }); // returns a NEW value; t unchangedInterview gotchas
Context
Interviewers use this to test whether you understand the conceptual conflation in Date (instant vs wall-clock vs timezone) that causes real production bugs.
The mechanical answer
Date conflates three distinct concepts: an absolute instant, a local wall-clock reading, and a timezone interpretation. Its methods mutate in place, work in milliseconds (losing sub-ms precision), and arithmetic like setDate is calendar- and DST-unaware — adding 24h across a DST transition changes the wall clock unexpectedly, and adding '1 month' is undefined. Temporal splits these into Instant, PlainDate/PlainTime/PlainDateTime, and ZonedDateTime, all immutable with nanosecond precision. Arithmetic is done through calendar and timezone algorithms (with explicit overflow rules for month-ends and DST gaps), so the results are deterministic and reproducible.
Trap
The naive answer is 'Date is fine if you just use UTC everywhere'. UTC-only breaks local calendar features and still leaves you with mutable in-place mutation bugs. Another trap: assuming Temporal is just 'Date with better method names' — it is a different type system (separate types for instants vs wall clocks), so adopting it means restructuring how you model time, not swapping one call for another.
Context
This tests whether you understand calendar-aware duration semantics — a subtle area where naive epoch-millisecond math fails silently.
The mechanical answer
A calendar month is not a fixed number of milliseconds, so Temporal computes month arithmetic through the calendar's date-add algorithm. Adding { months: 1 } to 2026-01-31 constrains the day to the last valid day of February: the default 'constrain' overflow produces 2026-02-28 (or 2026-02-29 in a leap year). The 'reject' option throws instead of clamping, and 'balance' lets out-of-range components roll over. The same machinery handles DST gaps and ambiguous wall-clock times in ZonedDateTime. This is the correct behavior for real business logic (subscriptions, billing, schedules) that epoch-ms math silently gets wrong.
Trap
The naive answer is 'just add 30 days or 2629746000 ms'. That drifts from calendar months and breaks date-of-month invariants. Another trap: assuming Temporal.Duration { months: 1 } is a fixed quantity — it is relative to the starting point's calendar, which is exactly the property that makes it correct. Candidates who name the constrain/reject/balance overflow options demonstrate real depth.