Now that DST in the USA is over, I see this unit test fail in the GitHub CI:
FAIL: test-localtime-w32 ======================== Windows API 1: 2026-09-12 00:00:00 Windows API 2: 2026-09-12 01:00:00 FAIL test-localtime-w32.exe (exit status: 7) And similarly on my Windows 10 and 11 machines: FAIL: test-localtime-w32 ======================== Windows API 1: 2026-09-12 09:00:00 Windows API 2: 2026-09-12 10:00:00 FAIL test-localtime-w32.exe (exit status: 7) So, apparently, the two computations are not equivalent. "Windows API 2" is the correct one, "Windows API 1" the wrong one. Similarly for the test-mktime-w32 test. This patch fixes the test failure. 2026-11-05 Bruno Haible <[email protected]> localtime, time_r, mktime tests: Fix test failure on native Windows. * tests/test-localtime-w32.c (main): Swap conversion codes. Ignore the result of the older conversion code. * tests/test-localtime_r-w32.c (main): Likewise. * tests/test-mktime-w32.c (main): Likewise. diff --git a/tests/test-localtime-w32.c b/tests/test-localtime-w32.c index f746dd9289..4d84cbddd5 100644 --- a/tests/test-localtime-w32.c +++ b/tests/test-localtime-w32.c @@ -58,46 +58,42 @@ main (void) (DWORD) (hundrednanos_since_1601_01_01 >> 32) }; - /* Convert like GNU clisp does. */ + /* Convert like + <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> + suggests. */ SYSTEMTIME lsystime1; { - FILETIME lfiletime; + SYSTEMTIME systime; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> */ - if (!FileTimeToLocalFileTime (&filetime, &lfiletime)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ + if (!FileTimeToSystemTime (&filetime, &systime)) return 3; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ - if (!FileTimeToSystemTime (&lfiletime, &lsystime1)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime> */ + if (!SystemTimeToTzSpecificLocalTime (NULL, &systime, &lsystime1)) return 4; } printf ("Windows API 1: %04d-%02d-%02d %02d:%02d:%02d\n", lsystime1.wYear, lsystime1.wMonth, lsystime1.wDay, lsystime1.wHour, lsystime1.wMinute, lsystime1.wSecond); - /* Convert like - <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> - suggests. */ + /* Convert like GNU clisp does. This is actually wrong. */ SYSTEMTIME lsystime2; { - SYSTEMTIME systime; + FILETIME lfiletime; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ - if (!FileTimeToSystemTime (&filetime, &systime)) + <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> */ + if (!FileTimeToLocalFileTime (&filetime, &lfiletime)) return 5; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime> */ - if (!SystemTimeToTzSpecificLocalTime (NULL, &systime, &lsystime2)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ + if (!FileTimeToSystemTime (&lfiletime, &lsystime2)) return 6; } printf ("Windows API 2: %04d-%02d-%02d %02d:%02d:%02d\n", lsystime2.wYear, lsystime2.wMonth, lsystime2.wDay, lsystime2.wHour, lsystime2.wMinute, lsystime2.wSecond); - /* Both methods should be equivalent. */ - if (lsystime1.wHour != lsystime2.wHour) - return 7; - /* Now see whether localtime is consistent with that. */ /* Documentation: <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64> */ diff --git a/tests/test-localtime_r-w32.c b/tests/test-localtime_r-w32.c index d84a15a63b..ff54903157 100644 --- a/tests/test-localtime_r-w32.c +++ b/tests/test-localtime_r-w32.c @@ -58,46 +58,42 @@ main (void) (DWORD) (hundrednanos_since_1601_01_01 >> 32) }; - /* Convert like GNU clisp does. */ + /* Convert like + <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> + suggests. */ SYSTEMTIME lsystime1; { - FILETIME lfiletime; + SYSTEMTIME systime; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> */ - if (!FileTimeToLocalFileTime (&filetime, &lfiletime)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ + if (!FileTimeToSystemTime (&filetime, &systime)) return 3; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ - if (!FileTimeToSystemTime (&lfiletime, &lsystime1)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime> */ + if (!SystemTimeToTzSpecificLocalTime (NULL, &systime, &lsystime1)) return 4; } printf ("Windows API 1: %04d-%02d-%02d %02d:%02d:%02d\n", lsystime1.wYear, lsystime1.wMonth, lsystime1.wDay, lsystime1.wHour, lsystime1.wMinute, lsystime1.wSecond); - /* Convert like - <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> - suggests. */ + /* Convert like GNU clisp does. This is actually wrong. */ SYSTEMTIME lsystime2; { - SYSTEMTIME systime; + FILETIME lfiletime; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ - if (!FileTimeToSystemTime (&filetime, &systime)) + <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> */ + if (!FileTimeToLocalFileTime (&filetime, &lfiletime)) return 5; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetotzspecificlocaltime> */ - if (!SystemTimeToTzSpecificLocalTime (NULL, &systime, &lsystime2)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime> */ + if (!FileTimeToSystemTime (&lfiletime, &lsystime2)) return 6; } printf ("Windows API 2: %04d-%02d-%02d %02d:%02d:%02d\n", lsystime2.wYear, lsystime2.wMonth, lsystime2.wDay, lsystime2.wHour, lsystime2.wMinute, lsystime2.wSecond); - /* Both methods should be equivalent. */ - if (lsystime1.wHour != lsystime2.wHour) - return 7; - /* Now see whether localtime_r is consistent with that. */ struct tm lm; if (localtime_r (&seconds_since_1970_01_01, &lm) == NULL) diff --git a/tests/test-mktime-w32.c b/tests/test-mktime-w32.c index 39cc7a769c..d32116b510 100644 --- a/tests/test-mktime-w32.c +++ b/tests/test-mktime-w32.c @@ -66,18 +66,20 @@ main (void) lsystime.wSecond = lm.tm_sec; lsystime.wMilliseconds = 0; - /* Convert like GNU clisp does. */ + /* Convert like + <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> + suggests. */ time_t utctime1; { - FILETIME lfiletime; + SYSTEMTIME systime; FILETIME filetime; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime> */ - if (!SystemTimeToFileTime (&lsystime, &lfiletime)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-tzspecificlocaltimetosystemtime> */ + if (!TzSpecificLocalTimeToSystemTime (NULL, &lsystime, &systime)) return 3; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-localfiletimetofiletime> */ - if (!LocalFileTimeToFileTime (&lfiletime, &filetime)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime> */ + if (!SystemTimeToFileTime (&systime, &filetime)) return 4; long long hundrednanos_since_1601_01_01 = ((unsigned long long) filetime.dwHighDateTime << 32) @@ -86,20 +88,18 @@ main (void) } printf ("Windows API 1: %lld\n", (long long) utctime1); - /* Convert like - <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-filetimetolocalfiletime> - suggests. */ + /* Convert like GNU clisp does. This is actually wrong. */ time_t utctime2; { - SYSTEMTIME systime; + FILETIME lfiletime; FILETIME filetime; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-tzspecificlocaltimetosystemtime> */ - if (!TzSpecificLocalTimeToSystemTime (NULL, &lsystime, &systime)) + <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime> */ + if (!SystemTimeToFileTime (&lsystime, &lfiletime)) return 5; /* Documentation: - <https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime> */ - if (!SystemTimeToFileTime (&systime, &filetime)) + <https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-localfiletimetofiletime> */ + if (!LocalFileTimeToFileTime (&lfiletime, &filetime)) return 6; long long hundrednanos_since_1601_01_01 = ((unsigned long long) filetime.dwHighDateTime << 32) @@ -108,10 +108,6 @@ main (void) } printf ("Windows API 2: %lld\n", (long long) utctime2); - /* Both methods should be equivalent. */ - if (utctime1 != utctime2) - return 7; - /* Now see whether mktime (a.k.a. timelocal) is consistent with that. */ /* Documentation: <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/mktime-mktime32-mktime64> */
