Developer Guides

ISO 8601 Dates and Times: A Practical Guide

YYYY-MM-DD, the T, Z and offsets, ISO week numbers and PT1H30M durations: the parts of ISO 8601 you'll actually use, and the parsing rule behind off-by-one-day bugs.

  • By Jacob
  • 6 min read
A long rounded bar in white and pale blue ending in a cobalt segment, next to a round clock face ringed with small dots

Is 10/11/2026 October 11 or November 10? It depends on who wrote it. ISO 8601 exists to end that kind of guessing: 2026-10-11 means October 11 everywhere, sorts correctly as plain text, and can carry a time and a UTC offset without ambiguity.

This guide covers the parts of ISO 8601 you'll actually use in code, spreadsheets and APIs: dates, times, offsets, Z, week numbers and durations, plus the one parsing difference that causes the most bugs.

The basic formats

ISO 8601 writes dates and times from the largest unit to the smallest:

WhatFormatExample
DateYYYY-MM-DD2026-10-18
Timehh:mm:ss14:30:00
Date and timeYYYY-MM-DDThh:mm:ss2026-10-18T14:30:00
With UTC offset…±hh:mm2026-10-18T14:30:00-04:00
In UTC…Z2026-10-18T18:30:00Z
With fractions of a second…ss.sss2026-10-18T18:30:00.250Z

A few rules make the format reliable: always four digits for the year, always two for month, day, hour, minute and second (so 2026-03-07, never 2026-3-7), and a 24-hour clock with no AM or PM.

The full ISO standard is sold by ISO and has many rarely used options. On the internet, the format you'll meet is RFC 3339, a strict, free profile of ISO 8601 for timestamps. Everything in the table above is valid RFC 3339.

T, spaces and Z

The T separates the date from the time. RFC 3339 allows a space instead "for the sake of readability," so 2026-10-18 14:30:00Z is common in databases and logs. Not every parser accepts the space, so use the T when you send data to other systems.

The Z at the end means UTC. RFC 3339 notes that it's "often spoken 'Zulu'" after the letter's name in the ICAO phonetic alphabet. Z and +00:00 mean the same instant.

Offsets are not time zones

An offset like -04:00 says how far the local clock was from UTC at that moment. It doesn't say which time zone that was. New York in summer is -04:00, but so is Santiago de Chile at times, and New York itself is -05:00 in winter.

That matters when you schedule things in the future. A meeting "every Monday at 9:00 in New York" needs the zone name America/New_York from the IANA time zone database, because the offset changes when daylight saving time starts and ends. For a single moment in the past, an offset is enough.

RFC 3339 also defines one special offset: -00:00 means "the time in UTC is known, but the offset to local time is unknown," which "differs semantically" from Z and +00:00.

UTC vs GMT

UTC (Coordinated Universal Time) is the modern reference time scale, kept by atomic clocks. GMT is the older name for the time at Greenwich, and today it's also the UK's winter time zone. For everyday computing they're interchangeable, which is why Etc/GMT and UTC sit side by side in time zone lists. In ISO 8601, write Z or an offset; "GMT" isn't part of the format.

The parsing difference that causes the most bugs

A timestamp without an offset is ambiguous, and different software resolves it differently. JavaScript is the classic example. Run in New York:

new Date("2026-10-18").toISOString()
// "2026-10-18T00:00:00.000Z"   date only: treated as UTC

new Date("2026-10-18T08:00").toISOString()
// "2026-10-18T12:00:00.000Z"   date and time, no offset: treated as local time

The same code in Tokyo returns 2026-10-17T23:00:00.000Z for the second line. MDN's Date.parse() reference describes this rule: date-only forms are read as UTC, date-time forms without an offset as local time. Showing a UTC-midnight date in a time zone west of UTC is how a birthday turns into the day before.

Other tools make other choices. Our Timestamp Converter reads text without an offset in the time zone you select, so 2026-10-18 with New York selected becomes midnight in New York, 2026-10-18T04:00:00Z. If the text includes Z or an offset, that wins and the converter says the zone was "taken from the text."

The fix is the same everywhere: send timestamps with an offset or Z, and store moments in UTC. Keep a bare date only for things that really are dates, like a birthday or a due date, and treat it as a date, not as midnight somewhere.

Sorting and comparing

Because the format goes from year down to second, ISO 8601 strings sort correctly as plain text, as long as they all use the same offset (ideally Z), the same separator and the same number of fractional digits. 2026-10-18T09:00:00Z sorts after 2026-10-18T08:59:59Z without any date parsing. RFC 3339 lists this as one of the format's goals. It's also why file names like report-2026-10-18.csv line up in date order in any folder.

Strings with mixed offsets don't sort correctly. 2026-10-18T09:00:00+02:00 is earlier than 2026-10-18T08:00:00Z even though it sorts after it. Convert to UTC first.

Week dates and ordinal dates

ISO 8601 also numbers weeks and days of the year:

  • Week date: 2026-W42-7 is the seventh day (Sunday) of ISO week 42 of 2026, which is October 18. ISO weeks start on Monday.
  • Ordinal date: 2026-291 is the 291st day of 2026, also October 18.

Week 1 is the week that contains the year's first Thursday. That means a few days at the start or end of a calendar year can belong to the neighboring ISO year: January 1, 2027 is a Friday in 2026-W53, and December 29, 2025 is the Monday that starts 2026-W01. If you label weekly reports by ISO week, use the ISO week-year, not the calendar year, or those days end up in the wrong file. The Timestamp Converter shows the day of the year and the ISO week for any timestamp.

Durations

ISO 8601 has a format for lengths of time too. It starts with P (period), and a T separates the date part from the time part:

DurationMeaning
PT30M30 minutes
PT1H30M1 hour 30 minutes
P3D3 days
P1Y2M10DT2H30M1 year, 2 months, 10 days, 2 hours, 30 minutes
P2W2 weeks

The T is what tells months from minutes: P1M is one month, PT1M is one minute. RFC 3339 includes the duration grammar in its Appendix A. You'll see durations in video metadata, calendar files and some APIs.

Months and years aren't fixed lengths, so P1M added to January 31 needs a rule. For counting exact days between two dates, use the Date Difference Calculator; How to Count Days Between Dates covers the inclusive-or-not question. For converting 90 minutes to hours or seconds, the Time Converter does the arithmetic.

Leap seconds and fractions

RFC 3339 allows a seconds value of 60 for leap seconds, for example 23:59:60Z. Most software, including Unix time, doesn't represent leap seconds and will reject or adjust that value, so don't rely on it. Fractions of a second can have any number of digits, but many systems keep only three (milliseconds). JavaScript turns 08:00:00.123456Z into 08:00:00.123Z. If you need microseconds, keep the original string or a separate integer.

For how all of this maps to the numbers stored inside computers, see The Year 2038 Problem, which explains Unix timestamps and their limits.

A practical checklist

  • Use YYYY-MM-DD for dates in file names, spreadsheets and anything people share across countries.
  • Send and store moments as UTC with Z: 2026-10-18T18:30:00Z.
  • Keep the IANA zone name (America/Chicago) for future local events, not just an offset.
  • Never parse a timestamp without an offset unless you know which zone it was written in.
  • Use the same precision everywhere if you sort timestamps as text.

FAQ

Is ISO 8601 the same as RFC 3339?

Not exactly. RFC 3339 is a stricter subset of ISO 8601 for internet timestamps. A full RFC 3339 timestamp always has a date, a time and an offset or Z. ISO 8601 also allows forms like week dates, durations and reduced precision such as 2026-10.

What does the Z in a timestamp mean?

UTC, with an offset of zero. It's read "Zulu."

Can I use a space instead of the T?

RFC 3339 allows it for readability, and many databases print it that way. Use the T when exchanging data, since some parsers require it.

How do I write midnight?

Use 00:00:00 at the start of the day: 2026-10-18T00:00:00Z. ISO 8601 also has a 24:00 form for the end of a day, but many parsers reject it, so avoid it in data.