Remediation Guide
In brief: You fix the Year 2038 problem by making every place that stores or moves a time value wide enough to go past 19 January 2038. That covers hardware clocks, the operating system, system libraries, programming languages, your own code, your databases, your files and your network messages. The only lasting fix is 64-bit time. Shortcuts such as "treat the number as unsigned" or "shift the starting date" can buy time, but they bring risks of their own. This page goes through the layers in order and explains which shortcuts are safe and which are not.
| Risk level | High for 32-bit embedded/OT estates; Medium for mixed enterprise estates; Low for fully 64-bit, current software |
| Status | Kernel, libc and major distributions are fixed upstream; applications, data formats, protocols and fielded devices still vary |
| Last reviewed | 2026-10-21 |
Overview
A time value is only as wide as the narrowest place it passes through. A 64-bit kernel cannot help if the application stores expires_at in a 32-bit INT column. A 64-bit column cannot help if a 32-bit sensor writes the value. Remediation therefore works layer by layer, bottom up. You fix each dependency before what sits on top of it, and you test each step (see Testing-and-Detection).
There are four broad strategies:
| Strategy | What it does | New limit | Verdict |
|---|---|---|---|
| 64-bit migration | Widen time_t and every stored copy to signed 64 bits |
~292 billion years | Preferred. It is the only permanent fix |
| Unsigned reinterpretation | Read the same 32 bits as uint32_t |
2106-02-07 06:28:15 UTC | Acceptable for on-wire or on-disk formats you can't change, with care |
| Epoch shifting | Count from a later epoch (e.g. 2000) in the same 32 bits | Epoch + 68 years | Dangerous: ambiguous data, breaks interoperability |
| Windowing | Decide at read time which 136-year "window" a wrapped value belongs to | Sliding | Dangerous: guesses, and the guesses fail at the edges |
Why the shortcuts are dangerous
- Unsigned reinterpretation gives up pre-1970 dates. Birth dates and historic records then become invalid or wrap. The approach must also be applied consistently by every reader. MIT Kerberos documents it explicitly: cast
(time_t)(uint32_t)timestamp, which is valid through February 2106 and only if the platform'stime_tis 64-bit [14]. The Linux kernel uses the same trick for interfaces it cannot change compatibly, and it accepts that the trick fails in 2106 [2]. Use it only where the field width is fixed by an external format. - Epoch shifting produces numbers that look like Unix time but are not. Any tool, log parser or partner that assumes the 1970 epoch will silently be off by decades. The value is right in your system and wrong everywhere it is exported. The shift also only buys "epoch + 68 years".
- Windowing (the Y2K technique of treating "00–49" as 20xx and "50–99" as 19xx) works only while all real values fall inside the chosen window. For 2038 that means guessing whether a negative
time_tmeans 1901 or 2038+. The guess is wrong for genuinely old data, and it is wrong again when the window itself rolls over. Many Y2K windowing fixes were explicitly temporary (see Y2K-Remediation-and-Cost and Y2K-Lessons-for-2038). - Sentinel and "forever" values. Never encode "infinite" as
now + 1,000,000,000or asINT_MAX. AOLserver's one-billion-second timeout broke installations on 2006-05-13 [21] (see Known-2038-Incidents). Use an explicit flag,NULL, or the maximum of the destination type with saturating arithmetic. - Clamping on display. OpenSSH truncated certificate dates for display at 2038-01-19T03:14:07 [22]. Clamping is a reasonable defensive fallback, but it hides the problem and it is not a fix.
Technical details: remediation by layer
1. Hardware
- RTC chips often store BCD year digits or a 32-bit seconds counter. Find out how the RTC counts and what the driver does with it. Some RTCs keep only two year digits plus a century bit. QEMU's emulated MC146818 has needed century-byte logic for exactly this reason (see Testing-and-Detection).
- 32-bit CPUs are not the problem by themselves. A 32-bit ARM core can run a 64-bit
time_tuser space on Linux ≥ 5.6 [1][2]. Hardware replacement is needed only when the vendor's firmware cannot be rebuilt. - GNSS receivers have their own counters (GPS week number). See GPS-Week-Rollover.
- Action: list every device with a battery-backed clock, then record the RTC part, the firmware vendor and whether the firmware can be updated. Detail: Devices-and-Embedded-Systems.
2. Operating system kernel
- Linux: The kernel conversion was designed from 2015 onwards [4]. Linux 5.1 added
*_time64syscalls for 32-bit architectures. Linux 5.6 (2020) is the first kernel that "can serve as a base for a 32-bit system designed to run beyond year 2038" [1][2]. Kernel builds can disable the legacy 32-bit time syscalls viaCOMPAT_32BIT_TIME, which helps you find stragglers in testing [3]. - BSD: NetBSD 6.0 (2012) and OpenBSD 5.5 (2014) use 64-bit
time_ton all architectures. NetBSD kept a compatibility layer for old binaries and OpenBSD did not [5]. - Windows: Kernel time is a 64-bit count of 100 ns intervals since 1601 [25]. The risk lies in the CRT and applications (see below).
- Action: plan to move 32-bit embedded Linux to ≥ 5.10 LTS. Get from vendors written confirmation of the kernel version and of the
COMPAT_32BIT_TIMEsetting. Detail: OS-Linux, OS-BSD-and-Unix, OS-Embedded-and-RTOS.
3. C library and ABI
- glibc ≥ 2.34 supports 64-bit
time_ton 32-bit Linux when code is built with-D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64. The support requires LFS and Linux ≥ 5.1 [6]. The default stays 32-bit, so every package must opt in. - musl ≥ 1.2.0 (released 2020-02-20) switched all 32-bit architectures to 64-bit
time_tby default [7]. - This is an ABI break. Any struct, function parameter or ioctl that carries
time_t,struct timevalorstruct timespecchanges layout. Gentoo's Michał Górny warns that mixing time32 and time64 objects can lead to reads and writes "out of bounds" [9]. Libraries must be rebuilt and renamed together. - How distributions did it:
- Debian renamed affected libraries with a
t64suffix (for examplelibfoo1→libfoo1t64). It changeddpkg-buildflags(1.22.5, 2024-02-27) to add the flags on armel/armhf and other 32-bit ports, and kept i386 on 32-bittime_tas a legacy-binary compatibility architecture [8][10]. - Gentoo chose a separate
time64profile and CHOST suffix (…-gnut64) so mismatched objects cannot be mixed [9]. - Yocto 4.3 builds 32-bit targets with 64-bit
time_tand ships a32bit-timeQA check [12].
- Debian renamed affected libraries with a
- Windows CRT: Visual C++ 2005 and later use 64-bit
time_tunless_USE_32BIT_TIME_Tis defined. Microsoft says that macro "isn't allowed on 64-bit platforms" and warns that such applications "may fail after January 18, 2038" [11]. Remove it from build files. - Standards: POSIX now requires
time_tto be at least 64 bits wide (Austin Group issue 1462, tagged for Issue 8 / POSIX.1-2024) [13]. - Steps:
# 1. Add flags globally (CMake toolchain / Autotools site config / Yocto distro)
CFLAGS="$CFLAGS -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64"
# 2. For Gnulib/Autoconf projects, make configure fail if 64-bit time isn't available
# (bootstrap.conf: add 'year2038-recommended') [15]
# 3. Rebuild the whole dependency closure; never mix time32/time64 objects
# 4. Verify: no 32-bit time imports left (scan-time32.sh from Testing-and-Detection)
4. Language runtimes
| Runtime | Typical remediation |
|---|---|
| C/C++ | Use time_t, int64_t and std::chrono end to end. Fix %d/%ld format strings (use %jd with a cast to intmax_t). Remove (int) casts. See Lang-C-and-CPP |
| Java/JVM | Instant/long milliseconds are safe. Look for int seconds and (int)(System.currentTimeMillis()/1000). See Lang-Java-and-JVM |
| Python | On 32-bit builds, datetime.fromtimestamp() and utcfromtimestamp() call the platform C library and still overflow. The LWPCookieJar bug report has stayed open since 2009 [20]. See Lang-Python |
| PHP, Perl, Ruby | Check the platform integer size and the date functions used. Ruby ≥ 1.9.2 uses a 64-bit internal representation [5]. See Lang-PHP, Lang-Perl-Ruby-and-Others |
| .NET, JS, Go, Rust | Native types are 64-bit. The risk is in interop and serialization. See Lang-DotNet, Lang-JavaScript-and-TypeScript, Lang-Go-Rust-Swift |
5. Application code
- Run the grep and static-analysis passes in Testing-and-Detection to build a defect list.
- Replace narrow types with
time_torint64_t. Keep intervals (durations) and instants (points in time) in separate types. - Replace "forever" sentinels with explicit flags, and use saturating addition for
now + timeout. - Prefer
CLOCK_MONOTONICfor timeouts, so that wall-clock jumps (including deliberate test jumps) cannot trigger them [2]. - Add regression tests that run with a faked clock and with future data. Make the build fail if
sizeof(time_t) < 8:_Static_assert(sizeof(time_t) >= 8, "need 64-bit time_t");
6. Data and schema
- MySQL
TIMESTAMPstill ends at 2038-01-19 03:14:07 UTC [16]. Migrate toDATETIME(range to 9999), or toBIGINTepoch seconds, and handle time zones explicitly. MySQL 8.0.28 madeFROM_UNIXTIME()/UNIX_TIMESTAMP()64-bit-safe on 64-bit platforms [17]. MariaDB 11.5.1 extendsTIMESTAMPto 2106 by treating it as unsigned [18], which is the unsigned-reinterpretation strategy again. - Integer epoch columns:
ALTER TABLE … MODIFY col BIGINTis a table rewrite. Schedule it, and update every consumer (ETL, reports, APIs) before values over 2^31 appear. - Serialization: in Protobuf
int32/sfixed32, Avroint, Thrifti32and fixed-width CSV, widen the field and version the schema. Where the schema cannot change, document unsigned interpretation. - Detail: Databases.
7. File formats and file systems
- ext4 extends timestamps to 2446 only with inodes larger than 128 bytes. Check with
tune2fs -l /dev/X | grep 'Inode size'[5]. - XFS "bigtime" (Linux 5.10) extends to 2486. xfsprogs 5.15 and later enable it by default for new filesystems. Older V5 filesystems can be upgraded while cleanly unmounted with
xfs_admin -O bigtime=1 /dev/X(xfsprogs ≥ 5.11). Check the result withxfs_info | grep bigtime[5][30]. - utmp/wtmp/lastlog, cpio/tar headers and application binary formats often embed 32-bit times. Gnulib warns not to include
<utmp.h>directly in time64 builds [15]. Debian lists utmp/wtmp, inn2 disk formats, NFSv3 and 32-bit WINE as known risk areas [8]. - Detail: File-Systems, File-Formats-and-Protocols.
8. Protocols
- Fields fixed by a standard cannot simply be widened. Instead:
- read them as unsigned (NFSv3 [5]; SFTPv3 to 2106);
- use era numbering (NTP, see NTP-Era-2036 [23]);
- or negotiate a newer protocol version (NFSv4 uses 64-bit seconds [5]).
- Check X.509 handling: certificates may carry
GeneralizedTimepast 2049, and 32-bit libraries have mis-stored such dates [19][24]. - Detail: File-Formats-and-Protocols.
9. Vendor and supply chain
- For everything you cannot rebuild, the vendor must do the remediation. Ask the questions in Risk-Assessment-Checklist.
- Real advisories already exist:
- CERT@VDE published WAGO controller (CVE-2025-0101) and switch (CVE-2025-1235) Y2038 advisories in 2025. WAGO's remediation is firmware 4.10.0 (FW32) for controllers. For the switches, the advisory gives no fix [26][27].
- CISA's Dover ProGauge advisory lists fixed versions 4.20.3/5.20.3 [28].
- The Veeder-Root TLS4B fix was still pending at publication [29].
- Where no fix will come, plan replacement or compensating controls: isolate the device, authenticate its NTP and GNSS time sources, and stop untrusted users from setting its clock.
Affected versions / products
| Layer | Minimum "2038-capable" baseline (32-bit targets) |
|---|---|
| Linux kernel | 5.6 (5.1 syscalls) [1][2] |
| glibc | 2.34 with _TIME_BITS=64 [6] |
| musl | 1.2.0 [7] |
| Debian | 13 "trixie" on armel/armhf; not i386 [8][10] |
| Yocto | 4.3 [12] |
| NetBSD / OpenBSD | 6.0 / 5.5 [5] |
MySQL TIMESTAMP |
no fix; migrate column type [16] |
Risk & compliance notes
Remediation work needs an owner per layer: platform team (OS/libc), application owners, DBAs and procurement. Track the status of each asset using the triage matrix in Risk-Assessment-Checklist. Treat any use of unsigned reinterpretation, epoch shifting or windowing as a documented exception with a stated expiry date.
Detection & mitigation
Verify every fix with the techniques in Testing-and-Detection. A remediation counts as complete only when post-2038 clock tests, future-data tests and binary scans all pass.
See also
- Testing-and-Detection · Risk-Assessment-Checklist · Known-2038-Incidents
- Operating-Systems · Programming-Languages · Databases
- Y2K-Remediation-and-Cost · Y2K-Lessons-for-2038 · Year-2106-and-Beyond
- Glossary
References
- Linux 5.6 Is The First Kernel For 32-Bit Systems Ready To Run Past Year 2038 — Phoronix, 2020-01-29 — 5.6 milestone; user space must also use 64-bit time.
- Need 32-bit Linux to run past 2038? When version 5.6 of the kernel pops, you're in for a treat — The Register, 2020-01-30 — Bergmann quotes; time64 syscalls since 5.1; unsigned/CLOCK_MONOTONIC for unchangeable interfaces.
- Approaching the kernel year-2038 end game — LWN, 2019-01-11 —
_time32/_time64syscalls,COMPAT_32BIT_TIME. - System call conversion for year 2038 — LWN, 2015-05-05 — design of the kernel conversion.
- Year 2038 problem — Wikipedia — NetBSD 6.0, OpenBSD 5.5, Ruby 1.9.2, ext4 2446, XFS bigtime 2486, NFS details, with primary links.
- GNU C Library 2.34 released — LWN, 2021-08-01 —
_TIME_BITS=64, LFS requirement, Linux 5.1 minimum. - musl 1.2.0 released — musl mailing list (Openwall), 2020-02-21 — 64-bit
time_ton 32-bit archs. - ReleaseGoals/64bit-time — Debian Wiki — t64 renaming, dates, architectures, known risk areas.
- The perils of transition to 64-bit time_t — Michał Górny (Gentoo), 2024-09-28 — ABI mixing hazards and Gentoo's strategy.
- Debian looks forward to 2038 — LWN, 2023-07-17 — Langasek's plan; i386 exclusion debate.
- gmtime, _gmtime32, _gmtime64 — Microsoft Learn —
_USE_32BIT_TIME_Twarnings. - Release 4.3 migration notes — Yocto Project docs — 64-bit
time_ton 32-bit targets from 4.3. - Austin Group issue 0001462: Require (at least) 64-bit time_t — accepted POSIX change.
- krb5 commit: Add y2038 documentation — MIT, 2017-05 — unsigned cast pattern valid to 2106.
- Avoiding the year 2038 problem — GNU Gnulib manual —
year2038(-recommended), utmp caveat. - The DATE, DATETIME, and TIMESTAMP Types — MySQL 8.0 Reference Manual — TIMESTAMP range.
- MySQL 8.0.28 Release Notes — Oracle — 64-bit Unix-time functions.
- MariaDB 11.5.1 Release Notes — MariaDB — TIMESTAMP to 2106.
- Curious tale of broken VPNs, the Year 2038, and certs that expired 100 years ago — The Register, 2024-02-09 — certificate date overflow in practice.
- Issue 5537: LWPCookieJar cannot handle cookies with expirations of 2038 or greater on 32-bit platforms — bugs.python.org — long-running runtime-level 2038 bug.
- Justin Mason, "first known instance of the Y2038 bug" — jmason.ie, 2006-07-20 — AOLserver one-billion-second timeout.
- OpenSSH Bug 3329 — openssh-bugs, 2021-07-02 — clamping certificate dates at INT_MAX for display.
- The NTP Era and Era Numbering — D. L. Mills, University of Delaware — era-number approach to NTP's 2036 wrap.
- Bug #6122 OpenSSL::PKCS7 verify — Ruby issue tracker, 2012 —
not_aftercapped at 2038 on 32-bit Windows Ruby. - File Times — Microsoft Learn — FILETIME definition.
- VDE-2025-007: WAGO: Year 2038 problem — CERT@VDE, 2025-04-15 (rev. 2026-08-07) — controller advisory, CVE-2025-0101, FW32 fix.
- VDE-2025-020: WAGO: Switches affected by year 2k38 problem — CERT@VDE, 2025-06-02 — CVE-2025-1235.
- ICSA-25-261-07 Dover Fueling Solutions ProGauge MagLink LX4 — CISA, 2025-09-18 — CVE-2025-55068 and fixed versions.
- ICSA-25-296-03 Veeder-Root TLS4B — CISA, 2025-10-23 — CVE-2025-55067, fix pending.
- XFS — Gentoo Wiki — bigtime upgrade with
xfs_admin; default since xfsprogs 5.15.