On the hosted Windows runner the CRT keeps the system's daylight bias (0) when TZ changes, so PST8PDT in July converted with -(28800 + 0) = -28800 instead of -25200. The two Windows zone checks now save the bias, set it to -3600 after each PST8PDT or EST5EDT zone is selected, and restore it on exit. The expected winter and summer offsets and daylight flags are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
_get_dstbias takes a long pointer, so the int variable added in the previous commit did not compile on Windows. The variable and its format specifier are now long.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The CI run of this branch reports PST8PDT at 995198400 with offset -28800 and daylight 1 where -25200 is expected. The failure message now also prints the host's _get_timezone, _get_dstbias and the host tm_isdst, so the next run shows whether the runner's C runtime leaves the daylight bias at 0. The checks themselves are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The %Z check now expects the guest tzname, since main leaves tzname[1] as three spaces for a zone without daylight saving time.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The region name zones resolve to UTC on Windows (UCRT does not read them), so the round trip passed with no offset applied. Windows now runs PST8PDT and EST5EDT with the round trip, and checks tm_gmtoff and the daylight flag at a January and a July moment in 2001. The name zones stay on Linux and macOS.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
FreeBSD 11 localtime.c initializes tzname to WILDABBR (" ") and settzname()
resets both entries to it before filling them from the zone's types, so a
zone with no daylight saving type keeps " " in tzname[1]. Windows left it
empty and Linux repeated tzname[0].
CheckRegionZones in guest_time_conversion was compiled out on Windows
(#ifndef _WIN32). It passes there, so the guards are removed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The zone race of guest_time_conversion changes TZ in a tight loop while
six threads convert times. On macOS the flipper keeps the guest
environment lock: the default mutex there lets the thread that unlocks
take the lock again before a woken waiter runs. The readers mostly
waited, and the test ran for 21 seconds, past its 20-second timeout; a
run with counters showed about 373 million TZ changes against the
readers' 15,000 local time conversions. The flipper now yields after
each change. It still changes TZ about 800,000 times while the readers
convert, they see each zone in about half of their conversions, and the
test takes about a second.
Nested conversions such as the one closing mktime no longer re-read TZ, so
the instant and the normalised fields come from one zone. The race test now
covers mktime and strftime.
Copy the guest TZ under the guest environment lock so a concurrent setenv
cannot free it while it is read, and hold one lock across the zone switch
and the conversion so the fields of two zones are never mixed. Document the
host rejection, Windows zone name and stale tm_zone limits.
Guest setenv, putenv and unsetenv only change the guest environment, while
the time conversions read the host TZ, so a guest that set TZ and called
tzset still got host local time. Copy a changed guest TZ to the host before
each local conversion and call tzset again.
guest_time_conversion now converts nine times, from the year 1 to 1950,
with localtime and back with mktime in America/Los_Angeles,
Europe/Berlin, Asia/Kolkata and America/Argentina/Buenos_Aires, once
with the daylight flag localtime gave and once with -1. The zones'
local mean time and early standard offsets are covered, including the
first day of 1900. It runs on Linux and macOS; the Windows CRT does not
read region zone names.
ES-DE in RetroArch (PPSA99169) imports tzname from libSceLibcInternal and
fails to load without it; ICU's uprv_tzname reads it after localtime. It
points at the host's zone names and tzset refreshes it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
difftime and libc_difftime passed their arguments to the host CRT.
UCRT's _difftime64 returns 0 and sets errno to EINVAL when either
argument is negative (common_difftime in ucrt/time/difftime.cpp), so
on Windows difftime(0, -1) returned 0 instead of 1 and every
difference involving a time before 1970 was 0. C17 7.27.2.2 defines
the result as time1 - time0, and FreeBSD 11's difftime
(contrib/tzcode/stdtime/difftime.c) and glibc on the Linux build
compute it for any arguments.
Subtract the smaller time from the larger as uint64_t, which cannot
overflow, convert the difference to double and negate it when time1
is the smaller one, as FreeBSD and glibc's fallback path do. The
result is time1 - time0 rounded once to double on both hosts.
difftime now forwards to libc_difftime like the other aliases in the
file.
asctime passed the guest tm to the host CRT. UCRT's asctime returns
null and sets errno to EINVAL when tm_year is negative or tm_mday is
past the end of the month (common_asctime_s in ucrt/time/asctime.cpp),
so on Windows asctime of any time in the years 1000 to 1899, which
gmtime converts, or of February 29 in a common year returned null.
C17 7.27.3.1 defines the text for every year from 1000 to 9999 when
each member is in its normal range (tm_mday 1 to 31), and glibc on
the Linux build and FreeBSD 11's asctime
(contrib/tzcode/stdtime/asctime.c) print it.
Format the text with the C17 algorithm's format into a thread-local
buffer that fits any member values. As in glibc and FreeBSD, a
weekday or month outside its range is printed as ???, and the year is
computed in 64 bits.
gmtime, localtime, mktime, strftime and asctime took and returned the
host std::tm. The guest's FreeBSD tm has tm_gmtoff and tm_zone after the
nine calendar fields, so on Windows, where std::tm is 36 bytes, a title
reading those fields read past the thread-local result, and gmtime_s left
them unwritten. Windows gmtime_s also accepts only 1969-12-31 12:00 to
3001-01-19 21:59 UTC, where the guest libc converts any time.
Convert through a GuestTm with the guest layout on both hosts. Compute
the UTC fields from the day count, so every time whose year fits an int
converts, and on Windows take the local offset and zone name from the
CRT. gmtime reports the zone as UTC like the guest libc. localtime and
mktime keep the CRT's range on Windows; recorded in TechnicalDebt.
Co-Authored-By: Claude Fable 5.1 <[email protected]>