JavaScript's upcoming Temporal API and what problems it will solve

Published on

Freelance
The Temporal API

JavaScript will soon have a new feature that many developers are eagerly awaiting. The feature is the Temporal API which will fix many problems and inconveniences of the old Date object.

What's wrong with JavaScript'a Date object?

Before jumping into the Temporal API lets first dig a little bit into the history of Date object and understand what problems it has.

JavaScript's original Date class was introduced in 1995 as part of the initial release of the language. Under intense time pressure (reportedly only 10 days to create JavaScript), Brendan Eich chose to copy the design of Java's early java.util.Date class to fulfill the requirement of date/time handling . This decision was made to "make it like Java", but unfortunately Java's date implementation at the time was deeply flawed. In fact, nearly all of java.util.Date's methods were deprecated and replaced in Java 1.1 (circa 1997) due to design problems . JavaScript, however, became stuck with this inherited API for decades, as web compatibility concerns prevented drastic changes.

From the start, the JavaScript Date object had a number of quirks and design mistakes. One notorious example was the getYear() method, which returned the year minus 1900 (so, the year 2000 would return 100, and 1995 returned 95) . This odd behavior, a remnant of the Java API, led to confusion and potential Year-2000 bugs. It was later effectively replaced by getFullYear() (which returns the actual four-digit year) , and getYear() was deprecated. This early mishap was a sign of deeper issues that would continue to plague date-handling in JavaScript.

Major limitations and inconsistencies of the Date class

Over the years, developers identified numerous limitations, inconsistencies, and bugs in the Date API. Many of these stem from the API's original design and have proven very difficult to fix without breaking the web. Some of the major issues are outlined below:

How does the Temporal API solve the problem

The Temporal API is designed as a comprehensive replacement for Date, tackling its deficiencies head-on. It introduces a suite of new immutable date/time objects and utility methods that cover a wide range of use cases. Some of the key goals and improvements of Temporal include:

The Temporal API has a separation of concepts and introduces the following main classes:

Temporal.PlainDate

Temporal.PlainDate represents just a year-month-day date on the calendar (for example, a birthday or holiday date) without any clock time. This can be used when you need to work with dates in an abstract sense, without scheduling to a specific hour or tying it to a particular time zone.

In the code below, we create a Temporal.PlainDate for May 23, 2025. We then add 7 days to it to get the date one week later, demonstrating date arithmetic. Notice that adding days does not affect any time (since none is present) and the result is a new PlainDate (Temporal objects are immutable):

const date = Temporal.PlainDate.from("2025-05-23"); // Create a date (YYYY-MM-DD) console.log(date.toString()); // "2025-05-23" const oneWeekLater = date.add({ days: 7 }); // Add 7 days to the date console.log(oneWeekLater.toString()); // "2025-05-30"

You can test the code here.

Temporal.PlainTime

Temporal.PlainTime represents a time of day (hours, minutes, seconds, fractions) independent of any specific date. It can be used for daily or clock-based times that recur every day or for representing a specific time of day in an abstract sense

In the code below we create two Temporal.PlainTime instances for a start and end time, then calculate the duration between them. This might represent a work day or the length of an event in hours and minutes:

const startTime = Temporal.PlainTime.from("09:00:00"); // 9:00 AM const endTime = Temporal.PlainTime.from("17:30:00"); // 5:30 PM // Calculate the duration between the two times: const workedDuration = endTime.since(startTime); console.log(workedDuration.toString()); // "PT8H30M" (8 hours 30 minutes)

Temporal.PlainMonthDay

Temporal.PlainMonthDay is the month and day of a calendar date, without a year or time zone. In other words, it is essentially the month-day portion of a full date (like July 4 or December 25) with no associated year. PlainMonthDay is useful for scenarios like:

Because it has no year, a PlainMonthDay is a partial date and has some limitations: it cannot be directly added to or subtracted from (no arithmetic like "+ 1 day") and cannot be meaningfully ordered without a reference year.

Here are few examples of using Temporal.PlainMonthDay:

// Month code + day const md = Temporal.PlainMonthDay.from({ monthCode: "M05", day: 2 }); console.log(md.toString()); // 05-02 // Month + day (only for ISO calendar) const md2 = Temporal.PlainMonthDay.from({ month: 7, day: 1 }); console.log(md2.toString()); // 07-01 // Year + month + day const md3 = Temporal.PlainMonthDay.from({ year: 2021, month: 7, day: 1 }); console.log(md3.toString()); // 07-01

Temporal.PlainYearMonth

Temporal.PlainYearMonth represents the year and month of a calendar date, without a day (no day-of-month) or time zone. In other words, it is essentially the year-month portion of a date. For example, "2025-07" would represent July 2025 as a whole, with no specific day included. This type is useful for cases where you want to work with or refer to an entire month or a year-month combination, rather than a specific date. PlainYearMonth is useful for scenarios like:

Here are some examples of using Temporal.PlainYearMonth:

// Create a PlainYearMonth for July 2023 (2023-07) in the ISO calendar const ym = Temporal.PlainYearMonth.from({ year: 2023, month: 7 }); console.log(ym.toString()); // → "2023-07" // Access year and month properties console.log(`Year: ${ym.year}, Month: ${ym.month}`); // → "Year: 2023, Month: 7" // Query calendar information console.log(`Days in month: ${ym.daysInMonth}`); // → "Days in month: 31" (July 2023 has 31 days) console.log(`Is leap year? ${ym.inLeapYear}`); // → "Is leap year? false" (2023 is not a leap year) // Add and subtract to navigate through months/years console.log(ym.add({ months: 1 }).toString()); // → "2023-08" (one month later is August 2023) console.log(ym.add({ years: 1 }).toString()); // → "2024-07" (one year later is July 2024) console.log(ym.subtract({ months: 7 }).toString()); // → "2022-12" (seven months earlier, wrapping into the previous year) // Compare two YearMonth values const dec2021 = Temporal.PlainYearMonth.from("2021-12"); const jan2022 = Temporal.PlainYearMonth.from("2022-01"); console.log(Temporal.PlainYearMonth.compare(dec2021, jan2022)); // → -1 (2021-12 comes before 2022-01) console.log(dec2021.equals(jan2022)); // → false (they are different year-months) // Converting a PlainYearMonth to a full date by specifying a day: const firstOfMonth = ym.toPlainDate({ day: 1 }); console.log(firstOfMonth.toString()); // → "2023-07-01" (PlainDate for July 1, 2023)

Temporal.PlainDateTime

Temporal.PlainDateTime combines a calendar date and a wall-clock time into a single object. It's essentially an aware local date-time (year, month, day, hour, minute, second, etc.) but not anchored to a specific time zone. For example, it could represent "2025-12-31T23:30" as a local date-time, without saying whether that is 11:30 PM in New York, London, or Tokyo. PlainDateTime for scenarios where you need a complete date-and-time but in a generic or local sense. This is ideal for scheduled events whose actual moment in time depends on a time zone context.

In the code below, we create a Temporal.PlainDateTime for December 31, 2025 at 23:30 (11:30 PM). We then add 1 hour to it. Because PlainDateTime isn't aware of time zones, the arithmetic is simple: adding an hour rolls the time into the next day (January 1, 2026). This shows how date and time components interact in a PlainDateTime:

const localDateTime = Temporal.PlainDateTime.from("2025-12-31T23:30:00"); console.log(localDateTime.toString()); // "2025-12-31T23:30:00" const plusOneHour = localDateTime.add({ hours: 1 }); console.log(plusOneHour.toString()); // "2026-01-01T00:30:00"

Temporal.ZonedDateTime

Temporal.ZonedDateTime is a timestamp that includes a time zone and calendar, effectively pinpointing a unique instant in time while also providing the local date/time representation for a specific zone. Internally, it combines three pieces: an exact Instant in time, a time zone (like America/New_York or Asia/Tokyo), and a calendar system . For example, "2025-12-31T23:30:00-05:00[America/New_York]" is a ZonedDateTime for 11:30 PM in New York time, which corresponds to a specific moment (which would be 04:30 UTC on Jan 1, 2026 in this case). Use ZonedDateTime when you need to represent or compute with exact moments in time with respect to a specific time zone. This is crucial for scheduling systems, calendar events, or any situation where the local time and actual timeline must be coordinated. For example, a flight departure at 9:00 AM Tokyo time on a certain date should be a ZonedDateTime - it knows the exact instant globally, and you can convert it to the local time in another zone if needed.

In this example, we get the current moment as a Temporal.ZonedDateTime in one time zone and then convert it to another time zone. This demonstrates how a ZonedDateTime carries an absolute time that can be expressed in different zones:

// Create date/time in Tokyo time zone: const tokyoTime = Temporal.ZonedDateTime.from({ timeZone: "Asia/Tokyo", year: 2025, month: 5, day: 24, hour: 10, minute: 0, second: 0, }); console.log(tokyoTime.toString()); // "2025-05-24T10:00:00+09:00[Asia/Tokyo]" // Convert the same instant to London time: const sameLondonTime = tokyoTime.withTimeZone("Europe/London"); console.log(sameLondonTime.toString()); // "2025-05-24T02:00:00+01:00[Europe/London]"

The local clock time changed from 10:00 to 02:00 because London is 8 hours behind Tokyo in this scenario. Both tokyoTime and sameLondonTime represent the same moment (same Instant under the hood), just in different regional contexts. This shows how Temporal.ZonedDateTime can be used to convert and compare times across time zones easily.

Temporal.Instant

Temporal.Instant represents a unique moment in time, with up to nanosecond precision. It's essentially an absolute time value independent of calendars or time zones. It is stored as a count of nanoseconds since the Unix epoch (1970-01-01T00:00:00Z)Instant can be used when you need to work with absolute times or timestamps, such as logging events, measuring time intervals, or interfacing with systems that use epoch-based times.

Below we create an Instant from a known epoch-based value, and also get the current instant. We then demonstrate converting an instant to a time zone for display, and accessing its epoch time properties:

// Create an Instant for the Unix epoch (1 seconds offset from 1970-01-01T00:00:00Z) const epoch = Temporal.Instant.fromEpochSeconds(1); console.log(epoch.toString()); // "1970-01-01T00:00:01Z" console.log(epoch.epochMilliseconds); // 1000 (milliseconds since epoch) // Get the current instant and show its value and a timezone conversion: const someInstant = Temporal.Instant.from("2025-05-23T00:00:00Z"); console.log(someInstant.toString()); // "2025-05-23T00:00:00Z" const nycTime = someInstant.toZonedDateTimeISO("America/New_York"); console.log(nycTime.toString()); // "2025-05-22T20:00:00-04:00[America/New_York]"

Temporal.Duration

Temporal.Duration represents an amount of time in terms of years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds. It's an immutable object that you can use for date/time math - for example, "5 days" or "PT3H20M". You can use Duration whenever you need to represent an interval of time or do arithmetic with Temporal objects.

Here are examples of arithmetic operations with Temporal objects:

const duration1 = Temporal.Duration.from({ hours: 1, minutes: 30 }); const duration2 = Temporal.Duration.from({ days: 2 }); const duration3 = duration2.add(duration1); const duration4 = Temporal.Duration.from({ days: -2 }); console.log(duration1.toString()); // "PT1H30M" console.log(duration2.toString()); // "P1D" console.log(duration3.toString()); // "P2DT1H30M" console.log(duration4.toString()); // "-P2D" console.log(duration1.total({ unit: 'minutes' })); // 90 console.log(duration2.total({ unit: 'minutes' })); // 2880 console.log(duration3.total({ unit: 'minutes' })); // 2970 console.log(duration4.total({ unit: 'minutes' })); // -2880 const plainTime = Temporal.PlainTime.from('00:20:25'); const plainDate = Temporal.PlainDate.from('2025-05-23'); const plainDateTime = Temporal.PlainDateTime.from('2025-12-31T23:30:00'); const zonedDateTime = Temporal.ZonedDateTime.from({ timeZone: "Asia/Tokyo", year: 2025, month: 5, day: 24, hour: 10, minute: 30, second: 0, }); const instantDateTime = Temporal.Instant.from("2025-05-23T00:00:00Z"); console.log(plainTime.add(duration1).toString()); // "01:50:25" console.log(plainTime.add(duration2).toString()); // "00:20:25", after 2 days it's the same time console.log(plainDate.add(duration1).toString()); // "2025-05-23", rounded to days console.log(plainDate.add(duration2).toString()); // "2025-05-25" console.log(plainDate.subtract(duration2).toString()); // "2025-05-21" console.log(plainDate.add(duration4).toString()); // "2025-05-21" console.log(plainDateTime.add(duration1).toString()); // "2026-01-01T01:00:00" console.log(zonedDateTime.add(duration1).toString()); // "2025-05-24T12:00:00+09:00[Asia/Tokyo]" console.log(instantDateTime.add(duration1).toString()); // "2025-05-23T01:30:00Z"

Calendar systems

Calendar systems are used for interpreting dates. They represent different calendar conventions (ISO/Gregorian, Hebrew, Islamic, Japanese, etc.) that can be used with Temporal dates/date-times.

Here is an example:

const islamicDate = Temporal.PlainDate.from({ year: 1446, month: 11, day: 27, calendar: 'islamic' }); console.log(islamicDate.toString()); // "2025-05-24[u-ca=islamic]" console.log(islamicDate.calendar.id); // "islamic"

Browser support

As of 2025, only Firefox 139+ has experimental support. Thus, this feature is not yet widely supported and should not be used in production code without polyfills.

Conclusion

After years of coping with the old Date API, JavaScript developers finally have a path to a better future with dates and times. The historical limitations - from the lack of time zones to DST bugs, parsing woes, and mutable dates - had real costs in development time and application correctness. These issues prompted action from TC39 to create something new rather than continue patching the old. The Temporal API represents that new solution: it brings JavaScript's date handling up to par with modern needs and aligns it with the kind of rich date/time support found in other languages' standard libraries. Crucially, Temporal was built with the lessons of the past in mind, directly addressing the pain points that developers encountered with Date.

With Temporal's introduction, working with dates and times in JavaScript will become more intuitive, reliable, and powerful, enabling developers to build time-sensitive features with greater confidence and less frustration.





Read previous