* NEWS: Mention this. * localtime.c (tzparse): New arg PARSELB specifying a lower bound for the desired transitions. Use this so that extending a table by interpreting a TZif file’s TZ string will go for the needed 400 years from the last explicit transition. All callers changed. --- NEWS | 5 +++++ localtime.c | 45 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 2e6b443..559e96e 100644 --- a/NEWS +++ b/NEWS @@ -66,6 +66,11 @@ Unreleased, experimental changes set to a POSIX-conforming but unusual TZ string like "EST5EDT4,0/0,J365/0", where almost all the year is DST. + Fix yet another bug that caused 'localtime' etc. to mishandle slim + TZif files containing leap seconds after the last explicit + transition in the table, or when handling far-future timestamps + in slim TZif files lacking leap seconds. + Fix an unlikely bug that caused 'localtime' etc. to misbehave if civil time changes a few seconds before time_t wraps around, when leap seconds are enabled. diff --git a/localtime.c b/localtime.c index aab806a..a18d130 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 *); +static bool tzparse(char const *, struct state *, time_t); #ifdef ALL_STATE static struct state * lclptr; @@ -596,9 +596,15 @@ 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)) { + if (tzparse(&up->buf[1], ts, parselb)) { /* Attempt to reuse existing abbreviations. Without this, America/Anchorage would be right on @@ -1064,7 +1070,7 @@ transtime(const int year, register const struct rule *const rulep, */ static bool -tzparse(const char *name, struct state *sp) +tzparse(const char *name, struct state *sp, time_t parselb) { const char * stdname; const char * dstname; @@ -1129,11 +1135,10 @@ tzparse(const char *name, struct state *sp) struct rule start; struct rule end; register int year; - register int yearlim; register int timecnt; time_t janfirst; int_fast32_t janoffset = 0; - int yearbeg; + int yearbeg, yearlim; ++name; if ((name = getrule(name, &start)) == NULL) @@ -1163,9 +1168,25 @@ tzparse(const char *name, struct state *sp) janoffset = -yearsecs; break; } - } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + } while (parselb < janfirst + && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); - yearlim = yearbeg + YEARSPERREPEAT + 1; + while (true) { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg)] * SECSPERDAY; + int yearbeg1 = yearbeg; + time_t janfirst1 = janfirst; + if (increment_overflow_time(&janfirst1, yearsecs) + || increment_overflow(&yearbeg1, 1) + || parselb <= janfirst1) + break; + yearbeg = yearbeg1; + janfirst = janfirst1; + } + + yearlim = yearbeg; + if (increment_overflow(&yearlim, YEARSPERREPEAT + 1)) + yearlim = INT_MAX; for (year = yearbeg; year < yearlim; year++) { int_fast32_t starttime = transtime(year, &start, stdoffset), @@ -1187,12 +1208,14 @@ tzparse(const char *name, struct state *sp) sp->ats[timecnt] = janfirst; if (! increment_overflow_time (&sp->ats[timecnt], - janoffset + starttime)) + janoffset + starttime) + && parselb <= sp->ats[timecnt]) sp->types[timecnt++] = !reversed; sp->ats[timecnt] = janfirst; if (! increment_overflow_time (&sp->ats[timecnt], - janoffset + endtime)) { + janoffset + endtime) + && parselb <= sp->ats[timecnt]) { sp->types[timecnt++] = reversed; } } @@ -1312,7 +1335,7 @@ static void gmtload(struct state *const sp) { if (tzload(gmt, sp, true) != 0) - tzparse("GMT0", sp); + tzparse("GMT0", sp, TIME_T_MIN); } /* Initialize *SP to a value appropriate for the TZ setting NAME. @@ -1335,7 +1358,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)) + if (err != 0 && name && name[0] != ':' && tzparse(name, sp, TIME_T_MIN)) err = 0; if (err == 0) scrub_abbrs(sp); -- 2.27.0