Arsalan Khattak
21 January 2026

JavaScript Temporal in 2026 – is it finally here?

In May 2025, Firefox 139 became the first browser to ship Temporal by default, followed by Chrome 144 in January […]

In May 2025, Firefox 139 became the first browser to ship Temporal by default, followed by Chrome 144 in January 2026. With two major browsers on board, replacing the problematic Date object is now within reach. Once Temporal has full cross-browser support, Date will be considered a legacy feature.

The problems with the Date object include:

The name itself is problematic because it actually represents a point in time and not a date: dates are inferred from that point in time.

You can see an example of one of the issues with the Date object in the code below. You may be surprised to find that the value of today gets seven days added to it:

// From https://maggiepint.com/2017/04/11/fixing-javascript-date-web-compatibility-and-reality/
function addOneWeek(myDate) {
    myDate.setDate(myDate.getDate() + 7);
    return myDate;
}
 
const today = new Date();
const oneWeekFromNow = addOneWeek(today);

console.log(`today is ${today.toLocaleString()}, and one week from today will be ${oneWeekFromNow.toLocaleString()}`);
// today is 23/01/2026, 10:50:42, and one week from today will be 23/01/2026, 10:50:42

The value of today changes because the Date object is mutable. The addOneWeek function changes the today variable by adding seven days to it.

Problems with the Date object stem from JavaScript’s origins. In 1995, Brendan Eich was tasked with writing JavaScript for the Netscape browser, given just ten days to go to market, and told to make it “like Java.” Eich adapted the problematic Java Date object, which was fixed by the release of Java 1.1 two years later. Most of its date methods were deprecated and replaced. The JavaScript implementation couldn’t be entirely fixed without breaking the web, though, and over the years, the quirks of the Date object – like its mutability and parsing issues – have become major pain points for developers, despite the emergence of popular libraries like Day.jsdate-fns, and Luxon for working with dates and times. The very existence of these libraries shows that the Date object is not fit for many purposes.

Temporal promises to fix all the problems associated with working with dates in JavaScript. It fully replaces the Date object with a better API for working with dates, times, time zones, and calendars. Temporal simplifies daylight saving time calculations and makes working with historical calendar changes easier.

Let’s compare Temporal with the Date object to see how they differ in code readability, clarity, and performance.

What is the Temporal API, and how does it compare to Date?

The Temporal API is a namespace object, like Intl, meaning it’s an object made of static properties and methods. These properties and methods handle different parts of dates and times:

You can see these in Firefox by typing Temporal in the console and pressing Enter:

Temporal { … }
​   Duration: function Duration()
​   Instant: function Instant()
   Now: Temporal.Now { … }
   ...

Compared to using Date, Temporal separates functionality, resulting in code that’s more readable and clearly shows which time zone is in use.

You can string these methods together to create a chain of methods, each returning a new object, which prevents mutation of the original object – as it should be.

Consider the Date object’s parse method for converting a date string into a timestamp:

const d = Date.parse('01 Feb 2026');
console.log(d); // 1769896800000

The value of the returned timestamp depends on the user’s time zone, a detail that’s easy to miss and can cause bugs in your code.

Let’s look at some code examples of common date tasks to see how Temporal compares to Date.

How to get a Unix timestamp

A Unix timestamp is the number of seconds since the beginning of the Unix epoch, which is midnight on the first of January 1970, UTC. To get this timestamp using the Date object, use the Date.now() method:

const timeStamp = Math.floor(Date.now() / 1000);
console.log(timeStamp); // 1768554351

To do the same using the Temporal object, use Temporal.now to get the current time and then use the Instant method to return an Instant object, which represents the number of nanoseconds since the Unix epoch:

const timeStamp = Math.floor(Temporal.Now.instant().epochMilliseconds / 1000);
console.log(timeStamp); // 1768554351

Both the Instant object and Date object represent a point in time since the Unix epoch, but the Instant object is more precise as it stores nanoseconds, not milliseconds. To get date or time information from an Instant object, you need to first convert it to a Temporal.ZonedDateTime object, which represents a date and time with a time zone. To do this, use the toZonedDateTimeISO() method.

In contrast to the Temporal object, the Date object implicitly uses the local time zone, which can lead to unexpected results if you’re expecting UTC. For example, the toString() method returns a string representing the date in the local time zone, which may not be obvious just by looking at the code:

const isoString = "2026-03-20T23:00:00Z";
const date = new Date(isoString);
console.log(date.toString()); // Sat Mar 21 2026 01:00:00 GMT+0200 (South Africa Standard Time)

The date string returns a date in the user’s local time zone, which in this case, moves the date from a Friday to a Saturday.

The equivalent code using Temporal requires explicit time zone conversion:

const instant = Temporal.Instant.from(isoString);
const zonedDateTime = instant.toZonedDateTimeISO('Africa/Johannesburg');
console.log(zonedDateTime.toString({ timeZoneName: "never" })); //2026-03-21T01:00:00+02:00

The timeZoneName option is set to "never" to exclude the time zone name in the returned string.

How to get the current date and time as an ISO 8601 string in the user’s local time

The ISO 8601 format is a standard that provides a well-defined, unambiguous method of representing calendar dates and times in global communication. Using the Date object, getting a string of the current date and time in ISO 8601 format using UTC is straightforward:

const d = new Date(Date.now()).toISOString();
console.log(d); // 2026-01-16T09:10:42.239Z

However, getting the current date and time in ISO 8601 format in the user’s time zone is a little more complicated. Here’s one possible implementation:

function toISOLocal(d) {
    const padToTwoDigits = n => ('0' + n).slice(-2);
    const padToThreeDigits = n => ('00' + n).slice(-3);
    let timezoneOffset = d.getTimezoneOffset();
    const sign = timezoneOffset > 0 ? '-' : '+';
    timezoneOffset = Math.abs(timezoneOffset);
    return d.getFullYear() + '-' +
        padToTwoDigits(d.getMonth() + 1) + '-' +
        padToTwoDigits(d.getDate()) + 'T' +
        padToTwoDigits(d.getHours()) + ':' +
        padToTwoDigits(d.getMinutes()) + ':' +
        padToTwoDigits(d.getSeconds()) + '.' +
        padToThreeDigits(d.getMilliseconds()) +
        sign + padToTwoDigits(timezoneOffset / 60 | 0) + ':' +   padToTwoDigits(timezoneOffset % 60);
}
console.log(toISOLocal(new Date())); // 2026-01-16T11:11:10.420+02:00

Using the Temporal object makes this task much easier:

const zonedDateTime = Temporal.Now.zonedDateTimeISO(); 
console.log(zonedDateTime.toString({ timeZoneName: "never" })); // 2026-01-16T11:11:30.552

The Temporal.Now.zonedDateTimeISO() method returns the current date and time as a Temporal.ZonedDateTime object, in the ISO 8601 calendar and the specified time zone. It uses the user’s time zone by default. You can change the time zone by passing in a time zone identifier string as an argument.

How to get the number of days and months until a future event

Another common date task is to calculate the duration between now and a date in the future. For example, the following code uses the Date object to calculate the number of days and months until a future date:

const d = '2028-10-11';
// Parse the date string into a Date (months are zero-based)
const [year, month, day] = d.split('-').map(Number);
const futureDate = new Date(year, month - 1, day);

// Get "today" as a Date at local midnight - so only the calendar date matters
const today = new Date();
today.setHours(0, 0, 0, 0);

const msPerDay = 1000 * 60 * 60 * 24;
const diffDays = Math.round((futureDate - today) / msPerDay);

// Calculate an approximate month difference
// First, get the month difference from the year and month parts
let diffMonths = (futureDate.getFullYear() - today.getFullYear()) * 12 +
                 (futureDate.getMonth() - today.getMonth());

// Then adjust based on the day of the month for a rough "rounding" effect.
if (futureDate.getDate() >= today.getDate()) {
    diffMonths++;  // Add a month if the day is later in the month
}
else if (futureDate.getDate() < today.getDate()) {
    diffMonths--;  // Subtract a month if the day hasn't been reached yet
}

console.log(diffDays, diffMonths);

This code first parses a string representing a future date and creates a Date object from it. The number of days between today and the future date is calculated by subtracting the today object from the futureDate object and converting the resulting millisecond difference into days and months, and rounding the result.

The equivalent code using the Temporal object requires less than half of the code and it’s more readable:

const d = '2028-10-11';
const futureDate = Temporal.PlainDate.from(d);
const today = Temporal.Now.plainDateISO();
const until = today.until(futureDate, { largestUnit: 'day' });
const untilMonths = until.round({ largestUnit: 'month', relativeTo: today });

console.log(until.days, untilMonths.months); 

The futureDate is a plain date, without a time zone. The today variable is the current date using the user’s time zone. The until variable is a Temporal.Duration object that represents the duration in days between today and the futureDate. It uses the until() method for the calculation. The untilMonths variable uses the round() method to round the duration to months.

To see more examples of common date operations, take a look at the Ecma Technical Committee 39 (TC39) Temporal proposal cookbook.

How to use Temporal

The Temporal proposal is at stage 3 in the TC39 proposal process, which means it’s recommended for implementation and no changes to the API are expected. However, changes may be made due to web incompatibilities and feedback from user testing. The last step in the process is stage 4.

The quickest way to try out Temporal is by using Firefox 139 or later.

Firefox and Chrome now have full support for Temporal. Chrome 144 shipped with Temporal support in January 2026. Edge has experimental support in beta, and most of the APIs are available in Safari Technical Preview, some under a flag.

Two polyfills are available to ensure cross-browser compatibility in your projects:

PolyfillGitHub repositoryStatus
@js-temporal/polyfilljs-temporal/temporal-polyfillAlpha release
temporal-polyfillfullcalendar/temporal-polyfillBeta release

Performance comparison: Temporal vs. Date

To compare the performance of Temporal and Date, we ran benchmarking tests using the code examples from the comparison section of this post and Tinybench, a lightweight JavaScript benchmarking library. The tests were conducted on a MacBook Air M1 using Firefox Nightly version 137.0a1 (2025-02-13) (64-bit) with thTo compare the performance of Temporal and Date, we ran benchmarking tests using the code examples from the comparison section of this post and Tinybench, a lightweight JavaScript benchmarking library. We ran the tests using the fully supported Temporal API in Firefox and Chrome. The performance of Temporal is now much closer to the performance of Date, unlike our previous comparison 11 months ago, when it was only available behind a flag in Firefox Nightly.

Current comparison using fully supported Temporal in Firefox and Chrome

We conducted our latest comparison tests on a MacBook Air M1 using Firefox version 147.0 (64-bit). We integrated the benchmarking code into a vanilla JavaScript Vite application built with vite build.

We used the following code to benchmark the date and time operations using Date and Temporal:

import { Bench } from 'tinybench'

const bench = new Bench({ 
  name: 'Date vs. Temporal', 
  warmupIterations: 100,  
  iterations: 10000,  
});

bench
  .add('Get Unix timestamp - Date', () => {
    const timeStamp = Math.floor(Date.now() / 1000);
    console.log(timeStamp);
  })
  .add('Get Unix timestamp - Temporal', () => {
    const timeStamp = Math.floor(Temporal.Now.instant().epochMilliseconds / 1000);
    console.log(timeStamp);
  })
  .add('Local time ISO 8601 date string - Date', () => {
    function toISOLocal(d) {
      const padToTwoDigits = n => ('0' + n).slice(-2);
      const padToThreeDigits = n => ('00' + n).slice(-3);
      let timezoneOffset = d.getTimezoneOffset();
      let sign = timezoneOffset > 0 ? '-' : '+';
      timezoneOffset = Math.abs(timezoneOffset);
    
      return d.getFullYear() + '-' +
        padToTwoDigits(d.getMonth() + 1) + '-' +
        padToTwoDigits(d.getDate()) + 'T' +
        padToTwoDigits(d.getHours()) + ':' +
        padToTwoDigits(d.getMinutes()) + ':' +
        padToTwoDigits(d.getSeconds()) + '.' +
        padToThreeDigits(d.getMilliseconds()) +
        sign + padToTwoDigits(timezoneOffset / 60 | 0) + ':' +   padToTwoDigits(timezoneOffset % 60); 
    }
    
    console.log(toISOLocal(new Date()));
  })
  .add('Local time ISO 8601 date string - Temporal', () => {
    const zonedDateTime = Temporal.Now.zonedDateTimeISO(); 
    console.log(zonedDateTime.toString({ timeZoneName: "never" })); 
  })
  .add('Days and months until a date - Date', () => {
    const d = '2028-10-11';
    // Parse the date string into a Date (months are zero-based)
    const [year, month, day] = d.split('-').map(Number);
    const futureDate = new Date(year, month - 1, day);
    
    // Get “today” as a Date at local midnight - so only the calendar date matters
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    
    const msPerDay = 1000 * 60 * 60 * 24;
    const diffDays = Math.round((futureDate - today) / msPerDay);
    
    // Calculate an approximate month difference
    // First, get the month difference from the year and month parts
    let diffMonths = (futureDate.getFullYear() - today.getFullYear()) * 12 + (futureDate.getMonth() - today.getMonth());
    
    // Then adjust based on the day-of-month for a rough “rounding” effect.
    // This is a simplified approximation - Temporal does more precise calendar arithmetic.
    if (futureDate.getDate() - today.getDate() >= 15) {
      diffMonths++;
    } else if (today.getDate() - futureDate.getDate() >= 15) {
      diffMonths--;
    }
    
    console.log(diffDays, diffMonths);
  })
  .add('Days and months until a date - Temporal', () => {
    const d = '2028-10-11';
    const futureDate = Temporal.PlainDate.from(d);
    const today = Temporal.Now.plainDateISO();
    const until = today.until(futureDate, { largestUnit: 'day' });
    const untilMonths = until.round({ largestUnit: 'month', relativeTo: today });
    
    console.log(until.days, untilMonths.months);
  })

await bench.run();

console.log(bench.name);
console.table(bench.table());

We instantiated the Bench class and added the benchmark tasks to it using the add method, which accepts a task name and function.

Each of the six benchmark tasks includes 100 warm-up iterations followed by 10,000 measured iterations. The warm-up ensures that the browser’s just-in-time (JIT) compilation optimizes the code and completes any necessary caching before each test runs, making the benchmark results more consistent.

The benchmark results for Firefox are as follows:

Task nameLatency avg (ns)Latency med (ns)Throughput avg (ops/s)Throughput med (ops/s)Samples
Get Unix timestamp – Date5398.8 ± 9.84%0.00 ± 0.00184380 ± 0.03%185226 ± 0185226
Get Unix timestamp – Temporal5115.9 ± 10.95%0.00 ± 0.00194631 ± 0.03%195468 ± 0195468
Local time ISO 8601 date string – Date6151.2 ± 11.21%0.00 ± 0.00161755 ± 0.03%162569 ± 0162732
Local time ISO 8601 date string – Temporal6157.9 ± 10.46%0.00 ± 0.00161554 ± 0.03%162393 ± 0162393
Days and months until a date – Date6165.9 ± 8.81%0.00 ± 0.00161310 ± 0.04%162182 ± 0162344
Days and months until a date – Temporal6424.0 ± 9.41%0.00 ± 0.00154795 ± 0.04%155666 ± 0155666

The benchmark results for Chrome are as follows:

Task nameLatency avg (ns)Latency med (ns)Throughput avg (ops/s)Throughput med (ops/s)Samples
Get Unix timestamp – Date15139 ± 2.11%0.00 ± 0.0057724 ± 0.26%66056 ± 066063
Get Unix timestamp – Temporal15934 ± 2.08%0.00 ± 0.0054546 ± 0.27%62760 ± 062760
Local time ISO 8601 date string – Date20807 ± 3.05%0.00 ± 0.0040826 ± 0.33%48061 ± 048066
Local time ISO 8601 date string – Temporal17930 ± 1.86%0.00 ± 0.0047659 ± 0.30%55772 ± 055772
Days and months until a date – Date17136 ± 2.76%0.00 ± 0.0050315 ± 0.29%58357 ± 058363
Days and months until a date – Temporal22480 ± 1.93%0.00 ± 0.0036976 ± 0.36%44485 ± 044489

The throughput average (number of operations performed per second) shows some differences between browsers. In Firefox, Temporal and Date performed similarly across all tasks, with Temporal even slightly outperforming Date for getting a Unix timestamp. In Chrome, results varied more: Temporal was about 17% faster for the ISO 8601 date string task, but Date was significantly faster (around 36%) for date arithmetic calculations. Firefox also showed notably higher throughput overall, suggesting its Temporal implementation is more optimized, which is expected as it has been available for a longer time.

Previous comparison using Firefox’s experimental Temporal feature from 11 months ago

We previously conducted the tests on a MacBook Air M1 using Firefox Nightly version 137.0a1 (2025-02-13) (64-bit) with the javascript.options.experimental.temporal preference enabled.

The benchmark results were as follows:

Task nameLatency avg (ns)Latency med (ns)Throughput avg (ops/s)Throughput med (ops/s)Samples
Get Unix timestamp – Date4972.1 ± 8.65%0.00 ± 0.00200235 ± 0.03%201124 ± 0201124
Get Unix timestamp – Temporal5133.5 ± 8.94%0.00 ± 0.00193927 ± 0.03%194797 ± 0194797
Local time ISO 8601 date string – Date6485.2 ± 11.19%0.00 ± 0.00153377 ± 0.04%154197 ± 0154197
Local time ISO 8601 date string – Temporal12344 ± 13.79%0.00 ± 0.0080365 ± 0.06%81014 ± 081500
Days and months until a date – Date6422.3 ± 8.99%0.00 ± 0.00154824 ± 0.04%155708 ± 0155708
Days and months until a date – Temporal12100 ± 9.94%0.00 ± 0.0081915 ± 0.06%82648 ± 082648

The throughput average showed that Date calculations outperformed Temporal – only 3% faster for getting a Unix timestamp but nearly twice as fast for the other calculations. This performance gap has closed significantly now that Temporal is fully supported in Firefox and Chrome, which is great for complex components like a Bryntum Grid that carry out hundreds or thousands of date calculations.

The improved clarity and immutability of Temporal, combined with its competitive performance in Firefox and mixed results in Chrome, make it a compelling alternative to Date. Since the API is still at Stage 3 in the TC39 proposal process, Chrome’s performance will likely improve as its implementation matures.

Is Temporal finally here?

Almost! Firefox and Chrome now have full support, covering the majority of desktop browser users. Edge has experimental support in beta, and most of the APIs are available in Safari Technical Preview.

The @js-temporal/polyfill and temporal-polyfill libraries provide cross-browser compatibility for the remaining browsers. You can contribute to the proposal by reporting bugs or suggesting ideas in the Temporal proposal GitHub repo.

When Temporal is finally here, it will make working with dates, times, time zones, and calendars much more pleasant. You’ll have clearer, less buggy code and fewer dependencies on large date-handling libraries. Here at Bryntum, we use the Date object for time zone support in our components. We plan on using Temporal instead for time zone support once it’s ready, which will drastically simplify our implementation.

Arsalan Khattak

Development JavaScript