ISO 8601 Explained

· formatsfundamentals

The interchange format that removes ambiguity from dates — and the handful of its features you should never use.

ISO 8601 is the international standard for writing dates and times as text. Its purpose is to make a date string mean exactly one thing regardless of who reads it.

The canonical form:

2024-03-10T06:00:00Z

Reading it left to right: year, month, day, the letter T, hour, minute, second, and Z for UTC.

The parts

2024-03-10 — Year, month, day, always in that order, always zero-padded, always four-digit years. Big-endian ordering means lexical sorting equals chronological sorting, which is why ISO dates are the correct choice for filenames and log prefixes.

T — A literal separator between the date and the time. It exists so parsers do not have to guess whether a space is a separator or padding. RFC 3339 permits a space instead for human readability, but T is the safe default.

06:00:00 — Hours (00–23), minutes, seconds. Fractional seconds are appended with a dot: 06:00:00.123.

Z — “Zulu”, meaning UTC, offset zero. Alternatively an explicit offset: +05:30, -08:00.

The offset is not optional

This is the part that matters most in practice:

2024-03-10T06:00:00Z         unambiguous — UTC
2024-03-10T11:30:00+05:30    unambiguous — same instant, written locally
2024-03-10T11:30:00          AMBIGUOUS  — whose clock?

A string without an offset is a local time with no indication of which locality. Different systems resolve it differently — some assume UTC, some assume the server’s zone, JavaScript historically assumed local for date-time strings but UTC for date-only strings. If you emit a timestamp without an offset, you have not specified an instant, and any two systems that disagree about the default will disagree about your data.

Always include Z or an explicit offset.

Offset is still not a time zone

An offset says what the clock read; it does not say where. +05:30 tells you the local clock was five and a half hours ahead of UTC, but not whether that was Kolkata, and not what the offset will be next July.

This distinction matters when storing a future local time. “9 a.m. on 3 November in New York” cannot be stored as an offset, because whether that is -04:00 or -05:00 depends on daylight saving rules that may change before the date arrives. Store the IANA zone name (America/New_York) alongside the local time. See time zones vs UTC offsets.

Durations and intervals

ISO 8601 also covers durations, prefixed with P (period):

P1Y2M10D        1 year, 2 months, 10 days
PT2H30M         2 hours, 30 minutes   (T separates time parts)
P1DT12H         1 day and 12 hours

The T rule catches people out: P1M is one month, PT1M is one minute.

Intervals join two points with a solidus:

2024-03-10T06:00:00Z/2024-03-11T06:00:00Z
2024-03-10T06:00:00Z/P1D

Features to avoid

The standard is much larger than the part everyone uses, and several corners are best left alone:

Week dates. 2024-W10-7 is the seventh day of ISO week 10. ISO weeks start on Monday and week 1 is the one containing the first Thursday — which means early January can fall in the previous ISO year. 2027-01-01 is in ISO week 53 of 2026. Correct, and a reliable source of off-by-one bugs.

Ordinal dates. 2024-070 is the 70th day of 2024. Rarely supported, easily confused with a malformed date.

Basic format. 20240310T060000Z without separators is legal and much harder to read. Fine inside filenames where colons are prohibited; not for interchange.

Two-digit years and truncated forms. Some editions permit 24-03-10. Do not.

24:00:00. Legal, and means midnight at the end of the stated day — so 2024-03-10T24:00:00Z equals 2024-03-11T00:00:00Z. Many parsers reject it. Never emit it.

Negative zero offset. -00:00 is prohibited by ISO 8601 but has a specific meaning in RFC 3339: the offset is unknown. Avoid emitting it; handle it on input.

Practical rules

  1. Emit YYYY-MM-DDTHH:MM:SSZ — UTC with Z — unless you have a reason not to.
  2. Always include an offset or Z. Never emit a naked local time.
  3. Store the IANA zone name separately when the local wall-clock time is what matters.
  4. Use a library to parse. Hand-rolled regexes miss fractional seconds, negative offsets or Z on the first input that includes one.
  5. For strict interoperability target RFC 3339, which is a tightened profile of ISO 8601 — see RFC 3339 vs ISO 8601.

Converting to and from epoch

The converter shows the ISO 8601 form of any timestamp, in whichever zone you select. Going the other way in code:

new Date('2024-03-10T06:00:00Z').getTime() / 1000;   // 1710050400
from datetime import datetime
datetime.fromisoformat('2024-03-10T06:00:00+00:00').timestamp()

More languages on the code examples pages.

Related guides