Timestamp Format Parser Differential

Paste timestamp strings, one per line. Each row shows whether RFC 3339 (as updated by RFC 9557), the ECMA-262 Date Time String Format, your browser's own Date.parse, Python 3.14 datetime.fromisoformat rules and Go time.Parse(time.RFC3339) rules accept it, and which instant each produces. Everything runs in your browser.

Input

Paste timestamps above, or load the examples.

Results

How each reader handles each timestamp
Input RFC 3339 (as updated by RFC 9557) ECMA-262 Date Time String Format Your browser, live: Date.parse Python 3.14 rules: fromisoformat Go time.Parse(time.RFC3339) rules

The Python and Go columns are emulations of documented and source-read behaviour, checked against Python 3.14.8 and Go 1.27.2; no code is reproduced. The live column is the only one that runs a real parser, and it is your engine only.

How each column decides

RFC 3339 (as updated by RFC 9557)

  • The 5.6 ABNF: date-time = full-date "T" full-time, with a partial-time of HH:MM:SS, an optional "." 1*DIGIT fraction, then an offset. time-numoffset = ("+" / "-") time-hour ":" time-minute time-offset = "Z" / time-numoffset
  • Lower case is allowed, upper case is what generators should send. NOTE: Per [ABNF] and ISO8601, the "T" and "Z" characters in this syntax may alternatively be lower case "t" or "z" respectively. ... Applications that generate this format SHOULD use upper case letters.
  • A space is outside the ABNF, and the NOTE permits it as an application choice. This page shows it that way, never as plain valid or plain invalid. Applications using this syntax may choose, for the sake of readability, to specify a full-date and full-time separated by (say) a space character.
  • 5.7 caps the day by month and leap year, allows second 60 at a leap second (this page does not check the leap second table), and restricts the hour. this profile of ISO 8601 only allows values between "00" and "23" for the hour in order to reduce confusion.
  • RFC 3339 is updated by RFC 9557 (rfc-editor.org metadata: updated_by RFC9557). Section 2.2 rewrites 4.3, so Z, not -00:00, now marks a known UTC time with an unknown local offset; -00:00 is still grammatical. If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of "Z".
  • Bracketed suffixes such as [Europe/Paris] are RFC 9557 annotations and out of scope for every column here.

Sources: https://www.rfc-editor.org/rfc/rfc3339.txt, https://www.rfc-editor.org/rfc/rfc3339.json, https://www.rfc-editor.org/rfc/rfc9557.txt

ECMA-262 Date Time String Format (21.4.1.32) and Date.parse (21.4.3.2)

  • Date-only forms are YYYY, YYYY-MM and YYYY-MM-DD. A date-time form adds THH:mm, THH:mm:ss or THH:mm:ss.sss, then an optional Z or +HH:mm / -HH:mm. The T and Z are upper case, and sss is exactly three digits.
  • Each element has bounds: month 01 to 12, day 01 to 31, hour 00 to 24, minutes and seconds 00 to 59. Note 1 says 24:00 is the midnight that ends a day. Because the day bound is 01 to 31 for every month, 2024-02-30 passes the format check.
  • Expanded years are a sign and six digits; -000000 is invalid.
  • With no offset, date-only forms are read as UTC and date-time forms as local time. A string outside the format may be handled by implementation-specific heuristics, so the live column on such a string describes your engine only.
  • Note 2 says the format does not support RFC 9557 time zone name annotations.
  • The local reading uses the zone you pick above. Date.parse itself always uses the host zone, and a page cannot change that zone for it.

Source, paraphrased: https://tc39.es/ecma262/#sec-date-time-string-format and https://tc39.es/ecma262/#sec-date.parse

Python 3.14 rules: datetime.fromisoformat

  • The Python documentation says it accepts any valid ISO 8601 format with listed exceptions: offsets may have fractional seconds; the T may be any single character; fractional hours and minutes are not supported; reduced precision dates (YYYY-MM, YYYY), extended years and ordinal dates (YYYY-OOO) are not supported. It broadened in 3.11; before that it only read what isoformat() writes.
  • Behaviour drifts between releases. Measured here: Python 3.11.9 rejects 2024-01-15T24:00:00Z and 3.14.8 accepts it as midnight of the next day (changelog entry gh-102450). Python 3.11.9 returns 10:00:00.500000 for 2024-01-15T10.5, which 3.14.8 rejects.
  • A string with no offset gives a naive datetime: it has no instant until the reader assumes a zone.

Sources: https://docs.python.org/3/library/datetime.html and https://docs.python.org/3/whatsnew/changelog.html

Go time.Parse(time.RFC3339) rules

  • Go's own documentation says the RFC layouts, when parsing, do not take every RFC form and take some forms the RFCs do not define. when used with time.Parse they do not accept all the time formats permitted by the RFCs and they do accept time formats not formally defined.
  • Parse tries a strict fast path first, then falls back to the general layout parser. The fallback is what accepts a one-digit hour, a comma fraction and offsets of +24:00 or +05:60: The range test use > rather than >=, as some people do write offsets of 24 hours or 60 minutes or 60 seconds.
  • The literal T and an upper case Z or a +hh:mm offset are required. There is no date-only form, and an offset without a colon is rejected. Second 60 and hour 24 are out of range.
  • The source notes that strict RFC 3339 checking is switched off for now (go.dev/issue/54580).

Sources: https://raw.githubusercontent.com/golang/go/master/src/time/format.go and https://raw.githubusercontent.com/golang/go/master/src/time/format_rfc3339.go

This is an independent tool. It is not affiliated with or endorsed by the IETF, Ecma International, the Python Software Foundation or the Go project. Their documents are cited as the rules each column follows.

Self test

Runs on load and on demand, against the same code that fills the table: every worked example through the Python and Go emulations (expected values measured on Python 3.14.8 and Go 1.27.2), the RFC 3339 and ECMA-262 validators, and a positive control written to fail. The live column is never asserted, because it is your engine.

Not run yet.

Show every check
Self test results
CheckExpectedGotResult