We all know the pain of working with dates in Javascript. It needs to be more explicit, has almost no method, and could not be more clunky. For example, to create the Date January 1, 2023, you have to write a new Date (2023, 0, 1)
, which can be confusing for beginners, and overall just not that clear. And because of these reasons, the community has made many libraries that attempt to make Date easier to work with over the past years.
Like momentjs or Date-fns. But the good news is that you won’t need these third-party libraries any longer. The Temporal Date API in Javascript attempts to completely replace the Date object and fix all of the issues we generally face when working with dates.
Introduction of Temporal Date API
Temporal API brings TONS of new data types and methods for handling various date-based concerns. The API has a unique global object called Temporal
that allows you to have an easy-to-use API for date and time computations.
The main goal of the temporal API is to provide more support for things like dates without times, times without dates, timezones, PlainDate, PlainTime, and PlainDateTime objects which don’t have an association with a time zone.
In this article, I will cover the essential parts of the API. However, I can’t go through everything about the API as It is massive. So if you want to go more in-depth on the API, you can read the full docs.
💡 NOTE: Throughout the article, I’ve used a lot of examples. But I needed help to come up with these examples. So instead, I’ve used an AI platform called Bito AI.
Bito AI
We all know the effectiveness of ChatGPT and its usefulness in programming. However, to use chatGPT, we need to visit their website. It can be frustrating when coding because it destroys momentum. What if you could add ChatGPT directly in your code editor? Bito AI does just that.
It uses ChatGPT in the background to generate code, helps to debug, write test cases, generate code comments, create code explanations, and more. Bito AI even contains a lot of shortcut options and keyboard shortcuts. They also do a lot of on-the-fly engineering to give you the best answer without any work on your part.
In general, it’s ChatGPT, but specifically for coding purposes. I recently started using it and love it!
Temporal API Data Types
The Temporal API has different data types, divided into plain and zoned versions. Plain date/time helps represent a date/time without caring about the timezone. In contrast, a zoned date and time type are helpful when dealing with the exact time in a particular time zone.
Temporal.Instant
A Temporal.Instant
represents a specific point in time and does not consider any particular calendar or location.
const today = Temporal.Now.instant()
console.log(today.toString())
// 2023-02-07T20:17:35.306655305Z
const date = Temporal.Instant.from("2023-02-07-06:00")
console.log(date.toString())
// 2022-01-01T06:00:00Z
Temporal.ZonedDateTime
A Temporal.ZonedDateTime
is a date/time object that contains all timezone-related information. It represents an actual event that has happened (or will happen) at a particular exact time from the perspective of a specific region on Earth.
const today = Temporal.Now.zonedDateTimeISO()
console.log(today.toString())
// 2023-02-07T14:17:35.306655305[America/Chicago]
const persian = Temporal.Now.ZonedDateTime("persian")
console.log(persian.toString())
// 2022-02-21T14:17:35.306655305[America/Chicago][u-ca=persian]
const date1 = Temporal.ZonedDateTime.from("2022-01-01")
console.log(date1.toString())
// 2022-01-01T00:00:00-06:00[America/Chicago]
const date2 = Temporal.ZonedDateTime.from({ year: 2022, month: 1, day: 1 })
console.log(date2.toString())
// 2022-01-01T00:00:00-06:00[America/Chicago]
const zonedDateTime = Temporal.ZonedDateTime.from({
timeZone: 'America/Los_Angeles',
year: 1995,
month: 12,
day: 7,
hour: 3,
minute: 24,
second: 30,
millisecond: 0,
microsecond: 3,
nanosecond: 500
}); // => 1995-12-07T03:24:30.0000035-08:00[America/Los_Angeles]
Temporal.PlainDate
A Temporal.PlainDate
object represents a date with no other information. It is not associated with a particular time or time zone.
const today = Temporal.Now.plainDateISO()
console.log(today.toString())
// 2023-02-07
const date = Temporal.PlainDate.from({ year: 2023, month: 2, day: 7 });
date.year; // => 2023
date.toString(); // => 2023-02-07
Temporal.PlainTime
A Temporal.PlainTime
object represents a time that is not associated with any timezone and date.
const today = Temporal.Now.plainTimeISO()
console.log(today.toString())
// 2023-02-07T14:17:35.306655305
const time1 = Temporal.PlainTime.from("04:03:25")
console.log(time1.toString())
// 04:03:25
const time2 = Temporal.PlainTime.from({ hour: 4, minute: 3, second: 25 })
console.log(time2.toString())
// 04:03:25
Temporal.PlainDateTime
The Temporal.PlainDateTime
object represents a date and time with no timezone information. You can create a new PlainDateTime by using the Temporal.Now.plainDateTimeISO
method.
const today = Temporal.Now.plainDateTimeISO()
console.log(today.toString())
// 2023-02-07T14:17:35.306655305
If you are working with a different calendar system than ISO 8601 then you can do the following
const today = Temporal.Now.plainDateTime("persian")
console.log(today.toString())
// 2023-02-07T14:17:35.306655305[u-ca=persian]
If you want to create a new PlainDateTime
with a custom date instead of just using the current time, there are two ways to do so.
(1) The first way is to give the method a date by specifying the year, month, and date. It can even have an hour, minute, second, millisecond, microsecond, nanosecond, and calendar.
const date = new Temporal.PlainDateTime(2023, 4, 1)
console.log(date.toString())
// 2023-04-01T00:00:00
(2) Alternatively, you can use from
method on the PlainDateTime
object instead. This method takes either a string that matches a date or an object with keys for each part of the date.
const date1 = Temporal.PlainDateTime.from("2022-05-06")
console.log(date1.toString())
// 2022-05-06T00:00:00
const date2 = Temporal.PlainDateTime.from({ year: 2022, month: 4, day: 1 })
console.log(date2.toString())
// 2022-04-01T00:00:00
Temporal.PlainMonthDay
Temporal.PlainMonthDate
works like a PlainDate
, However, It excludes any year information.
const date1 = Temporal.PlainMonthDay.from("01-01")
console.log(date1.toString())
// 01-01
const date2 = Temporal.PlainMonthDay.from({ month: 1, day: 1 })
console.log(date2.toString())
// 01-01
Temporal.Duration
Temporal.Duration
represents a duration of time. It is helpful for date/time arithmetic.
const duration = Temporal.Duration.from({
hours: 130,
minutes: 20
});
duration.total({ unit: 'second' }); // => 469200
You can even use arithmetic like add
, subtract
, with
, and round
(I explained them later in the article) methods on durations. There are even a few additional helper methods that you should know.
const duration = Temporal.Duration.from({ hours: 200, minutes: 17 })
console.log(duration.negated().toString())
// -PT200H17M
console.log(duration.negated().abs().toString())
// PT200H17M
console.log(duration.total("minutes"))
// 12017
Temporal.TimeZone
The Temporal.TimeZone
represents a particular timezone. You use this with the from
method or with the Temporal.Now.timeZone
method.
const timeZone = Temporal.TimeZone.from('Africa/Cairo')
console.log(timeZone.toString())
// Africa/Cairo
const localTimeZone = Temporal.Now.timeZone()
console.log(localTimeZone.toString())
// America/Chicago
Temporal.Calendar
Temporal.Calendar
represents a calendar system. You can create a calendar using the from
method.
const cal = Temporal.Calendar.from('iso8601');
const date = cal.dateFromFields({ year: 1999, month: 12, day: 31 }, {});
date.monthsInYear; // => 12
date.daysInYear; // => 365
Temporal API Helper Methods
By now, we’ve seen a lot of data types. However, there are also a bunch of helper functions you can use to compare dates, use arithmetic with dates like add/subtract, convert types, and much more.
add and subtract
As It sounds, these two methods let you do addition and subtraction with two or more dates.
const today = Temporal.Now.plainDateISO() // 2022-02-07
console.log(today.add({ days: 4, months: 2 }).toString())
// 2022-04-11
It also deals with overflow. For example, if the current date is February 7th, 2023
, and you want to add one month to it, the resulting date should have been March 7th, 2023
. However, the month of March doesn’t yet exist. Therefore, these results default to the nearest valid date, which would return February 28th. You can disable this behavior, though, with a second options argument.
const date = Temporal.PlainDate.from("2022-01-31")
console.log(date.add({ months: 1 }).toString())
// 2022-01-28
date.add({ months: 1 }, { overflow: "restrict" })
// Uncaught RangeError: value out of range: 1 <= 31 <= 28
since and until
The since
and until
methods determine the distance between two temporal date objects.
const today = Temporal.Now.plainDateISO()
const yesterday = today.subtract({ days: 1 })
console.log(today.since(yesterday).toString())
// P1D
The value returned by these methods is Temporal.Duration
object. You can even pass options to these methods to fine-tune how you want the duration calculated.
If you specify the largestUnit
, then the duration will be determined using that unit as the most significant value instead of the default value.
const today = Temporal.Now.plainDateISO()
const lastMonth = today.subtract({ months: 1, days: 4 })
console.log(today.since(lastMonth).toString())
// P35D
console.log(today.since(lastMonth, { largestUnit: "months" }).toString())
// P1M4D
In the same way, the smallestUnit
determines the duration by that unit as the smallest value instead of the default value. However, it could result in rounding, which you can further customize with the roundingIncrement
and roundingMode
options.
const today = Temporal.Now.plainDateISO()
const lastMonth = today.subtract({ months: 3, days: 4 })
console.log(today.since(lastMonth).toString())
// P96D
console.log(today.since(lastMonth, { smallestUnit: "months" }).toString())
// P3M
console.log(today.since(lastMonth, { smallestUnit: "months", roundingIncrement: 2 }).toString())
// P2M
console.log(today.since(lastMonth, { smallestUnit: "months", roundingMode: "ceil" }).toString())
// P4M
equals
Temporal.PlainMonthDate
works like a PlainDate
, However, It excludes any year information.
The equals method returns true only if the two temporal date objects have the same fields. Using == or === will return false unless the two objects are the same instance.
with
The with
method takes an object of fields to overwrite the current temporal date object. However, it doesn’t overwrite the original date object. Instead, It returns a new temporal date object with the new value specified.
const today = Temporal.Now.plainDateISO()
console.log(today.with({ year: 2023, month: 3 }).toString())
// 2023-03-21
round
This method rounds a temporal date to a specific unit.
const today = Temporal.Now.plainDateTimeISO()
console.log(today.round("hour").toString())
// 2022-02-22T14:00:00
To modify the performance of the round method, you can pass an object that takes smallestUnit
, roundingIncrement
, and roundingMode
.
const today = Temporal.Now.plainDateTimeISO()
console.log(today.round({ smallestUnit: "hour" }).toString())
// 2022-02-22T14:00:00
console.log(today.round({ smallestUnit: "hour", roundingMode: "ceil" }).toString())
// 2022-02-22T15:00:00
console.log(today.round({ smallestUnit: "hour", roundingIncrement: 6 }).toString())
// 2022-02-22T12:00:00
Browser Support
I love Temporal Date API. However, this API has yet to be available as it is still in proposal stage 3. No browsers currently support this API, but you can use a polyfill if you want to start using this API today. Multiple polyfills are available for this API, but my favorite is @js-temporal/polyfill. Once you install this library, you can immediately start using the temporal API.
Conclusion
Everyone used to hate working with Dates in JavaScript, but with the introduction of the temporal API working with dates will be something you can enjoy doing.