Unix Timestamp vs ISO 8601: How to Store Dates
JSON has no date type, so every API picks a format for dates. Here is how Unix timestamps and ISO 8601 strings compare, the bugs each one invites, and what to use where.
Unix timestamp vs ISO 8601 is a decision every API makes, because JSON has no date type: JSON.stringify turns a Date into a string, and JSON.parse leaves that string a string. The Unix Timestamp Converter converts between the two in the browser, and its page explains seconds, milliseconds and time zones. This guide is about choosing a format for storage and APIs.
Unix timestamp vs ISO 8601 at a glance
Unix seconds 1700000000
Unix milliseconds 1700000000000
ISO 8601, UTC 2023-11-14T22:13:20Z
ISO 8601, offset 2023-11-15T03:43:20+05:30All four describe the same instant. Unix timestamps are compact and easy to compare and do arithmetic on, but unreadable and ambiguous about their unit. ISO 8601 strings are readable and carry their time zone offset, but are longer and easy to compare wrongly as text.
Bugs that come with Unix timestamps
The classic one is mixing seconds and milliseconds. JavaScript's Date expects milliseconds, while most backends, databases and JWTs use seconds:
new Date(1700000000).toISOString(); // "1970-01-20T16:13:20.000Z" wrong unit
new Date(1700000000 * 1000).toISOString(); // "2023-11-14T22:13:20.000Z" correctA date in January 1970 in your data almost always means seconds were read as milliseconds. Name the unit in the field, as in createdAtMs or expires_at_s, or agree on one unit for the whole API.
Bugs that come with ISO 8601 strings
ISO 8601 strings sort correctly as text only when every value uses the same format and the same offset. With mixed offsets, string order and time order disagree:
["2026-10-18T09:15:00Z", "2026-10-18T10:00:00+02:00"].sort();
// both stay in this order as text, but 10:00+02:00 is 08:00 UTC, which is earlierWrite timestamps in UTC with a Z suffix and fixed precision, and convert to local time only for display. Then sorting and comparing strings gives the right answer.
Time zones in Python
from datetime import datetime, timezone
datetime.fromtimestamp(1700000000) # 2023-11-15 03:43:20 local time, no zone
datetime.fromtimestamp(1700000000, tz=timezone.utc) # 2023-11-14 22:13:20+00:00
datetime.now(timezone.utc).isoformat() # aware, safe to serialiseWithout tz, Python returns a naive datetime in the server's local time, here India's +05:30, with no record of the zone. The same code on a server in another zone produces a different value. datetime.utcnow() is deprecated since Python 3.12 for the same reason; use datetime.now(timezone.utc).
Storing dates in a database
Use the database's date type rather than a number or a string. In PostgreSQL that is timestamptz: it stores an exact instant in UTC and converts input with any offset:
select '2026-10-18 10:00:00+02'::timestamptz at time zone 'UTC';
-- 2026-10-18 08:00:00Despite its name, timestamptz does not store the original zone, only the instant. If the local time zone matters, such as the zone a meeting was booked in, store it in a separate column.
Which date format to use where
- JSON APIs: ISO 8601 in UTC, such as
2026-10-18T08:00:00Z. Readable in logs and debuggers, unambiguous, and parsed by every language. - Databases: the native type,
timestamptzin PostgreSQL. - Metrics, events and high-volume logs: Unix milliseconds, where size and fast arithmetic matter and the unit is fixed by the system.
- Tokens and protocols that specify a format: follow the specification. JWTs, for example, use Unix seconds.
Related tools
Related guides
Every modern runtime can generate a UUID without a library. Here are the one-liners for JavaScript, Python and PostgreSQL, a tested UUID v7 function, and two common mistakes.
Decoding a JWT shows its claims; verifying it proves they are genuine. Here are the checks a correct verification makes, written out in Node.js and Python so you can see each step.
Two formatters can disagree about the same JSON file. Here is how a team picks one, applies it in every editor and before every commit, and makes CI enforce it.