Two formats dominate API payloads when something needs a timestamp: a Unix epoch integer like 1714300000 and an ISO 8601 string like 2026-04-28T13:46:40Z. Both are precise, both are unambiguous in principle, and both have edge cases that produce subtle production bugs. This guide covers what each format actually specifies, where the gotchas hide, and how to pick between them for an API contract.
1. Unix time: seconds since the epoch
Unix time is the number of seconds elapsed since 00:00:00 UTC on 1 January 1970, ignoring leap seconds. POSIX defines this as time_t; it is the native time format for every Unix-derived operating system, every cron daemon, and every C library calendar function. JWT's exp and iat claims, OAuth 2.0 token responses, and most low-level Linux APIs use it.
Two practical points trip up every API team eventually. The first is units: JavaScript's Date.now() returns milliseconds, but Linux time(NULL) returns seconds, and there is no syntactic difference between the two on the wire — only an order-of-magnitude difference in value. A milliseconds value sent to a seconds-expecting endpoint is interpreted as a date in the year 56000+. The second is signedness: a 32-bit signed time_t overflows on 19 January 2038 (the "Y2K38" problem). 64-bit time_t is now standard on almost every modern system, but old MySQL TIMESTAMP columns and embedded firmware still use the 32-bit form.
2. ISO 8601: a self-describing string
ISO 8601 (most recently revised in ISO 8601-1:2019) defines a family of textual date and time formats. The combined date-and-time form looks like:
2026-04-28T13:46:40+09:00
2026-04-28T13:46:40.123456-05:00
The structure is <date>T<time><offset> where the offset is either the literal Z (meaning UTC) or a signed ±HH:MM from UTC. Sub-second precision uses a fractional component (.123 for milliseconds, .123456 for microseconds). The standard is verbose, but every component is named — there is no ambiguity about whether a number is seconds or milliseconds because that distinction is structural.
3. RFC 3339: the strict subset you actually want
ISO 8601 is broad — it allows week dates (2026-W17-2), ordinal dates (2026-118), basic format without dashes, and many other variants. Most parsers reject most of these. RFC 3339 defines a strict subset of ISO 8601 specifically for internet protocols, and it is what almost every JSON API accepts and produces. The rules:
- Use the extended format with dashes and colons (
2026-04-28T13:46:40Z). - Always include a timezone — either
Zor a numeric offset. - Use uppercase
Tas the date-time separator (a literal space is also permitted but discouraged). - Sub-second precision is optional but unbounded.
When someone says "ISO 8601 in an API" in 2026, they mean RFC 3339. JavaScript's new Date().toISOString() emits exactly this form.
4. Timezone offsets vs IANA zones
An RFC 3339 offset like +09:00 tells you the wall-clock difference from UTC at one specific instant. It does not tell you which timezone the value was authored in. 2026-07-15T14:00:00+09:00 could be Tokyo (Asia/Tokyo, fixed +09:00) or Seoul (Asia/Seoul, fixed +09:00) or any of several others.
For values that anchor a future event in a specific city — a calendar appointment at "9 AM in New York" — the offset is not enough. You need the IANA TZ Database name (America/New_York) so that DST transitions are computed correctly. Either store the IANA zone alongside the timestamp, or send a structured object like { "datetime": "...", "timezone": "America/New_York" }. The IANA TZ Database is updated several times a year as countries change their DST rules; never bake fixed offsets into business logic.
5. Database storage: TIMESTAMP, TIMESTAMPTZ, BIGINT
PostgreSQL has two types people get wrong: TIMESTAMP stores wall-clock without timezone (use only for naive times like "9 AM on the user's alarm"), TIMESTAMPTZ normalises to UTC on insert and converts back on read (use for everything else). MySQL's TIMESTAMP is 4 bytes, range 1970–2038, and Y2K38-vulnerable; DATETIME is 8 bytes, year 1000–9999, and timezone-unaware. SQL Server has DATETIME2 (no timezone) and DATETIMEOFFSET (with offset).
For interop-heavy systems that already exchange JSON, storing the canonical column as TIMESTAMPTZ (Postgres) and serialising as RFC 3339 is the cleanest path. For event streams optimised for compact storage, a 64-bit Unix milliseconds BIGINT is fine — and that is what Kafka, ClickHouse, and similar systems use natively.
6. JavaScript: Date.now() vs new Date().toISOString()
Date.now() returns a number — Unix milliseconds. new Date().toISOString() returns a string in the strict RFC 3339 form ending in Z. Both are immediately serialisable to JSON. The choice in JavaScript-heavy stacks usually comes down to a single question: do downstream consumers parse the value as a date, or do they only need to compare or store it?
For comparisons and storage, milliseconds are simpler and faster. For anything a human will read in logs, DBs, or admin tooling, ISO 8601 strings save you from squinting at 1714300000123. The Temporal API (Stage 3 in 2026, shipping in Firefox and Deno but not yet in V8) is set to make this discussion mostly historical, with first-class duration, instant, and zoned-date-time types.
7. Decision tree: which one for your API?
- Public REST/JSON API → RFC 3339 strings. Self-describing, debuggable, no unit ambiguity.
- JWT, OAuth, low-level OS interfaces → Unix seconds. The ecosystem standardised there long ago.
- High-throughput event log, binary protocol → Unix milliseconds in a 64-bit integer field.
- Calendar event in a specific city → RFC 3339 datetime + IANA zone name.
- Cookie expiry, HTTP Date header → Use the format the spec mandates (HTTP-date, RFC 7231).
Convert timestamps in your browser
DevToolNow's Timestamp Converter accepts Unix seconds, milliseconds, or ISO 8601 strings and shows the equivalent in every format with the user's local timezone. Useful for debugging API payloads.
Open Timestamp Converter →Frequently asked questions
Q. Should my API send Unix seconds or ISO 8601 strings?
A. For machine-to-machine APIs that exchange JSON, ISO 8601 / RFC 3339 strings are the better default in 2026. They are self-describing, round-trip cleanly through JSON, carry timezone information explicitly, and are human-readable in logs and dumps. Use Unix timestamps when you need compact binary representations, when interfacing with low-level systems (cron, systemd, Linux syscalls), or when the surrounding ecosystem (JWT exp/iat, OAuth) already standardised on epoch seconds.
Q. Seconds or milliseconds for Unix timestamps?
A. There is no universal convention — both are common — so always document which one your API uses. JavaScript's Date.now() and Date.getTime() return milliseconds. Most Unix system calls, JWT's exp/iat claims, and cron return seconds. Mixing the two silently causes bugs that look like "all my dates are in 1970". If you need sub-second precision, prefer microseconds in a 64-bit integer over fractional seconds in a float.
Q. Is the Year 2038 problem still real?
A. On 32-bit signed integer systems, yes. The Unix epoch overflows at 03:14:07 UTC on 19 January 2038. Most modern Linux systems, all 64-bit applications, and major databases moved to 64-bit time_t years ago, but embedded devices, MySQL TIMESTAMP columns (still 32-bit in some configurations), and old C code remain at risk. Audit any system that stores time as a 32-bit integer before 2038.
Q. Is RFC 3339 the same as ISO 8601?
A. RFC 3339 is a strict profile of ISO 8601 designed for internet protocols. It mandates the T separator between date and time, requires either Z or a numeric offset for timezone, and forbids most of ISO 8601's alternative forms (week dates, ordinal dates, basic format without dashes). When in doubt, target RFC 3339 — every parser that understands ISO 8601 will accept it, but not every emitter of ISO 8601 produces something a strict parser accepts.
Q. What about leap seconds?
A. Leap seconds are inserted into UTC when the Earth's rotation drifts relative to atomic time, most recently on 31 December 2016. Unix time officially does not count leap seconds, but POSIX allows different smearing strategies — Google smears one second across 24 hours, AWS does similar. The IERS announced in 2022 that the leap second will be retired by 2035, which removes the ambiguity. For most applications, you can ignore leap seconds; high-precision systems should use TAI or explicit smearing.
References
- IETF RFC 3339 — Date and Time on the Internet: Timestamps
- ISO 8601-1:2019 — Date and time — Representations for information interchange
- IANA Time Zone Database — tzdata
- POSIX.1-2017 — time.h, time_t
- U.S. Naval Observatory — Leap Seconds; IERS Bulletin C
Note: Date and time correctness depends on the runtime, the database column type, and the application's timezone configuration. Test round-trips across language boundaries before relying on a chosen format in production.
About the DevToolNow Editorial Team
DevToolNow's editorial team is made up of working software developers who use these tools every day. Every guide is reviewed against primary sources — IETF RFCs, W3C/WHATWG specifications, MDN Web Docs, and project repositories on GitHub — before publication. We update articles when standards change so the guidance stays current.
Sources we cite: IETF RFCs · MDN Web Docs · WHATWG · ECMAScript spec · Official project READMEs on GitHub