ISO 8601 and RFC 3339 formats

The exact syntax for dates, times, offsets and fractions, what the Z suffix means, how to write durations and intervals, and why interchange should use these formats and nothing else.

The pieces of a timestamp

2026-09-18                          date only
10:30:00                            time, no zone
2026-09-18T10:30:00Z                UTC (Z means the same as +00:00)
2026-09-18T10:30:00+02:00           offset from UTC
2026-09-18T10:30:00.123Z            fractional seconds
2026-09-18T10:30:00.123456789Z      nanoseconds
2026-09-18T10:30:00+02:00[Europe/Paris]   offset plus IANA zone (RFC 9557)
2026-W38-5                          ISO week date
2026-261                            ordinal date: day 261 of 2026
  • The separator is T between date and time; lowercase t is allowed by the standard but rarely accepted by parsers.
  • Z and +00:00 mean the same instant. Pick one — Z is shorter and conventional for UTC.
  • Fractional seconds use a dot, not a comma, and any number of digits is valid.
  • An offset of -00:00 has a specific meaning in RFC 3339: the time is UTC but the local offset is unknown.

Durations, intervals and recurrence

FormMeaningExample
PnYnMnDTnHnMnSDurationP1Y2M3DT4H5M6S
PnWWeeksP2W (same as P14D)
start/endInterval2026-09-18T00:00Z/2026-09-19T00:00Z
start/durationInterval2026-09-18T00:00Z/P1D
R[n]/start/durationRecurringR5/2026-09-18T09:00Z/P1D
R/start/durationUnbounded repeatR/2026-09-18T09:00Z/P1W
// JavaScript has no duration parser; Temporal does (shipping in modern runtimes)
const d = Temporal.Duration.from("P1Y2M3DT4H5M6S");
const later = Temporal.PlainDate.from("2026-01-31").add({ months: 1 });
later.toString();  // "2026-02-28" — clamped, not overflowing into March

The T is required when a duration has a time part, because P1M is ambiguous otherwise: M before the T is months, after it is minutes.

Why this and not something else

  • It sorts correctly as plain text, so string comparison equals chronological comparison for equal offsets.
  • It is unambiguous: no one has to guess whether 03/04 is March or April.
  • It includes the offset, which a local format like 18/09/2026 10:30 cannot express.
  • Every mainstream language parses it out of the box.
# RFC 3339 requires an uppercase T and Z, and a fixed-width offset
date -u +%Y-%m-%dT%H:%M:%SZ
# 2026-09-18T10:30:00Z

# GNU date can round-trip an offset into a zone
TZ=Europe/Paris date -d '2026-09-18T10:30:00+02:00' +%Y-%m-%dT%H:%M:%S%:z
⚠️
RFC 3339 permits a wider offset range than ISO 8601, and ISO 8601 permits forms RFC 3339 forbids (a comma for fractions, an offset without a colon). Choose RFC 3339 for protocols: it is a strict subset that every parser agrees on.

FAQ

Should I store nanoseconds?
Only if you need them. Databases commonly truncate to microseconds or milliseconds, so a nanosecond-precision input will not round-trip. Decide the precision and document it.
What does the Z mean?
It is the zone designator for UTC, from the military convention of calling UTC Zulu time. It is equivalent to an offset of +00:00.

Parsing and formatting dates safely Calendars, leap years and leap seconds

Last refreshed 2026-09-18.