Skip to content
Control Plane Labs

Unix Timestamp Converter: Epoch and Zones

Convert Unix timestamps safely: detect seconds versus milliseconds, render RFC 3339 with the right offset, handle DST gaps, leap seconds, and the 2038 limit.

Control Plane Labs Staff

Published October 3, 2026

The timestamp converter answers the question an incident review always asks: when did this actually happen? A Unix value is an absolute instant, so two people reading the same number will agree. The disagreement starts when one of them renders it in local time, guesses the wrong unit, or pastes a value that was never UTC to begin with.

What does a Unix timestamp actually count?

POSIX defines the value in Section 4.19, “Seconds Since the Epoch”, as a number that approximates the seconds elapsed since the Epoch. The key sentence is that “each and every day shall be accounted for by exactly 86400 seconds.” Unix time therefore has no slot for a leap second. The same section leaves the relationship between a system’s clock and true UTC “unspecified”, which is why clock discipline (NTP, smearing, or stepping) is an operations choice rather than part of the format.

Most ecosystems inherit that rule. JWT’s NumericDate is seconds since the epoch “ignoring leap seconds,” and it may be non-integer. The exp, nbf, and iat claims must use it, which is why the JWT decoder shows them as plain numbers. ECMAScript’s time value uses the same epoch at millisecond precision, does not account for leap seconds, and covers exactly 100,000,000 days either side of 1970.

Seconds, milliseconds, or nanoseconds?

Unit confusion is the most common timestamp bug. A 13-digit value read as seconds lands tens of thousands of years in the future, and a 10-digit value read as milliseconds lands in January 1970. Digit count is a reliable first guess for dates near the present:

Unit Digits today The instant 2026-10-03T12:00:00Z Where you meet it
Seconds 10 1791028800 date +%s, JWT claims, Prometheus API output
Milliseconds 13 1791028800000 JavaScript Date.now(), Prometheus TSDB block times
Microseconds 16 1791028800000000 Python datetime and timedelta resolution
Nanoseconds 19 1791028800000000000 Go UnixNano(), time.Time internals

The 10-digit window is wide but finite: it runs from 1000000000 (2001-09-09T01:46:40Z) to 9999999999 (2286-11-20T17:46:39Z). Outside that window, choose the unit explicitly. Real APIs mix them. The Prometheus HTTP API accepts RFC 3339 or Unix seconds with optional decimals, always returns seconds, and reports TSDB block bounds in milliseconds on the same page.

Language runtimes also bound the range. Go’s time package documents that UnixNano() is undefined before 1678 or after 2262, because nanoseconds since 1970 overflow an int64 there. UnixMilli() stays valid for roughly 292 million years in each direction.

Which text format should you emit?

For logs, APIs, and tickets, emit RFC 3339: full date, full time, and a mandatory offset (Z or ±hh:mm). The same instant renders differently by zone but stays one instant:

2026-10-03T12:00:00Z          UTC
2026-10-03T08:00:00-04:00     America/New_York (EDT)
2026-10-03T13:00:00+01:00     Europe/London (BST)
2026-10-03T17:30:00+05:30     Asia/Kolkata

An offset does not name a zone. When the zone matters, for example for a recurring meeting or a maintenance window that must follow DST, use RFC 9557, which adds a bracketed suffix such as 2026-10-03T08:00:00-04:00[America/New_York]. The zone rules themselves come from the IANA Time Zone Database, so keep runtimes and base images current when governments change their rules.

HTTP is the exception. Headers such as Date, Last-Modified, and Expires use the IMF-fixdate form defined in RFC 9110, Section 5.6.7, for example Sat, 03 Oct 2026 12:00:00 GMT. Recipients must also accept two obsolete forms. The HTTP header inspector guide covers how those dates drive caching.

Where do conversions go wrong?

Local time at the boundary. Python’s datetime.fromtimestamp() returns local time unless you pass tz. Go’s time.Unix() also returns a local Time, though its Unix() result does not depend on location. Pass UTC explicitly in both:

from datetime import datetime, timezone
datetime.fromtimestamp(1791028800, tz=timezone.utc).isoformat()
# '2026-10-03T12:00:00+00:00'

Ambiguous local times. When US daylight time ends on November 1, 2026, Detroit sees 01:30 twice. 1793511000 renders as 2026-11-01T01:30:00-04:00 and 1793514600 as 2026-11-01T01:30:00-05:00. A log that drops the offset makes those two events indistinguishable, and a scheduler that stores local wall time can run a job twice. Check cron expressions against this with the cron expression tool.

Leap seconds. RFC 9110’s grammar allows 23:59:60, but Go cannot parse a second-60 timestamp and Python’s datetime “does not support leap seconds.” The IERS Bulletin C records UTC-TAI at -37 s since 2017. Under CGPM Resolution 4 (2022), the maximum UT1-UTC difference will be increased by 2035, so that UTC stays continuous for at least a century. Until then, test what your parsers do with :60.

The 2038 limit. A signed 32-bit seconds counter tops out at 2147483647, which is 2038-01-19T03:14:07Z. glibc’s Y2038 design makes 64-bit time opt-in on 32-bit platforms through _TIME_BITS=64. On 64-bit systems, only 64-bit time is supported. Embedded agents, old database columns, and binary file formats are where 32-bit values still hide.

A five-minute timestamp review

  1. Paste the raw value into the timestamp converter and confirm the detected unit against the source’s documentation, not the digit count alone.
  2. Record the UTC RFC 3339 form in the ticket, then add the local rendering with its offset.
  3. For anything scheduled, write the IANA zone name alongside the offset.
  4. Check for 32-bit storage if the value could pass 2038 within the data’s retention period.
  5. When an event looks an hour off, check for a DST transition before you blame clock drift.

Frequently asked questions

How do I tell seconds from milliseconds in a Unix timestamp?+
For dates between 2001 and 2286, seconds have 10 digits and milliseconds have 13. Outside that window, or when a value has been truncated, check the producing system's documentation and select the unit explicitly.
Does a Unix timestamp include leap seconds?+
No. POSIX counts every day as exactly 86,400 seconds, and JWT NumericDate and ECMAScript time values follow the same rule. How a host clock handles a leap second is an NTP and operations choice.
Is a Unix timestamp in UTC?+
A Unix timestamp is an instant measured from 1970-01-01T00:00:00Z, so it has no timezone. A zone is applied only when you render it as a date. Render in UTC first to avoid local-time surprises.
What is the difference between RFC 3339 and ISO 8601?+
RFC 3339 is a strict internet profile of ISO 8601 that requires a full date, a full time, and an offset. ISO 8601 also allows week dates, ordinal dates, and omitted offsets, which many parsers reject.
What happens to 32-bit timestamps in 2038?+
A signed 32-bit seconds counter overflows after 2038-01-19T03:14:07Z and wraps to a negative value. Use 64-bit time; on 32-bit glibc builds that means compiling with _TIME_BITS=64.

Tags: #timestamps, #unix-time, #rfc3339, #timezones, #web-development