Open-source project
js-temporal/temporal-polyfill avatar
js-temporal/temporal-polyfill

temporal-polyfill: using Temporal before your engine ships it

Polyfill for Temporal (under construction)

796 stars43 forksTypeScriptISC

At a glance

What is it?
The @js-temporal/polyfill package is the reference implementation of the TC39 Temporal proposal for JavaScript, written by some of the proposal's own champions and kept in step with the specification, so an application can adopt the date and time API before engines ship it natively. It is ISC licensed and its own roadmap still lists the production release as unfinished.
Who is it for?
Adopt @js-temporal/polyfill if you want to start using the Temporal API now, in TypeScript, with the confidence that the behaviour matches the specification rather than a homegrown date library. Do not adopt it if you can wait for native engine support, since the polyfill adds a dependency and ships ES2020 code.
Can I use it commercially?
Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 31 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A polyfill written by the proposal's authors

The @js-temporal/polyfill package is a polyfill for the TC39 Temporal proposal, and the README makes the origin point clearly: it was kicked off by some of the champions of the Temporal proposal. That detail is the reason to care about this package rather than a generic date library.

The stated goal is to be ready for production use when the Temporal proposal reaches Stage 4, with the honest caveat that like all OSS work, progress is dependent on contributors.

Two disclaimers sit next to the pitch. The polyfill is not affiliated with TC39, and the README points to the proposal repository for a list of other polyfills. The second is worth internalising, because the temporal-polyfill is not the only implementation and the ecosystem has fragmented before. The roadmap records that all other earlier Temporal polyfills were deprecated, and the project is the one the champions maintain.

The compatibility floor is stated as Node.js 14 or later. The primary language is TypeScript, and the README records that the project was converted to TypeScript for better maintainability, with the typing of sources improved in the same spirit, and that the BigInt handling was migrated to JSBI for improved compile-time safety around BigInt operations.

The documentation pointer is worth noting too. The API documentation is on MDN and described as the main API reference going forward, and there is a cookbook at tc39.es for developers new to Temporal with recipes for common tasks. The project explicitly positions itself as the implementation and points at the specification and the docs for the semantics.

The v0.4 to v0.5 change is the upgrade warning

The README devotes a section to breaking changes and migration, and the reason it exists is instructive about how a proposal moves.

Until recently the polyfill lagged behind the proposal repository and its own not-for-production-use polyfill. It has now caught up with the last few years of changes to the Temporal proposal, including many removed APIs and other breaking changes. The 0.5.0 release on npm is the version that carries that catch-up.

The README also gives the state of the proposal itself, which determines how much churn you should expect: breaking changes to any TC39 proposal are always possible before Stage 4, but Temporal is close to adoption and no significant changes are expected in the future. The changelog at the 0.5.0 entry details what changed, including tips for migrating existing code to the latest version.

That is the key operational fact for anyone planning to depend on this. The 0.4 series and the 0.5 series are not a feature release apart, they are different implementations of a moving specification, and the migration tips are the practical part of the upgrade.

The releases make the cadence visible. v0.5.1 on 2025-03-31, v0.5.0 on 2025-03-28, and v0.4.4 back on 2023-05-17. The package.json version is 0.5.1, and the description calls it a polyfill for Temporal, an ECMA TC39 Stage 4 proposal.

The roadmap shows the remaining work honestly. Optimize performance of other slow operations is still open, and release production version to npm is still open, with the production release the one item that matters for a team deciding whether to depend on this in a shipped product.

Installing it and the two module styles

The install is one command:

bash
$ npm install @js-temporal/polyfill

The package ships three builds, and the exports map in the package.json shows how they are selected. There is a CommonJS build at ./dist/index.cjs, an ES module build at ./dist/index.esm.js, and a UMD build at ./dist/index.umd.js for the browser field. The root export has conditional entries so that the import condition resolves to the ESM build with the ./index.d.ts types, the require condition resolves to the CJS build with ./index.d.cts, and the default falls back to the CJS build.

The usage differs only in how you import it. For CommonJS:

javascript
const { Temporal, Intl, toTemporalInstant } = require('@js-temporal/polyfill');
Date.prototype.toTemporalInstant = toTemporalInstant;

And as an ES6 module:

javascript
import { Temporal, Intl, toTemporalInstant } from '@js-temporal/polyfill';
Date.prototype.toTemporalInstant = toTemporalInstant;

In both cases you pull in Temporal, the project's own Intl, and toTemporalInstant, and you attach the conversion helper to Date.prototype yourself. The polyfill exports its own Intl as well, which is the usual reason to reach for it rather than the global.

The reason toTemporalInstant has to be assigned by hand is the next paragraph, and it is the most surprising behaviour in the package. The polyfill currently does not install a global Temporal object like a real implementation will, and the reason is given: this behavior avoids hiding the global Temporal object in environments where a real Temporal implementation is present. The linked issue in the proposal repository holds the background, and the README notes that once JS engines start shipping with Temporal, the project may decide to change this behaviour to match the built-in behaviour more closely.

Two constraints on the output follow from this. The package ships ES2020 code for both the CommonJS and ES module bundles, so if your environment does not support ES2020 you have to transpile the package along with the rest of your code. And if your project only ever runs where native bigints exist, the README recommends configuring the JSBI Babel plugin that translates JSBI calls to their native bigint equivalent, which improves code size and performance. The project says it is exploring ways to provide separate builds for those cases.

Why JSBI is in there at all

The BigInt story is worth a separate look, because it is the kind of detail that only shows up in production.

Temporal needs exact nanosecond precision for instants and durations, and a JavaScript number cannot represent that range exactly. Native bigint can, but native bigint is not available in every environment the polyfill targets, and the semantics differ from what Temporal needs in places. So the polyfill uses JSBI, the GoogleChromeLabs library, to enable backwards compatibility for environments without native bigints.

The cost of that choice is code size and speed, because every bigint operation goes through a library rather than the engine's native implementation. The README's advice is to install the Babel plugin in environments that support native bigints, which converts the calls at build time rather than at runtime, and the roadmap lists optimising the performance of other slow operations as still open.

The earlier performance work in the roadmap is also instructive. There is a checked item for optimising slow operations by reducing calls to the Intl.DateTimeFormat constructor, which points to a specific and unglamorous problem. Formatting a Temporal value goes through Intl, and constructing an Intl.DateTimeFormat is expensive enough that doing it per call dominated the profile. The references to the issues and pull requests that fixed it are still in the roadmap text, which is a good sign for how the project tracks its own performance work.

For a team adopting this, the practical reading is that the polyfill's overhead is real, is concentrated in a few known places, and is being worked on. Whether that matters depends on how much date and time work your application does.

Testing against the specification, and the limits of the package

The repository layout shows how seriously the project takes conformance, and it is the strongest argument for using this polyfill over any alternative.

There is a test262 directory and a runtest262.mjs script, and the package.json defines a test262 script that builds and runs it. test262 is the ECMAScript conformance suite, so the polyfill is tested against the specification's own test cases rather than against hand written expectations. There is also a testValidStrings script and a test directory with the resolver used by the jest setup, and the jest configuration is split across jest.config.cjs and jest.resolve.cjs, with the test command running jest under Node with full ICU data.

The build is TypeScript compiled to tsc-out and then bundled with rollup, with the rollup config at rollup.config.js and a bundleConfigAsCjs flag on the rollup invocation, and the release process copies the type declarations with copy-types.mjs before building. There is a release-checklist.md, a playground script that builds a special bundle for trying things out, and an update script built on npm-check-updates.

The limits are equally clear. The description of the repository still says under construction. The compatibility floor of Node 14 is generous but the output is ES2020, so the effective requirement on older environments is a transpiler. The polyfill does not install a global, so you must import it and wire up Date.prototype yourself, and if your application has many entry points that is a step you have to not forget. And the production release is still an open roadmap item, which is the honest signal that this is a project you should pin a version of and re-check, rather than a dependency you install and forget.

The bug reporting guidance is also a good indicator of a well-run project. If you think you have found a bug in the Temporal API itself rather than in this implementation, the README asks you to file it in the tc39/proposal-temporal issue tracker. If the implementation here does not match the Temporal spec, that is a bug in this polyfill and belongs in this repository's tracker. The separation between the specification and its implementations is exactly the boundary you want maintained.

Editorial conclusion

Adopt @js-temporal/polyfill if you want to start using the Temporal API now, in TypeScript, with the confidence that the behaviour matches the specification rather than a homegrown date library. Do not adopt it if you can wait for native engine support, since the polyfill adds a dependency and ships ES2020 code. Verify first whether your target engines now provide Temporal natively, that your build can consume the ES module and CommonJS bundles the package exports, and that the version you pin is the one whose behaviour matches the proposal text you are coding against.

Frequently asked questions

How do I install the Temporal polyfill?

Run `npm install @js-temporal/polyfill`. The package ships a CommonJS build, an ES module build and a UMD browser build, and the exports map selects the right one for your import style.

Why doesn't the Temporal polyfill install a global Temporal object?

To avoid hiding the global Temporal object in environments where a real native implementation is present. The README notes that once engines ship with Temporal, the project may change this behaviour to match the built-in one more closely.

What changed between temporal-polyfill 0.4 and 0.5?

The polyfill caught up with the last few years of changes to the Temporal proposal, including many removed APIs and other breaking changes. The changelog entry for 0.5.0 includes migration tips, and the README warns that breaking changes to any proposal remain possible before Stage 4.

Why does the Temporal polyfill use JSBI?

To support environments without native bigints, since Temporal needs exact nanosecond precision. If your project only runs where native bigints exist, the README recommends the JSBI Babel plugin to translate those calls to native bigint at build time, improving code size and performance.

How does temporal-polyfill test itself against the specification?

It runs the test262 conformance suite through the runtest262.mjs script, alongside a jest test suite and a valid strings test, all under Node with full ICU data. The build is TypeScript compiled and bundled with rollup.

What license is temporal-polyfill released under?

ISC, with the LICENSE file at the repository root. The package is compatible with Node.js 14 or later, and the repository is not archived with the last push on 2026-09-22.

Official sources

  1. Issues
  2. js-temporal/temporal-polyfill on GitHub
  3. License: ISC
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/js-temporal-temporal-polyfill.svg)](https://hysenlabs.com/projects/js-temporal-temporal-polyfill)

Was this useful?