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

Reply via email to