JavaScript Temporal API- A Fix for the Date API
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:
- The
.subtract()
method to decrement days, months, or years from your date - The
.since()
method to calculate how many days, months, or years have passed since a specified date - The .equals() method to compare if two dates are the same
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:
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:00Temporal.PlainDateTime.from("2021-02-20T05:30");
// 2021-02-20T05:30:00Temporal.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;
// 6Temporal.PlainDate.from("2021-02-06").withCalendar("chinese").day;
// 25Temporal.PlainDate.from("2021-02-06").withCalendar("japanese").day;
// 6Temporal.PlainDate.from("2021-02-06").withCalendar("hebrew").day;
// 24Temporal.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 →