RFC 3339 · · Proposed Standard
The timestamp format you google ten times a week.
The official title is "Date and Time on the Internet: Timestamps." What it actually decides: one unambiguous way to write a moment in time, like 2026-07-05T14:30:00Z, that machines in every country read the exact same way.
- Status
- Proposed standard
- Updated by
- RFC 9557
Written by Noel Lang · IT trainer
TL;DR
RFC 3339 is the internet’s timestamp format. It pins down one strict, unambiguous way to write a date and time, such as 2026-07-05T14:30:00Z, as a tightly constrained profile of ISO 8601 with a required UTC offset. It is what most JSON APIs, logs and databases mean when they say “ISO-format timestamp”, because it strips out the ambiguities that full ISO 8601 still allows.
The problem it solves
Dates are where systems quietly betray each other. Is 03/04/2026 in March or April? Does a bare 14:30 mean the server’s clock, the user’s clock, or UTC? For decades every language, database and country wrote timestamps a little differently, and “a little” is enough to book a flight on the wrong day.
ISO 8601 was the international answer, and it helped. But it is a big standard with many optional forms: week numbers, day-of-year counts, missing separators, offsets you can leave off. Two systems can both be perfectly valid ISO 8601 and still disagree about how to write the same instant, which means a parser still has to guess.
RFC 3339’s move is subtraction. It takes ISO 8601 and throws away almost every option, leaving one rigid shape: four-digit year, dashes, a literal T, a time, and a UTC offset that is never optional. Because there is nothing left to guess, a parser can be a short regular expression, and a timestamp survives crossing from a phone in Osaka to a server in Ohio without changing meaning. That predictability is the whole reason it became the default for APIs, logs, and anything where a moment leaves one machine and lands on another.
The passport-stamp analogy
Think of a timestamp as a passport stamp for a moment in time. A note scribbled in a diary (“half two, Tuesday”) means something to you and nobody else: no year, no place, no way to check. ISO 8601 is the full international rulebook for stamps, thick enough that two border posts can follow it and still produce stamps that look different.
RFC 3339 is the one approved stamp design every booth agrees to use. Fixed fields, fixed order, and a mandatory line for the time zone, because a stamp that does not say where the clock was is a stamp you cannot trust. The Z at the end is the booth writing “read this against UTC”, the one clock the whole world shares. A time without an offset is a moment without a country; RFC 3339 refuses to issue one.
One timestamp, every field labeled
This is the shape RFC 3339 insists on. Nine fields, always the same order, always with the separators. The only optional piece is the fractional second; the offset at the end is never optional.
The format, field by field
The grammar in section 5.6 is only a dozen lines, and most of it you already read above. A few fields are worth a sentence each, because they are exactly the parts people get wrong.
The year is always four digits. RFC 3339 spends a whole short section (section 3) banning two-digit years, a small permanent monument to the Y2K panic. The month and day are two digits with the obvious ranges, and yes, the day is checked against the month, so 2026-02-30 is not a date.
The T is a literal letter joining date and time. A note lets you use a space instead for readability (that is what date --rfc-3339 does), and both the T and the Z may be written in lower case. So 2026-07-05t14:30:00z is legal, if a little cursed.
The fractional second is a dot followed by as many digits as you like: milliseconds, microseconds, nanoseconds, whatever your clock produces. RFC 3339 fixes no length, so a parser should accept 2026-07-05T14:30:00.5Z and ...00.123456789Z alike.
The Z means a zero offset from UTC. The spec itself notes it is “often spoken Zulu”, from the ICAO phonetic alphabet, which is why aviation and ops people say “fourteen thirty Zulu” out loud. Z is shorthand for +00:00.
Two genuinely surprising corners. Seconds can be 60: 1990-12-31T23:59:60Z is a real, legal timestamp marking a leap second, the extra tick occasionally added at the end of June or December to keep atomic clocks in step with the planet’s slightly irregular spin. And the offset -00:00 is not the same as +00:00: RFC 3339 defines the negative zero to mean “we know the UTC time, but we genuinely do not know the local offset”, a quiet, honest shrug that ISO 8601 does not even allow you to write.
What the RFC actually says
The original is ten pages and unusually pleasant to read. The map, in case you want to visit:
| Section | In plain words |
|---|---|
| 1 · Introduction | Why a single, tight timestamp format is worth writing down at all: the internet is global, and dates are where systems quietly disagree. |
| 2 · Definitions | The vocabulary: UTC, the meaning of Z (and why it is spoken "Zulu"), what an offset is. Short and worth reading. |
| 3 · Two digit years | A one-line ban: no. Four digits, always. The Y2K scar tissue made permanent. |
| 4 · Local time | Offsets, the unknown-offset trick (-00:00), and why the standard nudges you toward UTC. |
| 5 · Date and time format | The heart of it: the ABNF grammar, the fractional second, the leap second, and the notes about lower case and the space separator. |
| Appendices | The ISO 8601 grammar for comparison, plus worked examples including a leap second. Handy when you argue with someone. |
RFC 3339 vs ISO 8601, the argument that never ends
This is the question everyone actually types into a search box, so here is the honest answer: RFC 3339 is neither a subset nor a superset of ISO 8601. They overlap heavily, and most timestamps you meet are valid under both, but each allows something the other does not.
ISO 8601 is the sprawling parent. It allows week dates (2026-W27), ordinal dates (2026-186), a separator-free basic format (20260705T143000Z), and, crucially, times with no offset at all. RFC 3339 forbids every one of those, which is the entire point: fewer shapes, no guessing.
Going the other way, RFC 3339 permits -00:00 with its special “offset unknown” meaning, and ISO 8601 flatly forbids a negative zero offset. So a valid RFC 3339 timestamp can be invalid ISO 8601. That single case is why careful people say “profile of” rather than “subset of”.
Where the two formats overlap
Most real timestamps live in the middle. The edges are where the two standards quietly disagree, and where the “3339 vs 8601” arguments come from.
The differences, rule by rule
Same idea as the diagram, but as a checklist you can copy into a code review:
| Rule | RFC 3339 vs ISO 8601 |
|---|---|
| Four-digit year | Both use four digits. ISO 8601 additionally allows expanded years like +002026 by prior agreement; RFC 3339 does not. |
| UTC offset | RFC 3339 requires it. ISO 8601 makes it optional, which is exactly the ambiguity 3339 exists to remove. |
| Date/time separator | Both use a literal T. Both also allow a space, RFC 3339 by an explicit note, ISO 8601 only "by mutual agreement". |
| Week dates (2026-W27) | ISO 8601 only. RFC 3339 speaks nothing but plain year-month-day calendar dates. |
| Ordinal dates (2026-186) | ISO 8601 only. Day-of-year counting is not part of the RFC 3339 grammar. |
| Basic format (20260705) | ISO 8601 only. RFC 3339 always keeps the dashes and colons, so machines never have to guess where a field ends. |
| Negative zero offset (-00:00) | RFC 3339 only. It means "UTC known, local offset unknown". ISO 8601 forbids a negative zero offset outright. |
What is valid where
Concrete strings, checked against the grammar. This is the table to keep when someone insists their timestamp is fine:
| Written like this | Valid where |
|---|---|
| 2026-07-05T14:30:00Z | Valid in both. The canonical form: UTC, Z as the zero offset. |
| 2026-07-05T16:30:00+02:00 | Valid in both. Same instant as the line above, written in Berlin local time. |
| 2026-07-05 14:30:00Z | RFC 3339 accepts the space (per its note). ISO 8601 only by mutual agreement. This is what date --rfc-3339 prints. |
| 2026-07-05T14:30:00 | ISO 8601 only. No offset, so RFC 3339 rejects it: is that London or Tokyo? |
| 2026-W27-1 | ISO 8601 only. A week date (Monday of week 27). RFC 3339 has no idea what a week number is. |
| 2026-07-05T14:30:00-00:00 | RFC 3339 only. UTC is known, the local offset is deliberately unknown. ISO 8601 forbids the negative zero. |
Try it: is this a valid RFC 3339 timestamp?
Paste any timestamp. We'll say whether it's strict RFC 3339, only the broader ISO 8601, or simply broken, and break down every field.
Things people get wrong
-
"RFC 3339 is just ISO 8601."
Close, but the equals sign is wrong. RFC 3339 is a strict profile: it keeps a small, safe slice of ISO 8601 and bolts on one rule of its own. ISO 8601 lets you write week dates, ordinal dates, and times with no offset; RFC 3339 forbids all three. Meanwhile RFC 3339 permits -00:00, which ISO 8601 does not. Neither is a clean subset of the other, so "the same as" fails in both directions.
-
"Z and +00:00 are identical."
For the instant, yes: both are zero offset from UTC. The trap is -00:00. RFC 3339 defines it to mean "the UTC time is known, but the local offset is not". So Z and +00:00 assert UTC as the intended reference, while -00:00 quietly admits it does not know where on Earth this happened. Same numbers, different meaning.
-
"The T is optional and 23:59:60 is a typo."
Two myths in one. The T is part of the grammar; you may swap it for a space only because a note explicitly allows it, and it may be lower case. And 23:59:60 is not a bug: seconds legally run to 60 for a leap second, the extra tick the world occasionally inserts at the end of June or December to keep clocks aligned with Earth's wobble.
-
"A Unix timestamp is RFC 3339."
No. A Unix timestamp is one integer, seconds since 1 January 1970, like 1783000200. RFC 3339 is readable text, 2026-07-05T14:30:00Z. Both name an instant, but one is a number and the other is a string. You convert; you do not rename.
Where you'll meet this format
You have almost certainly read a hundred RFC 3339 timestamps today without noticing. A field guide:
| You see | You're looking at |
|---|---|
| A JSON API field | "createdAt": "2026-07-05T14:30:00Z". JSON has no date type, so REST and GraphQL APIs almost universally serialize instants as RFC 3339 strings. |
| Kubernetes and cloud logs | Every kubectl log line and most structured loggers stamp events in RFC 3339, so timelines from different machines line up. |
| date --rfc-3339=seconds | GNU coreutils has a dedicated flag. It prints 2026-07-05 14:30:00+00:00, using the space separator the note permits. |
| JavaScript toISOString() | new Date().toISOString() returns 2026-07-05T14:30:00.000Z. It is RFC 3339 by construction, which is why the two names blur together in practice. |
| Databases (Postgres, SQLite) | timestamptz columns render as RFC 3339, and it is the safest text format to feed back in without a locale surprise. |
| Atom feeds, TOML, OpenAPI | All three point at RFC 3339 for their date-time values, one of the quiet reasons it became the default. |
Questions people actually ask
Is RFC 3339 the same as ISO 8601? +
Not quite. RFC 3339 is a strict profile of ISO 8601: a small, unambiguous subset with a few of its own rules. Most RFC 3339 timestamps are valid ISO 8601, but ISO 8601 also permits forms that RFC 3339 forbids (week dates, ordinal dates, missing offsets), and RFC 3339 permits -00:00, which ISO 8601 does not. So neither is fully contained in the other.
What is an example of an RFC 3339 timestamp? +
2026-07-05T14:30:00Z means 5 July 2026, 14:30:00 UTC. The T separates date and time, and Z means Zulu, a zero offset from UTC. An offset can replace Z: 2026-07-05T16:30:00+02:00 is the same instant in Central European Summer Time.
Does RFC 3339 support milliseconds? +
Yes. A fractional second is allowed right after the seconds, with any number of digits: 2026-07-05T14:30:00.123Z is 123 milliseconds, 2026-07-05T14:30:00.123456Z is microseconds. RFC 3339 does not fix the length, so parsers should accept whatever digits they get.
Is the T mandatory in RFC 3339? +
In the core grammar, yes: date and time are joined by a literal T. But the spec adds a note allowing a space instead, for readability, so 2026-07-05 14:30:00Z is also accepted. Both the T and the Z may be lower case (t and z). The one thing you cannot drop is the offset.
What is the difference between Z and +00:00? +
For the actual instant, nothing: both are zero offset from UTC. The subtle case is -00:00, which RFC 3339 defines to mean 'the UTC time is known, but the local offset is not'. So Z and +00:00 assert UTC as the reference; -00:00 quietly says the offset is unknown.
Is a Unix timestamp an RFC 3339 timestamp? +
No. A Unix timestamp is a single integer counting seconds since 1970 (like 1783000200). RFC 3339 is a human-readable text format (2026-07-05T14:30:00Z). They describe the same kind of thing, an instant, but they are not interchangeable text; you convert between them.
How do I get an RFC 3339 timestamp in code? +
In JavaScript, new Date().toISOString() returns 2026-07-05T14:30:00.000Z, which is valid RFC 3339. In Python, datetime.now(timezone.utc).isoformat() gives 2026-07-05T14:30:00+00:00. On the shell, date --rfc-3339=seconds prints 2026-07-05 14:30:00+00:00 with a space separator.
Referenced by
Other explainers on this site that point back to this RFC:
How RFC 3339 connects
What it works with
-
RFC 2119 · written in
The MUST/SHOULD keywords
RFC 3339's grammar is enforced with the requirement words (MUST, SHOULD, MAY) defined here. When the spec says the offset MUST be present, this is the RFC that gives the word its weight.
-
RFC 8259 · where it lives
JSON has no date type
JSON only knows strings and numbers, never dates. So APIs put the moment in a string, and the string almost everyone agreed on is RFC 3339. That is why "ISO date" in a JSON payload nearly always means this.
-
RFC 9557 · updated by
The 2024 sequel (IXDTF)
<span class="text-neutral-500">(2024)</span> extends the format with optional bracket tags, so you can append an IANA time zone like <span class="font-mono text-sm">2026-07-05T14:30:00+02:00[Europe/Berlin]</span>. Old parsers still read the part in front; new ones learn the calendar and zone.
Its counterparts
-
RFC 5322 · the other date format
How email writes the date
Open any email header and the Date line looks nothing like this: <span class="font-mono text-sm">Tue, 05 Jul 2026 14:30:00 +0200</span>. That human-first format is defined in RFC 5322, and it is the reason mail and APIs never share a date parser.
-
RFC 9110 · http's date format
How HTTP writes the date
HTTP headers like <span class="font-mono text-sm">Date</span> and <span class="font-mono text-sm">Last-Modified</span> use yet a third format, <span class="font-mono text-sm">Tue, 05 Jul 2026 14:30:00 GMT</span>, fixed by RFC 9110. Three internet date formats, three different committees, one tired developer.