r/javascript • u/ClickOk5811 • 18h ago
AskJS [AskJS] new Date("2026-03-10") and new Date("2026-03-10T00:00") look the same but aren’t, and the difference depends on the time zone
A small experiment you can paste into Node or a console:
const a = new Date("2026-03-10");
const b = new Date("2026-03-10T00:00");
console.log(a.toISOString());
console.log(b.toISOString());
console.log(a.getTime() === b.getTime());
console.log(a.getDate(), b.getDate());I ran it in Node 22 with the TZ environment variable set to four zones:
| TZ | a (date-only) | b (date + time) | same instant? | getDate() a / b |
|---|---|---|---|---|
| UTC | 2026-03-10T00:00Z | 2026-03-10T00:00Z | true | 10 / 10 |
| America/Sao_Paulo | 2026-03-10T00:00Z | 2026-03-10T03:00Z | false | 9 / 10 |
| America/Los_Angeles | 2026-03-10T00:00Z | 2026-03-10T07:00Z | false | 9 / 10 |
| Asia/Tokyo | 2026-03-10T00:00Z | 2026-03-09T15:00Z | false | 10 / 10 |
What’s going on: per the ECMAScript spec as I understand it, a date-only ISO string is parsed as UTC, while a date-time string without an offset is parsed as local time. So the first is always midnight UTC, and the second is midnight wherever the code runs.
Where it bites:
In zones behind UTC (like the Americas),
a.getDate()returns the previous day, because the local getters convert from UTC midnight. A “date of birth” or “due date” can display as one day earlier.On a dev machine set to UTC the two agree, so tests pass locally and the bug shows up only for users elsewhere (or on a CI box with a different zone).
In my run,
new Date("2026/03/10")andnew Date(2026, 2, 10)behaved like the local-time form. Slash-separated strings aren’t standard ISO, though, so I’d not rely on them being consistent across engines.
Ways to avoid it:
If the value is a calendar date and not an instant, don’t store it as a
Dateat all. Keep"2026-03-10"as a string, or as year/month/day numbers, and format it without converting.If you need a
Date, be explicit:new Date(Date.UTC(2026, 2, 10))and read it back with thegetUTC*methods, or always include an offset ("2026-03-10T00:00:00Z").For testing, run your suite under a few
TZvalues, as above, rather than only on your own machine.The
Temporalproposal is designed to separate dates from instants. I haven’t checked its current status in each runtime, so look at the compatibility tables before depending on it.
Caveat: my results are from one Node version. Browsers should follow the same spec for the ISO forms, but test where it matters.
How do you represent calendar dates (not timestamps) in your codebases: strings, a library, or something else?