ISO-8601 is the international standard for representing dates and times as text. A correctly built ISO-8601 string sorts chronologically as plain text, is unambiguous across locales, and is accepted by virtually every programming language and database. This tool lets you assemble one field-by-field and validates each part so you never emit an impossible date.
How it works
The builder zero-pads each component to the right width and joins them in the canonical order:
YYYY-MM-DDThh:mm:ss±hh:mm
Before producing output it validates the calendar date. The number of days in a month depends on the month and, for February, on whether the year is a leap year. A year is a leap year if it is divisible by 4, except century years which must also be divisible by 400:
const leap = (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;
const daysInMonth = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
The time fields are checked against their ranges (hour 0–23, minute and second 0–59), and the timezone is rendered either as Z for UTC or as a signed ±hh:mm offset. As a final check, the assembled string is passed to Date.parse to confirm the runtime accepts it.
Worked examples
Example 1 — local business hour: Year 2026, month 6, day 6, 14:30:00 at offset +01:00 produces:
2026-06-06T14:30:00+01:00
Example 2 — same instant in UTC: Switching the offset to Z and the hour to 13 represents the identical moment:
2026-06-06T13:30:00Z
Example 3 — midnight UTC at year start: Year 2027, month 1, day 1, 00:00:00 at Z:
2027-01-01T00:00:00Z
Example 4 — leap-year validation: Entering February 29, 2028 is valid (2028 is a leap year: 2028 ÷ 4 = 507, not a century year). Entering February 29, 2027 is rejected — 2027 is not a leap year.
Why you should always include the timezone designator
A datetime string without any timezone — like 2026-06-06T14:30:00 — is technically a local datetime in ISO-8601. Its meaning is entirely context-dependent. If the string is read in a different timezone than it was written, the moment it refers to changes silently.
The fix is simple: always append Z for UTC, or the explicit offset +hh:mm / -hh:mm for a local time. The builder in this tool always emits a designator, so you cannot accidentally produce an ambiguous string.
Choosing between Z and an offset
| Use Z when | Use ±hh:mm when |
|---|---|
| Storing timestamps in databases or logs | Preserving the local civil time that was in effect |
| Comparing times across regions | Displaying to users in their own timezone |
| Writing API payloads that will be consumed globally | Recording times in contexts where the local offset matters (flight schedules, legal deadlines) |
For most server-side storage and API design, UTC (Z) is the safest default. Convert to a local offset only at display time.
Common mistakes this tool prevents
- Impossible dates like
2027-02-29(not a leap year) or2026-04-31(April has 30 days) — the builder rejects these with a clear error message. - Out-of-range times like hour 25 or minute 61.
- Missing
Tseparator — a space between date and time is tolerated by some parsers but is not valid ISO-8601 extended format. The builder always usesT. - No timezone — every output from this tool includes a designator so the timestamp is unambiguous.