Chrono Mock
LanguageEnglish

Testing for the Year 2038 problem on Windows

At 03:14:07 UTC on 19 January 2038, a signed 32-bit count of seconds since 1970 runs out. The Windows clock runs on past it - the programs, file formats and libraries that kept their own 32-bit copy do not. This is how to find those in one application, without setting the date of the whole machine to 2038.

Where the bug can still be

The Windows clock is a 64-bit count of 100-nanosecond steps from 1601, so it has no 2038. The C runtime's time_t has been 64-bit by default since Visual Studio 2005. What is left is code that chose a 32-bit time, or lost the upper half on the way:

  • 32-bit builds compiled with _USE_32BIT_TIME_T, or calling _time32 and __time32_t directly. Microsoft's own documentation warns that such an application may fail after 18 January 2038.
  • A 64-bit time stored in an int or a long. On Windows both are 32 bits, even in a 64-bit program.
  • Libraries, file formats, network protocols and database columns that keep seconds since 1970 in 32 bits.
  • Code that adds years. A twenty-year licence, a thirty-year loan schedule or a certificate valid until 2040 crosses the boundary years before 2038 arrives.

Crossing the boundary in one application

The limit is defined in UTC and --at is read in the session zone, so pin the zone to UTC when you write the moment yourself. The ready-made scenario already carries an absolute UTC moment and needs no zone.

# The scenario: the session starts at the limit itself, 03:14:07 UTC
chrono run "C:\apps\test.exe" --preset year-2038 --report session.txt

# Start seventeen seconds before it and let the application cross it live
chrono run "C:\apps\test.exe" --at 2038-01-19T03:13:50 --zone +00:00 --report session.txt

# Start earlier and run at 60 times speed - the limit arrives in about 14 seconds
chrono run "C:\apps\test.exe" --at 2038-01-19T03:00:00 --zone +00:00 --mode x60

# Or run on today's date and jump across it while the application is open
chrono run "C:\apps\test.exe" --jump-after 5:2038-01-19T03:14:05 --zone +00:00

One second past the limit, a signed 32-bit counter becomes -2,147,483,648, which reads as 13 December 1901, 20:45:52 UTC. A date in 1901, a negative number, an item that is suddenly expired or sorted first, an exception thrown from date code - those are what the overflow looks like from the outside.

Setting the real clock to 2038 is the other way to get here, and it is the one that breaks the most. What happens when you change the system date →

Checking that it reached the program

A test that passes because the program never saw 2038 is worse than no test. The report at the end of every session says which clock the program read and how many times:

Chrono Mock - session report
  target:   .\build\test.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:08
            real elapsed 1.3s, fake elapsed 1.3s
  covered channels (substituted, with call counts, 10 calls in all):
            - pid 21240: GetSystemTimeAsFileTime (5 calls)
            - pid 21240: GetLocalTime (5 calls)

The session line confirms the application's clock went past the limit. A verdict of works with call counts means the program read the clock and was handed the 2038 date; works partially names the channels it read some other way. What the verdicts mean →

What a pass does not prove

  • Only the program's own clock moved. A date it loads from a file or a database is whatever is stored there, so a 2038 date inside the data is a separate test. The date calculator works such dates out for you: chrono calc --base 2038-01-19T03:14:07 --zone +00:00 --shift +1s
  • A server the program talks to still runs on the real date. A 32-bit limit on that side is out of reach, and the session warns when it sees the program open a network connection.

Try it