Who is affected
Probably fine
- 64-bit desktop and server operating systems (Linux, macOS, Windows, the BSDs) have used a 64-bit
time_tfor many years. - Modern smartphones run 64-bit operating systems.
- Languages with their own time types, like Java, Go, Python 3, Rust or JavaScript, don’t depend on a 32-bit
time_tinternally, as long as they don’t exchange 32-bit values with the outside world.
Worth a closer look
- Embedded and IoT devices. Many 32-bit ARM and MIPS systems are built to run for 15 to 30 years: cars, routers, building automation, industrial control systems, medical devices, point-of-sale terminals. Their firmware is often old and rarely updated.
- 32-bit Linux userlands. The Linux kernel supports 64-bit time on 32-bit platforms since version 5.6 (2020), and glibc since 2.34 (2021). But every program has to be recompiled with 64-bit time to benefit. Debian only switched its 32-bit ARM ports with Debian 13 in 2025, and deliberately kept 32-bit time on i386 for compatibility with old binaries.
- File systems. Older formats store 32-bit timestamps on disk. Examples: ext3 and ext4 with small inodes, XFS without the
bigtimefeature, and many FAT and archive formats with their own limits. - Databases. Timestamps are in almost every table, and some column types end in 2038. See Databases below.
- Network protocols. Many protocols carry 32-bit time fields on the wire. See Network protocols below.
- File formats. Anything that writes seconds into a 4-byte field: binary file headers, serialization formats, custom log formats. Upgrading to a 64-bit operating system doesn’t change the format.
- Your own code. An
intused to store a timestamp, a(int)time(NULL)cast, a 32-bit column in a database schema, a struct written raw to disk.
Network protocols
A protocol is a contract between two machines, often built by different vendors decades apart. If the contract says “4 bytes of seconds”, updating one side’s operating system doesn’t help: the field on the wire stays 32 bits wide. Some examples:
| Protocol | Time field | Situation |
|---|---|---|
| NTP | 32-bit unsigned seconds since 1900 | Rolls over on 2036-02-07. NTPv4 can handle this, but only if the device’s clock is roughly right to begin with. |
| RADIUS | Event-Timestamp: 32-bit seconds since 1970 | Unsigned by the standard, so it lasts until 2106. Software that copies it into a signed 32-bit value breaks in 2038. |
| NFSv3 | 32-bit unsigned seconds | Lasts until 2106. NFSv4 uses 64 bits. |
| NetFlow v5 / IPFIX | 32-bit export time | Unsigned, lasts until 2106 if every collector reads it that way. |
| pcap (classic file format) | 32-bit seconds per packet | Unsigned by the specification, but tools that read it as signed show 1901 after 2038. |
| TLS 1.2 | first 4 bytes of the client/server random were meant as gmt_unix_time | Modern implementations such as OpenSSL fill them randomly; TLS 1.3 dropped the idea. |
| Modbus and other industrial protocols | a timestamp is often split across two 16-bit registers | 32 bits again, usually without saying signed or unsigned. |
Some protocols handle the wraparound on purpose. DNSSEC signature times are 32-bit Unix seconds too, but they’re compared with serial number arithmetic, which only asks which of two values is newer. TCP timestamps work the same way and aren’t clock time at all, just a counter. Both are designed to wrap and keep working.
Text and 64-bit formats look safe on the wire, but they still need checking:
- X.509 certificates write dates as text, and many root certificates already expire after 2038. Software that converts those dates into a 32-bit
time_tfails to validate them today. - JSON Web Tokens store
exp,iatandnbfas plain numbers. The format has no limit, but a parser that reads them into a 32-bit integer does. - Protocol Buffers (
google.protobuf.Timestamp) and most modern formats use 64-bit seconds. They’re fine as long as the code on each end keeps them at 64 bits.
The hard part is that both ends have to agree. A server fixed in 2030 still talks to a sensor, a payment terminal or a VPN appliance installed in 2015.
Custom protocols: the biggest blind spot
The protocols above at least have a specification, a standards body and people who think about their limits. Most of the protocols running in businesses have none of that. They were designed in-house, under deadline pressure, by a team that optimized for bandwidth and latency, not for the year 2038.
Games are a prime example. Game networking is almost always custom: hand-packed binary messages, every byte counted, and timestamps squeezed into 32 bits because they “obviously” fit. Time shows up everywhere:
- session tokens, logins and matchmaking tickets with expiry times
- server ticks, lag compensation and replay files
- anti-cheat checks that compare client and server time
- in-game events, seasons, timed rewards and item expiry
- leaderboards, bans and suspensions with end dates
- save games and backend telemetry
Nobody at an IETF meeting has reviewed these protocols. The specification is often just the source code, the original developers have moved on, and the same engine or networking library ends up in titles that are maintained, re-released and kept online for many years.
The same pattern exists far beyond gaming: trading and payment systems, telematics and fleet management, industrial and building automation, medical devices, and the countless proprietary formats between a company’s own services. If your company built a protocol itself, assume nobody has checked it for 2038 until someone has.
Databases
Almost every table has timestamps: created_at, updated_at, last_login, expires_at, valid_until. That makes databases one of the most common places for the 2038 problem, and they fail before 2038 because they store dates in the future.
MySQL and MariaDB TIMESTAMP
MySQL’s TIMESTAMP column type is stored internally as a 32-bit Unix timestamp. Its range is defined as
'1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC
and it’s very widespread: it is the classic choice for DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, and frameworks like Laravel create TIMESTAMP columns for created_at and updated_at by default.
Storing a later date fails today. In strict SQL mode it’s an error; in non-strict mode MySQL stores 0000-00-00 00:00:00 with only a warning. Think of a subscription “valid until 2040”, a 20-year warranty or a contract end date.
MySQL 8.0.28 extended FROM_UNIXTIME() and UNIX_TIMESTAMP() beyond 2038 on 64-bit platforms, but the TIMESTAMP column type keeps its limit. MariaDB 11.5 extended TIMESTAMP to 2106 on 64-bit platforms.
Epoch seconds in INT columns
Many applications skip date types entirely and store Unix seconds in a plain integer column:
| Column type | Ends |
|---|---|
INT (signed 32-bit) | 2038-01-19 03:14:07 UTC |
INT UNSIGNED | 2106-02-07 06:28:15 UTC, but no dates before 1970 |
BIGINT | about 292 billion years from now |
The same applies to the code that reads these values. A BIGINT column doesn’t help if the application maps it to a 32-bit integer.
Other databases
- PostgreSQL:
timestampandtimestamptzare 64-bit and reach the year 294276. Fine, unless epoch values are kept inintegercolumns. - SQL Server and Oracle:
datetime2,DATEandTIMESTAMPgo to the year 9999. - SQLite has no date type. Dates are stored as text, floating-point numbers or 64-bit integers, so it depends on the application.
- MongoDB: BSON dates are 64-bit milliseconds. The
ObjectId, however, starts with a 4-byte seconds field. It is meant to be read as unsigned and lasts until 2106, but drivers have treated it as signed: Ruby’s BSON 5.0, for example, couldn’t create ObjectIds after 2038.
Changing the column isn’t a one-liner
Switching a MySQL TIMESTAMP to DATETIME changes its meaning. TIMESTAMP values are converted between the session time zone and UTC; DATETIME values are stored as written, with no time zone at all. On large tables the ALTER TABLE also rewrites the whole table. Plan the migration, and decide on one time zone (UTC) first.
Related deadlines
The 2038 problem has relatives with their own dates:
| What | When |
|---|---|
| NTP era rollover (32-bit unsigned seconds since 1900) | 2036-02-07 06:28:16 UTC |
| Signed 32-bit Unix time | 2038-01-19 03:14:07 UTC |
| Unsigned 32-bit Unix time | 2106-02-07 06:28:15 UTC |
| Signed 64-bit Unix time | about 292 billion years from now |