Skip to content

16 — Dates and time

One line: A Date is one number of milliseconds since 1970 UTC, and nearly every date bug comes from converting to and from it: months counted from zero, strings read as UTC or as local time, and a zone chosen only when printing.

The chapter starts with building a Date from numbers, where months count from zero and overflow silently, then parsing one from a string, where a missing T00:00 moves the result by hours. Time zones and formatting follow: they decide how the one stored number is shown. Then Temporal, the Stage 4 replacement that Node does not ship yet. The last page measures durations, a job for other clocks than Date. Every probe that reads local time pins TZ.

The pages

Page Level In one line Status
Date months start at zero 101 Months count from zero and days overflow without a word: new Date(2026, 1, 30) is 2 March 2026, day 0 is the last day of the month before, and adding one month to 31 January with setMonth lands on 3 March. stub
Parsing dates 201 Under TZ=America/New_York, new Date("2026-03-08") is midnight UTC but new Date("2026-03-08T00:00") is midnight local, five hours later, and strings outside the ISO format are the engine's guess: V8 reads "08/03/2026" as 3 August. stub
Time zones 201 A Date is one number, milliseconds since 1970 UTC, and the zone appears only when you read it: the same Date has getHours() 8 under TZ=America/New_York and 21 under TZ=Asia/Tokyo, and getTimezoneOffset() is positive west of UTC. stub
Formatting dates 201 toISOString() prints UTC ending in Z whatever the machine's zone, but throws RangeError on an invalid date, which JSON.stringify quietly writes as null; toLocaleString prints the same instant as 22 September in New York and 23.9.2026 in Tokyo. stub
Temporal 301 Temporal replaces Date with separate types for an instant, a calendar date and a date-time in a zone, but Node 25.2.1 does not ship it: typeof Temporal is "undefined", and --harmony-temporal does not change that. stub
Measuring elapsed time 201 Date.now() is whole milliseconds since 1970 from the system clock, performance.now() is fractional milliseconds since the process started, so it reads under a second at launch, and process.hrtime.bigint() counts nanoseconds as a BigInt. stub

Boundaries

Timers that wait for a duration belong to the async chapter; this chapter only measures durations.