JavaScript Date Table

PieceSyntaxField note
new Date()Now, local zoneInternally UTC epoch MILLISECONDS wearing a calendar costume
month is 0-indexednew Date(2026, 9, 4)= October 4 - the perpetual JavaScript trap
string parsingnew Date('2026-10-04')ISO date-only parses as UTC midnight; 2026/10/04 parses LOCAL - same day, different instant
gettersgetFullYear/getMonth/getDateLOCAL zone readings - getMonth() + 1 for humans
Date.now()Milliseconds epochFastest timestamp; a day is 86400000 ms - do arithmetic in ms
toISOString()2026-10-04T...ZAlways UTC - THE storage and API exchange format
Intl.DateTimeFormatnew Intl.DateTimeFormat('de-DE')Locale names and order for free - one instant, many renderings
mutationsetDate/setMonth mutateIn place - copy first with new Date(old) or your history rewrites itself
Reference: the MDN Date reference. Date is a millisecond counter with calendar helpers: internally UTC epoch ms, and everything else is RENDERING. The parsing trap decides production bugs - date-only ISO strings parse as UTC midnight while slash formats parse local, so the same calendar date can arrive as yesterday across time zones. Bottom line: store and transmit toISOString(), format with Intl.DateTimeFormat (names and order per locale for free), never mutate a Date you kept (copy with new Date(old)), and validate with isNaN(d.getTime()) - Invalid Date is just NaN underneath. Related tools: timers table (scheduling against the clock), event loop table (when timer callbacks actually run), and world clock (Intl time zones live).

Date is a millisecond counter wearing a calendar costume: internally every Date is UTC epoch milliseconds, and everything else - years, months, hours - is rendering on top. Hold that model and the API's oddities stop being random: getters read your LOCAL zone, toISOString always renders UTC.

Bottom line: the month is 0-indexed (9 means October), and string parsing splits on format - date-only ISO ('2026-10-04') parses as UTC midnight while slash format ('2026/10/04') parses as local time, so the same calendar date can land on different INSTANTS and come back a day off across time zones.

The honest part: Date objects MUTATE in place. setDate() on a stored deadline silently rewrites your deadline. The working discipline: store and transmit toISOString(), format at display time with Intl.DateTimeFormat, and copy (new Date(old)) before any set* call - or reach for a date library that makes values immutable.

How to use

  1. Exchange in ISO only: d.toISOString() for storage and APIs; parse only full ISO strings with explicit time and Z - never the slash formats in code that crosses zones.
  2. Render with Intl: new Intl.DateTimeFormat('de-DE', {dateStyle: 'long'}).format(d) - correct names, order and calendar per locale, no hand-built month arrays.
  3. Do calendar math in ms: Date.now() + 86400000 for tomorrow; for month arithmetic use new Date(y, m + 1, d) and let the constructor normalize overflow (month 13 = January next year).

Frequently asked questions

Why do JavaScript months start at zero?

Historical implementation, kept for backwards compatibility forever: the original API indexed month names into an array (0 = January), while day-of-month and year use natural numbers - an inconsistency inside the same constructor call. new Date(2026, 9, 4) is October 4, not September 4, and it bites even experienced developers every autumn. Practical defenses: prefer ISO strings or {year, month: 10} object arguments where available, name your constants (const OCT = 9), and treat any literal month number in code review as a bug until proven otherwise.

Why does new Date('2026-10-04') differ from new Date('2026/10/04')?

The two strings take different specification paths. Date-only ISO format is standardized to parse as UTC midnight; the slash format is implementation-defined and engines parse it as LOCAL time. Both create 'October 4' on the machine that parsed them, but they are different INSTANTS - 24 hours apart in half the world - so rendering either with toISOString() or in another time zone can show October 3. The portable contract: send and store full ISO with time and zone designator (2026-10-04T12:00:00Z), and parse nothing else in cross-zone code.

When do I need UTC getters instead of local ones?

When the moment, not the local reading, is the data. getHours() reads the user's time zone - right for showing 'your local time'; getUTCHours() reads the instant - right for comparing, bucketing by day, or rendering a schedule fixed to one zone. The cross-zone bug shape: storing local readings as if universal, then re-reading them on a server in UTC, shifts everything by the zone difference. Rule: persist instants (ISO-UTC), convert to local only at the display edge, and never round-trip local getters through storage.

How dangerous is Date mutation, really?

Dangerous precisely because it looks harmless. date.setDate(date.getDate() + 1) works - and if date is a stored deadline, a cached prop, or shared state, you just rewrote it everywhere it is referenced. The failures surface days later as deadlines a day early or recurring events that drift. The discipline: treat Dates as mutable handles to immutable values - copy before every set* call (new Date(old)), or never mutate and always derive new instances. Libraries like date-fns encode exactly this value semantics, which is a large part of why teams adopt them even for simple calendars.

Related tools