On Monday 17 November 2025 17:43:41 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.
>
> > In itself, these test additions probably are fine though - the above would
> > be nice extras. But given that corner case handling close to DST
> > switchovers, I think it would be good to actually check such a case.
> >
> > So this patchset in itself looks ok, except for the one issue I found in
> > 2/7.
> >
> > // Martin
Here is diff for this 7/7 change which adds those tests which can uncover
localtime DST issue:
I chose year 2005 because it seems that forced PST8PDT timezone uses
hardcoded DST settings and older libraries do not have 2006 pacific zone
changes. So tests for year 2005 can be run also against older CRT libs.
diff --git a/mingw-w64-crt/testcases/t_time.c b/mingw-w64-crt/testcases/t_time.c
index 75a15d816d31..e65e89a4c0f4 100644
--- a/mingw-w64-crt/testcases/t_time.c
+++ b/mingw-w64-crt/testcases/t_time.c
@@ -98,7 +98,7 @@ int main()
assert (htm->tm_yday == 74);
assert (htm->tm_isdst == 0);
- time_t times[] = {
+ const time_t times[] = {
1700000000 /* Tue Nov 14 22:13:20 UTC 2023 */,
1600000000 /* Sun Sep 13 12:26:40 UTC 2020 */,
};
@@ -140,6 +140,114 @@ int main()
assert (memcmp (&tm2, &tm3, sizeof(tm2)) == 0);
}
+ /* following tests are explicitly for Pacific Time Zone */
+ putenv ("TZ=PST8PDT");
+ tzset ();
+ const struct {
+ __time64_t time;
+ int year; int mon; int day;
+ int hour; int min; int sec;
+ int dst;
+ } pt_times[] = {
+ { 1112520600, 2005, 4, 3, 1, 30, 0, 0 }, /* Sun Apr 3 09:30:00 UTC 2005
== Sun Apr 3 01:30:00 PST 2005 */
+ { 1112524200, 2005, 4, 3, 3, 30, 0, 1 }, /* Sun Apr 3 10:30:00 UTC 2005
== Sun Apr 3 03:30:00 PDT 2005 */
+ { 1130657400, 2005, 10, 30, 0, 30, 0, 1 }, /* Sun Oct 30 07:30:00 UTC 2005
== Sun Oct 30 00:30:00 PDT 2005 */
+ { 1130661000, 2005, 10, 30, 1, 30, 0, 1 }, /* Sun Oct 30 08:30:00 UTC 2005
== Sun Oct 30 01:30:00 PDT 2005 */
+ { 1130664600, 2005, 10, 30, 1, 30, 0, 0 }, /* Sun Oct 30 09:30:00 UTC 2005
== Sun Oct 30 01:30:00 PST 2005 */
+ { 1130668200, 2005, 10, 30, 2, 30, 0, 0 }, /* Sun Oct 30 10:30:00 UTC 2005
== Sun Oct 30 02:30:00 PST 2005 */
+ { 1130671800, 2005, 10, 30, 3, 30, 0, 0 }, /* Sun Oct 30 11:30:00 UTC 2005
== Sun Oct 30 03:30:00 PST 2005 */
+ };
+ for (size_t i = 0; i < sizeof(pt_times)/sizeof(*pt_times); i++) {
+ int skip_mktime_check = 0;
+
+ if (pt_times[i].time <= INT_MAX) {
+ t32 = pt_times[i].time;
+ htm = _localtime32 (&t32);
+ tm2 = *htm;
+ printf ("pacific _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);
+ assert (pt_times[i].year == htm->tm_year + 1900);
+ assert (pt_times[i].mon == htm->tm_mon + 1);
+ assert (pt_times[i].day == htm->tm_mday);
+ assert (pt_times[i].hour == htm->tm_hour);
+ assert (pt_times[i].min == htm->tm_min);
+ assert (pt_times[i].sec == htm->tm_sec);
+ assert (pt_times[i].dst == htm->tm_isdst);
+ t32_ = _mktime32 (htm);
+ printf ("pacific _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);
+#if __MSVCRT_VERSION__ < 0x800
+ /* Versioned msvcrt libraries older than msvcr80 (and some system msvcrt
version too)
+ * have broken native 32-bit mktime function for timestamps around the
DST change.
+ * If the native 32-bit mktime function is broken then skip tests for
64-bit mktime function.
+ */
+ if (t32_ != t32)
+ skip_mktime_check = 1;
+#endif
+ if (!skip_mktime_check) {
+ assert (t32_ == t32);
+ assert (memcmp (htm, &tm2, sizeof(tm2)) == 0);
+ } else {
+ printf ("Skipping _mktime32 assert because of broken CRT library\n");
+ }
+ }
+
+ if (sizeof(t) >= 8 || pt_times[i].time <= INT_MAX) {
+ t = pt_times[i].time;
+ htm = localtime (&t);
+ tm1 = *htm;
+ printf ("pacific 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);
+ assert (pt_times[i].year == htm->tm_year + 1900);
+ assert (pt_times[i].mon == htm->tm_mon + 1);
+ assert (pt_times[i].day == htm->tm_mday);
+ assert (pt_times[i].hour == htm->tm_hour);
+ assert (pt_times[i].min == htm->tm_min);
+ assert (pt_times[i].sec == htm->tm_sec);
+ assert (pt_times[i].dst == htm->tm_isdst);
+ t_ = mktime (htm);
+ printf ("pacific 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);
+ if (!skip_mktime_check) {
+ assert (t_ == t);
+ assert (memcmp (htm, &tm1, sizeof(tm1)) == 0);
+ } else {
+ printf ("Skipping mktime assert because of broken CRT library\n");
+ }
+ }
+
+ t64 = pt_times[i].time;
+ htm = _localtime64 (&t64);
+ tm3 = *htm;
+ printf ("pacific _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);
+ assert (pt_times[i].year == htm->tm_year + 1900);
+ assert (pt_times[i].mon == htm->tm_mon + 1);
+ assert (pt_times[i].day == htm->tm_mday);
+ assert (pt_times[i].hour == htm->tm_hour);
+ assert (pt_times[i].min == htm->tm_min);
+ assert (pt_times[i].sec == htm->tm_sec);
+ assert (pt_times[i].dst == htm->tm_isdst);
+ t64_ = _mktime64 (htm);
+ printf ("pacific _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);
+ if (!skip_mktime_check) {
+ assert (t64_ == t64);
+ assert (memcmp (htm, &tm3, sizeof(tm3)) == 0);
+ } else {
+ printf ("Skipping _mktime64 assert because of broken CRT library\n");
+ }
+
+ if (sizeof(t) >= 8 || pt_times[i].time <= INT_MAX)
+ assert (memcmp (&tm1, &tm3, sizeof(tm1)) == 0);
+
+ if (pt_times[i].time <= INT_MAX)
+ assert (memcmp (&tm1, &tm2, sizeof(tm1)) == 0);
+
+ if (pt_times[i].time <= INT_MAX)
+ 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");
tzset ();
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public