What happens at 03:14:07 UTC on 19 January 2038
Unix systems track time as a count of seconds elapsed since 00:00:00 UTC on 1 January 1970. For most of computing history that counter was a signed 32-bit integer, and a signed 32-bit integer cannot hold a value above 2,147,483,647.
That many seconds after the epoch lands on 03:14:07 UTC on Tuesday 19 January 2038. One second later the counter overflows. In two's complement arithmetic it does not saturate or throw, it wraps to negative, and code that reads the result without checking will report the time as 20:45:52 UTC on 13 December 1901.
Why the number is 2,147,483,647
A 32-bit signed integer spends one bit on the sign, leaving 31 bits of magnitude. That gives a maximum of 231 minus 1:
2^31 - 1 = 2147483647 seconds
= 68.05 years
1970-01-01 UTC + 2147483647s
= 2038-01-19T03:14:07Z
next second = -2147483648
= 1901-12-13T20:45:52Z
The choice was reasonable when it was made. In the early 1970s, 68 years of range on hardware where every byte was expensive was generous. The mistake was not the width, it was assuming the code would be gone by the time the width ran out.
What actually breaks
Not everything, and the failures are less dramatic than a system halting. The characteristic 2038 bug is a piece of date arithmetic quietly producing an answer from the wrong century:
- Expiry and validity checks that compare a stored timestamp against now, and conclude a certificate or session expired 137 years ago.
- Sorting and range queries, where wrapped values sort before everything else and silently fall out of result sets.
- Interval arithmetic, where subtracting two timestamps across the boundary produces an enormous or negative duration.
- Filesystem and archive metadata that cannot represent the date at all, so it is stored wrong on write rather than misread on read.
- Embedded controllers in industrial, medical, automotive, and building systems, where the firmware is 32-bit, the vendor is gone, and there is no update path.
The last category is the genuinely hard one. Recompiling a web service is a scheduling problem. Replacing a programmable logic controller welded into a production line is a capital expenditure.
What is already fixed
A great deal, and this is worth stating plainly because the problem is often
described as though nothing has been done. Any system running a 64-bit build
already uses a 64-bit time_t and is unaffected for about 292
billion years.
| Component | Version | Change |
|---|---|---|
| NetBSD | 6.0 (2012) | 64-bit time_t on all architectures |
| OpenBSD | 5.5 (2014) | 64-bit time_t on all architectures |
| Linux kernel | 5.6 (2020) | Completed 64-bit time support for 32-bit architectures |
| glibc | 2.34 (2021) | Opt-in _TIME_BITS=64 for 32-bit builds |
On 32-bit Linux the kernel side and the library side are separate problems.
A 5.6 or newer kernel can represent the time correctly, but a 32-bit
userspace program still needs to be compiled against a glibc that exposes
the 64-bit interfaces, which means building with
_TIME_BITS=64 and _FILE_OFFSET_BITS=64. Distributions
have been working through this rebuild for years.
What is still exposed
The remaining risk concentrates in places where recompiling is not an option or where the 32-bit width is baked into a format rather than a variable:
- Storage formats with fixed-width time fields. ext4 inodes of 128 bytes cannot represent dates past 2038. Inodes of 256 bytes carry extra epoch bits and reach 2446. The inode size was fixed when the filesystem was created and cannot be changed in place.
- Database column types. A MySQL or MariaDB
TIMESTAMPcolumn tops out at 2038-01-19 03:14:07 UTC.DATETIMEdoes not have the limit. This is a schema decision made years ago in a lot of production systems. - Binary protocols and file formats that specify a 32-bit time field on the wire. Changing those means a format version bump and a compatibility story.
- Long-lived 32-bit embedded firmware, especially where the toolchain itself predates the fix.
Worth noting for contrast: JavaScript is unaffected. The Date
type stores a floating point count of milliseconds and stays exact until
around the year 275760. The countdown at the top of this page has no 2038
problem of its own.
The 2036 NTP rollover arrives first
Two years before time_t overflows, a different clock runs out.
Network Time Protocol timestamps count seconds since 1 January 1900 in an
unsigned 32-bit field. That range ends at 06:28:16 UTC on 7
February 2036, when NTP era 0 rolls into era 1.
NTP was designed with this in mind and the protocol handles eras correctly, but implementations vary in how well they infer which era a timestamp belongs to. Anything that gets its time from the network will meet 2036 before it meets 2038, which makes it the better forcing function for an audit.
It has already broken things
The date on the countdown is when time itself overflows. Systems that do arithmetic forward from today crossed the boundary long ago. Anything calculating a twenty year term hit 2038 back in 2018. Thirty year mortgage schedules reached it in 2008.
This is the practical point that gets missed. Failures do not queue up politely and arrive on the day. They arrive whenever a system first calculates a date past the boundary, which for long-dated financial instruments, certificate lifetimes, and warranty and retention schedules is already happening. If you are going to find a 2038 bug, you will probably find it years before 2038.
How to check your own systems
Start with the width of time_t in the toolchain that actually
builds your binaries, which is not always the same as the host you are
typing on:
# Userspace word size
getconf LONG_BIT
# Actual width of time_t as your compiler sees it
printf '#include <stdio.h>\n#include <time.h>\n
int main(void){printf("time_t is %%zu bytes\\n", sizeof(time_t));return 0;}' \
| cc -x c - -o /tmp/tsize && /tmp/tsize
Four bytes means exposed. Eight means safe. For a 32-bit target that reports four, check whether your glibc supports the 64-bit interfaces and rebuild with them enabled:
cc -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64 ...
Then audit storage, which is where the problem outlives any recompile:
-- Columns that cannot hold a date past 2038
SELECT table_schema, table_name, column_name
FROM information_schema.columns
WHERE data_type = 'timestamp';
# ext4 inode size: 128 means capped at 2038
sudo tune2fs -l /dev/sdXN | grep -i 'inode size'
Finally, test the behaviour rather than reasoning about it. Do this in a container or a disposable virtual machine, never by setting the clock on a live host, since jumping a production system forward fourteen years will expire every certificate and token it holds:
faketime '2038-01-19 03:14:00' ./your-binary
How this page calculates the countdown
The countdown is the difference between the current Unix timestamp and 2,147,483,647, broken into years of 365.25 days, then days, hours, minutes, and seconds. The progress bar is the current timestamp divided by that same maximum, which is the fraction of the signed 32-bit range already consumed.
Every figure comes from your own system clock, so if your machine's time is wrong then so is this page. It was built by Tiempo Software as a single static HTML file with no backend and no tracking of what you run.
Sources and further reading
- Year 2038 problem Wikipedia. Overview, affected systems, and remediation history.
- Unix time Wikipedia. Epoch definition and encoding.
- POSIX: sys/time.h The Open Group. The standard that defines the interfaces in question.
- Preparing for the year 2038 LWN.net. Kernel-side history of the transition.
- The end of the 32-bit time_t LWN.net. Detail on how the kernel remediation was carried out.
-
glibc Y2038 proofness design
Sourceware. The design behind
_TIME_BITS=64. - 64-bit time symbol handling GNU C Library manual. How to build against the 64-bit interfaces.
- ext4 inode layout Linux kernel documentation. Where the extra timestamp bits live.
- TIMESTAMP MariaDB Knowledge Base. Documents the 2038 column limit, which MySQL shares.
- Network Time Protocol Wikipedia. The 2036 era rollover.
- Time formatting and storage bugs Wikipedia. The wider family of date handling failures.
This page is informational and is not a substitute for auditing your own systems. Command examples are illustrative. Run them in a disposable environment before running them anywhere that matters.