ES2026 Temporal API: The End of Date Suffering in JavaScript
Hello HaWkers, after years of waiting and development, the Temporal API is finally part of ECMAScript 2026. This new API promises to solve the historical problems that every JavaScript developer has faced when working with dates and times.
Let's explore what changes, how to use it, and why you'll want to abandon the Date object as soon as possible.
The Problem With Date
Why Date is So Bad
JavaScript's Date object was copied from Java 1.0 in 1995 and inherited all its problems. Java itself has already deprecated most of the original methods.
Classic problems:
// Months start at 0 (January = 0, not 1)
const date = new Date(2026, 0, 15); // January 15, not February
// Unexpected mutability
const original = new Date();
const copy = original;
copy.setMonth(5); // Oops, changed the original too!
// Inconsistent parsing between browsers
new Date('2026-01-15'); // May be interpreted differently
// Timezone hell
const local = new Date();
const utc = new Date(local.toISOString());
// What's the difference? Depends on where you are
// Strange math operations
const d1 = new Date(2026, 0, 31);
d1.setMonth(1); // Should it be February 31?
// Actually becomes March 3 (automatic overflow)Why libraries exist:
Moment.js, date-fns, Luxon, and Day.js exist precisely because Date is inadequate for serious work. But adding dependencies for something so fundamental has always been suboptimal.
The Temporal API
Fundamental Concepts
The Temporal API introduces several new types, each with a specific purpose.
Main types:
| Type | Purpose | Example |
|---|---|---|
Temporal.Instant |
Exact moment in time (UTC) | Log timestamp |
Temporal.ZonedDateTime |
Date/time with timezone | Meeting time |
Temporal.PlainDateTime |
Date/time without timezone | Generic local time |
Temporal.PlainDate |
Date only | Birthday |
Temporal.PlainTime |
Time only | Alarm |
Temporal.Duration |
Duration | 2 hours and 30 minutes |
Fundamental principle:
"Each type represents exactly what you need - nothing more, nothing less."
Immutability
All Temporal objects are immutable. Operations return new objects.
// Temporal - immutable and safe
const date = Temporal.PlainDate.from('2026-01-15');
const nextMonth = date.add({ months: 1 });
console.log(date.toString()); // 2026-01-15 (didn't change!)
console.log(nextMonth.toString()); // 2026-02-15
// Comparison with Date - mutable and dangerous
const oldDate = new Date('2026-01-15');
oldDate.setMonth(oldDate.getMonth() + 1);
// Original was modified!
Practical Examples
Working With Dates
Common operations become much more intuitive.
// Create a date
const birthday = Temporal.PlainDate.from('2026-06-15');
// or
const birthday2 = Temporal.PlainDate.from({
year: 2026,
month: 6,
day: 15
});
// Add/subtract time
const inOneWeek = birthday.add({ weeks: 1 });
const threeDaysAgo = birthday.subtract({ days: 3 });
// Compare dates (finally makes sense!)
const today = Temporal.Now.plainDateISO();
if (Temporal.PlainDate.compare(birthday, today) > 0) {
console.log('Birthday has not arrived yet');
}
// Calculate difference
const daysUntilBirthday = today.until(birthday, {
largestUnit: 'day'
});
console.log(`${daysUntilBirthday.days} days left`);Working With Timezones
Timezones are no longer a nightmare.
// Create date/time with timezone
const meeting = Temporal.ZonedDateTime.from({
year: 2026,
month: 1,
day: 27,
hour: 15,
minute: 0,
timeZone: 'America/New_York'
});
console.log(meeting.toString());
// 2026-01-27T15:00:00-05:00[America/New_York]
// Convert to another timezone
const meetingInLA = meeting.withTimeZone('America/Los_Angeles');
console.log(meetingInLA.toString());
// 2026-01-27T12:00:00-08:00[America/Los_Angeles]
// Get current time in specific timezone
const nowInTokyo = Temporal.Now.zonedDateTimeISO('Asia/Tokyo');
console.log(`In Tokyo it's ${nowInTokyo.hour}:${nowInTokyo.minute}`);
Working With Durations
Durations have their own representation and intuitive operations.
// Create duration
const runTime = Temporal.Duration.from({
hours: 1,
minutes: 45,
seconds: 30
});
// Operations with duration
const twoRuns = runTime.add(runTime);
console.log(twoRuns.toString()); // PT3H31M
// Round duration
const rounded = runTime.round({
smallestUnit: 'minute',
roundingMode: 'ceil'
});
console.log(rounded.toString()); // PT1H46M
// Calculate duration between two moments
const start = Temporal.PlainTime.from('09:00');
const end = Temporal.PlainTime.from('17:30');
const workday = start.until(end);
console.log(`Workday: ${workday.hours}h ${workday.minutes}min`);Formatting
Formatting is integrated with Intl.
const date = Temporal.PlainDate.from('2026-01-27');
// Formatting with Intl
const usFormat = new Intl.DateTimeFormat('en-US', {
dateStyle: 'full'
});
console.log(usFormat.format(date));
// Tuesday, January 27, 2026
// Custom formatting
const customFormat = new Intl.DateTimeFormat('en-US', {
weekday: 'short',
day: 'numeric',
month: 'short'
});
console.log(customFormat.format(date));
// Tue, Jan 27
Common Use Cases
Event Scheduling
A classic case that Date makes complicated.
// Schedule recurring event
function getNextOccurrences(start, interval, count) {
const occurrences = [];
let current = start;
for (let i = 0; i < count; i++) {
occurrences.push(current);
current = current.add(interval);
}
return occurrences;
}
const firstMeeting = Temporal.ZonedDateTime.from({
year: 2026,
month: 2,
day: 1,
hour: 10,
minute: 0,
timeZone: 'America/New_York'
});
const weeklyMeetings = getNextOccurrences(
firstMeeting,
{ weeks: 1 },
4
);
weeklyMeetings.forEach((m, i) => {
console.log(`Meeting ${i + 1}: ${m.toPlainDate().toString()}`);
});
// Meeting 1: 2026-02-01
// Meeting 2: 2026-02-08
// Meeting 3: 2026-02-15
// Meeting 4: 2026-02-22Age Calculation
Another case that Date makes unnecessarily complicated.
function calculateAge(birthDate) {
const birth = Temporal.PlainDate.from(birthDate);
const today = Temporal.Now.plainDateISO();
const duration = birth.until(today, {
largestUnit: 'year'
});
return {
years: duration.years,
months: duration.months,
days: duration.days
};
}
const age = calculateAge('1990-05-15');
console.log(`${age.years} years, ${age.months} months and ${age.days} days`);
Migration from Date and Libraries
From Date to Temporal
How to convert existing code.
// Date to Temporal
const oldDate = new Date();
// To Instant (exact moment)
const instant = Temporal.Instant.fromEpochMilliseconds(
oldDate.getTime()
);
// To ZonedDateTime (with local timezone)
const zdt = instant.toZonedDateTimeISO(
Temporal.Now.timeZoneId()
);
// To PlainDate (date only)
const plainDate = zdt.toPlainDate();
// Temporal to Date (when needed for old APIs)
const backToDate = new Date(
instant.epochMilliseconds
);From Moment/Day.js to Temporal
// Moment.js
// moment('2026-01-27').add(1, 'month').format('YYYY-MM-DD')
// Temporal equivalent
Temporal.PlainDate.from('2026-01-27')
.add({ months: 1 })
.toString();
// Day.js
// dayjs('2026-01-27').diff(dayjs('2026-01-01'), 'day')
// Temporal equivalent
Temporal.PlainDate.from('2026-01-01')
.until(Temporal.PlainDate.from('2026-01-27'))
.total('day');
Support and Polyfills
Implementation Status
As of January 2026, support is expanding.
Browsers:
| Browser | Status |
|---|---|
| Chrome | Available (flag until v130, default v135+) |
| Firefox | In implementation |
| Safari | Preview available |
| Edge | Follows Chrome |
Runtimes:
| Runtime | Status |
|---|---|
| Node.js | Available v23+ |
| Deno | Available v2.0+ |
| Bun | Available v1.2+ |
Using Polyfill
For environments without native support.
// Installation
// npm install @js-temporal/polyfill
// Usage
import { Temporal } from '@js-temporal/polyfill';
// Code works identically to native
const today = Temporal.Now.plainDateISO();
console.log(today.toString());Conclusion
The Temporal API represents one of the biggest improvements to JavaScript in years. After decades suffering with the Date object, we finally have a date and time API that makes sense, is immutable, and properly supports timezones.
Key points:
- Temporal API is part of ES2026 and is arriving in browsers
- Multiple types for different needs (Instant, ZonedDateTime, PlainDate, etc.)
- Immutability by default prevents common bugs
- Timezones work correctly
- Polyfills available for immediate use
For JavaScript developers, the recommendation is: start using Temporal as soon as possible, whether via polyfill or in environments with native support. It's one of those changes that, once adopted, you'll never want to go back.
For more on new JavaScript features, read: Pattern Matching in JavaScript: The TC39 Proposal That Will Change Your Code.

