Guides · 6 min read
ISO 8601 vs Unix timestamps: when to use which
Two ways to represent a moment in time, both correct, both with strong opinions in the developer community. Here is when each is the better choice — and the cases where the answer is honestly just preference.
Published 22 April 2026
A moment in time can be written down in two reasonable ways:
- ISO 8601:
2026-04-22T08:00:00Z - Unix timestamp:
1745301600
Both refer to the same instant. Both are unambiguous (assuming the ISO string has a timezone designator). Both round-trip cleanly in every modern language. Yet there are persistent strong opinions in both directions, and which to use in any given system has practical consequences for storage, debugging, and APIs.
This guide is the practical case for each, and the rules of thumb I’d use today.
ISO 8601: human-readable, type-safe, language-agnostic
ISO 8601 is the international standard for representing dates and times as text. The full form is YYYY-MM-DDTHH:MM:SS.sssZ, with optional precision and timezone designator.
It has three properties that make it good for text-based protocols:
1. It is sortable as a string. Lexicographic order matches chronological order. You can sort a list of ISO 8601 strings with the same comparison your programming language uses for any other text, and you’ll get them in time order. This is the property that makes ISO 8601 magic for log files, filenames, and anything that gets compared as a string.
2026-04-21T23:59:59Z
2026-04-22T00:00:00Z ← naturally sorts after the line above
2026-04-22T08:00:00Z
2. It is self-documenting. Anyone reading a JSON response with "created_at": "2026-04-22T08:00:00Z" can figure out what that field means without looking at documentation. Compare to "created_at": 1745301600, which requires you to know the unit, the epoch, and the timezone convention.
3. It carries the timezone with it. The Z (or +02:00, or any offset) is part of the format. There is no ambiguity about whether you’re looking at UTC or something else.
The downsides:
- It’s longer (20 characters versus 10 digits).
- Parsing is slightly slower than reading an integer (though usually irrelevant).
- Different ISO-8601-adjacent variants exist in the wild — RFC 3339 is a stricter subset; some systems omit the
T; some use a space instead — and code that’s strict about the format can get bitten by data that isn’t.
Unix timestamps: compact, fast, language-native
Unix timestamps are a single integer, in some unit (usually seconds or milliseconds). They have the opposite trade-offs:
1. They are compact. A 64-bit integer is 8 bytes. An ISO string is at least 20 bytes, often more. For high-volume data, this matters: a billion timestamps as ISO strings is 20+ GB; as integers, 8 GB.
2. They are fast. Reading and comparing integers is faster than parsing strings — sometimes meaningfully faster in hot loops. If you are processing log lines at high throughput, the difference compounds.
3. They have no parser. A timestamp is just a number. Different libraries can’t disagree on how to parse it, because there is nothing to parse. ISO 8601, by contrast, has subtly different parsers: some accept 2026-04-22 08:00:00, some don’t. Some accept fractional seconds with three decimal places but not six, or vice versa. Unix timestamps sidestep all of this.
4. They are timezone-free in the literal sense. A Unix integer represents an instant in physical time, full stop. There is no “what timezone is this in” question, because there is no calendar field embedded.
The downsides:
- They are not human-readable. Looking at
1745301600in a database log tells you nothing useful without a converter. - The unit is ambiguous: is it seconds, milliseconds, microseconds? Different ecosystems disagree (covered in the seconds vs milliseconds guide).
- The 32-bit form has the Year 2038 problem. Modern systems use 64-bit, which is fine, but legacy schemas catch you out.
Rules of thumb
Here is my opinionated take on when each format wins:
Use ISO 8601 for:
- HTTP API request and response bodies (especially JSON)
- Log files and structured logs
- Filenames, S3 keys, and anything else that gets sorted as text
- User-facing strings that might leak into a screenshot or email
- Documentation, tests, and example data
Use Unix timestamps for:
- Database columns, when you have a strong reason not to use the native timestamp type
- Inter-service messages on a binary protocol (Protobuf, MessagePack, FlatBuffers)
- High-throughput pipelines where bytes-per-event matters
- JWTs and other compact tokens (the JWT spec specifies seconds since the epoch)
- Cache TTLs and “expires at” fields in low-level systems
It depends, but I’d lean ISO 8601:
- Configuration files (developers will read them)
- Anything in a SQL database — but really, use the native
TIMESTAMPTZtype - Internal RPC calls in JSON
It depends, but I’d lean Unix timestamps:
- Telemetry data
- Time-series databases (most ingest as Unix int natively)
“Native timestamp type” is the secret third option
A subset of the ISO-vs-Unix argument disappears entirely if you use your database’s native timestamp type. TIMESTAMPTZ in Postgres, DATETIME(6) in MySQL, Instant in Java, OffsetDateTime in .NET — all of these are typed, indexable, comparable, and timezone-aware. You don’t have to pick a format, because the database stores the value in whatever internal representation it likes and exposes it via SQL functions.
For most application data, this is what you actually want. The ISO-vs-Unix question only really matters at the boundary of your system — what format goes on the wire, into the log, or into the JWT. Inside the database, neither.
Conversions
Both directions are one line in any modern language:
// ISO to Unix seconds
const ts = Math.floor(new Date('2026-04-22T08:00:00Z').getTime() / 1000);
// Unix seconds to ISO
const iso = new Date(1745301600 * 1000).toISOString();
from datetime import datetime, timezone
# ISO to Unix seconds
ts = int(datetime.fromisoformat('2026-04-22T08:00:00+00:00').timestamp())
# Unix seconds to ISO
iso = datetime.fromtimestamp(1745301600, tz=timezone.utc).isoformat()
-- Postgres: ISO -> Unix
SELECT EXTRACT(EPOCH FROM TIMESTAMPTZ '2026-04-22 08:00:00 UTC')::bigint;
-- Postgres: Unix -> ISO
SELECT to_char(to_timestamp(1745301600) AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"');
If you have an ISO string or a Unix integer in front of you and want to flip between them quickly, the Unixdates converter shows both at once on every conversion — paste either format and the other appears.
The honest summary
For brand-new code, I’d default to ISO 8601 strings on the wire and native timestamp types in the database. I’d reserve Unix integers for performance-sensitive contexts and when interoperating with systems that already use them.
If your team has a different convention and it’s working, keep using it. The actual cost of either choice, for most applications, is small. The cost of inconsistency — half your APIs in ISO, half in Unix, half in something else — is much larger than the choice between them.
Need to convert a timestamp right now? Try the Unixdates converter — auto-detects seconds, milliseconds and microseconds.