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:
13
ChangeLog
13
ChangeLog
@@ -1,5 +1,18 @@
|
|||||||
2024-06-16 Paul Eggert <eggert@cs.ucla.edu>
|
2024-06-16 Paul Eggert <eggert@cs.ucla.edu>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
time_r: refactor tm_zone tests
|
time_r: refactor tm_zone tests
|
||||||
* m4/tm_gmtoff.m4 (gl_TM_GMTOFF): Also check for tm_zone
|
* m4/tm_gmtoff.m4 (gl_TM_GMTOFF): Also check for tm_zone
|
||||||
and define HAVE_STRUCT_TM_TM_ZONE accordingly.
|
and define HAVE_STRUCT_TM_TM_ZONE accordingly.
|
||||||
|
@@ -144,6 +144,9 @@ typedef struct
|
|||||||
/* Meridian: am, pm, or 24-hour style. */
|
/* Meridian: am, pm, or 24-hour style. */
|
||||||
enum { MERam, MERpm, MER24 };
|
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. */
|
/* A reasonable upper bound for the buffer used in debug output. */
|
||||||
enum { DBGBUFSIZE = 100 };
|
enum { DBGBUFSIZE = 100 };
|
||||||
|
|
||||||
@@ -230,6 +233,11 @@ typedef struct
|
|||||||
|
|
||||||
/* Table of local time zone abbreviations, terminated by a null entry. */
|
/* Table of local time zone abbreviations, terminated by a null entry. */
|
||||||
table local_time_zone_table[3];
|
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;
|
} parser_control;
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -386,8 +394,6 @@ str_days (parser_control *pc, char *buffer, int n)
|
|||||||
|
|
||||||
/* Convert a time zone to its string representation. */
|
/* Convert a time zone to its string representation. */
|
||||||
|
|
||||||
enum { TIME_ZONE_BUFSIZE = INT_STRLEN_BOUND (intmax_t) + sizeof ":MM:SS" } ;
|
|
||||||
|
|
||||||
static char const *
|
static char const *
|
||||||
time_zone_str (int time_zone, char time_zone_buf[TIME_ZONE_BUFSIZE])
|
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));
|
| (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
|
/* Debugging: format a 'struct tm' into a buffer, taking the parser's
|
||||||
timezone information into account (if pc != NULL). */
|
timezone information into account (if pc != NULL). */
|
||||||
static char const *
|
static char const *
|
||||||
@@ -1833,61 +1866,37 @@ parse_datetime_body (struct timespec *result, char const *p,
|
|||||||
pc.debug_year_seen = false;
|
pc.debug_year_seen = false;
|
||||||
pc.debug_ordinal_day_seen = false;
|
pc.debug_ordinal_day_seen = false;
|
||||||
|
|
||||||
#if HAVE_STRUCT_TM_TM_ZONE
|
pc.local_time_zone_table[0].name = NULL;
|
||||||
pc.local_time_zone_table[0].name = tmp.tm_zone;
|
populate_local_time_zone_table (&pc, &tmp);
|
||||||
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;
|
|
||||||
|
|
||||||
/* Probe the names used in the next three calendar quarters, looking
|
/* Probe the names used in the next three calendar quarters, looking
|
||||||
for a tm_isdst different from the one we already have. */
|
for a tm_isdst different from the one we already have. */
|
||||||
{
|
for (int quarter = 1; quarter <= 3; quarter++)
|
||||||
int quarter;
|
|
||||||
for (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)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
pc.local_time_zone_table[1].name))
|
|
||||||
{
|
{
|
||||||
/* This locale uses the same abbreviation for standard and
|
time_t probe;
|
||||||
daylight times. So if we see that abbreviation, we don't
|
if (ckd_add (&probe, Start, quarter * (90 * 24 * 60 * 60)))
|
||||||
know whether it's daylight time. */
|
break;
|
||||||
pc.local_time_zone_table[0].value = -1;
|
struct tm probe_tm;
|
||||||
pc.local_time_zone_table[1].name = NULL;
|
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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
daylight times. So if we see that abbreviation, we don't
|
||||||
|
know whether it's daylight time. */
|
||||||
|
pc.local_time_zone_table[0].value = -1;
|
||||||
|
pc.local_time_zone_table[1].name = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (yyparse (&pc) != 0)
|
if (yyparse (&pc) != 0)
|
||||||
|
@@ -5,8 +5,8 @@ Files:
|
|||||||
doc/parse-datetime.texi
|
doc/parse-datetime.texi
|
||||||
lib/parse-datetime.h
|
lib/parse-datetime.h
|
||||||
lib/parse-datetime.y
|
lib/parse-datetime.y
|
||||||
m4/tm_gmtoff.m4
|
|
||||||
m4/parse-datetime.m4
|
m4/parse-datetime.m4
|
||||||
|
m4/tm_gmtoff.m4
|
||||||
|
|
||||||
Depends-on:
|
Depends-on:
|
||||||
assert-h
|
assert-h
|
||||||
@@ -28,7 +28,6 @@ time-h
|
|||||||
time_r
|
time_r
|
||||||
time_rz
|
time_rz
|
||||||
timegm
|
timegm
|
||||||
tzname
|
|
||||||
|
|
||||||
configure.ac:
|
configure.ac:
|
||||||
gl_PARSE_DATETIME
|
gl_PARSE_DATETIME
|
||||||
|
Reference in New Issue
Block a user