On native Windows (mingw and MSVC), the test 'test-parse-datetime' fails.
There are three problems that need to be worked around:
- The test exercises time points around the Unix epoch. However,
localtime() fails for negative arguments.
- Native Windows does not support time zone names with slashes.
See lib/tzset.c.
- After setting the TZ environment variable, it is necessary to
call tzset(), otherwise the previous GMT offset continue to be
used.
This patch fixes it all.
2024-05-27 Bruno Haible <[email protected]>
parse-datetime tests: Avoid failure on native Windows.
* tests/test-parse-datetime.c (SOME_TIMEPOINT): New macro.
(main): Use it. On native Windows, use Windows time zone names.
Invoke tzset() after setting TZ.
* doc/posix-functions/gmtime.texi: Mention the native Windows problem.
* doc/posix-functions/localtime.texi: Likewise.
diff --git a/doc/posix-functions/gmtime.texi b/doc/posix-functions/gmtime.texi
index 7590fe9108..f31eeaf09f 100644
--- a/doc/posix-functions/gmtime.texi
+++ b/doc/posix-functions/gmtime.texi
@@ -16,4 +16,8 @@
On some platforms, this function yields incorrect values for
timestamps before the year 1:
MacOS X 10.5, Solaris 11.3.
+@item
+On some platforms, this function returns NULL for arguments < -43200, that is,
+for timestamps before 1969-12-31 12:00:00 UTC:
+mingw, MSVC.
@end itemize
diff --git a/doc/posix-functions/localtime.texi
b/doc/posix-functions/localtime.texi
index 0cad93c1b2..7ce348e286 100644
--- a/doc/posix-functions/localtime.texi
+++ b/doc/posix-functions/localtime.texi
@@ -29,6 +29,10 @@
timestamps before the year 1:
MacOS X 10.5, Solaris 11.3.
@item
+On some platforms, this function returns NULL for negative arguments, that is,
+for timestamps before 1970-01-01 00:00:00 local time:
+mingw, MSVC.
+@item
Native Windows platforms (mingw, MSVC) support only a subset of time
zones supported by GNU or specified by POSIX@. @xref{tzset}.
@end itemize
diff --git a/tests/test-parse-datetime.c b/tests/test-parse-datetime.c
index 3b35cb70dd..330d5ea574 100644
--- a/tests/test-parse-datetime.c
+++ b/tests/test-parse-datetime.c
@@ -110,6 +110,17 @@ gmt_offset (time_t s)
return gmtoff;
}
+/* Define SOME_TIMEPOINT to some tv_sec value that is supported by the
+ platform's localtime() function and that is on the same weekday as
+ the Unix epoch. */
+#if defined _WIN32 && !defined __CYGWIN__
+/* On native Windows, localtime() fails for all time_t values < 0. */
+# define SOME_TIMEPOINT (700 * 86400)
+#else
+/* The Unix epoch. */
+# define SOME_TIMEPOINT 0
+#endif
+
int
main (_GL_UNUSED int argc, char **argv)
{
@@ -127,6 +138,7 @@ main (_GL_UNUSED int argc, char **argv)
a problem with glibc on sites that default to leap seconds; see
<https://bugs.gnu.org/12206>. */
ASSERT (setenv ("TZ", "EST5EDT,M3.2.0,M11.1.0", 1) == 0);
+ tzset ();
gmtoff = gmt_offset (ref_time);
@@ -225,14 +237,14 @@ main (_GL_UNUSED int argc, char **argv)
&& expected.tv_nsec == result.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "now";
ASSERT (parse_datetime (&result, p, &now));
LOG (p, now, result);
ASSERT (now.tv_sec == result.tv_sec && now.tv_nsec == result.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "tomorrow";
ASSERT (parse_datetime (&result, p, &now));
@@ -240,7 +252,7 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (now.tv_sec + 24 * 60 * 60 == result.tv_sec
&& now.tv_nsec == result.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "yesterday";
ASSERT (parse_datetime (&result, p, &now));
@@ -248,7 +260,7 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (now.tv_sec - 24 * 60 * 60 == result.tv_sec
&& now.tv_nsec == result.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "4 hours";
ASSERT (parse_datetime (&result, p, &now));
@@ -257,7 +269,7 @@ main (_GL_UNUSED int argc, char **argv)
&& now.tv_nsec == result.tv_nsec);
/* test if timezone is not being ignored for day offset */
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+400 +24 hours";
ASSERT (parse_datetime (&result, p, &now));
@@ -269,7 +281,7 @@ main (_GL_UNUSED int argc, char **argv)
&& result.tv_nsec == result2.tv_nsec);
/* test if several time zones formats are handled same way */
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+14:00";
ASSERT (parse_datetime (&result, p, &now));
@@ -285,7 +297,7 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (result.tv_sec == result2.tv_sec
&& result.tv_nsec == result2.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC-14:00";
ASSERT (parse_datetime (&result, p, &now));
@@ -301,7 +313,7 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (result.tv_sec == result2.tv_sec
&& result.tv_nsec == result2.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+0:15";
ASSERT (parse_datetime (&result, p, &now));
@@ -312,7 +324,7 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (result.tv_sec == result2.tv_sec
&& result.tv_nsec == result2.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC-1:30";
ASSERT (parse_datetime (&result, p, &now));
@@ -325,13 +337,13 @@ main (_GL_UNUSED int argc, char **argv)
/* TZ out of range should cause parse_datetime failure */
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+25:00";
ASSERT (!parse_datetime (&result, p, &now));
- /* Check for several invalid countable dayshifts */
- now.tv_sec = 4711;
+ /* Check for several invalid countable dayshifts */
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+4:00 +40 yesterday";
ASSERT (!parse_datetime (&result, p, &now));
@@ -349,7 +361,7 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (!parse_datetime (&result, p, &now));
/* And check correct usage of dayshifts */
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+400 tomorrow";
ASSERT (parse_datetime (&result, p, &now));
@@ -364,7 +376,7 @@ main (_GL_UNUSED int argc, char **argv)
LOG (p, now, result2);
ASSERT (result.tv_sec == result2.tv_sec
&& result.tv_nsec == result2.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+400 yesterday";
ASSERT (parse_datetime (&result, p, &now));
@@ -374,7 +386,7 @@ main (_GL_UNUSED int argc, char **argv)
LOG (p, now, result2);
ASSERT (result.tv_sec == result2.tv_sec
&& result.tv_nsec == result2.tv_nsec);
- now.tv_sec = 4711;
+ now.tv_sec = SOME_TIMEPOINT + 4711;
now.tv_nsec = 1267;
p = "UTC+400 now";
ASSERT (parse_datetime (&result, p, &now));
@@ -386,7 +398,12 @@ main (_GL_UNUSED int argc, char **argv)
&& result.tv_nsec == result2.tv_nsec);
/* If this platform has TZDB, check for GNU Bug#48085. */
+#if defined _WIN32 && !defined __CYGWIN__
+ ASSERT (setenv ("TZ", "US Eastern Standard Time", 1) == 0);
+#else
ASSERT (setenv ("TZ", "America/Indiana/Indianapolis", 1) == 0);
+#endif
+ tzset ();
now.tv_sec = 1619641490;
now.tv_nsec = 0;
struct tm *tm = localtime (&now.tv_sec);
@@ -404,9 +421,10 @@ main (_GL_UNUSED int argc, char **argv)
/* Check that some "next Monday", "last Wednesday", etc. are correct. */
ASSERT (setenv ("TZ", "UTC0", 1) == 0);
+ tzset ();
for (i = 0; day_table[i]; i++)
{
- unsigned int thur2 = 7 * 24 * 3600; /* 2nd thursday */
+ unsigned int thur2 = SOME_TIMEPOINT + 7 * 24 * 3600; /* 2nd thursday */
char tmp[32];
sprintf (tmp, "NEXT %s", day_table[i]);
now.tv_sec = thur2 + 4711;
@@ -425,6 +443,8 @@ main (_GL_UNUSED int argc, char **argv)
ASSERT (result.tv_sec == thur2 + ((i + 3) % 7 - 7) * 24 * 3600);
}
+/* On native Windows, localtime() fails for all time_t values < 0. */
+#if !(defined _WIN32 && !defined __CYGWIN__)
p = "1970-12-31T23:59:59+00:00 - 1 year"; /* Bug#50115 */
now.tv_sec = -1;
now.tv_nsec = 0;
@@ -448,6 +468,7 @@ main (_GL_UNUSED int argc, char **argv)
LOG (p, now, result);
ASSERT (result.tv_sec == 24 * 3600
&& result.tv_nsec == now.tv_nsec);
+#endif
/* Exercise a sign-extension bug. Before July 2012, an input
starting with a high-bit-set byte would be treated like "0". */