AppCrib
Developer Tools

Unix Time Isn't UTC: How POSIX Skipped Leap Seconds and the 2038 Cliff Ahead

Domain knowledge·Published by AppCrib··
EpochrThe Unix timestamp converter that tells you what you're looking at.

At the end of June 2012, sysadmins set alarms for 23:59:60 UTC, the timestamp that doesn't exist in any operating system. The 25th leap second ever inserted into Coordinated Universal Time fell on the 30th, and Linux servers running affected versions of NTP famously pegged CPU at 100% across a long list of companies that had not patched a kernel hrtimer bug. Cloudflare's DNS, LinkedIn, Mozilla, Reddit, and Yelp all went briefly sideways. The Unix timestamp for 23:59:60 UTC is undefined. The seconds counter either repeats the previous value or jumps over it, depending on which kernel patch and time daemon are driving the clock.

Unix time, despite what every developer assumes, is not UTC. It's a fairly close approximation of UTC almost all of the time. The gap is most of a minute and growing. Whether that matters depends on what your code thinks it's measuring.

What POSIX actually says a Unix timestamp is

POSIX-1.2017 (IEEE Std 1003.1-2017) defines "Seconds Since the Epoch" in section 4.16 with a formula that takes hours, minutes, seconds, day-of-year, year, and a leap-day correction, and adds them up assuming every day has exactly 86,400 seconds. The rationale in the same section is explicit: "These values are not the actual number of seconds between the time and the Epoch, because of leap seconds."

The standard does not say a Unix timestamp counts SI seconds since 1970-01-01T00:00:00Z. It says a Unix timestamp encodes a UTC wall clock as if leap seconds never happened. That's a different number. As of 2026, the difference is 27 seconds.

Twenty-seven leap seconds have been inserted into UTC since 1972, when the modern leap-second system started. None have ever been subtracted. The most recent one fell on 2016-12-31, and none have been needed since because the Earth's rotation has run slightly faster than the long-run average. The current offset between TAI (atomic time) and UTC is 37 seconds, of which 10 was the original 1972 step and 27 are the inserted leaps. A POSIX Unix timestamp tracks UTC, which is 37 seconds behind TAI, but the timestamp itself doesn't record that any leap seconds occurred. It rolls forward 86,400 ticks on every UTC calendar day, including the ones with a 61-second final minute.

How systems handle the second that isn't supposed to exist

The Linux kernel exposes CLOCK_REALTIME, which is supposed to be Unix time. When a leap second fires, the kernel has historically inserted it by repeating the same second number twice in a row. A clock reading 23:59:59.999 followed immediately by 23:59:59.000 looks broken to most application code. The 2012 leap second crashed Java applications because OpenJDK's high-resolution timer cached the previous reading and looped when the next one came in earlier than expected.

Google's approach, deployed publicly since 2008 and now used by AWS Time Sync as well, is the "leap smear." Spread the extra second over 24 hours starting at noon UTC the day before. The clock runs slow by 1 part in 86,400, or about 11.6 microseconds per real second, until it has shed exactly one full second. Application code never sees a 60th second and never sees a clock that goes backward. The cost is that the smeared clock disagrees with the standards-compliant clock by up to half a second during the smear window, which is fine for web traffic and breaks for financial trade reconciliation.

SystemBehavior on 2016-12-31 23:59:60 UTCVisible to app code
Linux NTP, kernel leap support23:59:59 occurred twice; clock went backward 1 second at midnightYes
Linux NTP, slewed correctionClock slowed gradually for ~1,000 seconds after midnightMostly hidden
Google / AWS leap smearClock ran 11.6 µs/sec slow for 24 hours starting 12:00 UTC on Dec 31Hidden
Windows W32time (Server 2019+)Smears over 24 hoursHidden
Raw POSIX, no leap supportClock rolled forward 1 second; offset from UTC drifted permanentlyNo (drift)
GPS receiver hardwareOutputs GPS time; consumer subtracts the 18-second offset to UTCDepends

The financial industry generally requires the standards-compliant version, sometimes with audit logs that record both wall-clock and elapsed-SI-second timestamps for the same event. The web generally runs on smeared time. NTP itself can advertise the leap second a day in advance via a status bit, but client behavior is implementation-defined.

TAI, UT1, GPS, and the clocks underneath UTC

UTC is a compromise. It's atomic time (TAI) adjusted to stay within 0.9 seconds of UT1, which is Earth's actual rotational time as measured against quasars by the International Earth Rotation Service. When Earth's rotation slows, UTC accumulates a deficit relative to UT1; when the deficit threatens to cross 0.9 seconds, the IERS schedules a leap second six months in advance. The decision is made twice a year, in January and July.

TAI is what the world's atomic clocks actually agree on. It has no leap seconds, no calendar adjustments, no daylight saving, no time zones. It has run continuously since 1958-01-01T00:00:00 and currently leads UTC by 37 seconds.

GPS time started on 1980-01-06T00:00:00 UTC and also has no leap seconds. It is therefore offset from TAI by 19 seconds (TAI was already 19 seconds ahead of UTC in 1980) and ahead of UTC by 18 seconds as of 2017. GPS receivers translate to UTC by subtracting the offset, which they receive from the satellite almanac. Cheap receivers occasionally report the offset wrong for hours after a cold boot.

If you've ever seen a timestamp from a particle physics experiment, a satellite tracking system, or a high-frequency trading log that didn't look like UTC, it was probably TAI or GPS time. The conversion is just adding or subtracting an integer offset, but the offsets change, so the conversion needs the right table for the right date.

The 2038 cliff

Unix timestamps are traditionally stored as signed 32-bit integers. The largest value that fits is 2,147,483,647. Counted from 1970-01-01T00:00:00Z, that lands at 2038-01-19T03:14:07Z. The next second wraps the integer to its most negative value, and the clock reads 1901-12-13T20:45:52Z.

This is called the Year 2038 problem, Y2K38, or the Epochalypse. It is exactly the same shape as the Y2K bug, just on a longer timer.

Modern Linux fixed this. The glibc 2.39 release in early 2024 made 64-bit time_t the default on 32-bit architectures, and the kernel has supported 64-bit time syscalls since 5.6 (2020). On a 64-bit system, time_t has been 64 bits since the architecture shipped, and 64 bits covers 292 billion years on either side of the epoch, which is roughly twenty times the age of the universe.

The remaining exposure is in the long tail. Embedded controllers running 32-bit MIPS or ARMv7 with old kernels still ship into industrial environments. Some databases (MySQL TIMESTAMP until 8.0.28) cap at 2038. Some file system formats (ext3 inode times before ext4 large_inode) cap at 2038. Some serialization formats default to 32-bit integers because that's what fits in 4 bytes. NTP itself uses 32-bit unsigned seconds since 1900 and rolls over on 2036-02-07T06:28:16Z, two years before the Unix problem hits.

Twelve years out from 2038, audit your stack now if you write code that handles dates in the 2040s. Loan amortization, lifetime warranty tracking, geological time series, and anything that signs a long-dated certificate are the usual surprises.

The leap second is being abolished by 2035

The 27th General Conference on Weights and Measures, in November 2022, passed Resolution 4. It directed BIPM to develop a proposal to stop inserting leap seconds no later than 2035. The replacement plan is to let UTC and UT1 drift apart by more than 0.9 seconds without correction. The drift will accumulate at roughly one second every couple of years for a while and one second every year or two by 2100. A discontinuity of up to one minute is being entertained as a future correction, possibly a century or more from now.

What this means operationally: applications written today should expect a leap second to remain rare and possible until 2035, then stop appearing. If you've never written code that handles a leap second, the simplest answer is to keep not writing it, because the standards are explicitly heading toward a world where you won't need to. If you've been smearing for the past decade, keep smearing through 2035 and then notice that you can stop.

The lie POSIX has told for fifty years is in the process of becoming the truth. UTC is going to look more like Unix time, not less.

What this means when you're just reading a timestamp

For day-to-day work, Unix time is a clock that doesn't run on time but everyone agrees on what it says. Subtracting two Unix timestamps gives the elapsed UTC wall-clock seconds, which equals elapsed SI seconds outside of the rare leap-second intervals. Application code, web logs, cron schedules, log correlation, and most database queries don't care about the difference. The places that do care are scientific timekeeping, financial settlement, satellite navigation, and certain physics measurements, all of which use TAI or GPS time instead.

The more common confusion when looking at a raw Unix timestamp isn't leap seconds. It's the unit. Different services and languages emit different units, and pasting a 13-digit millisecond timestamp into a converter expecting 10-digit seconds is the most common way to land on a date that's either in 1970 or in the year 56,000. If you want to see what a specific Unix timestamp resolves to and confirm the unit before trusting the result, Epochr detects seconds, milliseconds, microseconds, and nanoseconds automatically and shows the unit on the screen before showing the date.

Epochr
The Unix timestamp converter that tells you what you're looking at.
Try Epochr