Temporal Makes JavaScript Date and Time Explicit
A stored birthday can display as the previous day because JavaScript `Date` converts a calendar date into a timestamp. Temporal provides separate types for calendar dates, exact timestamps, and scheduled events.
An application stores a customer's birthday as 1990-06-15. The value is a calendar date, so it should remain June 15 wherever the application displays it. Instead, JavaScript parses that date-only string as midnight in Coordinated Universal Time (UTC), the global time standard. A browser west of UTC can then render June 14 in local time. Nothing in the stored value represents an exact point on the global timeline, but the conversion imposes one.
This is not one kind of date and time data. A birthday needs a calendar date only. An audit-log entry needs the exact moment an event occurred. A meeting needs a local clock time plus the time-zone rules for its location. A duration needs neither a date nor a time zone. Date can be used for all of these values, but the API does not show which one the application intends to represent. That intent is often supplied later during parsing, conversion, or display, where an incorrect assumption changes the result.
Temporal is the newer JavaScript date and time API designed to replace Date. It uses distinct types for calendar values, exact timestamps, time zones, and durations. MDN's Temporal reference documents the available types and their distinct responsibilities.
The Short Version
Temporal is JavaScript's date and time API for representing the value that the application actually has. Use Temporal.Instant for one exact point in time, Temporal.PlainDate for a calendar date without a time zone, and Temporal.ZonedDateTime for a local date and time in a named region.
The design does not make time-zone decisions disappear. It makes them explicit at the boundary where they matter.
Why Date Produces Ambiguous Code
Date represents an instant internally, as milliseconds from the Unix epoch. It also exposes local calendar getters, UTC getters, mutation methods, and string parsing through the same object. A date-only string is therefore easy to mistake for a calendar value when it is actually being interpreted as a timestamp.
Mutation is the other routine failure mode. A first attempt to make date arithmetic safe often clones the input before calling setDate. That avoids aliasing, but it does not document whether the operation means "seven calendar days" or "168 elapsed hours", and it leaves every caller responsible for copying correctly.
function addOneWeek(date) {
// This changes the object held by every reference to `date`.
date.setDate(date.getDate() + 7);
return date;
}
const today = new Date("2026-03-21T00:00:00Z");
const nextWeek = addOneWeek(today);
console.log(today === nextWeek); // true
console.log(today.toISOString()); // 2026-03-28T00:00:00.000ZThe culprit is date.setDate(...): it mutates the object that every reference shares. The unexpected part is not the arithmetic. It is that the caller's today value is now a week later.
For a calendar date, the direct Temporal replacement uses PlainDate. Its add() method returns a new value, so the original remains available without defensive copying.
const today = Temporal.PlainDate.from("2026-03-21");
const nextWeek = today.add({ weeks: 1 });
console.log(today.toString()); // 2026-03-21
console.log(nextWeek.toString()); // 2026-03-28Choose a Type That Matches the Data
Temporal values are immutable. Methods such as add() return a new value and preserve the input. The application also chooses a type that carries the data needed for its rule, rather than treating every value as an instant.
| Value the system has | Temporal type | Example |
|---|---|---|
| A date on a calendar | Temporal.PlainDate | A birthday or invoice due date |
| A clock time without a date | Temporal.PlainTime | A daily 09:00 opening time |
| Local date and time, no zone yet | Temporal.PlainDateTime | A form value awaiting a selected region |
| A specific real-world event in a region | Temporal.ZonedDateTime | A meeting in Asia/Jakarta |
| One exact point on the global timeline | Temporal.Instant | An audit-log timestamp |
| An amount of time | Temporal.Duration | A 90-minute session |
The distinction between PlainDateTime and ZonedDateTime is useful during daylight-saving changes. A local time can be skipped or occur twice in regions that change their clocks. A named IANA time-zone identifier, such as America/New_York, gives Temporal the rules needed to resolve the local value. The Temporal documentation describes the configurable disambiguation policy for these conversions.
A Small Scheduling Example
This example keeps a weekly meeting in the organiser's time zone, then produces an exact timestamp that can be stored or compared with other events.
const meeting = Temporal.ZonedDateTime.from({
timeZone: "Asia/Jakarta",
year: 2026,
month: 10,
day: 25,
hour: 9,
minute: 0,
});
// add() returns a new value. `meeting` remains 2026-10-25 at 09:00.
const followingWeek = meeting.add({ weeks: 1 });
const eventId = followingWeek.toInstant().toString();
console.log(meeting.toString());
// 2026-10-25T09:00:00+07:00[Asia/Jakarta]
console.log(followingWeek.toString());
// 2026-11-01T09:00:00+07:00[Asia/Jakarta]
console.log(eventId);
// 2026-11-01T02:00:00ZThe input remains a zone-aware wall-clock event. toInstant() is the deliberate conversion that produces a globally comparable timestamp. For a birthday, use Temporal.PlainDate instead and do not convert it to an instant unless the product has a real reason to attach a region and time.
When Temporal Is the Right Choice
Use Temporal for new application code that handles scheduling, bookings, expiry rules, international users, or domain values such as birthdays and billing cycles. It also reduces defensive copying in business logic because the values are immutable.
Use Instant at infrastructure boundaries where ordering and durable timestamps matter. Use plain types in forms and domain models when a time zone is unknown or irrelevant. Add a time zone only when converting a local human value into an actual event.
When Not To Use It Yet
Temporal is not Baseline. As of September 2026, MDN Browser Compatibility Data lists support from Chrome 144 and Firefox 139, with Edge mirroring Chromium support. Safari is listed as Preview only, and iOS Safari as unsupported. A public website that supports Safari cannot assume globalThis.Temporal exists.
For that audience, use the official @js-temporal/polyfill or retain an established date library while planning the migration. The polyfill adds payload and integration work, so a small internal service that only handles UTC timestamps may reasonably keep using Date or numeric epoch timestamps until its requirements grow.
Gotchas During Adoption
The first gotcha is treating Temporal as a drop-in Date replacement. It is intentionally not one. Each Date field needs a domain decision: is it an instant, a calendar date, or a local time paired with a zone?
The second is relying on the host machine's time zone. Pass an IANA identifier when scheduling or interpreting user-entered local times. A fixed offset such as +07:00 cannot express historical or future rule changes for a place.
The third is string persistence. Preserve enough context for the type. An Instant string such as 2026-11-01T02:00:00Z is suitable for an exact event, but it cannot recover the original organiser's time zone. A ZonedDateTime string retains that context.
Alternatives
Date remains appropriate for existing code and simple UTC timestamp work, especially where native Temporal support or a polyfill is not available. Intl.DateTimeFormat remains the standard choice for localized display formatting, and it works alongside Temporal values.
Libraries such as date-fns, Luxon, and Day.js are still valid options where their ecosystem integration or browser support solves an immediate need. Temporal's advantage is that the core semantic types are part of the JavaScript platform, rather than each codebase choosing a different convention.
Takeaway
Temporal makes a large class of date bugs easier to avoid by requiring code to distinguish a calendar value, a local time, and an exact instant before conversion hides that distinction.
References
- Bitter, Dave. "Temporal: JavaScript Finally Fixes Dates." iO tech_hub.
- MDN Web Docs. "Date." Mozilla.
- MDN Web Docs. "Temporal." Mozilla.
- MDN Web Docs. "Temporal.PlainDate.prototype.add()." Mozilla.
- MDN Browser Compatibility Data. "Temporal API." MDN.
- TC39 Temporal. "Temporal Documentation." Ecma International.
- TC39 Temporal. "Time Zones and Resolving Ambiguity." Ecma International.
- TC39. "Temporal Polyfill." GitHub.