← Back to course

What is Temporal?

Temporal is a new built-in JavaScript API that replaces Date. It fixes the long-standing problems with Date that JavaScript developers have struggled with for decades.

All Temporal objects are immutable. Operations return new objects instead of mutating in place. Temporal has first-class timezone support, handles DST (Daylight Saving Time) correctly, and separates concepts like "a date without a time" or "a time without a date" into distinct types. Parsing and formatting are unambiguous and locale-safe.

Key Types

Instead of one overloaded Date object, Temporal provides focused types:

Temporal TypeWhat it representsDate equivalent
Temporal.PlainDateA calendar date — no time, no timezone
e.g. "2025-06-15"
new Date() (date parts only)
Temporal.PlainTimeA wall-clock time — no date, no timezone
e.g. "09:30:00"
No direct equivalent
Temporal.PlainDateTimeA date + time — no timezone
e.g. "2025-06-15T09:30:00"
new Date() (ignoring timezone)
Temporal.ZonedDateTimeA date + time + timezone
e.g. "2025-06-15T09:30:00[Europe/Paris]"
new Date() (closest equivalent)
Temporal.InstantAn exact point in time (like a Unix timestamp)Date.now()
Temporal.DurationA length of time (years, months, days, hours…)Raw millisecond math
Temporal.NowNamespace for getting the current date/timenew Date() / Date.now()

Common Operations

Get today's date

With Date, getMonth() returns 0 for January — a well-known footgun. Temporal uses 1-based months everywhere.

const today = new Date();
const year = today.getFullYear();
const month = today.getMonth() + 1; // 0-indexed!
const day = today.getDate();
console.log(year);
console.log(month);
console.log(day);
const today = Temporal.Now.plainDateISO();
console.log(today.year);
console.log(today.month); // 1-indexed
console.log(today.day);
console.log(today.toString());

Get current date + time with timezone

ZonedDateTime bundles the date, time, and timezone name into one object. With Date you only get a UTC timestamp — the timezone name requires a separate Intl call.

const now = new Date();
// timezone name requires Intl
const tz = Intl.DateTimeFormat()
  .resolvedOptions().timeZone;
console.log(tz);
const now = Temporal.Now.zonedDateTimeISO();
console.log(now.timeZoneId);

Create a specific date

Use Temporal.PlainDate.from() with either an object or an ISO string. Both give you a fully typed date with named properties — no more accidentally creating April when you meant March!

// Month is 0-indexed — January = 0!
const d = new Date(2025, 5, 15); // June 15
console.log(d.getMonth()); // 5, not 6
console.log(d.toDateString());
// Month is 1-indexed — June = 6
const d = Temporal.PlainDate.from({
  year: 2025, month: 6, day: 15
});
console.log(d.month); // 6
console.log(d.toString()); // "2025-06-15"

Add / subtract a duration

Temporal objects are immutable — .add() and .subtract() always return a new date. Date mutations like setMonth() modify the original and are DST bug-prone.

const d = new Date(2025, 0, 1);
// setMonth mutates in place
d.setMonth(d.getMonth() + 3);
console.log(d.toDateString());

const d2 = new Date(2025, 0, 1);
d2.setDate(d2.getDate() - 7);
console.log(d2.toDateString());
const today = Temporal.PlainDate.from("2025-01-01");
// Returns a NEW date (immutable)
const future = today.add({ months: 3 });
const past = today.subtract({ days: 7 });
console.log(today.toString());
console.log(future.toString());
console.log(past.toString());

Compare two dates

Temporal.PlainDate.compare() returns -1, 0, or 1 — perfect for sorting. Use .equals() for equality. With Date you must compare .getTime() values to avoid reference equality traps.

const a = new Date("2025-01-01");
const b = new Date("2025-06-15");
// Must use getTime() for safe comparison
console.log(a.getTime() < b.getTime()); // true
console.log(a.getTime() === b.getTime()); // false
const a = Temporal.PlainDate.from("2025-01-01");
const b = Temporal.PlainDate.from("2025-06-15");
// compare() returns -1, 0, or 1
console.log(Temporal.PlainDate.compare(a, b)); // -1
console.log(Temporal.PlainDate.compare(b, a)); // 1
console.log(a.equals(b)); // false

Difference between two dates

.until() returns a Temporal.Duration. Use largestUnit to control the breakdown — ask for months and days, or just days. With Date you get raw milliseconds and have to do the math yourself.

const a = new Date("2025-01-01");
const b = new Date("2025-06-15");
// Raw ms — must do manual math
const diffMs = b - a;
const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24));
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
console.log(rtf.format(diffDays, "day"));
const a = Temporal.PlainDate.from("2025-01-01");
const b = Temporal.PlainDate.from("2025-06-15");
const diff = a.until(b, { largestUnit: "days" });
console.log(diff.days + " days");

Format a date

Temporal types expose .toLocaleString() directly with the same Intl.DateTimeFormat options you already know. No need to wrap in a separate formatter object.

const d = new Date("2025-06-15");
const formatted = new Intl.DateTimeFormat("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
}).format(d);
console.log(formatted);
const d = Temporal.PlainDate.from("2025-06-15");
const formatted = d.toLocaleString("en-US", {
  year: "numeric",
  month: "long",
  day: "numeric"
});
console.log(formatted);

Work with durations

Temporal.Duration is a first-class type for spans of time. It uses the ISO 8601 duration format (P1M15D) and can be added to any Temporal date. Date has no Duration concept at all.

// No Duration type — raw milliseconds only
const ms = 1000 * 60 * 60 * 24 * 30; // ~30 days
const start = new Date("2024-01-01");
const end = new Date(start.getTime() + ms);
console.log(end.toDateString());
const dur = Temporal.Duration.from({ months: 1, days: 15 });
console.log(dur.months);
console.log(dur.days);
console.log(dur.toString()); // "P1M15D"
const start = Temporal.PlainDate.from("2024-01-01");
console.log(start.add(dur).toString());

Browser Support

Temporal is already shipping in major browsers:

Chrome
144+
Supported
Firefox
139+
Supported
Safari
In progress
Coming soon

Need wider support today? Use the official polyfill:

npm install @js-temporal/polyfill

Then import it at the top of your app:

import { Temporal } from "@js-temporal/polyfill";

Learn JavaScript in depth

Master JavaScript from scratch — arrays, objects, async, and more — with interactive exercises and instant feedback.

Start learning for free
Learn JavaScript LogoPowered by Learn JavaScript