@js-temporal/polyfill review
A birthday needs a calendar date; a payment log needs an exact point in time. @js-temporal/polyfill 0.5.1 gives JavaScript those separate types through the Temporal API. PlainDate represents a calendar date independent of a time zone. Instant represents an exact point in time, while PlainDateTime can hold local date and clock fields before you choose a zone. Use this package when that distinction belongs in your data model. The polyfill does not install a global Temporal object. This guide uses the imported API, and its examples are documentation-based illustrations that have not been executed here.
Choose @js-temporal/polyfill 0.5.1 when separate calendar dates, local date-times and exact timestamps make your application rules clearer. Start with one input boundary, set its overflow and time-zone policies, and check the resulting behavior in your own runtime before migrating the rest.
Answers from our run
@js-temporal/polyfill or luxon: which should you use?
luxon: Choose Luxon when common or custom parsing and formatting are your main requirement. Its README documents those formats alongside DateTime, Duration and Interval types. Choose @js-temporal/polyfill 0.5.1 when separate calendar dates, local date-times and exact timestamps make your application rules clearer.
When should you not use @js-temporal/polyfill?
The task is a few formatting calls on existing Date objects. Introducing a second date model creates conversion work that date-fns may let you avoid.
Use it if
- You store birthdays or other calendar dates that should remain dates wherever the reader lives. Use PlainDate rather than giving those values a midnight timestamp.
- A booking form collects a local date and time, and your application must decide what to do when a zone change makes that time ambiguous or nonexistent.
- You need to keep an exact event timestamp separate from its display in a named time zone.
- Your team wants Temporal types at application boundaries and can budget for explicit conversions to libraries that still take Date objects.
- The task is a few formatting calls on existing Date objects. Introducing a second date model creates conversion work that date-fns may let you avoid.
- You expect npm install alone to make Temporal available globally. This package requires an import or an explicit integration decision.
- You cannot review older Temporal examples before adopting them. The 0.5 migration removed Temporal.TimeZone and Temporal.Calendar, so older tutorials can name APIs this release no longer provides.
- Your frontend budget cannot absorb another date library without a measured build comparison. No application bundle or runtime performance measurement supports this guide.
- You need a scheduler that runs jobs or preserves workflow progress. Date values do not supply an execution service; the separate [Temporal workflow review](https://mrkeyoor.com/repos/temporal/) covers that product.
Setup reality
Install the reviewed version with npm install @js-temporal/polyfill@0.5.1, then import { Temporal } from '@js-temporal/polyfill'. The published exports map provides ESM and CommonJS entry paths plus TypeScript declarations. Its runtime dependency is JSBI. The package metadata says Node >=12, but the version-pinned README says Node.js 14 or later. Follow the stricter documented floor and verify your actual runtime rather than treating an engine declaration as a compatibility test. The README also says the distributed bundles contain ES2020 code; older targets need transpilation.
Importing the package leaves the global Temporal object alone. Keep the import consistent across your application. Before replacing an existing Date helper, identify what each value means: a date on a calendar, local date and time awaiting a zone, or an exact event timestamp. Moving strings between those meanings without a documented conversion merely moves the old ambiguity into new classes.
In 0.5.1, PlainDateTime.toZonedDateTime accepts a disambiguation option through ToInstantOptions. Choose 'reject' when a form should ask the user to resolve a missing or repeated local time. The default is 'compatible', so omitting the option is a policy choice too. A rejected conversion raises RangeError; catch that at the form boundary and offer a correction instead of letting a failed booking appear successful. Date-field assignment and date arithmetic have a separate overflow option, whose default is 'constrain'. Use 'reject' when silently clamping a day would violate your rules.
Read the 0.5 migration notes before copying older examples: Temporal.TimeZone and Temporal.Calendar were removed. The 0.5.1 notes report a fix for infinite recursion involving Intl.DurationFormat in Node 23.x and CI coverage for Node 22.x and 23.x. Those are the maintainers' release statements, not results from this guide. We inspected documentation, declarations and package metadata; we did not install the library, execute these snippets, run its test suite or measure application compatibility.
Ratings below use a documentation-based scale: 1 means serious documented obstacles, 3 means useful support with material caveats, and 5 means comprehensive support in the inspected area. Intermediate scores sit between those anchors. API stability considers documented breaking changes; docs quality considers setup and API coverage; maintenance considers recorded fixes; ecosystem considers package integration paths. None measures adoption or promises future support.
Patterns
Import the package API import-temporal
import { Temporal } from '@js-temporal/polyfill';
const date = Temporal.PlainDate.from('2026-11-02');
console.log(date.toString());Import Temporal explicitly. This package does not install it globally; this and the remaining examples have not been executed here.
Keep a birthday as a calendar date represent-calendar-date
import { Temporal } from '@js-temporal/polyfill';
const birthday = Temporal.PlainDate.from('1990-06-15');
const stored = birthday.toString();
const restored = Temporal.PlainDate.from(stored);PlainDate represents a calendar date independent of a time zone. Store it as a date in your application contract rather than assigning an arbitrary time.
Reject invalid form fields reject-invalid-fields
import { Temporal } from '@js-temporal/polyfill';
try {
const date = Temporal.PlainDate.from(
{ year: 2026, month: 2, day: 30 },
{ overflow: 'reject' },
);
console.log(date.toString());
} catch (error) {
if (!(error instanceof RangeError)) throw error;
console.error('Choose a valid calendar date.');
}Object-field assignment defaults to constrain. The explicit reject policy prevents an out-of-range field from being clamped into an accepted date.
Make month-end arithmetic a decision reject-month-overflow
import { Temporal } from '@js-temporal/polyfill';
const date = Temporal.PlainDate.from('2026-01-31');
try {
const next = date.add({ months: 1 }, { overflow: 'reject' });
console.log(next.toString());
} catch (error) {
if (!(error instanceof RangeError)) throw error;
console.error('Choose a month-end policy before continuing.');
}Arithmetic defaults to constrain too. Reject raises RangeError for an out-of-range result; decide separately whether your billing rule should accept a shorter month's final day.
Sort calendar dates explicitly compare-calendar-dates
import { Temporal } from '@js-temporal/polyfill';
const dates = ['2026-11-03', '2026-11-01']
.map(value => Temporal.PlainDate.from(value));
dates.sort(Temporal.PlainDate.compare);Use the declared comparison function for date ordering. Keep all values in the same intended calendar model rather than relying on a formatted display string.
Resolve local input with a named zone resolve-local-time
import { Temporal } from '@js-temporal/polyfill';
const local = Temporal.PlainDateTime.from('2026-11-02T09:30');
try {
const appointment = local.toZonedDateTime(
'America/New_York',
{ disambiguation: 'reject' },
);
console.log(appointment.toInstant().toString());
} catch (error) {
if (!(error instanceof RangeError)) throw error;
console.error('Check the local time and time zone.');
}ToInstantOptions defines reject for ambiguous or nonexistent local times. The sample date is ordinary input, not an executed transition test, and the application must decide how to ask for a correction.
Give an exact event a display zone display-event-in-zone
import { Temporal } from '@js-temporal/polyfill';
const event = Temporal.Instant.from('2026-11-02T14:30:00Z');
const display = event.toZonedDateTimeISO('America/New_York');
console.log(display.toString());The declared conversion takes a time-zone identifier. Keep the event timestamp and the chosen display zone explicit in your application data.
Ask for a calendar-day difference difference-calendar-dates
import { Temporal } from '@js-temporal/polyfill';
const start = Temporal.PlainDate.from('2026-11-02');
const end = Temporal.PlainDate.from('2026-11-09');
const gap = start.until(end, { largestUnit: 'day' });
console.log(gap.days);PlainDate.until returns a Duration and accepts calendar units. This example asks a calendar-date question, not how many elapsed hours separate two zoned events.
Serialize and restore an event timestamp roundtrip-event-string
import { Temporal } from '@js-temporal/polyfill';
const event = Temporal.Instant.from('2026-11-02T14:30:00Z');
const payload = JSON.stringify({ at: event.toString() });
const decoded = JSON.parse(payload);
const restored = Temporal.Instant.from(decoded.at);
console.log(restored.equals(event));Instant declares string conversion, parsing and equality methods. Validate the surrounding JSON shape at the boundary; parsing a timestamp does not validate the rest of a request.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| luxon | npm | Choose Luxon when common or custom parsing and formatting are your main requirement. Its README documents those formats alongside DateTime, Duration and Interval types. |
| date-fns | npm | Choose date-fns when you want helpers around native Date values. Its README documents native dates and modular imports, so you can keep that existing data model. |
More utils guides
lru-cache · type-fest · ajv · p-limit · zod · find-up · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.