strftime: Pass the additional flags from "%EY" to "%Ey" [BZ #24096]

The full representation of the alternative calendar year (%EY)
typically includes an internal use of "%Ey".  As a GNU extension,
apply any flags on "%EY" (e.g. "%_EY", "%-EY") to the internal "%Ey",
allowing users of "%EY" to control how the year is padded.

Reviewed-by: Rafal Luzynski <digitalfreak@lingonborough.com>
Reviewed-by: Zack Weinberg <zackw@panix.com>

ChangeLog:

	[BZ #24096]
	* manual/time.texi (strftime): Document "%EC" and "%EY".
	* time/Makefile (tests): Add tst-strftime2.
	(LOCALES): Add ja_JP.UTF-8, lo_LA.UTF-8, and th_TH.UTF-8.
	* time/strftime_l.c (__strftime_internal): Add argument yr_spec to
	override padding for "%Ey".
	If an optional flag ('_' or '-') is specified to "%EY", interpret the
	"%Ey" in the subformat as if decorated with that flag.
	* time/tst-strftime2.c: New file.
This commit is contained in:
TAMUKI Shoichi 2019-01-24 23:04:12 +09:00
parent b22eed3710
commit 32f600a272
6 changed files with 171 additions and 9 deletions

View File

@ -5,6 +5,16 @@
* time/strftime_l.c (__strftime_internal): Set the default width * time/strftime_l.c (__strftime_internal): Set the default width
padding with zero of "%Ey" to 2. padding with zero of "%Ey" to 2.
[BZ #24096]
* manual/time.texi (strftime): Document "%EC" and "%EY".
* time/Makefile (tests): Add tst-strftime2.
(LOCALES): Add ja_JP.UTF-8, lo_LA.UTF-8, and th_TH.UTF-8.
* time/strftime_l.c (__strftime_internal): Add argument yr_spec to
override padding for "%Ey".
If an optional flag ('_' or '-') is specified to "%EY", interpret the
"%Ey" in the subformat as if decorated with that flag.
* time/tst-strftime2.c: New file.
2019-01-24 Adhemerval Zanella <adhemerval.zanella@linaro.org> 2019-01-24 Adhemerval Zanella <adhemerval.zanella@linaro.org>
* support/xsigstack.c (MAP_NORESERVE, MAP_STACK): Define if they * support/xsigstack.c (MAP_NORESERVE, MAP_STACK): Define if they

4
NEWS
View File

@ -60,6 +60,10 @@ Major new features:
alternative year numbers less than 10). Zero-padding can be alternative year numbers less than 10). Zero-padding can be
overridden with the '_' or '-' flags (which are GNU extensions). overridden with the '_' or '-' flags (which are GNU extensions).
* As a GNU extension, the '_' and '-' flags can now be applied to
"%EY" to control how the year number is formatted; they have the
same effect that they would on "%Ey".
Deprecated and removed features, and other changes affecting compatibility: Deprecated and removed features, and other changes affecting compatibility:
* The glibc.tune tunable namespace has been renamed to glibc.cpu and the * The glibc.tune tunable namespace has been renamed to glibc.cpu and the

View File

@ -1393,6 +1393,10 @@ The preferred calendar time representation for the current locale.
The century of the year. This is equivalent to the greatest integer not The century of the year. This is equivalent to the greatest integer not
greater than the year divided by 100. greater than the year divided by 100.
If the @code{E} modifier is specified (@code{%EC}), instead produces
the name of the period for the year (e.g.@: an era name) in the
locale's alternative calendar.
This format was first standardized by POSIX.2-1992 and by @w{ISO C99}. This format was first standardized by POSIX.2-1992 and by @w{ISO C99}.
@item %d @item %d
@ -1579,6 +1583,13 @@ can be overridden by an explicit field width or by the @code{_} and
The year as a decimal number, using the Gregorian calendar. Years The year as a decimal number, using the Gregorian calendar. Years
before the year @code{1} are numbered @code{0}, @code{-1}, and so on. before the year @code{1} are numbered @code{0}, @code{-1}, and so on.
If the @code{E} modifier is specified (@code{%EY}), instead produces a
complete representation of the year according to the locale's
alternative calendar. Generally this will be some combination of the
information produced by @code{%EC} and @code{Ey}. As a GNU extension,
the formatting flags @code{_} or @code{-} may be used with this
conversion specifier; they affect how the year number is printed.
@item %z @item %z
@w{RFC 822}/@w{ISO 8601:1988} style numeric time zone (e.g., @w{RFC 822}/@w{ISO 8601:1988} style numeric time zone (e.g.,
@code{-0600} or @code{+0100}), or nothing if no time zone is @code{-0600} or @code{+0100}), or nothing if no time zone is

View File

@ -43,13 +43,14 @@ tests := test_time clocktest tst-posixtz tst-strptime tst_wcsftime \
tst-getdate tst-mktime tst-mktime2 tst-ftime_l tst-strftime \ tst-getdate tst-mktime tst-mktime2 tst-ftime_l tst-strftime \
tst-mktime3 tst-strptime2 bug-asctime bug-asctime_r bug-mktime1 \ tst-mktime3 tst-strptime2 bug-asctime bug-asctime_r bug-mktime1 \
tst-strptime3 bug-getdate1 tst-strptime-whitespace tst-ftime \ tst-strptime3 bug-getdate1 tst-strptime-whitespace tst-ftime \
tst-tzname tst-y2039 bug-mktime4 tst-tzname tst-y2039 bug-mktime4 tst-strftime2
include ../Rules include ../Rules
ifeq ($(run-built-tests),yes) ifeq ($(run-built-tests),yes)
LOCALES := de_DE.ISO-8859-1 en_US.ISO-8859-1 ja_JP.EUC-JP fr_FR.UTF-8 \ LOCALES := de_DE.ISO-8859-1 en_US.ISO-8859-1 ja_JP.EUC-JP fr_FR.UTF-8 \
es_ES.UTF-8 pl_PL.UTF-8 ru_RU.UTF-8 es_ES.UTF-8 pl_PL.UTF-8 ru_RU.UTF-8 \
ja_JP.UTF-8 lo_LA.UTF-8 th_TH.UTF-8
include ../gen-locales.mk include ../gen-locales.mk
$(objpfx)tst-ftime_l.out: $(gen-locales) $(objpfx)tst-ftime_l.out: $(gen-locales)

View File

@ -434,7 +434,7 @@ static CHAR_T const month_name[][10] =
#endif #endif
static size_t __strftime_internal (CHAR_T *, size_t, const CHAR_T *, static size_t __strftime_internal (CHAR_T *, size_t, const CHAR_T *,
const struct tm *, bool * const struct tm *, int, bool *
ut_argument_spec ut_argument_spec
LOCALE_PARAM) __THROW; LOCALE_PARAM) __THROW;
@ -457,7 +457,7 @@ my_strftime (CHAR_T *s, size_t maxsize, const CHAR_T *format,
tp = &tmcopy; tp = &tmcopy;
#endif #endif
bool tzset_called = false; bool tzset_called = false;
return __strftime_internal (s, maxsize, format, tp, &tzset_called return __strftime_internal (s, maxsize, format, tp, 0, &tzset_called
ut_argument LOCALE_ARG); ut_argument LOCALE_ARG);
} }
#ifdef _LIBC #ifdef _LIBC
@ -466,7 +466,7 @@ libc_hidden_def (my_strftime)
static size_t static size_t
__strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
const struct tm *tp, bool *tzset_called const struct tm *tp, int yr_spec, bool *tzset_called
ut_argument_spec LOCALE_PARAM) ut_argument_spec LOCALE_PARAM)
{ {
#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
@ -838,11 +838,11 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
{ {
CHAR_T *old_start = p; CHAR_T *old_start = p;
size_t len = __strftime_internal (NULL, (size_t) -1, subfmt, size_t len = __strftime_internal (NULL, (size_t) -1, subfmt,
tp, tzset_called ut_argument tp, yr_spec, tzset_called
LOCALE_ARG); ut_argument LOCALE_ARG);
add (len, __strftime_internal (p, maxsize - i, subfmt, add (len, __strftime_internal (p, maxsize - i, subfmt,
tp, tzset_called ut_argument tp, yr_spec, tzset_called
LOCALE_ARG)); ut_argument LOCALE_ARG));
if (to_uppcase) if (to_uppcase)
while (old_start < p) while (old_start < p)
@ -1273,6 +1273,8 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
# else # else
subfmt = era->era_format; subfmt = era->era_format;
# endif # endif
if (pad != 0)
yr_spec = pad;
goto subformat; goto subformat;
} }
#else #else
@ -1294,6 +1296,8 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format,
if (era) if (era)
{ {
int delta = tp->tm_year - era->start_date[0]; int delta = tp->tm_year - era->start_date[0];
if (yr_spec != 0)
pad = yr_spec;
DO_NUMBER (2, (era->offset DO_NUMBER (2, (era->offset
+ delta * era->absolute_direction)); + delta * era->absolute_direction));
} }

132
time/tst-strftime2.c Normal file
View File

@ -0,0 +1,132 @@
/* Verify the behavior of strftime on alternative representation for
year.
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
<http://www.gnu.org/licenses/>. */
#include <array_length.h>
#include <locale.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
static const char *locales[] = { "ja_JP.UTF-8", "lo_LA.UTF-8", "th_TH.UTF-8" };
static const char *formats[] = { "%EY", "%_EY", "%-EY" };
static const struct
{
const int d, m, y;
} dates[] =
{
{ 1, 3, 88 },
{ 7, 0, 89 },
{ 8, 0, 89 },
{ 1, 3, 90 },
{ 1, 3, 97 },
{ 1, 3, 98 }
};
static char ref[3][3][6][100];
static void
mkreftable (void)
{
int i, j, k;
char era[10];
static const int yrj[] = { 63, 64, 1, 2, 9, 10 };
static const int yrb[] = { 2531, 2532, 2532, 2533, 2540, 2541 };
for (i = 0; i < array_length (locales); i++)
for (j = 0; j < array_length (formats); j++)
for (k = 0; k < array_length (dates); k++)
{
if (i == 0)
{
sprintf (era, "%s", (k < 2) ? "\xe6\x98\xad\xe5\x92\x8c"
: "\xe5\xb9\xb3\xe6\x88\x90");
if (yrj[k] == 1)
sprintf (ref[i][j][k], "%s\xe5\x85\x83\xe5\xb9\xb4", era);
else
{
if (j == 0)
sprintf (ref[i][j][k], "%s%02d\xe5\xb9\xb4", era, yrj[k]);
else if (j == 1)
sprintf (ref[i][j][k], "%s%2d\xe5\xb9\xb4", era, yrj[k]);
else
sprintf (ref[i][j][k], "%s%d\xe5\xb9\xb4", era, yrj[k]);
}
}
else if (i == 1)
{
sprintf (era, "\xe0\xba\x9e\x2e\xe0\xba\xaa\x2e ");
sprintf (ref[i][j][k], "%s%d", era, yrb[k]);
}
else
{
sprintf (era, "\xe0\xb8\x9e\x2e\xe0\xb8\xa8\x2e ");
sprintf (ref[i][j][k], "%s%d", era, yrb[k]);
}
}
}
static int
do_test (void)
{
int i, j, k, result = 0;
struct tm ttm;
char date[11], buf[100];
size_t r, e;
mkreftable ();
for (i = 0; i < array_length (locales); i++)
{
if (setlocale (LC_ALL, locales[i]) == NULL)
{
printf ("locale %s does not exist, skipping...\n", locales[i]);
continue;
}
printf ("[%s]\n", locales[i]);
for (j = 0; j < array_length (formats); j++)
{
for (k = 0; k < array_length (dates); k++)
{
ttm.tm_mday = dates[k].d;
ttm.tm_mon = dates[k].m;
ttm.tm_year = dates[k].y;
strftime (date, sizeof (date), "%F", &ttm);
r = strftime (buf, sizeof (buf), formats[j], &ttm);
e = strlen (ref[i][j][k]);
printf ("%s\t\"%s\"\t\"%s\"", date, formats[j], buf);
if (strcmp (buf, ref[i][j][k]) != 0)
{
printf ("\tshould be \"%s\"", ref[i][j][k]);
if (r != e)
printf ("\tgot: %zu, expected: %zu", r, e);
result = 1;
}
else
printf ("\tOK");
putchar ('\n');
}
putchar ('\n');
}
}
return result;
}
#include <support/test-driver.c>