1
0
mirror of https://git.savannah.gnu.org/git/gnulib.git synced 2025-08-08 17:22:05 +03:00

parse-datetime: don’t depend on tzname

* lib/parse-datetime.y (TIME_ZONE_BUFSIZE): Move earlier.
(parser_control) [!HAVE_STRUCT_TM_TM_ZONE]:
New member tz_abbr, to save abbrs calculated by strftime %Z.
(populate_local_time_zone_table): New function, which
optimizes the HAVE_STRUCT_TM_TM_ZONE case as before,
and falls back on strftime with %Z otherwise.
Although strftime %Z can be more accurate than the old tzname
based method, the new heuristic is still wrong so often that it
probably doesn’t help all that much.
(parse_datetime_body): Use it.
* modules/parse-datetime (Depends-on): Remove tzname.
This commit is contained in:
Paul Eggert
2024-06-16 14:58:56 -07:00
parent d320360f87
commit 75f19d5efb
3 changed files with 76 additions and 55 deletions

View File

@@ -1,5 +1,18 @@
2024-06-16 Paul Eggert <eggert@cs.ucla.edu>
parse-datetime: dont depend on tzname
* lib/parse-datetime.y (TIME_ZONE_BUFSIZE): Move earlier.
(parser_control) [!HAVE_STRUCT_TM_TM_ZONE]:
New member tz_abbr, to save abbrs calculated by strftime %Z.
(populate_local_time_zone_table): New function, which
optimizes the HAVE_STRUCT_TM_TM_ZONE case as before,
and falls back on strftime with %Z otherwise.
Although strftime %Z can be more accurate than the old tzname
based method, the new heuristic is still wrong so often that it
probably doesnt help all that much.
(parse_datetime_body): Use it.
* modules/parse-datetime (Depends-on): Remove tzname.
time_r: refactor tm_zone tests
* m4/tm_gmtoff.m4 (gl_TM_GMTOFF): Also check for tm_zone
and define HAVE_STRUCT_TM_TM_ZONE accordingly.

View File

@@ -144,6 +144,9 @@ typedef struct
/* Meridian: am, pm, or 24-hour style. */
enum { MERam, MERpm, MER24 };
/* Maximum length of a time zone abbreviation, plus 1. */
enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" };
/* A reasonable upper bound for the buffer used in debug output. */
enum { DBGBUFSIZE = 100 };
@@ -230,6 +233,11 @@ typedef struct
/* Table of local time zone abbreviations, terminated by a null entry. */
table local_time_zone_table[3];
#if !HAVE_STRUCT_TM_TM_ZONE
/* The abbreviations in LOCAL_TIME_ZONE_TABLE. */
char tz_abbr[2][TIME_ZONE_BUFSIZE];
#endif
} parser_control;
static bool
@@ -386,8 +394,6 @@ str_days (parser_control *pc, char *buffer, int n)
/* Convert a time zone to its string representation. */
enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" } ;
static char const *
time_zone_str (int time_zone, char time_zone_buf[TIME_ZONE_BUFSIZE])
{
@@ -1565,6 +1571,33 @@ mktime_ok (struct tm const *tm0, struct tm const *tm1)
| (tm0->tm_year ^ tm1->tm_year));
}
/* Populate PC's local time zone table with information from TM. */
static void
populate_local_time_zone_table (parser_control *pc, struct tm const *tm)
{
bool first_entry_exists = !!pc->local_time_zone_table[0].name;
/* The table entry to be filled in. There are only two, so this is
the first entry if it is missing, the second entry otherwise. */
table *e = &pc->local_time_zone_table[first_entry_exists];
e->type = tLOCAL_ZONE;
e->value = tm->tm_isdst;
char const *zone = NULL;
#if HAVE_STRUCT_TM_TM_ZONE
if (tm->tm_zone[0])
zone = tm->tm_zone;
#else
char *tz_abbr = pc->tz_abbr[first_entry_exists];
if (nstrftime (tz_abbr, TIME_ZONE_BUFSIZE, "%Z", tm, 0, 0))
zone = tz_abbr;
#endif
e->name = zone;
e[1].name = NULL;
}
/* Debugging: format a 'struct tm' into a buffer, taking the parser's
timezone information into account (if pc != NULL). */
static char const *
@@ -1833,54 +1866,25 @@ parse_datetime_body (struct timespec *result, char const *p,
pc.debug_year_seen = false;
pc.debug_ordinal_day_seen = false;
#if HAVE_STRUCT_TM_TM_ZONE
pc.local_time_zone_table[0].name = tmp.tm_zone;
pc.local_time_zone_table[0].type = tLOCAL_ZONE;
pc.local_time_zone_table[0].value = tmp.tm_isdst;
pc.local_time_zone_table[1].name = NULL;
pc.local_time_zone_table[0].name = NULL;
populate_local_time_zone_table (&pc, &tmp);
/* Probe the names used in the next three calendar quarters, looking
for a tm_isdst different from the one we already have. */
{
int quarter;
for (quarter = 1; quarter <= 3; quarter++)
for (int quarter = 1; quarter <= 3; quarter++)
{
time_t probe;
if (ckd_add (&probe, Start, quarter * (90 * 24 * 60 * 60)))
break;
struct tm probe_tm;
if (localtime_rz (tz, &probe, &probe_tm) && probe_tm.tm_zone
&& probe_tm.tm_isdst != pc.local_time_zone_table[0].value)
if (localtime_rz (tz, &probe, &probe_tm)
&& (! pc.local_time_zone_table[0].name
|| probe_tm.tm_isdst != pc.local_time_zone_table[0].value))
{
populate_local_time_zone_table (&pc, &probe_tm);
if (pc.local_time_zone_table[1].name)
{
pc.local_time_zone_table[1].name = probe_tm.tm_zone;
pc.local_time_zone_table[1].type = tLOCAL_ZONE;
pc.local_time_zone_table[1].value = probe_tm.tm_isdst;
pc.local_time_zone_table[2].name = NULL;
}
break;
}
}
}
#else
#if HAVE_TZNAME_ARRAY
{
int i;
for (i = 0; i < 2; i++)
{
pc.local_time_zone_table[i].name = tzname[i];
pc.local_time_zone_table[i].type = tLOCAL_ZONE;
pc.local_time_zone_table[i].value = i;
}
pc.local_time_zone_table[i].name = NULL;
}
#else
pc.local_time_zone_table[0].name = NULL;
#endif
#endif
if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name
&& ! strcmp (pc.local_time_zone_table[0].name,
if (! strcmp (pc.local_time_zone_table[0].name,
pc.local_time_zone_table[1].name))
{
/* This locale uses the same abbreviation for standard and
@@ -1890,6 +1894,11 @@ parse_datetime_body (struct timespec *result, char const *p,
pc.local_time_zone_table[1].name = NULL;
}
break;
}
}
}
if (yyparse (&pc) != 0)
{
if (debugging (&pc))

View File

@@ -5,8 +5,8 @@ Files:
doc/parse-datetime.texi
lib/parse-datetime.h
lib/parse-datetime.y
m4/tm_gmtoff.m4
m4/parse-datetime.m4
m4/tm_gmtoff.m4
Depends-on:
assert-h
@@ -28,7 +28,6 @@ time-h
time_r
time_rz
timegm
tzname
configure.ac:
gl_PARSE_DATETIME