Programming Languages
In brief: Almost every modern programming language already handles dates after 2038 correctly in its standard date and time library. Programs still fail when a programmer stores a timestamp in a 32-bit integer, when data passes through a 32-bit file format, database column or network field, or when a language runs on top of a 32-bit operating system or C library. This page compares the major languages and links to a detailed page for each.
| Risk level | Medium overall. Language runtimes are mostly safe, but application code and 32-bit builds are not |
| Status | Core time APIs are 64-bit (or wider) in all mainstream languages as of 2026-10-21. 32-bit builds of C-based runtimes and some systems interfaces remain exposed |
| Last reviewed | 2026-10-21 |
Overview
The Year 2038 problem lives in data types, not languages. A language is exposed when one of these holds:
- Its default time type is 32-bit. That's rare today. C's
time_ton 32-bit glibc is the main remaining case. - It delegates to the C library. Python's
time, PHP'smktime, Perl before 5.12, gawk,dateand the JVM's native layer all inherit the platformtime_t. - Programmers narrow the value.
(int),int32(),| 0,pack('i')andInt32Arraysilently or loudly truncate safe 64-bit values to 32 bits. - The data leaves the program. Database
TIMESTAMP/INTcolumns, binary protocols and file formats have their own widths. See Databases and File-Formats-and-Protocols.
The third and fourth causes affect every language, including those with unlimited integers.
Summary matrix
| Language / runtime | Default time representation | Range / limit | 2038 exposure | Common pitfalls | Details |
|---|---|---|---|---|---|
| C / C++ | time_t (seconds); std::chrono::system_clock (usually int64 ns) |
64-bit on LP64, MSVC and musl ≥ 1.2. 32-bit by default on 32-bit glibc (opt-in _TIME_BITS=64 since 2.34); chrono ≈ ±292 years |
High on 32-bit glibc and embedded. Low on 64-bit | (int)t, %ld format, library ABIs exposing time_t, raw syscalls |
Lang-C-and-CPP |
| Java / Kotlin / Scala (JVM) | long ms (currentTimeMillis); Instant = long s + int ns |
±292 million years (ms); Instant ±1 billion years |
Low (platform). Medium (apps) | (int)(ms/1000), JNI jint, 32-bit JVM native layer (JDK-8279444) |
Lang-Java-and-JVM |
| .NET (C#, F#, VB) | DateTime Int64 ticks (100 ns since year 1) |
to 9999-12-31 | Low (runtime). Medium (interop) | (int)ToUnixTimeSeconds(), P/Invoke int for time_t, SQL Server DATEADD(int), Arm32 runtime before .NET 9 |
Lang-DotNet |
| Python | time.time() float; time_ns() int; datetime year 1–9999 |
Unlimited ints. datetime to 9999 |
Low on 64-bit. Medium on 32-bit (C time_t) |
OverflowError from fromtimestamp on 32-bit, struct.pack('i'), C extensions |
Lang-Python |
| PHP | int = C long/zend_long |
64-bit builds: effectively unlimited. 32-bit builds: 1901–2038 | High on 32-bit (and Windows PHP < 7) | mktime/strtotime returning false, getTimestamp() ValueError, pack('l'), MySQL TIMESTAMP |
Lang-PHP |
| JavaScript / TypeScript | Date = double ms |
±8.64e15 ms (year 275760) | Low (language). Medium (code) | x \| 0, ~~x, Int32Array, readInt32LE, native build on 32-bit Arm |
Lang-JavaScript-and-TypeScript |
| Go | time.Time; Unix() int64 |
billions of years | Low. Medium on GOARCH=arm/386/mips via syscall |
int32(t.Unix()), syscall.Timespec{Sec int32} (issue #60248) |
Lang-Go-Rust-Swift |
| Rust | SystemTime (platform struct); Duration u64 s |
Platform time_t. 64-bit on 64-bit targets |
Low. Medium on 32-bit glibc/musl targets | as i32, libc crate 32-bit time_t (64-bit opt-in since 0.2.179) |
Lang-Go-Rust-Swift |
| Swift / Objective-C | Date/NSDate = Double seconds since 2001-01-01 |
sub-ms precision over 10,000 years | Low | Int32(date.timeIntervalSince1970) traps; ObjC (int) casts |
Lang-Go-Rust-Swift |
| Perl | IV/NV; core time functions 64-bit since 5.12 | Unlimited for practical purposes | Low (≥ 5.12). High for old Perls on 32-bit | Time::Local on old Perls, 2-digit years, pack('l') |
Lang-Perl-Ruby-and-Others |
| Ruby | Time = int63/Bignum/Rational (since 1.9.2) |
no boundary | Low | pack('l'); local time extrapolated on 32-bit time_t |
Lang-Perl-Ruby-and-Others |
| Erlang / Elixir | arbitrary-precision integers | unlimited | Low | <<Ts:32>> binary matches |
Lang-Perl-Ruby-and-Others |
| COBOL | YYYYMMDD display/packed fields, day counts |
n/a | Low internally. Medium at interfaces | PIC S9(9) COMP epoch fields from Unix systems |
Lang-Perl-Ruby-and-Others |
| Fortran (GNU) | TIME() → INTEGER(4); TIME8(); DATE_AND_TIME |
TIME() to 2038 |
Medium (legacy code) | TIME() going negative |
Lang-Perl-Ruby-and-Others |
| SQL | engine-specific types | MySQL TIMESTAMP to 2038 |
High for TIMESTAMP/INT epoch columns |
UNIX_TIMESTAMP() returns 0 out of range; T-SQL DATEADD(second, int) |
Lang-Perl-Ruby-and-Others, Databases |
Shell date, awk |
C time_t |
platform | Medium on 32-bit builds | date +%s math in scripts on old 32-bit hosts |
Lang-Perl-Ruby-and-Others |
| Lua | os.time() number (64-bit integer by default since 5.3) |
platform-dependent | Medium on LUA_32BITS embedded builds |
microcontroller firmware | Lang-Perl-Ruby-and-Others |
Sources for each row are on the linked pages. Key ones: glibc 2.34 [1], musl time64 [2], Java Instant [3], .NET DateTime.Ticks [4], Python time [5], PHP strtotime [6], MDN Date [7], Go time [8], Go issue #60248 [9], Rust libc #3248 [10], Apple TimeInterval [11], perl5120delta [12], Ruby Time [13].
Technical details: the universal bug and the universal fix
The same bug shows up in every language:
| Language | Bug (wraps or fails after 2038-01-19 03:14:07 UTC) | Fix |
|---|---|---|
| C | int t = time(NULL); |
time_t t = time(NULL); / int64_t |
| Java | int t = (int)(System.currentTimeMillis()/1000); |
long t = Instant.now().getEpochSecond(); |
| C# | int t = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(); |
long t = …ToUnixTimeSeconds(); |
| Python | struct.pack('<i', int(time.time())) |
struct.pack('<q', …) |
| PHP (32-bit) | mktime(0,0,0,1,1,2040) → false |
new DateTimeImmutable('2040-01-01') |
| JavaScript | Date.now()/1000 \| 0 |
Math.floor(Date.now()/1000) |
| Go | int32(time.Now().Unix()) |
time.Now().Unix() (int64) |
| Rust | secs as i32 |
i32::try_from(secs)? or keep u64/i64 |
| Swift | Int32(d.timeIntervalSince1970) (traps) |
Int64(…) |
Rules that apply everywhere:
- Keep time in the language's native date/time type (
Instant,DateTimeOffset,datetime,DateTimeImmutable,time.Time,Date) for as long as possible. - At boundaries, use 64-bit signed seconds (or ms/µs/ns) or ISO 8601 strings.
- Where a 32-bit field truly can't change, use a checked conversion that fails loudly:
Math.toIntExact,checked(...),try_from,Int32(exactly:). - On 32-bit platforms, verify that the runtime itself was built with 64-bit
time_t(glibc_TIME_BITS=64, musl ≥ 1.2).
Risk & compliance notes
- Inventory by runtime architecture first. A 64-bit runtime removes most language-level risk. Then search code for narrowing patterns, then audit data contracts.
- Future-dated values fail first. Expiry dates, certificates, long-term contracts and scheduled jobs reach 2038 before the clock does.
- Statically linked toolchains (Go, Rust) and bundled runtimes (Node, .NET, JVM) don't benefit from OS patches. They must be rebuilt or upgraded.
Detection & mitigation
- A combined grep starter list is on each language page. Run tests with the clock set to
2038-01-19T03:14:08Zand to a date well beyond, such as 2040. See Testing-and-Detection. - Prioritise and fix using Remediation-Guide and Risk-Assessment-Checklist.
See also
- Lang-C-and-CPP
- Lang-Java-and-JVM
- Lang-DotNet
- Lang-Python
- Lang-PHP
- Lang-JavaScript-and-TypeScript
- Lang-Go-Rust-Swift
- Lang-Perl-Ruby-and-Others
- Year-2038-Problem, Operating-Systems, Databases, File-Formats-and-Protocols
References
- GNU C Library 2.34 released — LWN.net, 2021-08-01 — 64-bit
time_topt-in on 32-bit glibc via_TIME_BITS=64. - time64 — musl libc — musl 1.2.0 makes
time_t64-bit on all archs. - java.time.Instant — Java SE 21 API, Oracle —
longseconds +intnanos; ±1 billion years. - DateTime.Ticks Property — Microsoft Learn — Int64 100 ns ticks since 0001-01-01.
- time — Python 3 documentation — C-library cut-off "for 32-bit systems, it is typically in 2038".
- strtotime — PHP Manual — 32-bit range 1901–2038; 64-bit "effectively infinite".
- Date — JavaScript, MDN Web Docs — ±8.64e15 ms range.
- Package time — pkg.go.dev —
Unix()int64. - golang/go issue #60248 — GitHub — 32-bit
time_ttypes insyscallon linux/arm, 386, mips. - Tracking issue for libc 1.0 — rust-lang/libc #3248 — single
time_tdefinition; 64-bit time plans. - TimeInterval — Apple Developer Documentation — Double; sub-millisecond precision over 10,000 years.
- perl5120delta — perldoc.perl.org — core time functions Y2038 compliant.
- class Time — Ruby documentation — no boundary or resolution limitations.