You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(libc): convert local times before 1970 and after 3000 on Windows - #4811
Fixes#4807. Carries @DavidSamir's fix branch from that issue (DavidSamir:fix/libc-localtime-outside-1970-3000-windows, 9cb4e363), keeping the commit and its authorship; it cherry-picks onto current main without changes.
On Windows, localtime, localtime_s, mktime and ctime failed for every time before 1970 and after about January 3001, because they call the C runtime's localtime_s and mktime, which only accept that range: localtime returned NULL, mktime -1 and ctime??? ??? ?? ??:??:?? ????, where the console and the Linux build convert. docs/dev/debt/libc.md recorded the gap. The Gregorian calendar repeats every 400 years (146097 days, a whole number of weeks), so when the C runtime refuses a time, localtime_s converts the same moment a whole number of cycles away inside 1970 to 2370 and moves the year back, and mktime does the reverse. The path is chosen before the one C runtime call, from the time value for localtime_s and from the broken-down time for mktime, so no call is retried after an error and errno is not saved and restored; a conversion inside the range is made by the same single call as before. The debt entry now records what remains: a time outside the range gets the daylight-saving rule the C runtime applies to the year it is converted in. Linux is unchanged.
guest_time_conversion gains CheckLocalRange (ten times from year 1 to 9999 through localtime_s, localtime, mktime and ctime, normalisation across 1969/1970 and 3100/3101, localtime_s(INT64_MIN) still failing), and guest_mktime loses its three Windows-only exceptions.
Tested
From the issue and its update for the second version: Windows 11, WinLibs MinGW-w64 GCC 15.2.0, Release: with main's Time.cpp both tests fail and with the fix both pass 5/5; a probe of 2,360,002 conversions under nine TZ settings has 1,374,526 failures with main's libraries and none with the branch's, every localtime matching gmtime plus tm_gmtoff and every mktime round-tripping; eleven catalog titles end the same way with both; full ctest 1001/1001; no measurable cost inside the range.
Linux, added for this carry: Ubuntu 24.04, GCC 13.3 (CI's compiler), Release with CI's configure flags, on main0c3dc9c. guest_time_conversion, guest_mktime, guest_utime, guest_rtc, guest_file_timestamps and guest_freebsd_clocks pass, and all 324 guest_* tests pass in two runs. The new checks also pass with main's Time.cpp, as expected since only the Windows path changes. I could not run Windows myself.
tools/check_conventions.py --base upstream/main passes. The debt entry it rewrites sits next to the one my fix(libc): keep a trailing slash on paths under a mount alias #4681 adds in docs/dev/debt/libc.md, so whichever of the two lands second needs a one-line rebase, which I'll do.
Checklist
Based on current main; no other open PR implements the same functions
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
The branch in #4807 has been updated to 9cb4e36: mktime no longer calls the C runtime a second time after a failure, it chooses the path from the broken-down time before the one call (the rule from the review of #4532). Details and the rerun results are in my comment on #4807. This PR still carries a133bd4; could you take the new commit?
Updated #4811 to your 9cb4e36 (now 7126090 on current main). Linux rechecked on the new version: all 324 guest_* tests pass in two runs, guest_time_conversion and guest_mktime also pass with main's Time.cpp, and a MinGW-w64 -fsyntax-only -Wall -Wextra compile of Time.cpp is clean. The PR body now describes the single-call version.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Fixes #4807. Carries @DavidSamir's fix branch from that issue (
DavidSamir:fix/libc-localtime-outside-1970-3000-windows,9cb4e363), keeping the commit and its authorship; it cherry-picks onto currentmainwithout changes.On Windows,
localtime,localtime_s,mktimeandctimefailed for every time before 1970 and after about January 3001, because they call the C runtime'slocaltime_sandmktime, which only accept that range:localtimereturned NULL,mktime-1 andctime??? ??? ?? ??:??:?? ????, where the console and the Linux build convert.docs/dev/debt/libc.mdrecorded the gap. The Gregorian calendar repeats every 400 years (146097 days, a whole number of weeks), so when the C runtime refuses a time,localtime_sconverts the same moment a whole number of cycles away inside 1970 to 2370 and moves the year back, andmktimedoes the reverse. The path is chosen before the one C runtime call, from the time value forlocaltime_sand from the broken-down time formktime, so no call is retried after an error anderrnois not saved and restored; a conversion inside the range is made by the same single call as before. The debt entry now records what remains: a time outside the range gets the daylight-saving rule the C runtime applies to the year it is converted in. Linux is unchanged.guest_time_conversiongainsCheckLocalRange(ten times from year 1 to 9999 throughlocaltime_s,localtime,mktimeandctime, normalisation across 1969/1970 and 3100/3101,localtime_s(INT64_MIN)still failing), andguest_mktimeloses its three Windows-only exceptions.Tested
main'sTime.cppboth tests fail and with the fix both pass 5/5; a probe of 2,360,002 conversions under nineTZsettings has 1,374,526 failures withmain's libraries and none with the branch's, everylocaltimematchinggmtimeplustm_gmtoffand everymktimeround-tripping; eleven catalog titles end the same way with both; fullctest1001/1001; no measurable cost inside the range.main0c3dc9c.guest_time_conversion,guest_mktime,guest_utime,guest_rtc,guest_file_timestampsandguest_freebsd_clockspass, and all 324guest_*tests pass in two runs. The new checks also pass withmain'sTime.cpp, as expected since only the Windows path changes. I could not run Windows myself.tools/check_conventions.py --base upstream/mainpasses. The debt entry it rewrites sits next to the one my fix(libc): keep a trailing slash on paths under a mount alias #4681 adds indocs/dev/debt/libc.md, so whichever of the two lands second needs a one-line rebase, which I'll do.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code