/* Data-driven tests for strftime/strptime.
Copyright (C) 2019 Free Software Foundation, Inc. This file is
part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
. */
#include
#include
#include
#include
#include
#include
#include
#include
/* These exist for the convenience of writing the test data, because
zero-based vs one-based. */
typedef enum
{
Sun, Mon, Tue, Wed, Thu, Fri, Sat
} WeekDay;
typedef enum
{
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
} Month;
typedef struct
{
/* A descriptive name of the test. */
const char *name;
/* The specific date and time to be tested. */
int y, m, d;
WeekDay w;
int hh, mm, ss;
/* The locale under which the conversion is done. */
const char *locale;
/* Format passed to strftime. */
const char *format;
/* Expected data, NUL terminated. */
const char *printed;
} Data;
/* Notes:
Years are full 4-digit years, the code compensates. Likewise,
use month and weekday enums (above) which are zero-based.
The encoded strings are multibyte strings in the C locale which
reflect the same binary data as the expected strings. When you run
the test, the strings are printed as-is to stdout, so if your
terminal is set for the correct encoding, they'll be printed
"correctly". Put the Unicode codes and UTF-8 samples in the
comments.
For convenience, mis-matched strings are printed in
paste-compatible format, raw text format, and Unicode format. Use
"" between a hex escape sequence (like \xe8) and a following hex
digit which should be considered as a printable character.
To verify text, save the correct text in a file, and use "od -tx1
-tc file" to see the raw hex values. */
const Data data[] = {
{ "Baseline test",
2019, Mar, 27, Wed, 14, 3, 22, "en_US.ISO-8859-1", "%Y-%m-%d %T",
"2019-03-27 14:03:22" },
{ "Japanese era change, BCE/CE, before transition",
0, Dec, 31, Sun, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 01 紀元前01年 */
"\xe7\xb4\x80\xe5\x85\x83\xe5\x89\x8d""01\xe5\xb9\xb4" },
{ "Japanese era change, BCE/CE, after transition",
1, Jan, 1, Mon, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 01 西暦01年 */
"\xe8\xa5\xbf\xe6\x9a\xa6""01\xe5\xb9\xb4" },
{ "Japanese era change, BCE/CE, before transition",
0, Dec, 31, Sun, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 01 紀元前01年 */
"\xb5\xaa\xb8\xb5\xc1\xb0""01\xc7\xaf" },
{ "Japanese era change, BCE/CE, after transition",
1, Jan, 1, Mon, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 01 西暦01年 */
"\xc0\xbe\xce\xf1""01\xc7\xaf" },
{ "Japanese era change, 1873, before transition",
1872, Dec, 31, Tue, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 1872 西暦1872年 */
"\xe8\xa5\xbf\xe6\x9a\xa6""1872\xe5\xb9\xb4" },
{ "Japanese era change, 1873, after transition",
1873, Jan, 1, Wed, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 06 明治06年 */
"\xe6\x98\x8e\xe6\xb2\xbb""06\xe5\xb9\xb4" },
{ "Japanese era change, 1873, before transition",
1872, Dec, 31, Tue, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 1872 西暦1872年 */
"\xc0\xbe\xce\xf1""1872\xc7\xaf" },
{ "Japanese era change, 1873, after transition",
1873, Jan, 1, Wed, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 06 明治06年 */
"\xcc\xc0\xbc\xa3""06\xc7\xaf" },
{ "Japanese era change, 1912, before transition year",
1911, Dec, 31, Sun, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 44 明治44年 */
"\xe6\x98\x8e\xe6\xb2\xbb""44\xe5\xb9\xb4" },
{ "Japanese era change, 1912, start of transition year",
1912, Jan, 1, Mon, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 45 明治45年 */
"\xe6\x98\x8e\xe6\xb2\xbb""45\xe5\xb9\xb4" },
{ "Japanese era change, 1912, before transition",
1912, Jul, 29, Mon, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 45 明治45年 */
"\xe6\x98\x8e\xe6\xb2\xbb""45\xe5\xb9\xb4" },
{ "Japanese era change, 1912, after transition",
1912, Jul, 30, Tue, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 大正元年 */
"\xe5\xa4\xa7\xe6\xad\xa3\xe5\x85\x83\xe5\xb9\xb4" },
{ "Japanese era change, 1912, before end of transition year",
1912, Dec, 31, Tue, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 大正元年 */
"\xe5\xa4\xa7\xe6\xad\xa3\xe5\x85\x83\xe5\xb9\xb4" },
{ "Japanese era change, 1912, after transition year",
1913, Jan, 1, Wed, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 02 大正02年 */
"\xe5\xa4\xa7\xe6\xad\xa3""02\xe5\xb9\xb4" },
{ "Japanese era change, 1912, before transition year",
1911, Dec, 31, Sun, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 44 明治44年 */
"\xcc\xc0\xbc\xa3""44\xc7\xaf" },
{ "Japanese era change, 1912, start of transition year",
1912, Jan, 1, Mon, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 45 明治45年 */
"\xcc\xc0\xbc\xa3""45\xc7\xaf" },
{ "Japanese era change, 1912, before transition",
1912, Jul, 29, Mon, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 45 明治45年 */
"\xcc\xc0\xbc\xa3""45\xc7\xaf" },
{ "Japanese era change, 1912, after transition",
1912, Jul, 30, Tue, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 大正元年 */
"\xc2\xe7\xc0\xb5\xb8\xb5\xc7\xaf" },
{ "Japanese era change, 1912, before end of transition year",
1912, Dec, 31, Tue, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 大正元年 */
"\xc2\xe7\xc0\xb5\xb8\xb5\xc7\xaf" },
{ "Japanese era change, 1912, after transition year",
1913, Jan, 1, Wed, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 02 大正02年 */
"\xc2\xe7\xc0\xb5""02\xc7\xaf" },
{ "Japanese era change, 1926, before transition year",
1925, Dec, 31, Thu, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 14 大正14年 */
"\xe5\xa4\xa7\xe6\xad\xa3""14\xe5\xb9\xb4" },
{ "Japanese era change, 1926, start of transition year",
1926, Jan, 1, Fri, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 15 大正15年 */
"\xe5\xa4\xa7\xe6\xad\xa3""15\xe5\xb9\xb4" },
{ "Japanese era change, 1926, before transition",
1926, Dec, 24, Fri, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 15 大正15年 */
"\xe5\xa4\xa7\xe6\xad\xa3""15\xe5\xb9\xb4" },
{ "Japanese era change, 1926, after transition",
1926, Dec, 25, Sat, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 昭和元年 */
"\xe6\x98\xad\xe5\x92\x8c\xe5\x85\x83\xe5\xb9\xb4" },
{ "Japanese era change, 1926, before end of transition year",
1926, Dec, 31, Fri, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 昭和元年 */
"\xe6\x98\xad\xe5\x92\x8c\xe5\x85\x83\xe5\xb9\xb4" },
{ "Japanese era change, 1926, after transition year",
1927, Jan, 1, Sat, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 02 昭和02年 */
"\xe6\x98\xad\xe5\x92\x8c""02\xe5\xb9\xb4" },
{ "Japanese era change, 1926, before transition year",
1925, Dec, 31, Thu, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 14 大正14年 */
"\xc2\xe7\xc0\xb5""14\xc7\xaf" },
{ "Japanese era change, 1926, start of transition year",
1926, Jan, 1, Fri, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 15 大正15年 */
"\xc2\xe7\xc0\xb5""15\xc7\xaf" },
{ "Japanese era change, 1926, before transition",
1926, Dec, 24, Fri, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 15 大正15年 */
"\xc2\xe7\xc0\xb5""15\xc7\xaf" },
{ "Japanese era change, 1926, after transition",
1926, Dec, 25, Sat, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 昭和元年 */
"\xbe\xbc\xcf\xc2\xb8\xb5\xc7\xaf" },
{ "Japanese era change, 1926, before end of transition year",
1926, Dec, 31, Fri, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 昭和元年 */
"\xbe\xbc\xcf\xc2\xb8\xb5\xc7\xaf" },
{ "Japanese era change, 1926, after transition year",
1927, Jan, 1, Sat, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 02 昭和02年 */
"\xbe\xbc\xcf\xc2""02\xc7\xaf" },
{ "Japanese era change, 1989, before transition year",
1988, Dec, 31, Sat, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 63 昭和63年 */
"\xe6\x98\xad\xe5\x92\x8c""63\xe5\xb9\xb4" },
{ "Japanese era change, 1989, start of transition year",
1989, Jan, 1, Sun, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 64 昭和64年 */
"\xe6\x98\xad\xe5\x92\x8c""64\xe5\xb9\xb4" },
{ "Japanese era change, 1989, before transition",
1989, Jan, 7, Sat, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 64 昭和64年 */
"\xe6\x98\xad\xe5\x92\x8c""64\xe5\xb9\xb4" },
{ "Japanese era change, 1989, after transition",
1989, Jan, 8, Sun, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 平成元年 */
"\xe5\xb9\xb3\xe6\x88\x90\xe5\x85\x83\xe5\xb9\xb4" },
{ "Japanese era change, 1989, end of transition year",
1989, Dec, 31, Sun, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 平成元年 */
"\xe5\xb9\xb3\xe6\x88\x90\xe5\x85\x83\xe5\xb9\xb4" },
{ "Japanese era change, 1989, after transition year",
1990, Jan, 1, Mon, 12, 00, 00, "ja_JP.UTF-8", "%EY",
/* 02 平成02年 */
"\xe5\xb9\xb3\xe6\x88\x90""02\xe5\xb9\xb4" },
{ "Japanese era change, 1989, before transition year",
1988, Dec, 31, Sat, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 63 昭和63年 */
"\xbe\xbc\xcf\xc2""63\xc7\xaf" },
{ "Japanese era change, 1989, start of transition year",
1989, Jan, 1, Sun, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 64 昭和64年 */
"\xbe\xbc\xcf\xc2""64\xc7\xaf" },
{ "Japanese era change, 1989, before transition",
1989, Jan, 7, Sat, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 64 昭和64年 */
"\xbe\xbc\xcf\xc2""64\xc7\xaf" },
{ "Japanese era change, 1989, after transition",
1989, Jan, 8, Sun, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 平成元年 */
"\xca\xbf\xc0\xae\xb8\xb5\xc7\xaf" },
{ "Japanese era change, 1989, end of transition year",
1989, Dec, 31, Sun, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 平成元年 */
"\xca\xbf\xc0\xae\xb8\xb5\xc7\xaf" },
{ "Japanese era change, 1989, after transition year",
1990, Jan, 1, Mon, 12, 00, 00, "ja_JP.EUC-JP", "%EY",
/* 02 平成02年 */
"\xca\xbf\xc0\xae""02\xc7\xaf" },
};
#define NDATA array_length(data)
/* Size of buffer passed to strftime. */
#define STRBUFLEN 1000
/* Size of buffer passed to tm_to_printed. */
#define TMBUFLEN 50
/* Helper function to compare strings and print out mismatches in a
format suitable for maintaining this test. TEST_COMPARE_STRINGS
prints out a less suitable format. */
static void
print_string_hex (const char *header, const char *str)
{
int tictoc = 0;
const char *s = str;
wchar_t w[STRBUFLEN];
size_t i, wlen;
printf ("%s : ", header);
if (str == NULL)
{
printf ("\n");
return;
}
while (*s)
{
/* isgraph equivalent, but independent of current locale. */
if (' ' <= *s && *s <= '~')
putchar (*s);
else
{
if (tictoc)
printf ("\033[36m");
else
printf ("\033[31m");
tictoc = ! tictoc;
printf ("\\x%02x\033[0m", (unsigned char) *s);
}
++ s;
}
printf (" - %s\n", str);
s = str;
wlen = mbsrtowcs (w, &s, strlen (s), NULL);
printf ("%*s", (int) strlen (header) + 3, " ");
for (i = 0; i < wlen && i < strlen (str); i ++)
{
if (' ' <= w[i] && w[i] <= '~')
putchar (w[i]);
else
printf ("", w[i]);
}
printf ("\n");
}
static void
compare_strings (const char *got, const char *expected,
const char *filename, int lineno)
{
if (got && expected && strcmp (got, expected) == 0)
return;
support_record_failure ();
printf ("%s:%d: error: strftime output incorrect\n", filename, lineno);
print_string_hex ("Got", got);
print_string_hex ("Exp", expected);
}
#define COMPARE_STRINGS(g,e) compare_strings (g, e, __FILE__, __LINE__)
const char *weekday_name[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat" };
/* Helper function to create a printable version of struct tm. */
static void
tm_to_printed (struct tm *tm, char *buffer)
{
const char *wn;
char temp[50];
if (0 <= tm->tm_wday && tm->tm_wday <= 6)
wn = weekday_name[tm->tm_wday];
else
{
wn = temp;
sprintf (temp, "%d", tm->tm_wday);
}
snprintf (buffer, TMBUFLEN, "%04d/%02d/%02d %02d:%02d:%02d %s",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec,
wn);
}
static int
do_test (void)
{
int i;
char buffer[STRBUFLEN];
char expected_time[TMBUFLEN];
char got_time[TMBUFLEN];
for (i = 0; i < NDATA; i ++)
{
const Data *d = &(data[i]);
struct tm tm;
struct tm tm2;
size_t rv;
char *rvp;
/* Print this just to help debug failures. */
printf ("%s:\n\t%s %s %s\n", d->name, d->locale, d->format, d->printed);
tm.tm_year = d->y - 1900;
tm.tm_mon = d->m;
tm.tm_mday = d->d;
tm.tm_wday = d->w;
tm.tm_hour = d->hh;
tm.tm_min = d->mm;
tm.tm_sec = d->ss;
tm.tm_isdst = -1;
/* LC_ALL may interfere with the snprintf in tm_to_printed. */
if (setlocale (LC_TIME, d->locale) == NULL)
{
/* See the LOCALES list in the Makefile. */
printf ("locale %s does not exist!\n", d->locale);
exit (EXIT_FAILURE);
}
/* This is just for printing wide characters if there's an error. */
setlocale (LC_CTYPE, d->locale);
rv = strftime (buffer, sizeof (buffer), d->format, &tm);
TEST_COMPARE (rv, strlen (d->printed));
COMPARE_STRINGS (buffer, d->printed);
/* Copy the original time, so that any fields not affected by
the call to strptime will match. */
tm2 = tm;
rvp = strptime (d->printed, d->format, &tm2);
TEST_COMPARE_STRING (rvp, "");
tm_to_printed (&tm, expected_time);
tm_to_printed (&tm2, got_time);
TEST_COMPARE_STRING (got_time, expected_time);
}
return 0;
}
#include