[tz] [PATCH 6/7] Fix localtime bug with slim TZif files with leaps
Paul Eggert
eggert at cs.ucla.edu
Wed Mar 17 01:46:04 UTC 2021
* 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
More information about the tz
mailing list