JavaScript Dates

A Pragmatic Tour of `Date` and `Intl`

JavaScript Dates

JavaScript's `Date` type is famously awkward, but for everyday work it's enough — especially paired with `Intl.DateTimeFormat`.

5 min read Level 2/5 #Date#Intl#time
What you'll learn
  • Create and read dates
  • Format with `Intl.DateTimeFormat` and `toISOString`
  • Avoid the common timezone and month-index bugs

A Date represents a moment in time as a number of milliseconds since 1970-01-01 UTC (“the epoch”). It has a reputation for awkwardness, but the common-case API is short.

Creating Dates

Four ways to make a Date script.js
const now = new Date();                      // right now
const a   = new Date("2026-05-11");          // ISO string
const b   = new Date(2026, 4, 11);           // year, month (0-indexed!), day
const c   = new Date(0);                     // 1970-01-01 UTC

console.log(now.toISOString());
▶ Preview: console

Reading Components

Year, month, day, time script.js
const d = new Date("2026-05-11T15:30:00");

console.log(d.getFullYear());   // 2026
console.log(d.getMonth());      // 4 (May — zero-indexed)
console.log(d.getDate());       // 11 (day of month)
console.log(d.getDay());        // day of week (0 = Sunday)
console.log(d.getHours());      // 15  (local time)
console.log(d.getUTCHours());   // hours in UTC
▶ Preview: console

Comparing and Arithmetic

Dates compare with < and >, but === doesn’t work (object identity).

Subtract to get a duration script.js
const a = new Date("2026-05-11");
const b = new Date("2026-05-15");

const ms = b - a;
const days = ms / (1000 * 60 * 60 * 24);
console.log(days);   // 4
▶ Preview: console

To add or subtract, use millisecond math or set* methods:

Add 7 days script.js
const d = new Date("2026-05-11");
d.setDate(d.getDate() + 7);
console.log(d.toISOString().slice(0, 10));   // "2026-05-18"
▶ Preview: console

setDate(35) correctly rolls over into the next month — the API handles overflow.

Formatting

For machine-readable output (logs, APIs), use toISOString().

ISO 8601 — always UTC, always sortable script.js
const d = new Date();
console.log(d.toISOString());
// "2026-05-11T12:34:56.789Z"
▶ Preview: console

For human-readable output, use Intl.DateTimeFormat:

Locale-aware formatting script.js
const d = new Date("2026-05-11T15:00:00Z");

console.log(d.toLocaleDateString("en-US"));   // "5/11/2026"
console.log(d.toLocaleDateString("en-GB"));   // "11/05/2026"
console.log(d.toLocaleDateString("ja-JP"));   // "2026/5/11"

console.log(d.toLocaleString("en-US", {
  dateStyle: "full",
  timeStyle: "short",
}));
// "Monday, May 11, 2026 at 3:00 PM"
▶ Preview: console

Relative Time

Intl.RelativeTimeFormat gives you “3 days ago”, “in 2 hours”.

Relative time script.js
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
console.log(rtf.format(-1, "day"));   // "yesterday"
console.log(rtf.format(3, "day"));    // "in 3 days"
console.log(rtf.format(2, "month"));  // "in 2 months"
▶ Preview: console

Timezone Pitfall

new Date("2026-05-11") parses as UTC midnight; new Date("2026-05-11T00:00") parses as local midnight. The two can be a full day apart depending on the user’s timezone.

Up Next

Numbers come with a sidekick — the Math global.

JavaScript Math →