The 2038 problem and 32-bit time

Signed 32-bit overflow on 19 January 2038, which systems and file formats are affected, how SMS and embedded time counters differ, and how to plan a migration.

What actually overflows

A signed 32-bit integer counting seconds from 1970 reaches its maximum on 2038-01-19T03:14:07Z. One second later it wraps to a negative value, which most programs read as 1901.

import datetime

max32 = 2**31 - 1                      # 2147483647

print(datetime.datetime.fromtimestamp(max32, datetime.timezone.utc))
# 2038-01-19 03:14:07+00:00

# what the wrap produces on a system that stores a signed 32-bit value
print(datetime.datetime.fromtimestamp(-2**31, datetime.timezone.utc))
# 1901-12-13 20:45:52+00:00
StorageRange endsStatus
Signed 32-bit seconds2038-01-19Affected
Unsigned 32-bit seconds2106-02-07Still a problem, later
64-bit secondsAbout year 292 billionSafe
64-bit millisecondsAbout year 292 millionSafe
32-bit milliseconds1970-02-24Already overflowed
Signed 32-bit daysAbout year 5.8 millionSafe

Where it hides

  • Old 32-bit Linux kernels — the time_t on a 32-bit userspace is 32 bits; a 64-bit kernel with 32-bit programs still has 32-bit time.
  • Filesystems — some on-disk formats store timestamps in 32 bits.
  • Embedded firmware — long-lived devices with a fixed build and no update path.
  • Database columns — MySQL TIMESTAMP historically limited to 2038; SQL Server smalldatetime ends in 2079.
  • Protocols — some binary protocols and file headers define a 32-bit epoch field.
  • SMS timestamps — a different counter: TP-SCTS uses semi-octets and is unrelated to Unix time.
#include <stdio.h>
#include <time.h>

int main(void) {
    printf("sizeof(time_t) = %zu bytes\n", sizeof(time_t));
    // 4 bytes on a 32-bit build: affected
    // 8 bytes on a 64-bit build: safe
    return 0;
}

Planning the migration

  1. Inventory: find every 32-bit time store — code, schemas, file formats and firmware.
  2. Measure: change the system clock to 2038-01-19T03:14:08Z in a test environment and observe.
  3. Widen: move to 64-bit seconds everywhere, then to an explicit type such as TIMESTAMPTZ.
  4. Fix formats: on-disk and on-wire 32-bit fields need a version bump or a documented escape.
  5. Re-test: repeat the clock test after each change, and add it to the release checklist.
  6. For devices you cannot update, document the failure date and a replacement plan.
# check a running system for 32-bit time
getconf LONG_BIT
date -d @2147483647 -u            # the last representable second
# in a container, run with an offset clock to simulate:
# faketime '2038-01-19 03:14:08' ./your-program
⚠️
Do not test by changing a production host clock. Use containers, libfaketime or a VM. A clock jump backwards can break TLS validation, distributed consensus and log ordering across the whole system.

FAQ

Is 2038 still a real problem today?
Less than it was, because most 64-bit platforms already use 64-bit time. The remaining risk is in embedded systems, old binaries, on-disk formats and database columns, which are exactly the parts nobody wants to touch.
Are 32-bit milliseconds affected?
They already overflowed: a 32-bit millisecond counter ran out in February 1970, so any such system has long since wrapped or been replaced.

Calendars, leap years and leap seconds Storing dates in databases

Last refreshed 2026-09-18.