From patchwork Wed Apr 17 08:30:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: TAMUKI Shoichi X-Patchwork-Id: 1086846 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=sourceware.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=libc-alpha-return-101458-incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linet.gr.jp Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b="E68Ssw1c"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 44kb6l5NF9z9s4Y for ; Wed, 17 Apr 2019 18:31:11 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:message-id:from:date:to:subject:mime-version :content-type; q=dns; s=default; b=notwzCM+TOFzGDVBVFCIG1YRt80TQ CMFU6A/Xnsdr6c/haqUJtwtBlSEl0qhx2cc5WZ2ClTKx5uiMCwuVmav7kSpppsaJ p8L9gerIhTcva4ACyYfMKV9x4JTkoQCGqpLzZbmX6JyuR6H5+JhStp5QHnsUYK30 0j76+q6E2Z7Nz0= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:message-id:from:date:to:subject:mime-version :content-type; s=default; bh=3S9+wa0wDbiattm4iWVCBgxfS0c=; b=E68 Ssw1cV6Ibe2nStih7PQiO8g0d52c5znUrLjCPcPzNxuWmfRVxutopT/BbQ/t54po AhiChCs7wFW1DD2gsB1UxTzojNfsJnsQ/giRkQSip0cW1zUxmh1BZXln+jiUMUTz +Kd0zyMrguo8+ig7ycMMJkcgnv3gYh2LkIU2uH04= Received: (qmail 75951 invoked by alias); 17 Apr 2019 08:31:04 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 75941 invoked by uid 89); 17 Apr 2019 08:31:04 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-20.0 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 spammy=chinese, Chinese, era, japanese X-HELO: mail.linet.jp Message-Id: <201904170830.AA04338@tamuki.linet.gr.jp> From: TAMUKI Shoichi Date: Wed, 17 Apr 2019 17:30:26 +0900 To: libc-alpha@sourceware.org Subject: [PATCH] strftime: Allow use of both 'E' and 'O' like "%OEy", "%OEY" [BZ #24453] MIME-Version: 1.0 As a GNU extension, both the 'E' and 'O' modifiers can now be applied simultaneously like "%OEy" and "%OEY". The full representation of the alternative calendar year with alternative numeric symbols (%OEY) typically includes an internal use of "%Ey". Apply "%OEY" to the internal "%Ey", allowing users of "%OEY" to control how the year number is represented. This change applies to all locales. For locales that use era and alt_digits, such as ja_JP and lzh_TW, this feature should be useful. The variable "modifier" in __strftime_internal function now represents four states: 'E', 'O', "E with O", or 0 (neither 'E' nor 'O'). Furthermore, the newly added MODIFIER_E and MODIFIER_O macros help to reduce conditional statement changes using "modifier" variable. Tested on x86_64. ChangeLog: [BZ #24453] * manual/time.texi (strftime): Document "%Ey" and "%EY". * time/strftime_l.c (__strftime_internal): Allow both the 'E' and 'O' modifiers to be applied simultaneously to conversion specifications. (MODIFIER_E, MODIFIER_O): New macros. If a modifier ('O') is specified to "%EY", interpret the "%Ey" in the subformat as if decorated with that modifier. * time/tst-strftime2.c (formats): Add "%OEY". (mkreftable): Add new variables "alt_yr", "alt_yrj", and "alt_yrc" to represent Japanese era and R.O.C. calendar year in Chinese numeral to be checked. --- NEWS | 5 +++ manual/time.texi | 12 +++++++ time/strftime_l.c | 93 ++++++++++++++++++++++++++-------------------------- time/tst-strftime2.c | 39 ++++++++++++++++++---- 4 files changed, 96 insertions(+), 53 deletions(-) diff --git a/NEWS b/NEWS index b58e2469d4..e7e15d0bc5 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,11 @@ Major new features: * The entry for the new Japanese era has been added for ja_JP locale. +* As a GNU extension, both the 'E' and 'O' modifiers can now be + applied simultaneously to "%Y" (%OEY) to produce the alternative + calendar year with alternative numeric symbols; they have the same + effect that they would on "%OEy". + Deprecated and removed features, and other changes affecting compatibility: * The functions clock_gettime, clock_getres, clock_settime, diff --git a/manual/time.texi b/manual/time.texi index bfa46dd45b..a5ff92dabc 100644 --- a/manual/time.texi +++ b/manual/time.texi @@ -1579,6 +1579,13 @@ However, by default it is zero-padded to a minimum of two digits (this can be overridden by an explicit field width or by the @code{_} and @code{-} flags). +As a GNU extension, if both the @code{E} and @code{O} modifiers are +specified (@code{%OEy}), instead produces the year number according to +locale-specific alternative calendar with alternative numeric symbols. +Generally, since the alternative numeric symbols are defined from 0 to +99, when the calendar year number is more than 100, produces the +ordinary number as a fallback. + @item %Y 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. @@ -1590,6 +1597,11 @@ 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. +As a GNU extension, if both the @code{E} and @code{O} modifiers are +specified (@code{%OEY}), instead produces a complete representation of +the year according to the locale's alternative calendar with +alternative numeric symbols. + @item %z @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 diff --git a/time/strftime_l.c b/time/strftime_l.c index 98c35d58a2..abdcf90789 100644 --- a/time/strftime_l.c +++ b/time/strftime_l.c @@ -710,17 +710,11 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, } /* Check for modifiers. */ - switch (*f) - { - case L_('E'): - case L_('O'): - modifier = *f++; - break; - - default: - modifier = 0; - break; - } + modifier = 0; + while (*f == L_('E') || *f == L_('O')) + modifier |= (*f++ == L_('E')) ? L_('E') : L_('O') << 8; +#define MODIFIER_E (modifier & 0xff) +#define MODIFIER_O (modifier >> 8 & 0xff) /* Now do the specified format. */ format_char = *f; @@ -786,10 +780,10 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, to_uppcase = 1; to_lowcase = 0; } - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; #if defined _NL_CURRENT || !HAVE_STRFTIME - if (modifier == L_('O')) + if (MODIFIER_O == L_('O')) cpy (aam_len, a_altmonth); else cpy (am_len, a_month); @@ -799,7 +793,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif case L_('B'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; if (change_case) { @@ -807,7 +801,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, to_lowcase = 0; } #if defined _NL_CURRENT || !HAVE_STRFTIME - if (modifier == L_('O')) + if (MODIFIER_O == L_('O')) cpy (STRLEN (f_altmonth), f_altmonth); else cpy (STRLEN (f_month), f_month); @@ -817,10 +811,10 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif case L_('c'): - if (modifier == L_('O')) + if (MODIFIER_O == L_('O')) goto bad_format; #ifdef _NL_CURRENT - if (! (modifier == L_('E') + if (! (MODIFIER_E == L_('E') && (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_T_FMT))) @@ -858,7 +852,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, { /* The relevant information is available only via the underlying strftime implementation, so use that. */ - char ufmt[4]; + char ufmt[5]; char *u = ufmt; char ubuf[1024]; /* enough for any single format in practice */ size_t len; @@ -871,8 +865,10 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, # endif *u++ = '%'; - if (modifier != 0) - *u++ = modifier; + if (MODIFIER_O == L_('O')) + *u++ = 'O'; + if (MODIFIER_E == L_('E')) + *u++ = 'E'; *u++ = format_char; *u = '\0'; len = strftime (ubuf, sizeof ubuf, ufmt, tp); @@ -884,7 +880,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif case L_('C'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) { #if HAVE_STRUCT_ERA_ENTRY struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); @@ -912,10 +908,10 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, } case L_('x'): - if (modifier == L_('O')) + if (MODIFIER_O == L_('O')) goto bad_format; #ifdef _NL_CURRENT - if (! (modifier == L_('E') + if (! (MODIFIER_E == L_('E') && (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_D_FMT))) != L_('\0')))) @@ -935,13 +931,13 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, goto subformat; case L_('d'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, tp->tm_mday); case L_('e'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER_SPACEPAD (2, tp->tm_mday); @@ -957,7 +953,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, do_number: /* Format the number according to the MODIFIER flag. */ - if (modifier == L_('O') && 0 <= number_value) + if (MODIFIER_O == L_('O') && 0 <= number_value) { #ifdef _NL_CURRENT /* Get the locale specific alternate representation of @@ -1047,43 +1043,43 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, goto subformat; case L_('H'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, tp->tm_hour); case L_('I'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, hour12); case L_('k'): /* GNU extension. */ - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER_SPACEPAD (2, tp->tm_hour); case L_('l'): /* GNU extension. */ - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER_SPACEPAD (2, hour12); case L_('j'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (3, 1 + tp->tm_yday); case L_('M'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, tp->tm_min); case L_('m'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, tp->tm_mon + 1); @@ -1130,7 +1126,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, #endif case L_('S'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, tp->tm_sec); @@ -1175,10 +1171,10 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, } case L_('X'): - if (modifier == L_('O')) + if (MODIFIER_O == L_('O')) goto bad_format; #ifdef _NL_CURRENT - if (! (modifier == L_('E') + if (! (MODIFIER_E == L_('E') && (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT))) != L_('\0')))) @@ -1203,7 +1199,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1); case L_('U'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7); @@ -1211,7 +1207,7 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, case L_('V'): case L_('g'): case L_('G'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; { int year = tp->tm_year + TM_YEAR_BASE; @@ -1250,19 +1246,19 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, } case L_('W'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7); case L_('w'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) goto bad_format; DO_NUMBER (1, tp->tm_wday); case L_('Y'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) { #if HAVE_STRUCT_ERA_ENTRY struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); @@ -1273,8 +1269,8 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, # else subfmt = era->era_format; # endif - if (pad != 0) - yr_spec = pad; + if (pad != 0 || MODIFIER_O == L_('O')) + yr_spec = (MODIFIER_O == L_('O')) ? L_('O') : pad; goto subformat; } #else @@ -1283,13 +1279,13 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, # endif #endif } - if (modifier == L_('O')) + if (MODIFIER_O == L_('O')) goto bad_format; else DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE); case L_('y'): - if (modifier == L_('E')) + if (MODIFIER_E == L_('E')) { #if HAVE_STRUCT_ERA_ENTRY struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG); @@ -1297,7 +1293,12 @@ __strftime_internal (CHAR_T *s, size_t maxsize, const CHAR_T *format, { int delta = tp->tm_year - era->start_date[0]; if (yr_spec != 0) - pad = yr_spec; + { + if (yr_spec == L_('O')) + modifier |= yr_spec << 8; + else + pad = yr_spec; + } DO_NUMBER (2, (era->offset + delta * era->absolute_direction)); } diff --git a/time/tst-strftime2.c b/time/tst-strftime2.c index 18dbf1b32f..ce3b50e320 100644 --- a/time/tst-strftime2.c +++ b/time/tst-strftime2.c @@ -41,7 +41,7 @@ enum zh_TW, cmn_TW, hak_TW, nan_TW, lzh_TW }; -static const char *formats[] = { "%EY", "%_EY", "%-EY" }; +static const char *formats[] = { "%EY", "%_EY", "%-EY", "%OEY" }; typedef struct { @@ -88,13 +88,23 @@ static void mkreftable (void) { int i, j, k, yr; - const char *era, *sfx; + const char *era, *alt_yr, *sfx; /* Japanese era year to be checked. */ static const int yrj[] = { 43, 44, 45, 2, 63, 64, 1, 2, 9, 10, 22, 23, 31, 1 }; + /* Japanese era year in Chinese numeral to be checked. */ + static const char *alt_yrj[] = + { + "\u56db\u5341\u4e09", "\u56db\u5341\u56db", + "\u56db\u5341\u4e94", "\u4e8c", + "\u516d\u5341\u4e09", "\u516d\u5341\u56db", + "", "\u4e8c", "\u4e5d", "\u5341", + "\u4e8c\u5341\u4e8c", "\u4e8c\u5341\u4e09", + "\u4e09\u5341\u4e00", "" + }; /* Buddhist calendar year to be checked. */ static const int yrb[] = { @@ -108,6 +118,14 @@ mkreftable (void) -2, -1, 1, 2, 77, 78, 78, 79, 86, 87, 99, 100, 108, 108 }; + /* R.O.C. calendar year in Chinese numeral to be checked. */ + static const char *alt_yrc[] = + { + "\u4e8c", "\u4e00", "", "\u4e8c", + "\u4e03\u5341\u4e03", "\u4e03\u5341\u516b", "\u4e03\u5341\u516b", + "\u4e03\u5341\u4e5d", "\u516b\u5341\u516d", "\u516b\u5341\u4e03", + "\u4e5d\u5341\u4e5d", "", "", "" + }; for (i = 0; i < array_length (locales); i++) for (j = 0; j < array_length (formats); j++) @@ -120,29 +138,36 @@ mkreftable (void) : (is_before (k, 8, 1, 1989)) ? "\u662d\u548c" : (is_before (k, 1, 5, 2019)) ? "\u5e73\u6210" : "\u4ee4\u548c"; - yr = yrj[k], sfx = "\u5e74"; + yr = yrj[k], alt_yr = alt_yrj[k], sfx = "\u5e74"; } else if (i == lo_LA) - era = "\u0e9e.\u0eaa. ", yr = yrb[k], sfx = ""; + era = "\u0e9e.\u0eaa. ", yr = yrb[k], alt_yr = sfx = ""; else if (i == th_TH) - era = "\u0e1e.\u0e28. ", yr = yrb[k], sfx = ""; + era = "\u0e1e.\u0e28. ", yr = yrb[k], alt_yr = sfx = ""; else if (i == zh_TW || i == cmn_TW || i == hak_TW || i == nan_TW || i == lzh_TW) { era = (is_before (k, 1, 1, 1912)) ? "\u6c11\u524d" : "\u6c11\u570b"; - yr = yrc[k], sfx = "\u5e74"; + yr = yrc[k], alt_yr = alt_yrc[k], sfx = "\u5e74"; } else FAIL_EXIT1 ("Invalid table index!"); if (yr == 1) sprintf (ref[i][j][k], "%s\u5143%s", era, sfx); - else if (j == 0) + else if (j == 0 || (j == 3 + && ((i != ja_JP && i != lzh_TW) || abs (yr) > 99 + /* Currently, alt_digits in lzh_TW are defined up + to 31, so generating Chinese numerals is + temporarily limited to this range. */ + || (i == lzh_TW && abs (yr) > 31)))) sprintf (ref[i][j][k], "%s%02d%s", era, abs (yr), sfx); else if (j == 1) sprintf (ref[i][j][k], "%s%2d%s", era, abs (yr), sfx); else if (j == 2) sprintf (ref[i][j][k], "%s%d%s", era, abs (yr), sfx); + else if (j == 3) + sprintf (ref[i][j][k], "%s%s%s", era, alt_yr, sfx); else FAIL_EXIT1 ("Invalid table index!"); }