# ISO 8601 vs RFC 3339 timestamps

How to tell an ISO 8601 timestamp from an RFC 3339 one: a T or a space between date and time, a Z, an offset or no zone, what each standard allows, and the rules finetype uses.

Page: <https://meridian.online/docs/projects/finetype/types/told-apart/iso-8601-rfc-3339>

## The types

| Type | Key | Form | Pattern | DuckDB transform | Sample |
| --- | --- | --- | --- | --- | --- |
| [ISO 8601](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_8601) | `datetime.timestamp.iso_8601` | T and Z | `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,6})?Z$` | `strptime({col}, '%Y-%m-%dT%H:%M:%SZ')` | `2024-01-15T10:30:00Z` |
| [ISO 8601 with Timezone Offset](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_8601_offset) | `datetime.timestamp.iso_8601_offset` | T and offset | `^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}[+-](?:\d{2}(?::?\d{2})?\|\d:\d{2})$` | `CAST(regexp_replace(upper({col}), '([+-])([0-9]):([0-9]{2})$', '\10\2:\3') AS TIMESTAMPTZ)` | `2024-01-15T10:30:00+05:00` |
| [ISO Datetime, second precision (no timezone)](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_seconds) | `datetime.timestamp.iso_seconds` | T, no zone | `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$` | `strptime({col}, '%Y-%m-%dT%H:%M:%S')` | `2013-06-04T01:02:03` |
| [ISO with Space Separator and Z](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_space_zulu) | `datetime.timestamp.iso_space_zulu` | Space and Z | `^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}Z$` | `strptime({col}, '%Y-%m-%d %H:%M:%SZ')` | `2024-01-15 14:30:00Z` |
| [RFC 3339 (Space Separator)](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/rfc_3339) | `datetime.timestamp.rfc_3339` | Space and offset | `^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)? ?(?:[Zz]\|[+-]\d{2}:?\d{2})$` | `CAST(regexp_replace(upper({col}), ' ([+-]\|Z)', '\1') AS TIMESTAMPTZ)` | `2024-01-15 10:30:00+00:00` |

## What the standards say

ISO 8601 joins a date and a time of day into one value with the letter T.[[1]](https://en.wikipedia.org/wiki/ISO_8601) ISO 8601:2004 allowed the T to be left out by mutual agreement, but ISO 8601-1:2019 removed that provision.[[1]](https://en.wikipedia.org/wiki/ISO_8601) ISO 8601 itself does not allow a space between the date and time parts.[[1]](https://en.wikipedia.org/wiki/ISO_8601) RFC 3339 defines a date and time format for use in Internet protocols that is a profile of the ISO 8601 standard.[[2]](https://www.rfc-editor.org/rfc/rfc3339.html) RFC 3339's grammar joins the full date and the full time with the letter T.[[3]](https://www.rfc-editor.org/rfc/rfc3339) A note in RFC 3339 says applications may choose, for the sake of readability, to separate the full date and full time with a space character instead.[[3]](https://www.rfc-editor.org/rfc/rfc3339)

ISO 8601 says that if no UTC relation information is given with a time representation, the time is assumed to be in local time.[[1]](https://en.wikipedia.org/wiki/ISO_8601) RFC 3339's date-time grammar requires a time offset, either Z or a numeric offset, after the time.[[3]](https://www.rfc-editor.org/rfc/rfc3339) RFC 3339 defines Z as a suffix that, applied to a time, denotes a UTC offset of 00:00.[[3]](https://www.rfc-editor.org/rfc/rfc3339) Wikipedia notes that RFC 3339 deviates from ISO 8601 by allowing a zero offset to be written as -00:00.[[1]](https://en.wikipedia.org/wiki/ISO_8601)

RFC 3339 notes that the T and Z characters may alternatively be written in lower case as t and z.[[3]](https://www.rfc-editor.org/rfc/rfc3339) RFC 3339 says applications that generate its format should use upper case letters.[[3]](https://www.rfc-editor.org/rfc/rfc3339) RFC 3339 only allows hours from 00 to 23, although ISO 8601 permits the hour to be 24.[[2]](https://www.rfc-editor.org/rfc/rfc3339.html)

FineType's registry labels the space-separated form datetime.timestamp.rfc\_3339.[[4]](https://github.com/meridian-online/finetype/blob/main/labels/definitions_datetime.yaml) FineType's registry labels the T-separated form iso\_8601\_offset.[[4]](https://github.com/meridian-online/finetype/blob/main/labels/definitions_datetime.yaml)

## How finetype tells them apart

From finetype 0.6.66's source.

finetype's datetime format reader reads a value with a T between the date and the time, and whole seconds, as iso\_8601 when a Z follows the seconds, as iso\_8601\_offset when a four-digit offset such as +05:30 follows them, and as iso\_seconds when nothing does.[[5]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L124)[[6]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L145-L192) It reads a value with a space between the date and the time, and whole seconds, as iso\_space\_zulu when a Z follows the seconds, and as rfc\_3339 when a four-digit offset such as +05:30 does; a two-digit offset such as +03 reads as pg\_short\_offset instead.[[7]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L125)[[8]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L202-L230) The reader takes the offset with or without a colon, as +05:30 or +0530, and for rfc\_3339 it also takes a space before the offset.[[9]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L145-L150)[[10]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L214-L221) finetype's source says each format is anchored at both ends with exact field widths, so a value matches at most one format.[[11]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L10-L18) The reader picks a format only when at least 95% of the column's sampled non-null values match it.[[12]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L46-L47)[[13]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L424-L438)

Because these forms carry separators, finetype does not ask its model to agree before applying the reader's choice, as it does for a bare integer.[[14]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L20-L29)[[15]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L2056-L2059) finetype applies that reading only when at least 90% of the column's non-empty sampled values pass the chosen type's own validation rule.[[16]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L2063-L2075)[[17]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L461-L484)

finetype's own tests pin that a space-separated value ending in Z reads as iso\_space\_zulu, not rfc\_3339.[[18]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L592-L595) The same tests pin that the reader reads none of 2024-01-01t10:00:00+05:30, 2024-01-01 10:00:00z, 2024-01-01T10:00:00+05 or 2024-01-01T10:00:00+5:30, although the registry's published rules accept them.[[19]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L596-L606)[[20]](https://github.com/meridian-online/finetype/blob/v0.6.66/labels/definitions_datetime.yaml#L146)[[21]](https://github.com/meridian-online/finetype/blob/v0.6.66/labels/definitions_datetime.yaml#L222) When no single format matches at least 95% of a column of these timestamps, the reader returns nothing and this step leaves the column's type as it was.[[22]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L424-L451)[[23]](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L2052-L2055)

Read the names as a description of the characters a value holds, not as a verdict on which standard its writer followed. (Meridian's view)

## What finetype's evaluation shows

The evaluation report in finetype's repository at release 0.6.66 measured release 0.6.60 on 2026-09-23.[[24]](https://github.com/meridian-online/finetype/blob/v0.6.66/eval/eval_output/report.md#L1-L5) The report says its tables are generated by a separate tool that has drifted from the SQL harness it claims to mirror, and that a figure taken from it for publication must be its SQL headline.[[25]](https://github.com/meridian-online/finetype/blob/v0.6.66/eval/eval_output/report.md#L7) Until finetype publishes a measurement for each type, this page gives no figure for how often it mistakes one form for another. (Meridian's view)

## Who writes each form

### [T and Z](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_8601)

MDN documents the output of JavaScript's Date.prototype.toISOString() as always 24 or 27 characters long, in the form YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ.[[26]](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) GitHub's REST API documentation says all timestamps return in UTC time, ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ.[[27]](https://docs.github.com/en/rest/using-the-rest-api/getting-started-with-the-rest-api)

### [T and offset](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_8601_offset)

The OpenAQ air-quality API returns each datetime with both a UTC value and a local value, the local one written with an offset such as 2019-07-11T14:00:00-06:00.[[28]](https://docs.openaq.org/using-the-api/dates-datetimes)

### [T, no zone](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_seconds)

Java's LocalDateTime class is a date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.[[29]](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/LocalDateTime.html) The HTML Standard's normalised local date and time string separates the date from the time with a capital T.[[30]](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html)

### [Space and Z](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/iso_space_zulu)

Elixir's DateTime.to\_string renders a UTC datetime in this shape, with 2000-02-29 23:00:07Z as its documented example.[[31]](https://elixir.hexdocs.pm/DateTime.html)

### [Space and offset](https://meridian.online/docs/projects/finetype/types/datetime/timestamp/rfc_3339)

PostgreSQL accepts the T separator on input, but on output it uses a space rather than T.[[32]](https://www.postgresql.org/docs/current/datatype-datetime.html) Python's documentation shows isoformat(' ') producing 2002-12-25 00:00:00-06:39.[[33]](https://docs.python.org/3/library/datetime.html)

## Sources

1. [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) — secondary source
2. [Date and Time on the Internet: Timestamps](https://www.rfc-editor.org/rfc/rfc3339.html) — standard
3. [RFC 3339: Date and Time on the Internet: Timestamps](https://www.rfc-editor.org/rfc/rfc3339) — standard
4. [finetype/labels/definitions\_datetime.yaml at main · meridian-online/finetype · GitHub](https://github.com/meridian-online/finetype/blob/main/labels/definitions_datetime.yaml) — FineType
5. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L124) — FineType
6. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L145-L192) — FineType
7. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L125) — FineType
8. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L202-L230) — FineType
9. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L145-L150) — FineType
10. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L214-L221) — FineType
11. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L10-L18) — FineType
12. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L46-L47) — FineType
13. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L424-L438) — FineType
14. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L20-L29) — FineType
15. [finetype 0.6.66, column/mod.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L2056-L2059) — FineType
16. [finetype 0.6.66, column/mod.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L2063-L2075) — FineType
17. [finetype 0.6.66, column/mod.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L461-L484) — FineType
18. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L592-L595) — FineType
19. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L596-L606) — FineType
20. [finetype 0.6.66, definitions\_datetime.yaml](https://github.com/meridian-online/finetype/blob/v0.6.66/labels/definitions_datetime.yaml#L146) — FineType
21. [finetype 0.6.66, definitions\_datetime.yaml](https://github.com/meridian-online/finetype/blob/v0.6.66/labels/definitions_datetime.yaml#L222) — FineType
22. [finetype 0.6.66, datetime\_format.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-core/src/datetime_format.rs#L424-L451) — FineType
23. [finetype 0.6.66, column/mod.rs](https://github.com/meridian-online/finetype/blob/v0.6.66/crates/finetype-model/src/column/mod.rs#L2052-L2055) — FineType
24. [finetype evaluation report (release 0.6.60)](https://github.com/meridian-online/finetype/blob/v0.6.66/eval/eval_output/report.md#L1-L5) — FineType
25. [finetype evaluation report (release 0.6.60)](https://github.com/meridian-online/finetype/blob/v0.6.66/eval/eval_output/report.md#L7) — FineType
26. [Date.prototype.toISOString() - JavaScript \| MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) — documentation
27. [Getting started with the REST API](https://docs.github.com/en/rest/using-the-rest-api/getting-started-with-the-rest-api) — organisation
28. [Dates, times and timezones](https://docs.openaq.org/using-the-api/dates-datetimes) — organisation
29. [LocalDateTime (Java SE 21 \& JDK 21)](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/LocalDateTime.html) — documentation
30. [HTML Standard](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html) — standard
31. [DateTime (Elixir v1.20.4)](https://elixir.hexdocs.pm/DateTime.html) — documentation
32. [PostgreSQL: Documentation: 18: 8.5. Date/Time Types](https://www.postgresql.org/docs/current/datatype-datetime.html) — documentation
33. [datetime — Basic date and time types](https://docs.python.org/3/library/datetime.html) — documentation
