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:
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 1970 | Stored in 32 bits | Reads as |
|---|---|---|
| 2,147,483,646 | 2,147,483,646 | 2038-01-19 03:14:06 UTC |
| 2,147,483,647 | 2,147,483,647 | 2038-01-19 03:14:07 UTC |
| 2,147,483,648 | -2,147,483,648 | 1901-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:
| Counter | Runs out |
|---|---|
| Signed 32-bit seconds | 2038-01-19 03:14:07 UTC |
| Unsigned 32-bit seconds | 2106-02-07 06:28:15 UTC |
| Signed 64-bit seconds | about 292 billion years from 1970 |
| JavaScript Date (milliseconds) | September 13, 275760 (per MDN) |
| Signed 32-bit milliseconds | 24.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_tto 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 timevalorstruct timespecwere replaced, because their seconds field overflows in 2038 on 32-bit architectures. Their replacements usetimespec64andtime64_t. - GNU C Library. Version 2.34 added 64-bit
time_ton 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,utmpxandlastlogon 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
intsilently drops the upper bits on platforms whereintis 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
- The Open Group Base Specifications Issue 8 (IEEE Std 1003.1-2024), section 3.125 Epoch (00:00:00 on January 1, 1970, UTC)
- The Open Group Base Specifications Issue 8, section 4.19 Seconds Since the Epoch (the formula; each day is accounted for by exactly 86400 seconds)
- The Open Group Base Specifications Issue 8, sys/types.h (time_t shall be an integer type with a width of at least 64 bits; Austin Group Defect 1462)
- The Linux Kernel documentation: ktime accessors (interfaces returning struct timeval or struct timespec replaced because tv_sec overflows in 2038 on 32-bit architectures; timespec64 and time64_t)
- GNU C Library NEWS (2.34: 64-bit time_t on configurations like x86 with _TIME_BITS=64, only with _FILE_OFFSET_BITS=64, Linux only, full support from kernel 5.1, default may change; 2.40: unsigned 32-bit time fields in utmp and lastlog reach 2106)
- MDN Web Docs: Date (milliseconds since the epoch; at most ±8,640,000,000,000,000 ms, April 20, 271821 BC to September 13, 275760 AD)