Dates and time
Timestamps, UTC vs local, Date's traps, calculations and formatting with Intl.
Updated
What a Date is in JavaScript
A Date object represents a moment in time — internally, just a number: the milliseconds since January 1, 1970, 00:00 UTC (a timestamp).
const now = new Date()
now.getTime() // 1791460800000 — the timestamp
Date.now() // the same thing, without an objectEverything you see as "October 8, 2:30 PM" is an interpretation of that number in a time zone. That's where almost all date bugs come from.
UTC vs local time
| UTC | Local time | |
|---|---|---|
| What it is | the universal reference time, no time zone | the time in the device's time zone (New York: UTC−5 in winter, UTC−4 in summer) |
| Where you use it | storage, APIs, databases | only for display |
| Methods | getUTCHours(), toISOString() |
getHours(), toLocaleString() |
The golden rule: store and transmit in UTC (ISO 8601), display in local time.
new Date().toISOString() // '2026-10-08T11:30:00.000Z' — Z = UTCCreating a date — the traps
| Code | Result | Trap |
|---|---|---|
new Date('2026-10-08') |
midnight UTC | in New York it shows the evening of October 7 |
new Date('2026-10-08T10:00') |
local time | no Z = local |
new Date('2026-10-08T10:00Z') |
UTC time | explicit, safe |
new Date(2026, 9, 8) |
October 8 | months start at 0 |
new Date('08/10/2026') |
depends on the browser | an ambiguous format — don't use it |
Calculations
const DAY = 24 * 60 * 60 * 1000
const diffDays = Math.round((b - a) / DAY) // subtracting two Dates gives milliseconds
const tomorrow = new Date(date)
tomorrow.setDate(tomorrow.getDate() + 1) // handles the end of the month correctlyCareful: Date is mutable — setDate changes the object. Copy it first (new Date(date)).
Daylight saving time makes a day last 23 or 25 hours — that's why there's a Math.round on the day difference.
Display with Intl
new Intl.DateTimeFormat('en-US', { dateStyle: 'long' }).format(date) // 'October 8, 2026'
new Intl.DateTimeFormat('en-US', { dateStyle: 'short', timeStyle: 'short' }).format(date)
new Intl.DateTimeFormat('en-US', { timeZone: 'Europe/London', timeStyle: 'short' }).format(date)
new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(-1, 'day') // 'yesterday'
new Intl.RelativeTimeFormat('en', { numeric: 'auto' }).format(3, 'day') // 'in 3 days'Dates in Next.js — hydration
A date formatted on the server (the server's time zone, usually UTC) and again on the client (the user's time zone) can produce different text → a hydration error. Solutions: format with a fixed timeZone, or show relative / local time only in a Client Component, after mounting.
Libraries and the future
| Option | When |
|---|---|
Date + Intl |
most applications |
| date-fns | lots of calculations (add months, start of week) — small, tree-shakable functions |
| Temporal (native, new) | a modern, immutable API with correct time zones — the replacement for Date, rolling out to browsers |
Summary
Date= a UTC timestamp; everything else is an interpretation in a time zone.- Store in UTC / ISO, display locally with
Intl; months start at 0;Dateis mutable. - Watch out for hydration in Next; date-fns for calculations, Temporal for the future.