From: Paul Eggert Date: Sun, 16 Jun 2024 22:44:17 +0000 (-0700) Subject: nstrftime,time_rz: don’t depend on tzname X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=acb87f5090820b3a856456b9707b940aaea85d2b;p=gnulib.git nstrftime,time_rz: don’t depend on tzname * lib/strftime.c (HAVE_TZNAME_ARRAY) [_LIBC]: Remove. All uses removed. (__strftime_internal): Simplify calculation of zone to be closer to what glibc does. However, in Gnulib do not look at tzname as this is problematic for all the usual reasons; instead, use the underlying strftime with %Z, making sure that call to the underlying strftime is now always compiled in the non-glibc case. Set and revert TZ as needed around the underlying call. * lib/time-internal.h (struct tm_zone) [HAVE_TZNAME_ARRAY && !HAVE_STRUCT_TM_TM_ZONE]: Remove tzname_copy. All uses removed. (set_tz, revert_tz): Declare, as they’re now extern. (local_tz): Move here from time_rz.c, and now a macro. * lib/time_rz.c (save_abbr): Do not worry about tzname_copy, since strftime.c no longer needs it. Instead, save abbrs only from tm_zone. * modules/c-nstrftime, modules/nstrftime, modules/time_rz: (Depends-on): Remove tzname. --- diff --git a/ChangeLog b/ChangeLog index d956d9a046..b93e4b0c32 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,25 @@ 2024-06-16 Paul Eggert + nstrftime,time_rz: don’t depend on tzname + * lib/strftime.c (HAVE_TZNAME_ARRAY) [_LIBC]: Remove. + All uses removed. + (__strftime_internal): Simplify calculation of zone to be closer + to what glibc does. However, in Gnulib do not look at tzname as + this is problematic for all the usual reasons; instead, use the + underlying strftime with %Z, making sure that call to the + underlying strftime is now always compiled in the non-glibc case. + Set and revert TZ as needed around the underlying call. + * lib/time-internal.h (struct tm_zone) + [HAVE_TZNAME_ARRAY && !HAVE_STRUCT_TM_TM_ZONE]: Remove tzname_copy. + All uses removed. + (set_tz, revert_tz): Declare, as they’re now extern. + (local_tz): Move here from time_rz.c, and now a macro. + * lib/time_rz.c (save_abbr): Do not worry about tzname_copy, + since strftime.c no longer needs it. Instead, save abbrs + only from tm_zone. + * modules/c-nstrftime, modules/nstrftime, modules/time_rz: + (Depends-on): Remove tzname. + parse-datetime: don’t depend on tzname * lib/parse-datetime.y (TIME_ZONE_BUFSIZE): Move earlier. (parser_control) [!HAVE_STRUCT_TM_TM_ZONE]: diff --git a/lib/strftime.c b/lib/strftime.c index cd216fb6a5..6688c90e11 100644 --- a/lib/strftime.c +++ b/lib/strftime.c @@ -27,7 +27,6 @@ # define HAVE_STRUCT_ERA_ENTRY 1 # define HAVE_STRUCT_TM_TM_GMTOFF 1 # define HAVE_STRUCT_TM_TM_ZONE 1 -# define HAVE_TZNAME_ARRAY 1 # include "../locale/localeinfo.h" #else # include @@ -809,9 +808,9 @@ static CHAR_T const c_month_names[][sizeof "September"] = #endif -/* When compiling this file, GNU applications can #define my_strftime - to a symbol (typically nstrftime) to get an extended strftime with - extra arguments TZ and NS. */ +/* When compiling this file, Gnulib-using applications should #define + my_strftime to a symbol (typically nstrftime) to name their + extended strftime with extra arguments TZ and NS. */ #ifdef my_strftime # define extra_args , tz, ns @@ -925,9 +924,6 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) /* The English AM/PM strings happen to have the same length, namely 2. */ # define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11)) # define ap_len 2 -#endif -#if HAVE_TZNAME_ARRAY - char **tzname_vec = tzname; #endif const char *zone; size_t i = 0; @@ -946,37 +942,13 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) This is bogus: though POSIX allows bad behavior like this, POSIX does not require it. Do the right thing instead. */ zone = (const char *) tp->tm_zone; -#endif -#if HAVE_TZNAME_ARRAY +#else if (!tz) { if (! (zone && *zone)) zone = "GMT"; } - else - { -# if !HAVE_STRUCT_TM_TM_ZONE - /* Infer the zone name from *TZ instead of from TZNAME. */ - tzname_vec = tz->tzname_copy; -# endif - } - /* The tzset() call might have changed the value. */ - if (!(zone && *zone) && tp->tm_isdst >= 0) - { - /* POSIX.1 requires that local time zone information be used as - though strftime called tzset. */ -# ifndef my_strftime - if (!*tzset_called) - { - tzset (); - *tzset_called = true; - } -# endif - zone = tzname_vec[tp->tm_isdst != 0]; - } #endif - if (! zone) - zone = ""; if (hour12 > 12) hour12 -= 12; @@ -1007,6 +979,9 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) bool change_case = false; int format_char; int subwidth; +#ifndef _LIBC + bool set_and_revert_tz = false; +#endif #if DO_MULTIBYTE && !defined COMPILE_WIDE switch (*f) @@ -1327,40 +1302,56 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) } break; -#if !((defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY) || (USE_C_LOCALE && !HAVE_STRFTIME_L)) +#ifndef _LIBC underlying_strftime: { - /* The relevant information is available only via the - underlying strftime implementation, so use that. */ - char ufmt[5]; - char *u = ufmt; char ubuf[1024]; /* enough for any single format in practice */ size_t len; - /* Make sure we're calling the actual underlying strftime. - In some cases, config.h contains something like - "#define strftime rpl_strftime". */ + if (set_and_revert_tz && !tz) + { + memcpy (ubuf, " UTC", 4); + len = 4; + } + else + { + /* The relevant information is available only via the + underlying strftime implementation, so use that. */ + char ufmt[5]; + char *u = ufmt; + /* Make sure we're calling the actual underlying strftime. + In some cases, time.h contains something like + "#define strftime rpl_strftime". */ # ifdef strftime # undef strftime - size_t strftime (char *, size_t, const char *, struct tm const *); # endif - /* The space helps distinguish strftime failure from empty - output. */ - *u++ = ' '; - *u++ = '%'; - if (modifier != 0) - *u++ = modifier; - *u++ = format_char; - *u = '\0'; - -# if USE_C_LOCALE /* implies HAVE_STRFTIME_L */ - locale_t locale = c_locale (); - if (!locale) - return 0; /* errno is set here */ - len = strftime_l (ubuf, sizeof ubuf, ufmt, tp, locale); + /* The space helps distinguish strftime failure from empty + output. */ + *u++ = ' '; + *u++ = '%'; + if (modifier != 0) + *u++ = modifier; + *u++ = format_char; + *u = '\0'; + + timezone_t old_tz = NULL; + if (set_and_revert_tz) + { + old_tz = set_tz (tz); + if (!old_tz) + return 0; + } +# if USE_C_LOCALE && HAVE_STRFTIME_L + locale_t locale = c_locale (); + if (!locale) + return 0; /* errno is set here */ + len = strftime_l (ubuf, sizeof ubuf, ufmt, tp, locale); # else - len = strftime (ubuf, sizeof ubuf, ufmt, tp); + len = strftime (ubuf, sizeof ubuf, ufmt, tp); # endif + if (old_tz && !revert_tz (old_tz)) + return 0; + } if (len != 0) { # if (__GLIBC__ == 2 && __GLIBC_MINOR__ < 31) || defined __NetBSD__ || defined __sun /* glibc < 2.31, NetBSD, Solaris */ @@ -1941,6 +1932,26 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) to_lowcase = true; } + /* The tzset() call might have changed the value. */ + if (!(zone && *zone) && tp->tm_isdst >= 0) + { +#ifdef _LIBC + /* POSIX.1 requires that local time zone information be used as + though strftime called tzset. */ + if (!*tzset_called) + { + tzset (); + *tzset_called = true; + } + zone = tp->tm_isdst <= 1 ? tzname[tp->tm_isdst] : "?"; +#else + set_and_revert_tz = tz != local_tz; + goto underlying_strftime; +#endif + } + if (! zone) + zone = ""; + #ifdef COMPILE_WIDE { /* The zone string is always given in multibyte form. We have diff --git a/lib/time-internal.h b/lib/time-internal.h index 045e9e0ac7..3d778ba50e 100644 --- a/lib/time-internal.h +++ b/lib/time-internal.h @@ -17,6 +17,9 @@ /* Written by Paul Eggert. */ +/* This file is for Gnulib internal use only. + Applications should not use it. */ + /* A time zone rule. */ struct tm_zone { @@ -24,12 +27,6 @@ struct tm_zone members are zero. */ struct tm_zone *next; -#if HAVE_TZNAME_ARRAY && !HAVE_STRUCT_TM_TM_ZONE - /* Copies of recent strings taken from tzname[0] and tzname[1]. - The copies are in ABBRS, so that they survive tzset. Null if unknown. */ - char *tzname_copy[2]; -#endif - /* If nonzero, the rule represents the TZ environment variable set to the first "abbreviation" (this may be the empty string). Otherwise, it represents an unset TZ. */ @@ -41,9 +38,17 @@ struct tm_zone actually a TZ environment value) may be empty. Otherwise all strings must be nonempty. - Abbreviations are stored here because otherwise the values of - tm_zone and/or tzname would be dead after changing TZ and calling + Abbreviations are stored here even on platforms with tm_zone, because + otherwise tm_zone values would be dead after changing TZ and calling tzset. Abbreviations never move once allocated, and are live until tzfree is called. */ char abbrs[FLEXIBLE_ARRAY_MEMBER]; }; + +timezone_t set_tz (timezone_t); +bool revert_tz (timezone_t); + +/* Magic cookie timezone_t value, for local time. It differs from + NULL and from all other timezone_t values. Only the address + matters; the pointer is never dereferenced. */ +#define local_tz ((timezone_t) 1) diff --git a/lib/time_rz.c b/lib/time_rz.c index b28d55c2fd..a6523e1285 100644 --- a/lib/time_rz.c +++ b/lib/time_rz.c @@ -44,11 +44,6 @@ enum { DEFAULT_MXFAST = 64 * sizeof (size_t) / 4 }; used. */ enum { ABBR_SIZE_MIN = DEFAULT_MXFAST - offsetof (struct tm_zone, abbrs) }; -/* Magic cookie timezone_t value, for local time. It differs from - NULL and from all other timezone_t values. Only the address - matters; the pointer is never dereferenced. */ -static timezone_t const local_tz = (timezone_t) 1; - /* Copy to ABBRS the abbreviation at ABBR with size ABBR_SIZE (this includes its trailing null byte). Append an extra null byte to mark the end of ABBRS. */ @@ -70,9 +65,6 @@ tzalloc (char const *name) if (tz) { tz->next = NULL; -#if HAVE_TZNAME_ARRAY && !HAVE_STRUCT_TM_TM_ZONE - tz->tzname_copy[0] = tz->tzname_copy[1] = NULL; -#endif tz->tz_is_set = !!name; tz->abbrs[0] = '\0'; if (name) @@ -81,34 +73,16 @@ tzalloc (char const *name) return tz; } -/* Save into TZ any nontrivial time zone abbreviation used by TM, and update - *TM (if HAVE_STRUCT_TM_TM_ZONE) - or *TZ (if !HAVE_STRUCT_TM_TM_ZONE && HAVE_TZNAME_ARRAY) - if they use the abbreviation. +/* If HAVE_STRUCT_TM_TM_ZONE, save into TZ any nontrivial time zone + abbreviation used by TM, and update *TM to contain the saved abbreviation. Return true if successful, false (setting errno) otherwise. */ static bool save_abbr (timezone_t tz, struct tm *tm) { -#if HAVE_STRUCT_TM_TM_ZONE || HAVE_TZNAME_ARRAY - char const *zone = NULL; +#if HAVE_STRUCT_TM_TM_ZONE + char const *zone = tm->tm_zone; char *zone_copy = (char *) ""; -# if HAVE_TZNAME_ARRAY - int tzname_index = -1; -# endif - -# if HAVE_STRUCT_TM_TM_ZONE - zone = tm->tm_zone; -# endif - -# if HAVE_TZNAME_ARRAY - if (! (zone && *zone) && 0 <= tm->tm_isdst) - { - tzname_index = tm->tm_isdst != 0; - zone = tzname[tzname_index]; - } -# endif - /* No need to replace null zones, or zones within the struct tm. */ if (!zone || ((char *) tm <= zone && zone < (char *) (tm + 1))) return true; @@ -145,12 +119,7 @@ save_abbr (timezone_t tz, struct tm *tm) } /* Replace the zone name so that its lifetime matches that of TZ. */ -# if HAVE_STRUCT_TM_TM_ZONE tm->tm_zone = zone_copy; -# else - if (0 <= tzname_index) - tz->tzname_copy[tzname_index] = zone_copy; -# endif #endif return true; @@ -203,7 +172,7 @@ change_env (timezone_t tz) Return LOCAL_TZ if the time zone setting is already correct. Otherwise return a newly allocated time zone representing the old setting, or NULL (setting errno) on failure. */ -static timezone_t +timezone_t set_tz (timezone_t tz) { char *env_tz = getenv_TZ (); @@ -230,7 +199,7 @@ set_tz (timezone_t tz) /* Restore an old setting returned by set_tz. It must not be null. Return true (preserving errno) if successful, false (setting errno) otherwise. */ -static bool +bool revert_tz (timezone_t tz) { if (tz == local_tz) @@ -303,9 +272,7 @@ mktime_z (timezone_t tz, struct tm *tm) tm_1.tm_isdst = tm->tm_isdst; time_t t = mktime (&tm_1); bool ok = 0 <= tm_1.tm_yday; -#if HAVE_STRUCT_TM_TM_ZONE || HAVE_TZNAME_ARRAY ok = ok && save_abbr (tz, &tm_1); -#endif if (revert_tz (old_tz) && ok) { *tm = tm_1; diff --git a/modules/c-nstrftime b/modules/c-nstrftime index fdd054c10e..e737fe70d6 100644 --- a/modules/c-nstrftime +++ b/modules/c-nstrftime @@ -5,8 +5,8 @@ Files: lib/strftime.h lib/c-nstrftime.c lib/strftime.c -m4/tm_gmtoff.m4 m4/c-nstrftime.m4 +m4/tm_gmtoff.m4 Depends-on: attribute @@ -20,7 +20,6 @@ locale stdbool stdckdint time_rz -tzname configure.ac: gl_C_GNU_STRFTIME diff --git a/modules/nstrftime b/modules/nstrftime index 35ed887a70..ccdfdccd02 100644 --- a/modules/nstrftime +++ b/modules/nstrftime @@ -5,8 +5,8 @@ Files: lib/strftime.h lib/nstrftime.c lib/strftime.c -m4/tm_gmtoff.m4 m4/nstrftime.m4 +m4/tm_gmtoff.m4 Depends-on: attribute @@ -19,7 +19,6 @@ localename-unsafe-limited stdbool stdckdint time_rz -tzname configure.ac: gl_FUNC_GNU_STRFTIME diff --git a/modules/time_rz b/modules/time_rz index da621684f5..f487e24c6b 100644 --- a/modules/time_rz +++ b/modules/time_rz @@ -10,7 +10,6 @@ Depends-on: c99 extensions time-h -tzname flexmember [test $HAVE_TIMEZONE_T = 0] idx [test $HAVE_TIMEZONE_T = 0] setenv [test $HAVE_TIMEZONE_T = 0]