How to Use the New Temporal API for Date Handling in JavaScript

Web developers can finally sigh relief as Javascript temporals got shipped in small experimental releases. Temporals are poised to phase out the Date object that had been used since Javascript was written in 1993.

The Date object has many pain points, including its mutability, gregorian-only calendar support and error-prone string-to-date parsing. These limitations usually put the software at high risk of bugs. As a result, most developers have resorted to alternative tools like moment.js and date.fns. How about finally having a JS Object and method that can date any way you’d like? Javascript Temporal provides a way out.

So, Why Javascript Temporal?

Despite its limitations, legacy Date objects can’t be changed since millions of databases and APIs used today are built with workarounds on it. Hence, a more realistic approach is to create a new Date API from scratch, which temporal is.

First, Javascript temporals separate concerns and bring consistency to date handling. But beyond that, it also introduces clear naming conventions for every use case, which means you don’t have to tweak one legacy code to fit all solutions while ensuring you have a happier day coding.

Limitations of the Date Object and How Temporals Addresses Them.

  1. Inconsistent Zero-based and One-based Indexing

The date object uses zero-based indexing for Months(0 for January and 11 for December) and 1 based for days (day 1 - day 31). This can lead to confusion or off-by-one errors, especially for those expecting 1 to represent January.

Here is an example where 1 is passed to month and it returns February.

const date = new Date(2024, 1, 24); // Month is 1 → February (0 = January)

console.log(date.toDateString());   // "Sat Feb 24 2024"

console.log(date.getMonth());       // 1 (February)
  • Temporals Use only One-based Indexing.

However, Temporals start all dates indexing from one. 1 connote January while 12 connotes December as shown in the example below. This gives better clarity

const temporalDate = Temporal.PlainDate.from({ year: 2024, month: 2, day: 24 }); // month: 2 → February

console.log(temporalDate.toString());  // "2024-02-24"

console.log(temporalDate.month);       // 2 (February)
  1. Mutability

Original date objects are mutable, that is, any method or function run on a date object reflects the result on it instead of returning a new instance. In the example below, we mutate the original date object by passing a setMonth() method which automatically changes the Month from January to February.

const firstDayInYear = new Date("January 1, 2025")

firstDayInYear.setMonth(1)

console.log(firstDayInYear) // returns February 01 2025

This mutability behaviour can lead to unexpected issues as shown in the code below.

function nextDay(myDate) {

    myDate.setDate(myDate.getDate() + 1);

    return myDate;
}

const today = new Date()

const tomorrow = nextDay(today)

console.log(today) // returns Feb 25 2025

console.log(tomorrow) // returns Feb 25 2025

As you can see, the today variable, assigned the new Date() object, returns values processed by the nextDay() function because it is mutable. Hence it changes the original new Date() object.

  • Temporals Are Immutable

Unlike legacy date, temporals return a new copy of the original date when called in a function.

const newDate = date.add({ days: 5 }); // Returns a new instance

console.log(newDate.toString()); // "2024-02-29"

console.log(date.toString());    // "2024-02-24" (Original remains unchanged)
  1. UTC, Local Time and Wall Calendars

This is the most absurd of all. When converting dates with the string() method, it returns UTC when the hours and seconds are specified and local time when they are not. This is at best, inconsistent and creates very unstable applications where you want to set certain expectations.

new Date('2024-11-12').toISOString();
// "2024-11-12T00:00:00.000Z"

new Date('2024-11-12T14:45').toISOString();
// "2024-11-12T21:45:00.000Z"

Besides, you cannot set wall-calendar times that reference a static point in history. Examples include your date of birth or Christmas Holiday date.

  • Temporals Separate Concerns with Explicit Local Time and UTC Parsing:

Temporals enable explicitly stating when using local time zones, UTC or even plain dates and times. In the code below, the Temporal.PlainDateTime displays the date and time as written.

const dateTime = Temporal.PlainDateTime.from('2024-02-24T00:00');

console.log(dateTime.toString()); // "2024-02-24T00:00:00"

Then, the Temporal.ZonedDateTime turns a plain date and time into a specified time zone including the UTC.

const plainDateTime = Temporal.PlainDateTime.from('2024-02-24T00:00');

// Attach a time zone (e.g., UTC)
const utcDateTime = plainDateTime.toZonedDateTime({ timeZone: 'UTC' });

console.log(utcDateTime.toString()); // "2024-02-24T00:00:00+00:00[UTC]"

// Attach a different time zone (e.g., America/New_York)
const nyDateTime = plainDateTime.toZonedDateTime({ timeZone: 'America/New_York' });

console.log(nyDateTime.toString());  // "2024-02-24T00:00:00-05:00[America/New_York]"

// Attach another time zone (e.g., Asia/Tokyo)
const tokyoDateTime = plainDateTime.toZonedDateTime({ timeZone: 'Asia/Tokyo' });

console.log(tokyoDateTime.toString()); // "2024-02-24T00:00:00+09:00[Asia/Tokyo]"
  1. Non-Gregorian Calendars:

You can’t set non-gregorian calendars like Chinese or Japanese New Year or Hijri calendar on the Date object.

  • Temporals Support Different Calendars

In the example below, the Temporal API is used to set a Hijri calendar to calculate the next Eid date.

const eventDate = Temporal.PlainDate.from('2024-04-10'); // Gregorian date for Eid estimate

const hijriEventDate = hijriFormatter.format(eventDate.toString());

console.log(`Eid Estimate (Hijri): ${hijriEventDate}`);  
// Example: "Wednesday, Ramadan 30, 1445 AH"

Understanding Temporal Core Components

The temporal object has several classes housing over 200 individual methods, with each tailored to very specific use cases. While each is at different stages of production, these classes and methods below are just enough to get you going.

Temporal.PlainDateTime:

As suggested by its naming conventions, this method generates one-off date and time, without timezones. These dates are ISO 8601 calendar dates. That is, they don’t change based on time zones, daylight saving time or UTC. For instance, as a plain date, it might be used to connote a popular holiday, an upcoming event or a billing cycle.

const dateTime = Temporal.PlainDateTime.from('2024-02-24T14:30:00');

console.log(dateTime.toString());  // "2024-02-24T14:30:00"

console.log(dateTime.year);        // 2024

console.log(dateTime.month);       // 2

console.log(dateTime.day);         // 24

console.log(dateTime.hour);        // 14

console.log(dateTime.minute);      // 30

The other variations of Temporal.PlainDateTime includes

Temporal Class

Purpose

Example Input

Use Case

Temporal.PlainDate

Date only

"2024-02-24"

Birthdays, deadlines

Temporal.PlainTime

Time only

"14:30:15.123"

Meeting times, alarms

Temporal.PlainDateTime

Date and time

"2024-02-24T14:30"

Timestamps, event scheduling

Temporal.PlainMonthDay

Month and day (no year)

"--02-24"

Recurring annual events

Temporal.PlainYearMonth

Year and month (no day)

"2024-02"

Billing cycles, monthly logs

  1. Temporal.PlainDateTime:

Set a static string for dates, affected by no timezone, DST or UTC. Example

const date = Temporal.PlainDate.from('2024-02-24');

console.log(date.toString());  // "2024-02-24"

console.log(date.year);        // 2024

console.log(date.month);       // 2

console.log(date.day);         // 24
  1. Temporal.PlainTime

Set one-off plain times that can’t be affected by time zones. For example, your “8 am Wake Up Time” is the same anywhere in the world, For instance,

const time = Temporal.PlainTime.from('14:30:15.123');

console.log(time.toString());  // "14:30:15.123"

console.log(time.hour);        // 14

console.log(time.minute);      // 30

console.log(time.second);      // 15

console.log(time.millisecond); // 123
  1. Temporal.PlainMonthDay

Used for setting static year and month without a day. Particularly useful for recurring yearly events. Here is an example

const monthDay = Temporal.PlainMonthDay.from('--02-24');  // Note the double dash

console.log(monthDay.toString()); // "--02-24"

console.log(monthDay.month);      // 2

console.log(monthDay.day);        // 4
  1. Temporal.PlainYearMonth

Sets a year and Month without a day or timezone. This is particularly good for Monthly routines like billing cycles.

const yearMonth = Temporal.PlainYearMonth.from('2024-02');

console.log(yearMonth.toString()); // "2024-02"

console.log(yearMonth.year);       // 2024

console.log(yearMonth.month);      // 2

2. Temporal.ZonedDateTime

Handles timestamps by combining date and time with time zones. Unlike plain dates, it renders dates and times based on the preset local time within a location. Standards expect you to declare dates with Temporal.PlainDateTime and then convert it to time-zoned format with Temporal.ZonedDateTime, as shown in the example below.

const plainDateTime = Temporal.PlainDateTime.from('2024-02-24T14:30');

const zonedDateTime = plainDateTime.toZonedDateTime({ timeZone: 'America/New_York' });

console.log(zonedDateTime.toString());  // "2024-02-24T14:30:00-05:00[America/New_York]"

console.log(zonedDateTime.offset);      // "-05:00"

It can also be converted to UTC. Below, we first converted a plain date and time to local New York time, then to a UTC.

const plainDateTime = Temporal.PlainDateTime.from('2024-02-24T14:30');
// Attach New York time zone

const nyZonedDateTime = plainDateTime.toZonedDateTime({ timeZone: 'America/New_York' });

console.log(nyZonedDateTime.toString());  
// "2024-02-24T14:30:00-05:00[America/New_York]"


// Convert to UTC
const utcZonedDateTime = nyZonedDateTime.withTimeZone('UTC');

console.log(utcZonedDateTime.toString());  
// "2024-02-24T19:30:00+00:00[UTC]"

3. Temporal.instant

References a single point in history or point in time. It points to a single time in the past, present or future and is set to UTC. It doesn't carry plain calendar dates or local time unless converted to such.

const now = Temporal.Instant.now();

console.log(now.toString());  
// Example: "2025-02-24T15:45:23.456789123Z"

Temporal.instant works much like the conventional date object. But it is even more precise because it counts nanoseconds. It also doesn't return any inconsistent behaviour often seen in legacy Date.

Real-World Use Cases of Temporal API

  1. Global Applications and Scheduling

Coordinating events across different time zones can be challenging, especially for global organizations. Temporal simplifies international event planning by managing time zone complexities, daylight saving changes, and ensuring scheduled tasks execute reliably.

Imagine a company hosting global team meetings. Using Temporal.ZonedDateTime, meeting invitations are sent at the correct local times, with Temporal.TimeZone handling conversions. If a meeting is rescheduled, Temporal.Duration can be used to inform participants how much time remains until the new start time.

  1. Financial and E-commerce Systems

Handling financial processes and e-commerce operations requires accuracy, reliability, and compliance with strict deadlines. Temporal enables businesses to manage billing cycles, promotions, and transaction deadlines without worrying about date arithmetic edge cases.

A subscription-based service can use Temporal.PlainDateTime.add() to automatically generate future invoice dates. For example, adding a one-month Temporal.Duration to the current invoice date ensures accurate billing cycles, adjusting for month-end discrepancies.

Key Takeaways

The Temporal API is poised to become the go-to solution for date and time handling in JavaScript. Its thoughtful design addresses long-standing issues with Date, making it easier for developers to manage time zones, calendar differences, and formatting complexities. As the ecosystem evolves and more libraries, frameworks, and tools adopt Temporal, developers can expect smoother integrations and more consistent behaviour across projects.

Embracing Temporal now not only simplifies current development but also prepares codebases for long-term scalability. With global applications becoming the norm, accurate time handling is more critical than ever. By adopting Temporal, developers can future-proof their projects, ensuring they meet the demands of increasingly complex, international environments.

Resources for Further Exploration:

0
Subscribe to my newsletter

Read articles from Abdullah Salaudeen directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Abdullah Salaudeen
Abdullah Salaudeen