[tz] [PATCH 5/8] Improve fix for localtime bug with slim leaps
Paul Eggert
eggert at cs.ucla.edu
Wed Mar 24 03:58:50 UTC 2021
* localtime.c (tzparse): Replace arg PARSELB with arg BASEP,
and do the parselb calculations ourselves rather than
relying on the caller. All callers changed. Use a different
bound for timezone transitions than for leap seconds, since
we need the former to decide how to merge a TZif file's TZ
string transitions, and we also need the latter to know when
to stop extending the transition table. This fixes a bug
when the last leap second is after the last explicit
timezone transition.
---
localtime.c | 47 +++++++++++++++++++++++++++++------------------
1 file changed, 29 insertions(+), 18 deletions(-)
diff --git a/localtime.c b/localtime.c
index a18d130..f37280e 100644
--- a/localtime.c
+++ b/localtime.c
@@ -155,7 +155,7 @@ static bool normalize_overflow32(int_fast32_t *, int *, int);
static struct tm *timesub(time_t const *, int_fast32_t, struct state const *,
struct tm *);
static bool typesequiv(struct state const *, int, int);
-static bool tzparse(char const *, struct state *, time_t);
+static bool tzparse(char const *, struct state *, struct state *);
#ifdef ALL_STATE
static struct state * lclptr;
@@ -596,15 +596,9 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
sp->typecnt + 2 <= TZ_MAX_TYPES) {
struct state *ts = &lsp->u.st;
- time_t parselb = TIME_T_MIN;
- if (0 < sp->timecnt)
- parselb = sp->ats[sp->timecnt - 1];
- if (0 < sp->leapcnt
- && parselb < sp->lsis[sp->leapcnt - 1].ls_trans)
- parselb = sp->lsis[sp->leapcnt - 1].ls_trans;
up->buf[nread - 1] = '\0';
- if (tzparse(&up->buf[1], ts, parselb)) {
+ if (tzparse(&up->buf[1], ts, sp)) {
/* Attempt to reuse existing abbreviations.
Without this, America/Anchorage would be right on
@@ -1070,7 +1064,7 @@ transtime(const int year, register const struct rule *const rulep,
*/
static bool
-tzparse(const char *name, struct state *sp, time_t parselb)
+tzparse(const char *name, struct state *sp, struct state *basep)
{
const char * stdname;
const char * dstname;
@@ -1081,6 +1075,7 @@ tzparse(const char *name, struct state *sp, time_t parselb)
int_fast32_t dstoffset;
register char * cp;
register bool load_ok;
+ time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
stdname = name;
if (*name == '<') {
@@ -1103,9 +1098,19 @@ tzparse(const char *name, struct state *sp, time_t parselb)
charcnt = stdlen + 1;
if (sizeof sp->chars < charcnt)
return false;
- load_ok = tzload(TZDEFRULES, sp, false) == 0;
- if (!load_ok)
- sp->leapcnt = 0; /* so, we're off a little */
+ if (basep) {
+ if (0 < basep->timecnt)
+ atlo = basep->ats[basep->timecnt - 1];
+ load_ok = false;
+ sp->leapcnt = basep->leapcnt;
+ memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis);
+ } else {
+ load_ok = tzload(TZDEFRULES, sp, false) == 0;
+ if (!load_ok)
+ sp->leapcnt = 0; /* So, we're off a little. */
+ }
+ if (0 < sp->leapcnt)
+ leaplo = sp->lsis[sp->leapcnt - 1].ls_trans;
if (*name != '\0') {
if (*name == '<') {
dstname = ++name;
@@ -1168,7 +1173,7 @@ tzparse(const char *name, struct state *sp, time_t parselb)
janoffset = -yearsecs;
break;
}
- } while (parselb < janfirst
+ } while (atlo < janfirst
&& EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
while (true) {
@@ -1178,7 +1183,7 @@ tzparse(const char *name, struct state *sp, time_t parselb)
time_t janfirst1 = janfirst;
if (increment_overflow_time(&janfirst1, yearsecs)
|| increment_overflow(&yearbeg1, 1)
- || parselb <= janfirst1)
+ || atlo <= janfirst1)
break;
yearbeg = yearbeg1;
janfirst = janfirst1;
@@ -1209,16 +1214,22 @@ tzparse(const char *name, struct state *sp, time_t parselb)
if (! increment_overflow_time
(&sp->ats[timecnt],
janoffset + starttime)
- && parselb <= sp->ats[timecnt])
+ && atlo <= sp->ats[timecnt])
sp->types[timecnt++] = !reversed;
sp->ats[timecnt] = janfirst;
if (! increment_overflow_time
(&sp->ats[timecnt],
janoffset + endtime)
- && parselb <= sp->ats[timecnt]) {
+ && atlo <= sp->ats[timecnt]) {
sp->types[timecnt++] = reversed;
}
}
+ if (endtime < leaplo) {
+ yearlim = year;
+ if (increment_overflow(&yearlim,
+ YEARSPERREPEAT + 1))
+ yearlim = INT_MAX;
+ }
if (increment_overflow_time
(&janfirst, janoffset + yearsecs))
break;
@@ -1335,7 +1346,7 @@ static void
gmtload(struct state *const sp)
{
if (tzload(gmt, sp, true) != 0)
- tzparse("GMT0", sp, TIME_T_MIN);
+ tzparse("GMT0", sp, NULL);
}
/* Initialize *SP to a value appropriate for the TZ setting NAME.
@@ -1358,7 +1369,7 @@ zoneinit(struct state *sp, char const *name)
return 0;
} else {
int err = tzload(name, sp, true);
- if (err != 0 && name && name[0] != ':' && tzparse(name, sp, TIME_T_MIN))
+ if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
err = 0;
if (err == 0)
scrub_abbrs(sp);
--
2.27.0
More information about the tz
mailing list