The Year 2038 problem
On this page
TL;DR. On 2038-01-19 03:14:07 UTC, the signed 32-bit Unix timestamp overflows from
2147483647to-2147483648, making the system think it’s 1901. Most modern Linux/Unix systems already use 64-bittime_t, but embedded systems, old databases, file formats, and protocols may still be 32-bit. Audit now.
What overflows, exactly
A signed 32-bit integer holds values from -2147483648 to +2147483647.
For Unix seconds, that range covers:
-2,147,483,648 = 1901-12-13 20:45:52 UTC
0 = 1970-01-01 00:00:00 UTC (the epoch)
2,147,483,647 = 2038-01-19 03:14:07 UTC
At the moment of 2147483647 + 1, the value wraps to -2147483648 —
the system interprets the new “now” as 1901-12-13 20:45:52 UTC.
The exact moment
Tuesday, January 19, 2038 at 03:14:07 UTC
= Monday, January 18, 2038 at 22:14:07 EST
= Tuesday, January 19, 2038 at 12:14:07 JST
You can paste 2147483647 into the converter and see the date
yourself.
What’s affected
Definitely safe (64-bit time_t)
- Linux x86_64 — 64-bit since the architecture was introduced.
- macOS x86_64 / arm64 — 64-bit
time_tsince 10.6 (2009). - Windows x86_64 — 64-bit since 2003-era Windows Server.
- iOS, Android (modern) — 64-bit on all current devices.
- Modern PostgreSQL, MySQL, SQL Server — store 64-bit timestamps.
- Modern programming languages — JavaScript (53-bit number range),
Python
int(arbitrary precision), Go (64-bit by default).
For these, Y2038 is a non-issue. The 64-bit range covers ~292 billion years — well past any practical horizon.
Potentially affected (32-bit time_t)
- Embedded systems — IoT devices, industrial controllers, vehicle electronics, point-of-sale terminals running old Linux kernels.
- Some 32-bit Linux distributions — though most fixed
time_tto 64-bit by ~2020. Checkglibcversion and architecture. - Old file formats —
tar(resolved in modern versions),zip(now stretched),cpio. - Some embedded databases — older SQLite versions; modern SQLite is fine.
- Legacy industrial protocols — Modbus and similar that pack timestamps into 32-bit fields.
- Old GPS firmware — some had Y2038-related rollovers separate from the GPS Week Number rollover (which already happened in 2019).
Definitely affected (without action)
- C/C++ code on 32-bit platforms that explicitly uses
int32_tor legacytime_t. Check ports of older Unix software running on embedded ARM or MIPS. - Database
INTEGERcolumns holding timestamps explicitly typed as 32-bit. Many systems silently promoted these to BIGINT, but decade-old apps may not have been updated. - Some old HTTP cookie expirations — old browsers stored cookie expiry as 32-bit time.
Real incidents already
Yes — Y2038 has caused production outages already, decades early:
- 2017 — Linode crashed. A miscalculation set a future cron expiry beyond 2038, hitting the limit on a 32-bit system. Various services affected.
- 2014 — IIS6 cert validation broken. Time-skew calculations against
certificates with
notAfterpast 2038 caused validation failures. - 2020 — many embedded devices failed when trying to set internal test timestamps for “20 years from now.”
Expect more in the run-up to 2038, especially in audit/compliance code that intentionally sets dates 5–20 years in the future.
How to check your stack
# C / Linux
echo | gcc -E -dM -include <time.h> | grep -i time_t
# Look for sizeof(time_t)
# C check
cat <<'EOF' > t.c
#include <stdio.h>
#include <time.h>
int main(void) {
printf("sizeof(time_t) = %zu\n", sizeof(time_t));
return 0;
}
EOF
gcc t.c -o t && ./t
# Want: 8
# Database — Postgres
\d+ table_name
# Look for timestamp / bigint columns; INTEGER on time fields is suspect
# Application code — search for hard-coded 2147483647 or 0x7FFFFFFF
grep -r 2147483647 .
grep -r 0x7FFFFFFF .
If your timestamps are stored as 32-bit anywhere, plan a migration to 64-bit before 2038. The fix is mechanical (column type change + data copy) but disruptive in production.
Languages that handle it natively
- JavaScript / Node —
Date.now()returns a 53-bit integer (ms since epoch); no overflow until year 285,616. - Python
int— arbitrary precision; no overflow ever. - Go
time.Time— usesint64seconds internally. - Rust
chrono::DateTime—i64based. - Java 8+
Instant—longseconds + nanoseconds.
If your application code is in any of these on a 64-bit OS, you can ignore Y2038 for application-level time. The risk lives in:
- Stored data (database column types)
- External systems (embedded devices, legacy mainframes)
- Wire protocols (any that hardcode a 32-bit timestamp field)
What to do today
- Audit any 32-bit
inttimestamp columns in your databases. Migrate to 64-bit. Most modern ORMs default to 64-bit but check. - Update embedded device firmware if you ship hardware with a long service life. A 2026 device that’s expected to run for 15 years will hit 2038.
- Avoid hardcoding 2147483647 as a “max date” sentinel — use the
language’s
MAX_VALUEfor the actual integer type, or store NULL. - Test with future dates. Set a system clock to 2038-02 in a sandbox and run your test suite. Things that break will surface.
Try the tool
Paste these into the converter to see for yourself:
2147483647— last 32-bit second2147483648— first second past the cliff (treated as milliseconds by the auto-detector, since it’s just over 10^9)2147483647000— the last 32-bit second, expressed in milliseconds (still fine in JavaScript / 64-bit world)
Related reading
- What is Unix epoch? — the basics
- Seconds vs milliseconds — the most common time bug
- Wikipedia: Year 2038 problem — comprehensive
Related across the network
- date.tooljo.com — UTC-anchored date math that doesn’t trip the Y2038 boundary at all (uses ms-precision).
- date.tooljo.com/date-format-reference — covers how each format handles the post-2038 transition.
- jwt.tooljo.com/standard-claims
— JWT
expis Unix seconds; check whether your library’s parser uses 32- or 64-bit integers.