* 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.
2024-06-16 Paul Eggert <eggert@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]:
# 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>
#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
/* 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;
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;
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)
}
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 */
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
/* Written by Paul Eggert. */
+/* This file is for Gnulib internal use only.
+ Applications should not use it. */
+
/* A time zone rule. */
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. */
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)
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. */
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)
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;
}
/* 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;
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 ();
/* 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)
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;
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
stdbool
stdckdint
time_rz
-tzname
configure.ac:
gl_C_GNU_STRFTIME
lib/strftime.h
lib/nstrftime.c
lib/strftime.c
-m4/tm_gmtoff.m4
m4/nstrftime.m4
+m4/tm_gmtoff.m4
Depends-on:
attribute
stdbool
stdckdint
time_rz
-tzname
configure.ac:
gl_FUNC_GNU_STRFTIME
c99
extensions
time-h
-tzname
flexmember [test $HAVE_TIMEZONE_T = 0]
idx [test $HAVE_TIMEZONE_T = 0]
setenv [test $HAVE_TIMEZONE_T = 0]