~ / topics / Year 2038
$man year-2038-problem

Year 2038 Problem

In brief: Many computers count time as the number of seconds since 1 January 1970. On systems that store that count in a signed 32-bit number, the count runs out at 03:14:07 UTC on 19 January 2038. One second later the number "wraps around" and the computer thinks it is December 1901. Most modern desktops, servers and phones are already fixed, but older software, 32-bit embedded devices, file formats and databases that still use 32-bit time can fail. Some of them fail years early, as soon as they handle a date after 2038.

Risk level High (Critical for long-lived 32-bit embedded and industrial systems)
Status Fixed in most current OS kernels and C libraries. Legacy 32-bit binaries, on-disk formats, protocols and embedded fleets are still at risk
Last reviewed 2026-10-21

Overview

What time_t is

Unix and Unix-like systems, and the C language they spawned, represent a moment in time as a single integer: the number of seconds since the Unix epoch, 1970-01-01 00:00:00 UTC. The C type that holds the count is time_t. Functions such as time(), gmtime(), mktime() and stat(), together with the timestamps of almost every file on a Unix file system, are built on it. The same "seconds since 1970" convention spread well beyond C and Unix. It turns up in network protocols, database column types, file formats, embedded firmware, and the runtimes of languages such as Java, PHP and Python.

For decades time_t was a signed 32-bit integer on most platforms, because long was 32 bits wide on the 16- and 32-bit machines where Unix grew up. A signed 32-bit integer holds values from −2,147,483,648 to +2,147,483,647, so it can represent:

Value Meaning (UTC)
−2,147,483,648 (−2³¹) Fri 1901-12-13 20:45:52
0 Thu 1970-01-01 00:00:00 (the epoch)
1,000,000,000 Sun 2001-09-09 01:46:40 ("Unix billennium")
2,147,483,647 (2³¹ − 1) Tue 2038-01-19 03:14:07
4,294,967,295 (2³² − 1, if unsigned) Sun 2106-02-07 06:28:15

The exact overflow moment

At 2038-01-19 03:14:07 UTC a signed 32-bit time_t reaches its maximum value. Adding one more second causes an integer overflow. On two's-complement hardware, which is effectively all hardware, the value wraps to −2,147,483,648. Software then reads that value as 1901-12-13 20:45:52 UTC [1][2]. Depending on the program, the result can be a date shown in 1901, a negative interval, a timer that never fires or fires at once, an expired certificate, a failed mktime() that returns −1, or a crash.

Why signed 32-bit?

time_t was signed so that dates before 1970 could be stored as negative numbers, and so that subtracting two times gives a negative difference without special handling. The cost is that half the range is spent on 1901–1970, so the forward range ends in 2038 and not in 2106. Some systems took the opposite trade-off: they made the 32-bit count unsigned, which pushes the limit to 2106 but makes pre-1970 dates unrepresentable. OpenBSD [3], glibc's utmp/lastlog records [4], Linux struct input_event [5] and QNX before SDP 8.0 [6] all made this choice in some form. See Year-2106-and-Beyond.

Technical details

Worked binary example

The largest signed 32-bit value has a 0 sign bit followed by thirty-one 1s:

2147483647  = 0111 1111 1111 1111 1111 1111 1111 1111   -> 2038-01-19 03:14:07 UTC
        + 1
-----------------------------------------------------------------------------
-2147483648 = 1000 0000 0000 0000 0000 0000 0000 0000   -> 1901-12-13 20:45:52 UTC

The bit pattern 0x80000000 is exactly 2³¹. In a signed two's-complement interpretation the top bit is the sign bit and carries a weight of −2³¹. The same bits that meant "one second after 2038-01-19 03:14:07" therefore now mean "2,147,483,648 seconds before 1970". In C, signed overflow is formally undefined behaviour, so an optimising compiler is free to do something even stranger than wrapping.

You can reproduce the two endpoints with GNU date:

$ date -u -d @2147483647
Tue 19 Jan 2038 03:14:07 UTC
$ date -u -d @-2147483648
Fri 13 Dec 1901 20:45:52 UTC

A minimal C demonstration of how a 32-bit counter misbehaves:

#include <stdint.h>
#include <stdio.h>
#include <time.h>

int main(void) {
    int32_t t32 = INT32_MAX;              /* 2038-01-19 03:14:07 UTC */
    t32 = (int32_t)((uint32_t)t32 + 1u);  /* wrap explicitly (defined behaviour) */
    time_t t = t32;                       /* widen to the platform's time_t */
    printf("%s", asctime(gmtime(&t)));    /* Fri Dec 13 20:45:52 1901 */
    printf("sizeof(time_t) = %zu\n", sizeof(time_t));
    return 0;
}

The fix: 64-bit time_t

A signed 64-bit time_t gives a range of about ±292 billion years, which in practice removes the problem. The underlying change is simple. Deploying it is not, because time_t is embedded in ABIs (struct stat, struct timespec, struct timeval, struct utmp, ioctl structures, library interfaces), in on-disk formats and in wire protocols. Widening the type changes struct layouts, so binaries and libraries compiled against the old size become incompatible with the new one. This is why the fix took decades on 32-bit platforms:

Not just time_t

A 64-bit time_t does not help when a timestamp is squeezed back into 32 bits somewhere else. Common hiding places include:

History of awareness

Year Milestone
1990s Developers discuss the 2038 limit in the context of Y2K and the move to 64-bit (LP64) Unix. A November 1997 thread on the tz mailing list already discusses time_t and LP64 [9]. See Year-2000-Problem.
2000 A January 2000 linux-kernel thread titled "time_t size: The year 2038 bug?" argues over whether to widen time fields in new 64-bit ABIs [10].
2001-09-09 time_t passes 1,000,000,000. This is a cosmetic milestone, but it exposed software that sorted or formatted timestamps as strings.
2006-05-13 AOLserver fails because a one-billion-second timeout, added to the current time, overflowed past 2038 [2].
2012 NetBSD 6.0 ships a 64-bit time_t on all ports [11].
2013 LWN's "Pondering 2038" describes the Linux options. OpenBSD commits its 64-bit time_t change [12].
2014 OpenBSD 5.5 (May 2014) is "year 2038 ready" [3]. Arnd Bergmann posts the first Linux y2038 patch series for inode timestamps [13].
2015–2018 Linux kernel conversion work, including Deepa Dinamani's Outreachy VFS work, merged in 2018 [14].
2019 Linux 5.1 adds the *_time64 system calls for 32-bit architectures [5][15].
2020 musl 1.2.0 switches all 32-bit architectures to 64-bit time_t [16]. Linux 5.6 is "the first release that can serve as a base for a 32-bit system designed to run beyond year 2038" [5].
2021 glibc 2.34 adds _TIME_BITS=64 for legacy 32-bit ABIs [4].
2022-01-01 Microsoft Exchange's "Y2K22" bug: a date-based version number overflowed a signed 32-bit integer [17]. See OS-Windows.
2024 Debian (February 2024 onward) and Ubuntu 24.04 LTS move armhf to 64-bit time_t [18][19]. POSIX.1-2024 requires a time_t of at least 64 bits [8].
2025 Debian 13 "trixie" (2025-08-09) ships with a 64-bit time_t ABI on every architecture except i386 [20]. FreeBSD 15.0 retires i386 [21].

Current state of industry fixes (as of 2026-10-21)

Layer State Details
64-bit OSes (Linux, BSD, macOS, Windows, Solaris/illumos 64-bit userland) Fixed 64-bit time_t by design. Remaining issues lie in file systems, formats and applications.
Linux kernel (32-bit) Fixed in 5.6+ Legacy 32-bit syscalls are kept for compatibility [5].
glibc (32-bit) Opt-in since 2.34 -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64 [4].
musl (32-bit) Default since 1.2.0 [16]
Debian / Ubuntu armhf Fixed (trixie, 24.04) i386 deliberately left at 32-bit [18][19][20].
OpenBSD / NetBSD Fixed in 2014 / 2012 [3][11]
FreeBSD Fixed except i386, which was retired in 15.0 [21][22]
Windows / MSVC Not affected by default 64-bit time_t since VS2005. FILETIME is 64-bit [23][24].
Android (32-bit) Not fixed Bionic keeps a 32-bit time_t on LP32 and has no plan to change [25].
Embedded RTOS / toolchains Mixed Depends on vendor, C library and build configuration. See OS-Embedded-and-RTOS.

The detailed matrix is on Operating-Systems.

Why it matters now: future dates

The rollover is about 11 years and 3 months away (4,108 days from 2026-10-21), but software meets 2038 long before the clock reaches it. Any calculation that projects into the future can cross the boundary today:

Affected versions / products

The per-OS pages carry the detailed tables:

Area Page
OS summary matrix Operating-Systems
Linux kernel, glibc, musl, distributions OS-Linux
BSDs and commercial Unix OS-BSD-and-Unix
Windows OS-Windows
macOS and iOS OS-macOS-and-iOS
Android OS-Android
RTOS and embedded Linux OS-Embedded-and-RTOS
Languages Programming-Languages
Devices Devices-and-Embedded-Systems
Databases, file systems, formats Databases, File-Systems, File-Formats-and-Protocols

Risk & compliance notes

Detection & mitigation

  1. Check type widths: sizeof(time_t) on every target, and getconf LONG_BIT on Linux.
  2. Rebuild 32-bit Linux userland with -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64 (glibc ≥ 2.34, kernel ≥ 5.1/5.6), or move to musl ≥ 1.2.
  3. Test with clocks set past 2038 (for example faketime, or VMs and containers with a shifted clock) and with future-dated inputs.
  4. Audit file systems (ext4 inode size, XFS bigtime), database types and binary formats.
  5. Full procedures are in Testing-and-Detection and Remediation-Guide. Real-world failures are collected in Known-2038-Incidents.

See also

References

  1. Pondering 2038 — LWN.net (Jonathan Corbet), 2013-08-14. Linux options, the OpenBSD ABI break, and the embedded lifespan concern.
  2. Year 2038 problem — Wikipedia. Secondary overview: the AOLserver 2006 failure, the Oracle Access Manager 2022 cookie issue, and a solutions table.
  3. OpenBSD 5.5 release page — OpenBSD, 2014-05-01. "time_t is now 64 bits on all platforms". Unsigned 32-bit fields are "good till the year 2106".
  4. glibc NEWS file (mirror of sourceware glibc.git) — GNU C Library. 2.34: _TIME_BITS=64, which requires LFS and kernel ≥ 5.1. 2.40: utmp/lastlog fields made unsigned (to 2106).
  5. [GIT PULL] y2038: core, driver and file system changes — Arnd Bergmann, LKML, 2020-01-29. Linux 5.6 readiness, the 5.1 time64 syscalls, input_event moved to unsigned, and remaining file systems.
  6. What time is it? (QNX OS 8.0 Programmer's Guide) — BlackBerry QNX. Previous releases used unsigned 32-bit seconds; 8.0 uses signed 64-bit.
  7. Austin Group defect 1462: time_t width — Austin Group. Accepted requirement for a 64-bit time_t in Issue 8.
  8. <sys/types.h> — POSIX.1-2024 (The Open Group Base Specifications Issue 8). "time_t shall be an integer type with a width … of at least 64 bits."
  9. 32-bit 2038 issue — tz mailing list, November 1997. Early discussion of 2038 and LP64 time_t.
  10. Re: time_t size: The year 2038 bug? — linux-kernel, 2000-01-05. The 2000 kernel thread on widening time fields.
  11. Announcing NetBSD 6.0 — The NetBSD Project, 2012. "time_t is now a 64-bit quantity on all NetBSD ports."
  12. Pondering 2038 — LWN.net, 2013-08-14. Reports Philip Guenther's OpenBSD 64-bit time_t commit of 2013-08-12.
  13. Making inode time stamps y2038 ready (RFC 00/32) — Arnd Bergmann via LWN, 2014-05-30. The first Linux y2038 series.
  14. Solving the Year 2038 problem in the Linux kernel — Deepa Dinamani, opensource.com, 2019-01-21. VFS timestamp conversion merged in June 2018.
  15. y2038: add time64 syscalls — Arnd Bergmann via LWN, 2019-01-10. Syscall series for the 5.1 merge window.
  16. musl 1.2.0 released — musl mailing list (Openwall), 2020-02-20. "moves all 32-bit archs to 64-bit time_t".
  17. Microsoft Exchange year 2022 bug in FIP-FS breaks email delivery — BleepingComputer, 2022-01-01. Value 2,201,010,001 exceeded the signed int32 maximum.
  18. ReleaseGoals/64bit-time — Debian Wiki. Transition began 2024-02-27; i386 and hurd-i386 excluded.
  19. Ubuntu 24.04 LTS release notes — Canonical. "Ubuntu 24.04 LTS solves the Year 2038 problem that existed on armhf."
  20. Debian 13 "trixie" release notes §2.2.6 — Debian. "All architectures other than i386 now use a 64-bit time_t ABI." Released 2025-08-09 per Debian News.
  21. FreeBSD 15.0-RELEASE Release Notes — FreeBSD Project. i386, armv6 and 32-bit powerpc retired; armv7 remains.
  22. arch(7) — FreeBSD Manual Pages. "time_t is 8 bytes on all supported architectures except i386."
  23. time, _time32, _time64 — Microsoft Learn. time_t is 64-bit by default. _USE_32BIT_TIME_T is discouraged and not allowed on 64-bit.
  24. FILETIME structure — Microsoft Learn. 64-bit count of 100 ns intervals since 1601-01-01 UTC.
  25. 32-bit ABI bugs (bionic docs) — Android Open Source Project. "On 32-bit Android, time_t is 32-bit, which will overflow in 2038."
  26. IT incident report: VPN CA certificates dated 2037–1910 — The Register, 2024-02-09. A CA with 2037→1910 validity dates caused by 32-bit time and bad NTP.