ISO 8601 vs. RFC 3339 vs. Unix Timestamp: What's the Difference?

Three formats show up constantly in APIs, logs, and databases, and are often treated as if they were interchangeable — they're not quite. Here's what each one actually is, where they diverge, and which to reach for.

Unix Timestamp: A Number, Not a Date

A Unix timestamp (or "epoch time") is the number of seconds — sometimes milliseconds — since 00:00:00 UTC on January 1, 1970. It's compact, sorts correctly as a plain number, and is completely unambiguous about the instant it represents, since it's always relative to UTC by definition. What it doesn't carry is any notion of a calendar date, time zone, or human-readable format — a bare number means nothing to a human without also knowing whether it's seconds or milliseconds, plus code to convert it. JavaScript's Date.now() returns milliseconds; many other languages and most database epoch columns use seconds — mixing the two up by a factor of 1000 is a classic bug, and exactly why our own Unix Timestamp Converter lets you pick the unit explicitly.

ISO 8601: A Big Family of Formats

ISO 8601 is an international standard for representing dates and times as text, but it's more permissive than most people realize — it allows dates without times (2026-07-22), times without dates, week-based dates (2026-W30-3), durations, and repeating intervals, plus several valid ways to express a UTC offset. That flexibility is exactly why ISO 8601 alone isn't always enough to guarantee two systems agree on how to parse a given string — a date-time string that's valid ISO 8601 might omit the time entirely, or express the offset in a form your particular parser doesn't expect.

RFC 3339: ISO 8601's Strict, Internet-Friendly Subset

RFC 3339 takes the parts of ISO 8601 most useful for computer-to-computer communication — combined date and time, an explicit UTC offset (or a literal Z for UTC) — and drops the ambiguous or rarely-needed parts, producing one predictable, unambiguous format: 2026-07-22T14:30:00Z or 2026-07-22T14:30:00-03:00. This is the format specified by name in most modern API documentation, and it's what JavaScript's Date.prototype.toISOString() produces. If you need a text date-time format two different systems will parse identically, RFC 3339 — not "ISO 8601" in general — is the safer thing to actually specify.

new Date().toISOString(); // "2026-07-22T14:30:00.000Z" — RFC 3339, always UTC, always unambiguous

The One Thing All Three Get Right

The common thread, and the reason all three are safe choices for storage and transmission: none of them depend on the reader's or writer's local time zone setting to be interpreted correctly. A Unix timestamp is inherently UTC-based math. An RFC 3339 string carries its offset explicitly in the text itself. Compare that to a bare string like 07/22/2026 2:30 PM with no offset attached — that's ambiguous by construction, and exactly the kind of value that causes silent bugs when it crosses a server or time zone boundary (see our Handling Timezones & UTC in JavaScript guide for how new Date() handles this distinction).

Which One Should You Actually Use?

  • Storing timestamps in a database: a native timezone-aware column (TIMESTAMPTZ in PostgreSQL), which stores the equivalent of a Unix timestamp internally regardless of the column's display format.
  • Sending timestamps over an API: RFC 3339 text, since it's human-readable in logs and JSON payloads and every mainstream language can parse it reliably.
  • Doing date math or comparisons in code: whichever your language's native type uses internally — usually a Unix-timestamp-equivalent millisecond count under the hood, exactly like JavaScript's Date object.
  • A pure numeric sort key or cache key: a raw Unix timestamp, since it sorts correctly as a number with no parsing required.

A Quick Gotcha: Fractional Seconds and "Z" vs. "+00:00"

Both RFC 3339 and ISO 8601 allow an optional fractional-seconds component (14:30:00.123Z) and allow UTC to be written either as a literal Z or as +00:00 — they mean exactly the same instant, but a strict string-equality check between the two will (incorrectly) treat them as different values. Always compare parsed instants, never raw timestamp strings, for exactly this reason.

Related

For the database side of this — why TIMESTAMP and TIMESTAMPTZ columns aren't interchangeable — see Time Zone Bugs in Databases & Logs. To convert a raw timestamp by hand, use our Unix Timestamp Converter.

Tools

Articles & Guides