[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