Who is affected

Probably fine

  • 64-bit desktop and server operating systems (Linux, macOS, Windows, the BSDs) have used a 64-bit time_t for 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_t internally, 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 bigtime feature, 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 int used 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:

ProtocolTime fieldSituation
NTP32-bit unsigned seconds since 1900Rolls over on 2036-02-07. NTPv4 can handle this, but only if the device’s clock is roughly right to begin with.
RADIUSEvent-Timestamp: 32-bit seconds since 1970Unsigned by the standard, so it lasts until 2106. Software that copies it into a signed 32-bit value breaks in 2038.
NFSv332-bit unsigned secondsLasts until 2106. NFSv4 uses 64 bits.
NetFlow v5 / IPFIX32-bit export timeUnsigned, lasts until 2106 if every collector reads it that way.
pcap (classic file format)32-bit seconds per packetUnsigned by the specification, but tools that read it as signed show 1901 after 2038.
TLS 1.2first 4 bytes of the client/server random were meant as gmt_unix_timeModern implementations such as OpenSSL fill them randomly; TLS 1.3 dropped the idea.
Modbus and other industrial protocolsa timestamp is often split across two 16-bit registers32 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_t fails to validate them today.
  • JSON Web Tokens store exp, iat and nbf as 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 typeEnds
INT (signed 32-bit)2038-01-19 03:14:07 UTC
INT UNSIGNED2106-02-07 06:28:15 UTC, but no dates before 1970
BIGINTabout 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: timestamp and timestamptz are 64-bit and reach the year 294276. Fine, unless epoch values are kept in integer columns.
  • SQL Server and Oracle: datetime2, DATE and TIMESTAMP go 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.

The 2038 problem has relatives with their own dates:

WhatWhen
NTP era rollover (32-bit unsigned seconds since 1900)2036-02-07 06:28:16 UTC
Signed 32-bit Unix time2038-01-19 03:14:07 UTC
Unsigned 32-bit Unix time2106-02-07 06:28:15 UTC
Signed 64-bit Unix timeabout 292 billion years from now