CURRENT EPOCH · EPOCHTIME.TOOLS · A PRECISION INSTRUMENT FOR TIME
Converter Batch Difference Blog
Languages
JavaScript Python TypeScript Go Rust Java PHP SQL Bash
Specialty
LDAP Timestamp .NET Ticks Chrome/WebKit Cocoa / Core Data Discord Timestamp Excel OADate Unix Hex
Standards
ISO 8601 Guide Year 2038 NTP Timestamp GPS Time Julian Day

The discrepancy that catches everyone once

The first time you see GPS time next to a Unix timestamp from the same instant, you'll notice something off. Take a snapshot of "now" from a GPS receiver and a server clock at the same moment, and they'll disagree by 18 seconds.

Not a millisecond drift. Not a timezone confusion. Exactly 18 seconds — and it'll be that exact number for the foreseeable future. Until it isn't.

Why 18, specifically?

GPS time started on January 6, 1980 at 00:00:00 UTC. At that exact moment, GPS time and UTC were equal. They have not been equal since.

The reason is that UTC inserts leap seconds, and GPS does not. UTC follows the Earth's rotation, which is gradually slowing down. To keep our clocks aligned with the actual position of the sun, the International Earth Rotation and Reference Systems Service (IERS) periodically inserts a "leap second" into UTC — making one specific minute 61 seconds long instead of 60.

Between 1980 and 2017, the IERS inserted 18 leap seconds. Each one made UTC fall one more second behind GPS time. The last leap second was inserted on December 31, 2016 at 23:59:60 UTC. There hasn't been one since, and the IERS has announced that no leap seconds will be added through at least 2026.

So as of right now, in 2026: GPS time is 18 seconds ahead of UTC. That's the formula:

GPS_seconds = UTC_seconds + 18  (currently)

The conversion gets uglier than that

You can't just always add 18. That number was 17 for most of 2015, 16 from 2012 to 2014, and so on back. If you're processing historical GPS data — drone telemetry from 2014, surveying records from 2010, satellite logs from any era — you need the leap-second offset that was in effect at that timestamp.

Here's the actual table you need:

Leap second addedOffset after
1981-06-301
1982-06-302
1983-06-303
1985-06-304
1987-12-315
1989-12-316
1990-12-317
1992-06-308
1993-06-309
1994-06-3010
1995-12-3111
1997-06-3012
1998-12-3113
2005-12-3114
2008-12-3115
2012-06-3016
2015-06-3017
2016-12-3118

Notice how irregular this is. There were three leap seconds added in three consecutive years (1981–1983), then a 19-month gap, then a 28-month gap. Between 1998 and 2005 there was no leap second at all — almost 7 years. There's no formula here. The IERS adds them when Earth's rotation drift demands it.

The practical implication for your code

Most code that converts GPS to Unix time uses a constant: LEAP_SECONDS = 18. This is fine for current-day work. It's wrong if you're processing historical data — silently wrong, in the way that gives you off-by-one errors that pass all your unit tests.

If you're writing forensics code, satellite telemetry analysis, or anything that processes historical GPS data, you need a proper lookup function:

LEAP_TABLE = [
    (78796800, 1),    # 1972-06-30 23:59:60 UTC
    (94694400, 2),    # 1972-12-31
    # ... continues for every leap second ...
    (1483228799, 18), # 2016-12-31
]

def leap_seconds_at(unix_time):
    """Return the cumulative leap second offset at a given Unix time."""
    count = 0
    for ts, total in LEAP_TABLE:
        if unix_time >= ts:
            count = total
    return count

Bonus complication: leap seconds inside a Unix timestamp

Here's where it gets genuinely strange. Unix time pretends leap seconds don't exist. By definition, every Unix day is exactly 86400 seconds long. When a leap second happens, Unix doesn't add a 86401st second to that day — it just repeats the second number that came before it. The clock goes 23:59:59 → 23:59:60 → 23:59:59 → 00:00:00 in the world of POSIX time_t, you get the second-to-last second twice.

This means there is a one-second window during every leap second insertion where the same Unix timestamp represents two different moments in time. Most production systems handle this with "leap smearing" — Google and Amazon and Cloudflare all spread the leap second out across a 24-hour window so nothing ever has to handle a 61-second minute. But the raw POSIX behavior is what it is.

What this means for the GPS-to-Unix conversion: during the second of a leap-second insertion, the mapping is ambiguous. For 99.99999% of timestamps, this doesn't matter. If you ever build a financial trading system or a transaction log that needs sub-second precision around leap second boundaries, it matters a lot.

The future

In November 2022, the International Bureau of Weights and Measures voted to abolish leap seconds by 2035. The resolution lets the UTC-UT1 gap grow up to a few seconds before being corrected with a much rarer "leap minute" or similar adjustment.

If this happens, GPS-UTC offset will stay at 18 (or whatever it is when leap seconds end) for decades. The historical lookup table will still be needed for old data, but new code can finally hard-code a constant without quietly accumulating errors.

What to do today


Published May 12, 2026. Tagged: gps, time-sync, leap-seconds.

← Back to blog  ·  Try the converter