Temporal Instead of Date
Not having control over time — and indeed not being able to — is a concept in itself.
As JavaScript developers, we’ve been sharing a collective trauma for years: the Date object.
Inconsistent parsing rules, unpredictable timezone conversions, and the complex code required for even basic operations like adding a few days to a date.
To fix this chaos, massive libraries like Moment.js became the norm. Then Moment got bloated, and we moved to “tree-shakeable” alternatives like Day.js or date-fns. But at the end of the day, we all wanted the same thing: Why can’t JavaScript handle this natively and properly?
This is where the Temporal API enters the picture. It’s not just a polished version of the Date object — it’s a ground-up reconstruction of date and time concepts built with correct mathematics and logic.
A quick note: I mention this at the end of the article, but Chrome and Firefox now have Temporal support, so you can try the code examples in your browser console.
1. Immutability
Date’s greatest sin was that objects were mutable. If you performed operations on a date without copying it first, you’d corrupt the original data. This led to hard-to-track bugs, especially in large applications.
Old Way (Date - BUG):
const appointment = new Date('2024-01-10');
const reminder = appointment; // Reference copied!
reminder.setDate(reminder.getDate() - 1); // Move back 1 day
console.log(appointment.toISOString()); // 2024-01-09 — Original appointment corrupted.
Temporal Way (Immutable - CORRECT): Temporal is inherently immutable. Every operation always returns a new value.
const appointment = Temporal.PlainDate.from('2024-01-10');
const reminder = appointment.subtract({ days: 1 });
console.log(appointment.toString()); // 2024-01-10 (Still the same, safe)
console.log(reminder.toString()); // 2024-01-09 (New object)
2. Separation of “Time” and “Date” (Type Safety)
The Date object always contains everything: Year, Month, Day, Hour, Minute… But sometimes when you only need a birthday, the time information is unnecessary, and due to timezone shifts, the 10th in one country might show as the 9th in another.
Temporal separates concerns into clear objects based on use case (machine time vs. human time):
-
Temporal.Instant: An exact point in time (machine time). Ideal for timestamps returned from APIs.
const instant = Temporal.Now.instant(); console.log(instant.toString()); // Output: 2026-01-24T06:30:00.000000000Z -
Temporal.PlainDate: Date only (1938-11-10). No timezone or time info. For birthdays and special dates.
const date = Temporal.PlainDate.from('1938-11-10'); console.log(date.toString()); // Output: 1938-11-10 -
Temporal.PlainTime: Time only (09:05). For “Set alarm at 08:00 every day.”
const time = Temporal.PlainTime.from('09:05'); console.log(time.toString()); // Output: 09:05:00 -
Temporal.ZonedDateTime: Full date and time in a specific timezone. Automatically manages DST (Daylight Saving Time) rules.
const zdt = Temporal.Now.zonedDateTimeISO('Europe/Istanbul'); console.log(zdt.toString()); // Output: 2026-01-24T09:30:00+03:00[Europe/Istanbul] -
Unlike Date, months now start from 1.
Temporal.PlainDate.from({ year: 2025, month: 1, day: 1 });
3. Math and Overflow Management
Adding “1 month” to a date is harder than you think. What happens if you add 1 month to January 31? February 28? March 2? The Date object “overflows” here, pushing you to March 2nd. Temporal gives you control.
const endOfJan = Temporal.PlainDate.from('2024-01-31');
// Default: Constrains to the nearest valid day
const endOfFeb = endOfJan.add({ months: 1 });
console.log(endOfFeb.toString()); // 2024-02-29 (Leap year)
// You can also make it throw an error
const noOverflow = endOfJan.add({ months: 1 }, { overflow: 'reject' });
4. The Timezone Nightmare and RFC 9557
Date only knows two things: UTC and your local time. Performing operations in another country’s time zone is impossible without a library. Temporal natively uses the IANA timezone database.
const now = Temporal.Now.instant();
// Convert to New York and Istanbul time
const nyTime = now.toZonedDateTimeISO('America/New_York');
const istTime = now.toZonedDateTimeISO('Europe/Istanbul');
console.log(nyTime.toString());
// Output: 2024-01-23T01:51:00-05:00[America/New_York]
This output format (ISO 8601 extension) allows us to store which timezone the data belongs to within a standard string.
Where Are We Now?
Temporal is currently at Stage 3, meaning it’s very close to standardization. Browsers (Chrome 144+, Firefox 139+) have already started adding support. You can start using it now with a polyfill for all environments:
npm install @js-temporal/polyfill
Temporal in Backend
Deno currently supports the Temporal API as unstable. It will be stable very soon. Deno Temporal Docs #31873 #31928
On the Node.js side, from what I understand, they’re waiting for Rust support. Node.js Temporal Issue
On the Bun side, I couldn’t figure out what’s going on :D Bun Temporal Issue
This article was written as a result of research sparked by seeing this tweet.