↑↓ to move ↵ to open Esc to close Browse all tools

Developer guide

The Year 2038 Problem: When 32-Bit Time Runs Out

Many systems keep time as a count of seconds since January 1, 1970. Stored in a signed 32-bit integer, that count runs out on January 19, 2038, at 03:14:07 UTC, and the next second reads as a date in 1901. Here is the arithmetic behind the date, what happens at the limit, what Linux, the GNU C Library and the POSIX standard changed, and where the problem still hides.

How Unix time counts

POSIX, the standard behind Unix-like systems, defines the Epoch as 00:00:00 on January 1, 1970, Coordinated Universal Time. A timestamp is the number of seconds since then. The standard also says that every day is accounted for by exactly 86400 seconds, which keeps the arithmetic simple: divide by 86400 and you have days. Programs store this count in a type called time_t. The Unix timestamp converter turns any such number into a date and back.

The limit

For decades time_t was a signed 32-bit integer on many systems. Signed means one bit is used for the sign, so the largest value is 231 − 1 = 2,147,483,647. Count that many seconds from the Epoch and you land here:

Last second a signed 32-bit time_t can hold
2,147,483,647 s = 2038-01-19 03:14:07 UTC

That is early on a Tuesday, January 19, 2038, in UTC. The next second needs a value the type cannot hold. In the usual two's complement arithmetic, adding 1 to the largest value gives the smallest one:

Real seconds since 1970Stored in 32 bitsReads as
2,147,483,6462,147,483,6462038-01-19 03:14:06 UTC
2,147,483,6472,147,483,6472038-01-19 03:14:07 UTC
2,147,483,648-2,147,483,6481901-12-13 20:45:52 UTC

So the clock jumps from 2038 back to 1901-12-13 20:45:52 UTC. Software that compares dates suddenly sees the future as the distant past: a certificate can look not yet valid, a file 136 years old, a timer can wait forever or fire at once. Where the overflow is caught, the call fails instead.

Working it out by hand

Because POSIX counts every day as exactly 86400 seconds, the date follows from two divisions. 2,147,483,647 ÷ 86400 = 24,855 whole days, with 11,647 seconds left over. Those leftover seconds are 03:14:07 on the clock: 3 hours, 14 minutes and 7 seconds. Counting 24,855 days forward from January 1, 1970 lands on 2038-01-19. Put together, that is 2038-01-19 03:14:07 UTC.

The same steps work for any timestamp, and they explain why leap seconds never show up in Unix time: the standard simply does not count them. Going the other way, from a date to a timestamp, is what the converter tool does, and the leap year rules are the only calendar detail the day count has to get right.

Other counters, other limits

The same arithmetic applies to every counter with a fixed size. Some run out much sooner than 2038, some never in practice:

CounterRuns out
Signed 32-bit seconds2038-01-19 03:14:07 UTC
Unsigned 32-bit seconds2106-02-07 06:28:15 UTC
Signed 64-bit secondsabout 292 billion years from 1970
JavaScript Date (milliseconds)September 13, 275760 (per MDN)
Signed 32-bit milliseconds24.9 days after its start

Two rows deserve a note. A signed 32-bit counter of milliseconds overflows after about 24.9 days, so any timer or uptime value kept that way wraps every few weeks. JavaScript is safe for a long time: MDN says a Date holds milliseconds since the Epoch and can represent up to ±8,640,000,000,000,000 of them, which reaches September 13, 275760.

What has been fixed

The fix is a 64-bit time_t, and most of the work is done on current systems.

  • POSIX. Issue 8 of the standard, published as IEEE Std 1003.1-2024, now requires time_t to be an integer type at least 64 bits wide. Its change history credits Austin Group Defect 1462 for the change.
  • Linux kernel. The kernel's timekeeping documentation says interfaces returning struct timeval or struct timespec were replaced, because their seconds field overflows in 2038 on 32-bit architectures. Their replacements use timespec64 and time64_t.
  • GNU C Library. Version 2.34 added 64-bit time_t on configurations like x86 where it was traditionally 32-bit. It has to be switched on with _TIME_BITS=64, only works together with _FILE_OFFSET_BITS=64, is Linux only, and needs at least kernel 5.1 for full support. The NEWS file adds that the 32-bit default may change in future versions.
  • Login records. glibc 2.40 changed the 32-bit time fields in utmp, utmpx and lastlog on several architectures from signed to unsigned, which its NEWS says lets them hold timestamps until 2106. The table above shows the exact moment: 2106-02-07 06:28:15 UTC.

Note what the glibc entry implies. On a 32-bit platform, a program only gets 64-bit time if it is compiled with the right flags. Old binaries keep their old size.

Where the problem still hides

A 64-bit operating system is not the end of it, because a timestamp is often copied into something with its own fixed size:

  • Embedded devices. A device with 32-bit software that is installed today and never updated may still be running in 2038, with whatever time type it shipped with.
  • File formats and protocols. Any format that stores seconds in a 4-byte signed field has the same limit, regardless of the machine reading it.
  • Databases. A timestamp kept in a 32-bit integer column, for example to save space, overflows like a 32-bit time_t. Prefer a 64-bit integer or a proper date-time type.
  • Code. A cast of a time value to int silently drops the upper bits on platforms where int is 32 bits.
  • Future dates. The problem arrives early for anything that looks ahead. A 30-year loan that starts in 2026 has its last payment in 2056, 18 years past the limit, and a schedule of all its payments crosses the limit in year 13. A system that computes such dates today already needs 64-bit time, twelve years before the clock itself gets there.

That last point is the easiest test. Enter a date after 2038-01-19 03:14:07 UTC into the system, or the timestamp 2,147,483,648, and see whether it comes back unchanged. The UTC and time zones guide explains why stored timestamps should be in UTC, and the ISO 8601 guide covers the text format for dates, which stores a year as digits rather than a count of seconds.

Frequently asked questions

What exactly happens in 2038?

Unix time counts seconds since January 1, 1970, 00:00:00 UTC. A signed 32-bit integer can hold at most 2,147,483,647, which is reached at 2038-01-19 03:14:07 UTC. One second later the counter wraps around to its most negative value, which reads as 1901-12-13 20:45:52 UTC. Software that still stores time in 32 bits jumps back to 1901 or fails.

Is my computer affected by the year 2038 problem?

Probably not at the operating system level, if it is current: POSIX now requires a time_t of at least 64 bits, and the Linux kernel replaced its 32-bit time interfaces. The risk sits in older 32-bit systems, embedded devices that are never updated, and in software, file formats and database columns that store a timestamp in a 32-bit field, even on a 64-bit machine.

Has Linux fixed the 2038 problem?

The kernel documentation says interfaces returning struct timeval or struct timespec were replaced because their seconds field overflows in 2038 on 32-bit architectures, with 64-bit replacements such as timespec64 and time64_t. The GNU C Library added 64-bit time_t on 32-bit platforms like x86 in version 2.34, switched on with _TIME_BITS=64, and its NEWS file says full support needs at least kernel 5.1.

Why not use an unsigned 32-bit number?

It doubles the range forward: an unsigned 32-bit counter lasts until 2106-02-07 06:28:15 UTC. glibc 2.40 did this for the login records in utmp and lastlog on some architectures, which its NEWS file says lets them store timestamps until 2106. The price is that dates before 1970 can no longer be stored.

How do I test for the year 2038 problem?

Feed the software dates after the limit, such as the timestamp 2147483648, and check that they come back unchanged. Set a test machine's clock past 2038-01-19 03:14:07 UTC and run the program. Look for 32-bit integer columns or fields that hold timestamps, and for code that casts time values to a 32-bit int.

Sources