JavaScript has come a long way in terms of language features, but one area that has notoriously lagged behind is date and time handling. The ECMAScript Date object has been a source of confusion and frustration due to its inconsistent behaviors, mutability, tricky timezone handling, and limitations. This leaves many developers relying on third-party libraries to perform even fundamental date calculations.
Fortunately, we have champions who are well-versed of these challenges, leading to the Temporal API proposal. Having reached Stage 3 in the ECMAScript standardization process, Temporal offers a modern, robust, and ergonomic alternative for working with dates, times, time zones, and durations.
What’s Wrong with Date?
Before discussing what Temporal brings to the table, it’s important to understand where the existing Date object falls short:
// Beware: month is zero-based!
const date = new Date(2022, 0, 15); // January 15, 2022
// Adding 2 days mutates the original object
date.setDate(date.getDate() + 2);
// Printing in different time zones affects only formatting, not the underlying datetime
const options = { timeZone: 'America/New_York' };
console.log(new Date().toLocaleString('en-US', options));
// The internal date/time remains in the local timezone or UTC
Common pain points include:
- Zero-indexed months, a recurring source of off-by-one errors.
- Mutable date objects, which can lead to bugs as they’re modified in-place.
- Poor support for explicit time zone calculations.
- Inconsistent parsing behavior, especially with ISO strings.
- Ambiguities when dealing with daylight saving or leap seconds.
Due to these issues, developers have been forced to adopt external libraries like Moment.js, date-fns, Day.js, or Luxon, increasing project complexity and bundle sizes.
Introducing the Temporal API
The Temporal API addresses these frustrations with a comprehensive, immutable, and explicit suite of types and methods tailored for different date/time use cases.
Here’s a basic example:
import { Temporal } from '@js-temporal/polyfill';
// Creating a calendar date (note: months are intuitive, starting at 1)
const date = Temporal.PlainDate.from({ year: 2022, month: 1, day: 15 });
// Date math returns new instances, leaving the original unmodified
const newDate = date.add({ days: 2 });
console.log(date.toString()); // 2022-01-15
console.log(newDate.toString()); // 2022-01-17
// Zoned current date/time retrieval
const nowInNY = Temporal.Now.zonedDateTimeISO('America/New_York');
const nowInTokyo = Temporal.Now.zonedDateTimeISO('Asia/Tokyo');
Key Temporal types:
PlainDate: Dates without time and without timezone info.PlainTime: Time-of-day components, no date.PlainDateTime: Date and time, without timezone.ZonedDateTime: Date/time with timezone, essential for accurate calendar events worldwide.PlainYearMonth: Year and month, useful for billing cycles.PlainMonthDay: Month and day (no year), great for birthdays.Instant: A specific point in UTC time.Duration: A length of time, without a start or end.
This explicit modeling makes it easy to pick the right type based on your needs and avoids confusing conversions.
Practical Examples
Calculating Date Differences
// With Date (imperative and error-prone)
const start = new Date(2022, 0, 1);
const end = new Date(2022, 0, 15);
const diffDays = Math.floor((end - start) / (1000 * 60 * 60 * 24));
console.log(diffDays); // 14
// With Temporal (simpler and more expressive)
const startDate = Temporal.PlainDate.from({ year: 2022, month: 1, day: 1 });
const endDate = Temporal.PlainDate.from({ year: 2022, month: 1, day: 15 });
const diff = startDate.until(endDate);
console.log(diff.days); // 14
Time Zone Conversion
// Create New York meeting time
const meeting = Temporal.ZonedDateTime.from({
timeZone: 'America/New_York',
year: 2022, month: 1, day: 20,
hour: 15, minute: 30
});
// Convert to Tokyo’s time zone
const meetingInTokyo = meeting.withTimeZone('Asia/Tokyo');
console.log(meeting.hour); // 15 (3 PM NY time)
console.log(meetingInTokyo.hour); // 5 (5 AM the next day in Tokyo)
Date Arithmetic
const today = Temporal.Now.plainDateISO();
const nextWeek = today.add({ days: 7 });
const lastMonth = today.subtract({ months: 1 });
// Getting a duration between two dates
const interval = today.until(nextWeek);
console.log(interval.days); // 7
Marching ahead
JavaScript’s Temporal API proposal offers a long-needed, modern approach for handling date and time that addresses the shortcomings of the classic Date object. It stands to reduce reliance on third-party libraries, improve developer experience, and avoid many tough bugs related to time zones and date arithmetic.
While it awaits full adoption into the language and browser implementations, developers can explore it today using the official polyfill to modernize their codebases and prepare for the future.
I’m hopeful that browser vendors and JavaScript engine implementers will prioritize Temporal and add support in the near future. Unlike some Stage 3 proposals that have languished for years (looking at you, proper tail calls which remain Safari-only after years in the spec), Temporal addresses such a fundamental developer need that it deserves rapid adoption.
The TC39 committee has done stellar work crafting a robust solution to date/time handling, now we need implementers to bring it to life in V8, SpiderMonkey, and JavaScriptCore. Until then, the polyfill provides an excellent bridge that lets us start enjoying these benefits immediately.
If you work with dates and times regularly in JavaScript, getting familiar with Temporal is a smart investment that will pay dividends when native implementations arrive.