* 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.
---
 ChangeLog           |  20 +++++++
 lib/strftime.c      | 123 ++++++++++++++++++++++++--------------------
 lib/time-internal.h |  21 +++++---
 lib/time_rz.c       |  45 +++-------------
 modules/c-nstrftime |   3 +-
 modules/nstrftime   |   3 +-
 modules/time_rz     |   1 -
 7 files changed, 108 insertions(+), 108 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d956d9a046..b93e4b0c32 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
 2024-06-16  Paul Eggert  <egg...@cs.ucla.edu>
 
+       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 <libc-config.h>
@@ -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]
-- 
2.43.0


Reply via email to