How to Use This Tool
Paste a string to see how it is really interpreted, or use the builder to produce one. Everything is computed in your browser, using your machine's timezone for the local readings.
The rule almost nobody knows
ISO 8601 and the JavaScript specification agree on something surprising:
- A date-only string like
2026-08-10is treated as UTC. - A date-time string with no offset like
2026-08-10T00:00is treated as local time.
So adding a time to a string changes which timezone it means. In UTC+8 the two examples above are
eight hours apart and sit on different calendar dates when expressed in UTC. This is the source
of the classic off-by-one-day bug: a date picker hands you 2026-08-10, you store it, and a
user in the Americas sees the 9th.
The fix is to be explicit. Always include an offset — Z if you mean UTC,
or +08:00 if you mean a real place. A string with an offset means exactly one instant, and
no reader has to guess.
Silent rollovers, which are worse than errors
An impossible date does not raise an error in JavaScript. It quietly moves:
2026-02-30becomes 2 March 20262026-02-29becomes 1 March 2026, because 2026 is not a leap year2026-13-01is rejected, because the month is out of range rather than the day
This tool flags a rollover explicitly, because a value that changes itself and reports success is harder to find than one that throws.
Forms that are valid ISO 8601 but not valid JavaScript
ISO 8601 is a large standard and browsers implement a subset. Verified in Node:
- Week dates —
2026-W33-1is legal ISO 8601 and returnsInvalid Date. - Basic format —
20260810T123456Z, the separator-free form, is also rejected. Only the extended form with hyphens and colons works. - Leap seconds —
2016-12-31T23:59:60Zis a real second that existed, and JavaScript has no representation for it.Invalid Date. - A space instead of T —
2026-08-10 00:00is not in the spec, yet every major browser accepts it. Convenient, and not portable to other parsers.
Fractional seconds are truncated, not rounded
JavaScript dates carry millisecond precision. Give one six digits and the extra three are discarded:
2026-08-10T12:34:56.789123Z becomes ...56.789Z. If you are reading microsecond
timestamps out of a database, the sub-millisecond part is gone the moment it enters a
Date, silently.
Which form to store
For an instant — when something happened — store UTC with an explicit
Z. It is unambiguous, sorts correctly as text, and survives being read anywhere.
For a calendar date — a birthday, a due date, a public holiday — store the plain date and no time at all. Attaching midnight in some timezone is what causes it to drift by a day. A birthday is not an instant.