On Wednesday 19 November 2025 14:12:52 Martin Storsjö wrote:
> On Mon, 17 Nov 2025, Pali Rohár wrote:
>
> > On Monday 17 November 2025 16:18:14 Martin Storsjö wrote:
> > > On Sat, 25 Oct 2025, Pali Rohár wrote:
> > >
> > > > ---
> > > > mingw-w64-crt/testcases/t_time.c | 96 ++++++++++++++++++++++++++++++--
> > > > 1 file changed, 92 insertions(+), 4 deletions(-)
> > > >
> > > > diff --git a/mingw-w64-crt/testcases/t_time.c
> > > > b/mingw-w64-crt/testcases/t_time.c
> > > > index 50b07d0083a7..75a15d816d31 100644
> > > > --- a/mingw-w64-crt/testcases/t_time.c
> > > > +++ b/mingw-w64-crt/testcases/t_time.c
> > > > @@ -10,14 +10,17 @@ int __cdecl ftime64(struct __timeb64 *tb64);
> > > >
> > > > int main()
> > > > {
> > > > - time_t t;
> > > > - __time32_t t32;
> > > > - __time64_t t64;
> > > > + time_t t, t_;
> > > > + __time32_t t32, t32_;
> > > > + __time64_t t64, t64_;
> > > > struct timeb tb;
> > > > struct _timeb tb_;
> > > > struct __timeb32 tb32;
> > > > struct __timeb64 tb64;
> > > > struct tm *htm;
> > > > + struct tm tm1;
> > > > + struct tm tm2;
> > > > + struct tm tm3;
> > > > int ret1, ret2, ret3;
> > > > const char *str;
> > > > const wchar_t *wstr;
> > > > @@ -95,7 +98,49 @@ int main()
> > > > assert (htm->tm_yday == 74);
> > > > assert (htm->tm_isdst == 0);
> > > >
> > > > - /* ctime returns time string in local timezone, so set local
> > > > timezone to UTC to have test timezone independent */
> > > > + time_t times[] = {
> > > > + 1700000000 /* Tue Nov 14 22:13:20 UTC 2023 */,
> > > > + 1600000000 /* Sun Sep 13 12:26:40 UTC 2020 */,
> > > > + };
> > > > + for (size_t i = 0; i < sizeof(times)/sizeof(*times); i++) {
> > > > + t = times[i];
> > > > + htm = localtime (&t);
> > > > + tm1 = *htm;
> > > > + printf ("localtime(%lld): sec=%d min=%d hour=%d mday=%d mon=%d
> > > > year=%d wday=%d yday=%d isdst=%d\n",
> > > > + (long long)t, htm->tm_sec, htm->tm_min, htm->tm_hour,
> > > > htm->tm_mday, htm->tm_mon, htm->tm_year, htm->tm_wday, htm->tm_yday,
> > > > htm->tm_isdst);
> > > > + t_ = mktime (htm);
> > > > + printf ("mktime(): %lld sec=%d min=%d hour=%d mday=%d mon=%d
> > > > year=%d wday=%d yday=%d isdst=%d\n",
> > > > + (long long)t_, htm->tm_sec, htm->tm_min, htm->tm_hour,
> > > > htm->tm_mday, htm->tm_mon, htm->tm_year, htm->tm_wday, htm->tm_yday,
> > > > htm->tm_isdst);
> > > > + assert (t_ == t);
> > > > + assert (memcmp (htm, &tm1, sizeof(tm1)) == 0);
> > > > +
> > > > + t32 = t;
> > > > + htm = _localtime32 (&t32);
> > > > + tm2 = *htm;
> > > > + printf ("_localtime32(%d): sec=%d min=%d hour=%d mday=%d mon=%d
> > > > year=%d wday=%d yday=%d isdst=%d\n",
> > > > + t32, htm->tm_sec, htm->tm_min, htm->tm_hour, htm->tm_mday,
> > > > htm->tm_mon, htm->tm_year, htm->tm_wday, htm->tm_yday, htm->tm_isdst);
> > > > + t32_ = _mktime32 (htm);
> > > > + printf ("_mktime32(): %d sec=%d min=%d hour=%d mday=%d mon=%d
> > > > year=%d wday=%d yday=%d isdst=%d\n",
> > > > + t32_, htm->tm_sec, htm->tm_min, htm->tm_hour, htm->tm_mday,
> > > > htm->tm_mon, htm->tm_year, htm->tm_wday, htm->tm_yday, htm->tm_isdst);
> > > > + assert (t32_ == t32);
> > > > + assert (memcmp (htm, &tm2, sizeof(tm2)) == 0);
> > > > +
> > > > + t64 = t;
> > > > + htm = _localtime64 (&t64);
> > > > + tm3 = *htm;
> > > > + printf ("_localtime64(%lld): sec=%d min=%d hour=%d mday=%d mon=%d
> > > > year=%d wday=%d yday=%d isdst=%d\n",
> > > > + t64, htm->tm_sec, htm->tm_min, htm->tm_hour, htm->tm_mday,
> > > > htm->tm_mon, htm->tm_year, htm->tm_wday, htm->tm_yday, htm->tm_isdst);
> > > > + t64_ = _mktime64 (htm);
> > > > + printf ("_mktime64(): %lld sec=%d min=%d hour=%d mday=%d mon=%d
> > > > year=%d wday=%d yday=%d isdst=%d\n",
> > > > + t64_, htm->tm_sec, htm->tm_min, htm->tm_hour, htm->tm_mday,
> > > > htm->tm_mon, htm->tm_year, htm->tm_wday, htm->tm_yday, htm->tm_isdst);
> > > > + assert (t64_ == t64);
> > > > + assert (memcmp (htm, &tm3, sizeof(tm3)) == 0);
> > > > +
> > > > + assert (memcmp (&tm1, &tm2, sizeof(tm1)) == 0);
> > > > + assert (memcmp (&tm2, &tm3, sizeof(tm2)) == 0);
> > > > + }
> > > > +
> > > > + /* ctime, localtime and mktime returns time string in local
> > > > timezone, so set local timezone to UTC to have test timezone
> > > > independent */
> > > > putenv ("TZ=UTC");
> > >
> > > In addition to setting TZ=UTC here, is it possible to set some other
> > > well-known timezone that would be available everywhere, which we could use
> > > for some testcases for cases closer to the DST switchover time? We could
> > > do
> > > that for both mktime and localtime IMO.
> >
> > I could try to add, but I'm not sure if CRT libraries would be able to
> > process DST support for non-system timezone. Because Windows system and
> > WinAPI has just one fixed timezone, which provides bunch of timezone and
> > DST related functions. And CRT library allows to define any timeshift,
> > so then there can be a mix of WinAPI timezone, CRT timezone, WinAPI DST
> > and CRT DST... That is why I wrote test below the quoted part in UTC as
> > it does not have any DST and no offset.
>
> It does support setting a different timezone, but it seems a bit limited in
> exactly which aspects you can pass. It doesn't seem like I can set a custom
> timezone and get proper DST switchover time for other time zones than USA
> time. But that's enough for a testcase that does trigger the cases we're
> interested in here.
>
> See my test code here:
>
> #include <time.h>
> #include <stdio.h>
> #include <stdlib.h>
>
> void print(struct tm *tm) {
> printf("date %d-%d-%d %d:%d:%d isdst %d", tm->tm_year + 1900, tm->tm_mon
> + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_isdst);
> #ifdef __GLIBC__
> printf(" gmtoff %ld", tm->tm_gmtoff);
> #endif
> printf("\n");
> }
>
> void print_localtime(time_t t) {
> printf("time: %d\n", (int)t);
> #ifdef _WIN32
> __time64_t t64 = t;
> struct tm *tm = _localtime64(&t64);
> #else
> struct tm *tm = localtime(&t);
> #endif
> print(tm);
> }
>
> int main(int argc, char **argv) {
> putenv("TZ=PST8PDT");
> tzset();
> printf("timezone %ld daylight %d tzname %s %s\n", timezone, daylight,
> tzname[0], tzname[1]);
>
> struct tm input;
> input.tm_year = 2025 - 1900;
> input.tm_mon = 3 - 1;
> input.tm_mday = 9;
> input.tm_hour = 1;
> input.tm_min = 30;
> input.tm_sec = 0;
> input.tm_isdst = 0;
> print_localtime(mktime(&input));
> input.tm_hour = 3;
> input.tm_isdst = 1;
> print_localtime(mktime(&input));
>
> input.tm_mon = 11 - 1;
> input.tm_mday = 2;
> input.tm_hour = 1;
> input.tm_isdst = 1;
> print_localtime(mktime(&input));
> input.tm_isdst = 0;
> print_localtime(mktime(&input));
>
> return 0;
> }
>
> With this, I generate the time_t for the USA Pacific DST switch times this
> year, 30 minutes before and after the switch, and convert that back to a
> local time value.
>
> I'm pretty sure this routine should work portably (the Wine source for
> parsing the TZ variable also handles it in the same way), and this string,
> TZ=PST8PDT is mentioned in the MS documentation as well.
>
> So it would be good to include some case based on these time_t values in our
> testing. Specifically this testcase does showcase the missing "t32 +=
> local_timezone;" and shows that it does fix the issue.
>
> // Martin
Thanks for input. I started writing new TZ=PST8PDT tests and I they are
failing, seems like some another issue with _localtime64. I started
debugging it and I figured out that the my test is failing even with the
native CRT 32-bit locatime version on Windows XP.
Here is the test:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#ifdef __GLIBC__
typedef long int __time32_t;
struct tm *_localtime32(const __time32_t *timep) { return
localtime(&(time_t){*timep}); }
__time32_t _mktime32(struct tm *tm) { return mktime(tm); }
#endif
int main() {
__time32_t t32, t32_;
struct tm *htm;
struct tm tm2;
putenv ("TZ=PST8PDT");
tzset ();
t32 = 1130661000; /* Sun Oct 30 08:30:00 UTC 2005 == Sun Oct 30 01:30:00 PDT
2005 */
htm = _localtime32 (&t32);
tm2 = *htm;
printf ("pacific _localtime32(%ld): sec=%d min=%d hour=%d mday=%d mon=%d
year=%d wday=%d yday=%d isdst=%d\n",
t32, htm->tm_sec, htm->tm_min, htm->tm_hour, htm->tm_mday, htm->tm_mon,
htm->tm_year, htm->tm_wday, htm->tm_yday, htm->tm_isdst);
t32_ = _mktime32 (htm);
printf ("pacific _mktime32(): %ld sec=%d min=%d hour=%d mday=%d mon=%d
year=%d wday=%d yday=%d isdst=%d\n",
t32_, htm->tm_sec, htm->tm_min, htm->tm_hour, htm->tm_mday, htm->tm_mon,
htm->tm_year, htm->tm_wday, htm->tm_yday, htm->tm_isdst);
fflush (stdout);
assert (t32_ == t32);
assert (memcmp (htm, &tm2, sizeof(tm2)) == 0);
return 0;
}
On Windows XP when compiled with system os msvcrt.dll library it is
failing with error:
pacific _localtime32(1130661000): sec=0 min=30 hour=1 mday=30 mon=9 year=105
wday=0 yday=302 isdst=1
pacific _mktime32(): 1130664600 sec=0 min=30 hour=1 mday=30 mon=9 year=105
wday=0 yday=302 isdst=0
Assertion failed: t32_ == t32, file test.c, line 30
Do you have any idea what is wrong here? Bug in the msvcrt.dll or I have
missed something in the test?
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public