JavaScript Temporal API- A Fix for the Date API

Nathan Sebhastian
Bits and Pieces
Published in
6 min readFeb 23, 2021

--

JavaScript has a bad date handling API because the Date object implementation was copied directly from Java’s Date Class. Java maintainers eventually deprecated many of Date class methods and created the Calendar Class in 1997 to replace it.

But JavaScript’s Date API never got a proper fix, which is why we have the following problems with it today:

  • Date object is mutable
  • Messy API for date and time computations (for example, adding and subtracting days )
  • Only support UTC and the local timezone
  • Parsing date from a string is unreliable
  • No support for non-Gregorian calendars

Currently, there’s no way to fix the bad parts of the Date API because of its widespread use in libraries and browser engines. Changing the way Date API works will most likely break many websites and libraries out there.

The new Temporal API proposal is designed to solve the problems with the Date API. It brings the following fixes to JavaScript date/time manipulation:

  • Creating and dealing only with immutable Temporal objects
  • Straightforward API for date and time computations
  • Support for all timezones
  • Strict date parsing from ISO-8601 format
  • Support for non-Gregorian calendars

Please keep in mind that the Temporal proposal is currently at stage 2, so it’s not ready for production use yet.

Let’s see how Temporal API feature works with code examples. All Temporal API code is created using the Temporal Polyfill.

Immutable date objects

The Date object created through JavaScript’s new Date() construct is mutable, which means you can change the value of the object after its initialization:

let date = new Date("2021-02-20");
console.log(date); // 2021-02-20T00:00:00.000Z
date.setYear(2000);
console.log(date); // 2000-02-20T00:00:00.000Z

Although it may seem harmless, this kind of mutable object will cause bugs when not handled properly. One case may occur when you try to add days to the current date.

For example, here’s a function that adds one week to the current date. Since setDate modifies the object itself, you will end up with two objects holding the same date value:

function addOneWeek(date) {
date.setDate(date.getDate() + 7);
return date;
}
let today = new Date();
let oneWeekLater = addOneWeek(today);
console.log(today);
console.log(oneWeekLater); // same with today

Temporal will fix this problem by providing methods that won’t modify the object directly. For example, here’s how you can add a week using Temporal API:

const date = Temporal.now.plainDateISO();
console.log(date); // 2021-02-20
console.log(date.add({ days: 7 })); // 2021-02-27
console.log(date); // 2021-02-20

As seen in the code above, Temporal provides you with the .add() method that allows you to add days, weeks, months, or years into the current date object, but the method won’t modify the original value.

APIs for date and time computations

The previous Temporal example already shows you the .add() method, which helps you to perform computations on a date object. It’s much easier and more straightforward than the current Date API, which only provides you with methods to get and set your date values.

Temporal also provides you with several more APIs to compute your date values. One example is the until() method, which calculates the difference between the firstDate and the secondDate .

With the Date API, you need to calculate the number of days between two dates manually as follows:

const oneDay = 24 * 60 * 60 * 1000; 
const firstDate = new Date(2008, 1, 12);
const secondDate = new Date(2008, 1, 22);
const diffDays = Math.round(Math.abs((firstDate - secondDate) / oneDay));
console.log(diffDays); // 10

With the Temporal API, you can easily calculate the diffDays with the .until() method:

const firstDate = Temporal.PlainDate.from('2008-01-12');
const secondDate = Temporal.PlainDate.from('2008-01-22');
const diffDays = firstDate.until(secondDate).days;
console.log(diffDays); // 10

Other methods to help you perform computations include:

These APIs will help you to perform computations, so you don’t need to create your own solutions.

Support for all timezones

The current Date API internally tracks time in UTC standard, and it generally produces date objects in the computer’s timezone. There’s no easy way for manipulating the timezone.

One option I found to manipulate the timezone is by using the Date.toLocaleString() method as follows:

let date = new Date();
let tokyoDate = date.toLocaleString("en-US", {
timeZone: "Asia/Tokyo"
});

let singaporeDate = date.toLocaleString("en-US", {
timeZone: "Asia/Singapore",
});
console.log(tokyoDate); // 2/21/2021, 1:36:46 PM
console.log(singaporeDate); // 2/21/2021, 12:36:46 PM

But since this method returns a string, further date and time manipulation requires you to turn the string back into date first.

Temporal API allows you to define the timezone when you create a date using the zonedDateTimeISO method. You can get the current date/time using the .now object:

let tokyoDate = Temporal.now.zonedDateTimeISO('Asia/Tokyo');
let singaporeDate = Temporal.now.zonedDateTimeISO('Asia/Singapore');
console.log(tokyoDate);
// 2021-02-20T13:48:24.435904429+09:00[Asia/Tokyo]
console.log(singaporeDate);
// 2021-02-20T12:48:24.429904404+08:00[Asia/Singapore]

Since the value returned is still a Temporal date, you can further manipulate it with methods from Temporal itself:

let date = Temporal.now.zonedDateTimeISO('Asia/Tokyo');
let oneWeekLater = date.add({weeks: 1});
console.log(oneWeekLater);
// 2021-02-27T13:48:24.435904429+09:00[Asia/Tokyo]

The Temporal API follows a convention of using types, where names that start with “Plain” doesn't have an associated timezone (.PlainDate, .PlainTime, .PlainDateTime) as opposed to .ZonedDateTime

Strict date parsing from ISO-8601 format

The current Date parsing from a string is unreliable because when you pass a date string in ISO-8601 format, the return value will be different depending on whether you pass the timezone offset or not.

Consider the following example:

new Date("2021-02-20").toISOString();
// 2021-02-20T00:00:00.000Z
new Date("2021-02-20T05:30").toISOString();
// 2021-02-20T10:30:00.000Z

The first Date construct above considers the string to be UTC+0 timezone, while the second construct considers the string to be UTC-5 timezone (the timezone I’m currently at) and so it adjusts the returned value to UTC+0 timezone (5:30 UTC-5 equals 10:30 UTC+0)

The Temporal proposal solves this issue by making a distinction between PlainDateTime and ZonedDateTime as follows:

Source: Temporal proposal doc

When you need the date to be timezone-aware, you need to use the ZonedDateTime object. Otherwise, you can use the PlainDateTime object.

By separating the creation of date with and without the timezone, Temporal API helps you to parse the right date/time combination from the provided string:

Temporal.PlainDateTime.from("2021-02-20");
// 2021-02-20T00:00:00
Temporal.PlainDateTime.from("2021-02-20T05:30");
// 2021-02-20T05:30:00
Temporal.ZonedDateTime.from("2021-02-20T05:30[Asia/Tokyo]");
// 2021-02-20T05:30:00+09:00[Asia/Tokyo]

As you can see from the examples above, Temporal API won’t make assumptions about your timezone.

Support for non-Gregorian calendars

Although Gregorian calendar was the most used calendar system in the world, there are times when you may need to use other calendar systems to observe special dates with cultural or religious significance.

The Temporal API allows you to specify the calendar system you want to use with your date/time calculations.

The NPM Polyfill implementation of the calendars are not finished yet, so you need to try withCalendar() method from the Browser Polyfill. Visit the Temporal documentation page and paste the following code into your browser’s console:

Temporal.PlainDate.from("2021-02-06").withCalendar("gregory").day;
// 6
Temporal.PlainDate.from("2021-02-06").withCalendar("chinese").day;
// 25
Temporal.PlainDate.from("2021-02-06").withCalendar("japanese").day;
// 6
Temporal.PlainDate.from("2021-02-06").withCalendar("hebrew").day;
// 24
Temporal.PlainDate.from("2021-02-06").withCalendar("islamic").day;
// 24

All possible calendar values from Intl.DateTimeFormat are expected to be implemented when the proposal is finished.

Conclusion

The Temporal API is a new proposal for JavaScript that promises to offer a modern date/time API for the language. Based on my test with the Polyfill, the API does provide easier date/time manipulation while taking the timezone and calendar differences into account.

The proposal itself is still at stage 2, so if you’re interested to learn more and provide feedback, you can visit Temporal documentation and try out its Polyfill NPM package.

Build with independent components, for speed and scale

Instead of building monolithic apps, build independent components first and compose them into features and applications. It makes development faster and helps teams build more consistent and scalable applications.

OSS Tools like Bit offer a great developer experience for building independent components and composing applications. Many teams start by building their Design Systems or Micro Frontends, through independent components.
Give it a try →

An independently source-controlled and shared “card” component. On the right => its dependency graph, auto-generated by Bit.

--

--

Web Developer and Writer. Sharing what I learn on productivity and success.