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 unambiguousThe 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
(
TIMESTAMPTZin 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
Dateobject. - 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.