Who maintains ISO 8601 and what it replaced

ISO 8601 is published by the International Organization for Standardization, maintained by ISO TC 154. First released in 1988, it merged several earlier standards into a single document for numeric date and time representation. Major revisions followed in 2000, 2004, and 2019.

The 2019 revision split the standard into two parts. ISO 8601-1 covers the basic rules for date and time representation. ISO 8601-2 adds extensions: uncertain dates, sets, and features most people never touch. Software developers work with Part 1. Researchers documenting archaeological dates or data scientists flagging data quality find tools in Part 2.

The purpose is simple. A date written as 03/04/2026 means the third of April to a reader in Manchester and the fourth of March to a reader in Chicago. Nothing in the string settles the argument. ISO 8601 does.

YYYY-MM-DDThh:mm:ss: the core format

The format orders fields largest to smallest: year, month, day, then hours, minutes, seconds. All fields are zero-padded. Date and time join with a capital T.

A complete timestamp: 2026-07-29T03:14:07Z

That reads as July 29, 2026, at 3:14:07 AM UTC. Field widths never vary: four digits for the year, two each for month and day, two each for hours, minutes, seconds.

This ordering is not arbitrary. Because fields go largest to smallest, ISO 8601 strings sort correctly as plain text. Lexicographic order equals chronological order. Filenames, log entries, database keys all benefit. Try sorting 07/29/2026 or 29-07-2026 and the order breaks.

Reduced precision is legal. 2026-07 names a month. 2026 names a year. Both are valid. You can drop seconds: 2026-07-29T03:14 works.

One oddity: 24:00:00 is allowed as an end-of-day marker. It equals 00:00:00 on the following date. July 29 at 24:00 is the same instant as July 30 at 00:00. Most APIs never emit this. Schedules sometimes do.

Extended vs basic format: when to drop the hyphens

Two written styles are permitted: extended and basic.

The extended format keeps separators: hyphens between date fields, colons between time fields. This is what humans read: 2026-07-29T03:14:07Z.

The basic format drops all separators: 20260729T031407Z. Same instant, no punctuation. Shorter, still sorts correctly. You see this in filenames and archive names.

Which to use? Extended when a human might read it. Basic for file names, URLs, machine contexts where brevity matters. Both are equally standards-compliant.

The capital T marks unambiguously where the date ends and the time begins. A parser never guesses whether the 12 in 2026-07-12T08 is a day or an hour. The T separates them. Time runs in 24-hour notation, always zero-padded: 09:05:00, not 9:5:0.

What T and Z actually mean in a timestamp

The T is a literal character. It stands for "time" and separates date from time. Always uppercase, always present when both portions appear.

The Z is different. It stands for "Zulu," the military designation for UTC. A timestamp ending in Z means the time is expressed at UTC+00:00 specifically. Not some offset. UTC itself.

This matters. A timestamp carrying no offset at all is a local time. Its meaning depends entirely on who reads it. Your 03:14 AM is someone else's 10:14 PM. Without an offset or a Z, the string is ambiguous across time zones. With Z, exactly one correct interpretation exists.

ISO 8601:2004 permitted a space instead of the T. The 2019 revision discourages this. The standard now says T is the preferred separator. If you are designing an API today, use the T.

Time zone offsets: +hh:mm, -hh:mm, and Z

When the time is not UTC, express the offset explicitly. The format is +hh:mm or -hh:mm appended directly to the time.

2026-07-29T03:14:07+05:30 means 3:14 AM in a zone five and a half hours ahead of UTC. That is India Standard Time. To get UTC, subtract the offset: 3:14 minus 5 hours 30 minutes is 21:44 on the previous day. The UTC instant is 2026-07-28T21:44:07Z.

Offsets range from -12:00 to +14:00. Some zones use half-hour offsets. Nepal sits at +05:45. The Chatham Islands are at +12:45.

The basic format allows offsets without colons: +0530. Omit the offset entirely and you get a local time with no zone information. The standard calls this a "local time" and notes it is only meaningful within the context where it was produced. Send such a timestamp across a network and you are asking for trouble.

Week dates (YYYY-Www-D) and ordinal dates (YYYY-DDD)

ISO 8601 includes two alternative calendar systems beyond month-day format.

Week dates express a date by week number and day of week. Format: YYYY-Www-D. Four-digit year, a W, two-digit week number, a hyphen, then day of week (1 for Monday through 7 for Sunday). 2026-W31-3 is Wednesday of week 31 in 2026.

The rules: weeks start Monday. Week 1 is the week containing the year's first Thursday. The ISO week-numbering year can differ from the calendar year for days in early January or late December. December 29, 30, 31 can belong to week 1 of the next year. January 1, 2, 3 can belong to the last week of the previous year.

This system derives from German commercial practice (DIN 1355) and is widely used in Europe for payroll, production planning, school calendars. Yet it is rarely implemented in software. Most programming languages need a library to compute ISO week numbers. The edge cases, especially December and January, trip up even experienced developers. Test carefully against known reference values.

Ordinal dates express the date as day of year: YYYY-DDD. DDD runs from 001 to 365, or 366 in leap years. This format suits scientific data and inventory systems where you sort by date without parsing months.

Confusion persists around "Julian date." Many people use the term for ordinal dates. True Julian dates, a continuous day count used in astronomy, are different. If someone asks for "today's Julian date," they probably want an ordinal date. Confirm before assuming.

How ISO 8601 writes a duration: the P-syntax

Durations use a different syntax from instants. The format begins with the letter P (for "period"), followed by date components, then the letter T, then time components.

One year, two months, three days, four hours, five minutes, six seconds: P1Y2M3DT4H5M6S.

The designators: Y for years, M for months (in the date portion), W for weeks, D for days, T introduces time, H for hours, M for minutes (in the time portion), S for seconds.

The letter M does double duty. Months in the date part, minutes after the T. The T is required if you include any time components. Zero values can be omitted: PT1H is one hour, P1D is one day, P1M is one month.

Weeks are a special case. P2W is two weeks. No other date components can combine with weeks. You cannot write P1Y2W.

Here is the catch: months and years have variable lengths. A month can be 28, 29, 30, or 31 days. A year can be 365 or 366 days. ISO 8601 durations define these as calendar units, not fixed second counts. P1M from January 31 is ambiguous. Does it end on February 28, or March 3? The standard does not resolve this. Applications must define their own rules for arithmetic. Different systems do it differently. If you are storing durations that must be precise, store them in seconds or days.

Intervals: start/end and start/duration

Intervals express a period of time with a start and an end, or a start and a duration. The separator is a solidus (forward slash).

Three forms are valid:

  1. Start/End: 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z
  2. Start/Duration: 2007-03-01T13:00:00Z/P1Y2M10DT2H30M
  3. Duration/End: P1Y2M10DT2H30M/2008-05-11T15:30:00Z

The second form is the most common in practice. Scheduling systems, API responses, calendar applications all use it. A duration-only interval (P1Y2M10D) is also valid, meaning "this long, starting from some context."

The solidus is the only separator permitted. Some older systems use a double hyphen. That is not ISO 8601. If you control the API design, use the solidus and document it.

ISO 8601:2019 and the split into Parts 1 and 2

The 2019 revision was structural. The standard split into two parts to make future maintenance easier.

ISO 8601-1:2019: "Date and time, Representations for information interchange, Part 1: Basic rules." This is the core standard: date, time, time zone offsets, durations, intervals. If you are implementing a parser, this is what you need.

ISO 8601-2:2019: "Part 2: Extensions." This covers uncertain dates, sets of dates, and advanced features. You can express "the year 2026, give or take two years" or "either July 29 or July 30." These extensions target research contexts, archaeology, history, geology, where exact dates are not always known.

The split means you can implement Part 1 fully and ignore Part 2 without breaking compliance. Many software libraries do exactly that. If you need uncertain dates, check whether your language's date library supports Part 2 before building your own.

Where you encounter ISO 8601: APIs, logs, databases

ISO 8601 is everywhere in computing.

APIs use it almost universally. JSON has no native date type, so dates transmit as strings. ISO 8601 is the de facto standard for how those strings look. When a REST API returns "created_at": "2026-07-29T03:14:07Z", that is ISO 8601. RFC 3339, which defines the ISO 8601 profile for internet protocols, is the formal basis.

Logs use it because it sorts correctly. A log file full of ISO 8601 timestamps can be sorted alphabetically and entries come out chronological. The basic format, no hyphens or colons, is common in filenames: backup-20260729-031407.tgz sorts correctly and is unambiguous.

Databases store it because it is portable. PostgreSQL's timestamptz type stores an instant internally but accepts ISO 8601 on input and output. MySQL's TIMESTAMP type converts to the session time zone on read and write. The key rule: store dates in UTC, format them in ISO 8601, convert to local time only at display time.

Email headers are the exception. RFC 2822 defines the date format for email: Tue, 01 Jul 2026 14:30:00 +0000. This format was frozen by legacy. Changing it would break decades of email clients. If you parse email dates, handle both.

JavaScript has a footgun. The Date object's toISOString() method returns ISO 8601 in UTC. That is great. But Date.parse() and the Date constructor are notoriously inconsistent about parsing ISO 8601 strings with offsets. Some browsers handle them correctly. Others treat them as local time. Always pass timestamps around as ISO 8601 strings and parse them explicitly with a library like date-fns or Luxon rather than relying on the built-in parser.