1 /* Test of nstrftime-like functions.
2 Copyright (C) 2011-2025 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Jim Meyering and Bruno Haible. */
19 #define FUNC_NAME STRINGIFY(FUNC)
20 #define STRINGIFY(x) #x
22 /* Support for settings like TZ='<+00>0' was added in IEEE Std 1003.1-2001. */
23 #define TZ_ANGLE_BRACKETS_SHOULD_WORK (200112 <= _POSIX_VERSION)
25 /* A wrapper around FUNC that checks the return value. */
27 FUNC_CHECKED (char *restrict s
, size_t maxsize
,
29 struct tm
const *tp
, timezone_t tz
, int ns
)
31 size_t ret
= FUNC (s
, maxsize
, format
, tp
, tz
, ns
);
34 ASSERT (ret
< maxsize
);
35 ASSERT (strlen (s
) == ret
);
40 /* -------------------------------------------------------------------------- */
50 static struct posixtm_test
const T
[] =
52 { 1300000000, 0, "%F", "2011-03-13" },
53 { 0, 10, "%T.%N", "00:00:00.000000010" },
54 { 56, 123456789, "%T.%12N", "00:00:56.123456789000" },
55 { 0, 123000000, "%T.%_6N", "00:00:00.123 " },
65 for (i
= 0; T
[i
].fmt
; i
++)
69 struct tm
*tm
= gmtime (&t
);
74 n
= FUNC_CHECKED (buf
, sizeof buf
, T
[i
].fmt
, tm
, NULL
, T
[i
].in_ns
);
78 printf ("%s failed with format %s\n", FUNC_NAME
, T
[i
].fmt
);
82 if (! STREQ (buf
, T
[i
].exp
))
85 printf ("%s: result mismatch: got %s, expected %s\n",
86 T
[i
].fmt
, buf
, T
[i
].exp
);
94 /* -------------------------------------------------------------------------- */
102 static struct tzalloc_test TZ
[] =
105 #if defined _WIN32 && !defined __CYGWIN__
108 { NULL
, "PST8PDT,M3.2.0,M11.1.0" },
118 #if defined _WIN32 && !defined __CYGWIN__
119 { NULL
, "CET-1CEST" },
121 { NULL
, "CET-1CEST,M3.5.0,M10.5.0/3" },
128 #if defined _WIN32 && !defined __CYGWIN__
129 { NULL
, "NST-13NDT" },
131 { NULL
, "NZST-12NZDT,M9.5.0,M4.1.0/3" },
140 struct localtime_rz_test
142 /* Input parameters. */
143 struct tzalloc_test
*tza
;
146 /* Expected result. */
149 /* Determines if an incorrectly unset tm_isdst
150 results in failure or just a warning. */
154 static struct localtime_rz_test LT
[] =
156 #if !(defined __DragonFly__ || defined __NetBSD__ || defined __ANDROID__)
157 { TZ
+Pacific
, 0, "1969-12-31 16:00:00 -0800 (PST)", 0 },
159 { TZ
+Arizona
, 0, "1969-12-31 17:00:00 -0700 (MST)", 0 },
160 { TZ
+UTC
, 0, "1970-01-01 00:00:00 +0000 (UTC)", 0 },
161 #if !(defined __DragonFly__ || defined __NetBSD__ || defined __ANDROID__)
162 { TZ
+CentEur
, 0, "1970-01-01 01:00:00 +0100 (CET)", 0 },
164 { TZ
+Japan
, 0, "1970-01-01 09:00:00 +0900 (JST)", 0 },
165 #if !defined __FreeBSD__
166 { TZ
+NZ
, 0, "1970-01-01 13:00:00 +1300 (NZDT)", 1 },
168 { TZ
+Pacific
, 500000001, "1985-11-04 16:53:21 -0800 (PST)", 0 },
169 { TZ
+Arizona
, 500000001, "1985-11-04 17:53:21 -0700 (MST)", 0 },
170 { TZ
+UTC
, 500000001, "1985-11-05 00:53:21 +0000 (UTC)", 0 },
171 { TZ
+CentEur
, 500000001, "1985-11-05 01:53:21 +0100 (CET)", 1 },
172 { TZ
+Japan
, 500000001, "1985-11-05 09:53:21 +0900 (JST)", 0 },
173 { TZ
+NZ
, 500000001, "1985-11-05 13:53:21 +1300 (NZDT)", 0 },
174 #if !(defined _WIN32 && !defined __CYGWIN__)
175 { TZ
+Pacific
, 1000000002, "2001-09-08 18:46:42 -0700 (PDT)", 0 },
177 { TZ
+Arizona
, 1000000002, "2001-09-08 18:46:42 -0700 (MST)", 0 },
178 { TZ
+UTC
, 1000000002, "2001-09-09 01:46:42 +0000 (UTC)", 0 },
179 #if !(defined _WIN32 && !defined __CYGWIN__)
180 { TZ
+CentEur
, 1000000002, "2001-09-09 03:46:42 +0200 (CEST)", 0 },
182 { TZ
+Japan
, 1000000002, "2001-09-09 10:46:42 +0900 (JST)", 0 },
183 #if !(defined _WIN32 && !defined __CYGWIN__)
184 { TZ
+NZ
, 1000000002, "2001-09-09 13:46:42 +1200 (NZST)", 0 },
186 #if TZ_ANGLE_BRACKETS_SHOULD_WORK && !defined __FreeBSD__
187 { TZ
+Unknown
, 0, "1970-01-01 00:00:00 -0000 (-00)", 0 },
188 { TZ
+Unknown
, 500000001, "1985-11-05 00:53:21 -0000 (-00)", 0 },
189 { TZ
+Unknown
, 1000000002, "2001-09-09 01:46:42 -0000 (-00)", 0 },
200 for (i
= 0; LT
[i
].tza
; i
++)
202 struct tzalloc_test
*tza
= LT
[i
].tza
;
204 timezone_t tz
= tza
->tz
;
206 static char const format
[] = "%Y-%m-%d %H:%M:%S %z (%Z)";
211 if (!tz
&& tza
->setting
)
213 tz
= tzalloc (tza
->setting
);
217 printf ("%s: tzalloc: %s\n", TZ
[i
].setting
, strerror (errno
));
224 setting
= tza
->setting
? tza
->setting
: "UTC0";
226 if (!localtime_rz (tz
, <
[i
].t
, &tm
))
229 printf ("%s: %ld: localtime_rz: %s\n", setting
, lt
,
235 n
= FUNC_CHECKED (buf
, sizeof buf
, format
, &tm
, tz
, 0);
239 printf ("%s: %ld: %s failed\n", setting
, lt
, FUNC_NAME
);
244 if (! (STREQ (buf
, LT
[i
].exp
)
245 || (!tz
&& n
== strlen (LT
[i
].exp
)
246 && memcmp (buf
, LT
[i
].exp
, n
- sizeof "(GMT)" + 1) == 0
247 && STREQ (buf
+ n
- sizeof "(GMT)" + 1, "(GMT)"))
248 #if defined _WIN32 && !defined __CYGWIN__
249 /* On native Windows, the time zone is printed differently. */
250 || strncmp (buf
, LT
[i
].exp
, 20) == 0
254 /* Don't fail for unhandled dst in ahistorical entries,
255 as gnulib doesn't currently fix that issue, seen on Darwin 14. */
256 if (!LT
[i
].ahistorical
|| tm
.tm_isdst
)
258 printf ("%s: expected \"%s\", got \"%s\"\n",
259 setting
, LT
[i
].exp
, buf
);
267 /* -------------------------------------------------------------------------- */
276 for (mon
= 1; mon
<= 12; mon
++)
280 struct tm qtm
= { .tm_mon
= mon
- 1 };
281 char fmt
[3] = {'%','q','\0'};
283 size_t r
= FUNC_CHECKED (out
, sizeof (out
), fmt
, &qtm
, NULL
, 0);
286 printf ("%s(\"%%q\") failed\n", FUNC_NAME
);
292 exp
[0] = mon
< 4 ? '1' : mon
< 7 ? '2' : mon
< 10 ? '3' : '4';
293 if (strcmp (out
, exp
) != 0)
295 printf ("%s %%q: expected \"%s\", got \"%s\"\n", FUNC_NAME
, exp
, out
);
305 /* -------------------------------------------------------------------------- */
311 struct tm tm
= { .tm_year
= 2020 - 1900, .tm_mday
= 1 };
312 char buf
[INT_BUFSIZE_BOUND (time_t)];
314 int bigyear
= LLONG_MAX
- 1900 < INT_MAX
? LLONG_MAX
- 1900 : INT_MAX
;
317 n
= FUNC_CHECKED (buf
, 0, "%m", &tm
, NULL
, 0);
318 if (! (n
== 0 && errno
== ERANGE
))
321 printf ("%s failed to set errno = ERANGE\n", FUNC_NAME
);
326 n
= FUNC_CHECKED (buf
, sizeof buf
, "", &tm
, NULL
, 0);
327 if (! (n
== 0 && errno
== 0))
330 printf ("%s failed to leave errno alone\n", FUNC_NAME
);
335 tm
.tm_year
= bigyear
;
337 n
= FUNC_CHECKED (buf
, sizeof buf
, "%s", &tm
, NULL
, 0);
340 if (errno
!= EOVERFLOW
)
343 printf ("%s failed to set errno = EOVERFLOW\n", FUNC_NAME
);
347 if (mktime_z (NULL
, &tm
) != (time_t) -1)
350 printf ("%s %%s failed but mktime_z worked for tm_year=%d\n",
357 long long int text_seconds
= atoll (buf
);
358 if (text_seconds
<= (LLONG_MAX
- 1 < TYPE_MAXIMUM (time_t)
359 ? LLONG_MAX
- 1 : TYPE_MAXIMUM (time_t)))
361 time_t bigtime
= text_seconds
;
362 struct tm
*tmp
= gmtime (&bigtime
);
366 printf ("gmtime failed on %s result\n", FUNC_NAME
);
371 char buf1
[sizeof buf
];
372 size_t n1
= FUNC_CHECKED (buf1
, sizeof buf1
, "%s", tmp
, NULL
, 0);
374 if (! STREQ (buf
, buf1
))
377 printf ("%s %%s first returned '%s', then '%s'\n",
378 FUNC_NAME
, buf
, buf1
);
388 /* -------------------------------------------------------------------------- */
390 /* Test various format directives. */
392 typedef enum { english
, french
} language_t
;
395 locales_test (language_t language
)
399 time_t t
= 1509000003;
400 struct tm
*tm
= gmtime (&t
);
405 n
= FUNC_CHECKED (buf
, sizeof buf
, "%+4Y-%m-%d %H:%M:%S.%N", tm
, NULL
, ns
);
407 printf ("Format as ISO 8601: %s\n", buf
);
409 ASSERT (STREQ (buf
, "2017-10-26 06:40:03.123456789"));
411 /* Exercise various POSIX format directives. */
413 n
= FUNC_CHECKED (buf
, sizeof buf
, "%a", tm
, NULL
, ns
);
415 printf ("%%a directive: %s\n", buf
);
420 ASSERT (STREQ (buf
, "Thu"));
423 ASSERT (STREQ (buf
, "jeu.") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
424 || STREQ (buf
, "Jeu") /* macOS, older FreeBSD */);
428 n
= FUNC_CHECKED (buf
, sizeof buf
, "%A", tm
, NULL
, ns
);
430 printf ("%%A directive: %s\n", buf
);
435 ASSERT (STREQ (buf
, "Thursday"));
438 ASSERT (STREQ (buf
, "jeudi") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
439 || STREQ (buf
, "Jeudi") /* macOS, older FreeBSD */);
443 n
= FUNC_CHECKED (buf
, sizeof buf
, "%b", tm
, NULL
, ns
);
445 printf ("%%b directive: %s\n", buf
);
450 ASSERT (STREQ (buf
, "Oct"));
453 ASSERT (STREQ (buf
, "oct.") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
454 || STREQ (buf
, "oct") /* macOS, older FreeBSD */);
458 n
= FUNC_CHECKED (buf
, sizeof buf
, "%B", tm
, NULL
, ns
);
460 printf ("%%B directive: %s\n", buf
);
465 ASSERT (STREQ (buf
, "October"));
468 ASSERT (STREQ (buf
, "octobre"));
472 n
= FUNC_CHECKED (buf
, sizeof buf
, "%c", tm
, NULL
, ns
);
474 printf ("%%c directive: %s\n", buf
);
479 ASSERT (STREQ (buf
, "Thu Oct 26 06:40:03 2017"));
482 ASSERT (STREQ (buf
, "jeu. 26 oct. 2017 06:40:03") /* glibc, Cygwin */
483 || STREQ (buf
, "jeu. 26 oct. 06:40:03 2017") /* FreeBSD */
484 || STREQ (buf
, "Jeu 26 oct 06:40:03 2017") /* macOS, older FreeBSD */
485 || STREQ (buf
, "26 octobre 2017 06:40:03") /* NetBSD */
486 || STREQ (buf
, "26 octobre 2017 Ã 06:40:03") /* Solaris (UTF-8) */
487 || STREQ (buf
, "26 octobre 2017 \340 06:40:03") /* Solaris (ISO-8859-1) */);
491 n
= FUNC_CHECKED (buf
, sizeof buf
, "%C", tm
, NULL
, ns
);
493 printf ("%%C directive: %s\n", buf
);
495 ASSERT (STREQ (buf
, "20"));
497 n
= FUNC_CHECKED (buf
, sizeof buf
, "%d", tm
, NULL
, ns
);
499 printf ("%%d directive: %s\n", buf
);
501 ASSERT (STREQ (buf
, "26"));
503 n
= FUNC_CHECKED (buf
, sizeof buf
, "%D", tm
, NULL
, ns
);
505 printf ("%%D directive: %s\n", buf
);
507 ASSERT (STREQ (buf
, "10/26/17"));
509 n
= FUNC_CHECKED (buf
, sizeof buf
, "%e", tm
, NULL
, ns
);
511 printf ("%%e directive: %s\n", buf
);
513 ASSERT (STREQ (buf
, "26"));
515 n
= FUNC_CHECKED (buf
, sizeof buf
, "%F", tm
, NULL
, ns
);
517 printf ("%%F directive: %s\n", buf
);
519 ASSERT (STREQ (buf
, "2017-10-26"));
521 n
= FUNC_CHECKED (buf
, sizeof buf
, "%g", tm
, NULL
, ns
);
523 printf ("%%g directive: %s\n", buf
);
525 ASSERT (STREQ (buf
, "17"));
527 n
= FUNC_CHECKED (buf
, sizeof buf
, "%G", tm
, NULL
, ns
);
529 printf ("%%G directive: %s\n", buf
);
531 ASSERT (STREQ (buf
, "2017"));
533 n
= FUNC_CHECKED (buf
, sizeof buf
, "%h", tm
, NULL
, ns
);
535 printf ("%%h directive: %s\n", buf
);
540 ASSERT (STREQ (buf
, "Oct"));
543 ASSERT (STREQ (buf
, "oct.") /* glibc, FreeBSD, NetBSD, Solaris, Cygwin */
544 || STREQ (buf
, "oct") /* macOS, older FreeBSD */);
548 n
= FUNC_CHECKED (buf
, sizeof buf
, "%H", tm
, NULL
, ns
);
550 printf ("%%H directive: %s\n", buf
);
552 ASSERT (STREQ (buf
, "06"));
554 n
= FUNC_CHECKED (buf
, sizeof buf
, "%I", tm
, NULL
, ns
);
556 printf ("%%I directive: %s\n", buf
);
558 ASSERT (STREQ (buf
, "06"));
560 n
= FUNC_CHECKED (buf
, sizeof buf
, "%j", tm
, NULL
, ns
);
562 printf ("%%j directive: %s\n", buf
);
564 ASSERT (STREQ (buf
, "299"));
566 n
= FUNC_CHECKED (buf
, sizeof buf
, "%m", tm
, NULL
, ns
);
568 printf ("%%m directive: %s\n", buf
);
570 ASSERT (STREQ (buf
, "10"));
572 n
= FUNC_CHECKED (buf
, sizeof buf
, "%M", tm
, NULL
, ns
);
574 printf ("%%M directive: %s\n", buf
);
576 ASSERT (STREQ (buf
, "40"));
578 n
= FUNC_CHECKED (buf
, sizeof buf
, "%n", tm
, NULL
, ns
);
580 printf ("%%n directive: %s\n", buf
);
582 ASSERT (STREQ (buf
, "\n"));
584 n
= FUNC_CHECKED (buf
, sizeof buf
, "%p", tm
, NULL
, ns
);
589 printf ("%%p directive: %s\n", buf
);
591 ASSERT (STREQ (buf
, "AM"));
598 n
= FUNC_CHECKED (buf
, sizeof buf
, "%r", tm
, NULL
, ns
);
600 printf ("%%r directive: %s\n", buf
);
605 ASSERT (STREQ (buf
, "06:40:03 AM"));
608 ASSERT (STREQ (buf
, "06:40:03 ") /* glibc */
609 || STREQ (buf
, "06:40:03") /* NetBSD, Cygwin */
610 || STREQ (buf
, " 6:40:03") /* Solaris */);
614 n
= FUNC_CHECKED (buf
, sizeof buf
, "%R", tm
, NULL
, ns
);
616 printf ("%%R directive: %s\n", buf
);
618 ASSERT (STREQ (buf
, "06:40"));
620 n
= FUNC_CHECKED (buf
, sizeof buf
, "%S", tm
, NULL
, ns
);
622 printf ("%%S directive: %s\n", buf
);
624 ASSERT (STREQ (buf
, "03"));
626 n
= FUNC_CHECKED (buf
, sizeof buf
, "%t", tm
, NULL
, ns
);
628 printf ("%%t directive: %s\n", buf
);
630 ASSERT (STREQ (buf
, "\t"));
632 n
= FUNC_CHECKED (buf
, sizeof buf
, "%T", tm
, NULL
, ns
);
634 printf ("%%T directive: %s\n", buf
);
636 ASSERT (STREQ (buf
, "06:40:03"));
638 n
= FUNC_CHECKED (buf
, sizeof buf
, "%u", tm
, NULL
, ns
);
640 printf ("%%u directive: %s\n", buf
);
642 ASSERT (STREQ (buf
, "4"));
644 n
= FUNC_CHECKED (buf
, sizeof buf
, "%U", tm
, NULL
, ns
);
646 printf ("%%U directive: %s\n", buf
);
648 ASSERT (STREQ (buf
, "43"));
650 n
= FUNC_CHECKED (buf
, sizeof buf
, "%V", tm
, NULL
, ns
);
652 printf ("%%V directive: %s\n", buf
);
654 ASSERT (STREQ (buf
, "43"));
656 n
= FUNC_CHECKED (buf
, sizeof buf
, "%w", tm
, NULL
, ns
);
658 printf ("%%w directive: %s\n", buf
);
660 ASSERT (STREQ (buf
, "4"));
662 n
= FUNC_CHECKED (buf
, sizeof buf
, "%W", tm
, NULL
, ns
);
664 printf ("%%W directive: %s\n", buf
);
666 ASSERT (STREQ (buf
, "43"));
668 n
= FUNC_CHECKED (buf
, sizeof buf
, "%x", tm
, NULL
, ns
);
670 printf ("%%x directive: %s\n", buf
);
675 ASSERT (STREQ (buf
, "10/26/17"));
678 ASSERT (STREQ (buf
, "26/10/2017") /* glibc, NetBSD, Solaris, Cygwin */
679 || STREQ (buf
, "26.10.2017") /* FreeBSD, macOS */);
683 n
= FUNC_CHECKED (buf
, sizeof buf
, "%X", tm
, NULL
, ns
);
685 printf ("%%X directive: %s\n", buf
);
687 ASSERT (STREQ (buf
, "06:40:03"));
689 n
= FUNC_CHECKED (buf
, sizeof buf
, "%y", tm
, NULL
, ns
);
691 printf ("%%y directive: %s\n", buf
);
693 ASSERT (STREQ (buf
, "17"));
695 n
= FUNC_CHECKED (buf
, sizeof buf
, "%Y", tm
, NULL
, ns
);
697 printf ("%%Y directive: %s\n", buf
);
699 ASSERT (STREQ (buf
, "2017"));
701 n
= FUNC_CHECKED (buf
, sizeof buf
, "%z", tm
, NULL
, ns
);
703 printf ("%%z directive: %s\n", buf
);
705 ASSERT (STREQ (buf
, "+0000"));
707 n
= FUNC_CHECKED (buf
, sizeof buf
, "%Z", tm
, NULL
, ns
);
709 printf ("%%Z directive: %s\n", buf
);
711 ASSERT (STREQ (buf
, "GMT") /* glibc, NetBSD, OpenBSD, AIX, Solaris, Cygwin, Android */
712 || STREQ (buf
, "UTC") /* musl, macOS, FreeBSD */);
714 n
= FUNC_CHECKED (buf
, sizeof buf
, "%%", tm
, NULL
, ns
);
716 printf ("%%%% directive: %s\n", buf
);
718 ASSERT (STREQ (buf
, "%"));
720 /* Exercise various GNU extensions from glibc. */
722 n
= FUNC_CHECKED (buf
, sizeof buf
, "%k", tm
, NULL
, ns
);
724 printf ("%%k directive: %s\n", buf
);
726 ASSERT (STREQ (buf
, " 6"));
728 n
= FUNC_CHECKED (buf
, sizeof buf
, "%l", tm
, NULL
, ns
);
730 printf ("%%l directive: %s\n", buf
);
732 ASSERT (STREQ (buf
, " 6"));
734 n
= FUNC_CHECKED (buf
, sizeof buf
, "%P", tm
, NULL
, ns
);
739 printf ("%%P directive: %s\n", buf
);
741 ASSERT (STREQ (buf
, "am"));
748 n
= FUNC_CHECKED (buf
, sizeof buf
, "%s", tm
, NULL
, ns
);
750 printf ("%%s directive: %s\n", buf
);
752 ASSERT (STREQ (buf
, "1509000003"));
754 /* Exercise various GNU extensions from gnulib. */
756 n
= FUNC_CHECKED (buf
, sizeof buf
, "%N", tm
, NULL
, ns
);
758 printf ("%%N directive: %s\n", buf
);
760 ASSERT (STREQ (buf
, "123456789"));
765 /* -------------------------------------------------------------------------- */
769 indent-tabs-mode: nil