[tz] [PROPOSED 2/4] Fix localtime bug with unusual TZif data

Paul Eggert eggert at cs.ucla.edu
Sun Oct 15 20:10:50 UTC 2023


To reproduce the bug, use the following file foo.zi:
	Rule R 2073 max  - Mar Sat<=30 2:00 1:00 S
	Rule R 2073 max  - Oct Sat<=30 2:00 0    -
	Zone Z 2:00 R EE%sT
and run ‘zic foo.zi; zdump -i -c 2072,2073 Z’.  Without the fix,
this incorrectly outputs the following two lines:
	2072-03-26	03	+03	EEST	1
	2072-10-29	01	+02	EET
even though there are no transitions in 2072.
* localtime.c (tzloadbody): Compute sp->goahead from ts->goahead.
Do not guess goahead and goback from the timestamps, as that was
just a guess that predates having TZ strings in the TZif file, and
the guess is no longer needed and sometimes guesses wrong.
(typesequiv): Remove; no longer used.
(tzparse): Last arg is now pointer to const.  This is just for
clarity; it doesn’t affect behavior.
---
 NEWS        |  4 ++++
 localtime.c | 62 ++++++++---------------------------------------------
 2 files changed, 13 insertions(+), 53 deletions(-)

diff --git a/NEWS b/NEWS
index 411935a5..44ed20f3 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ Unreleased, experimental changes
 
   Changes to code
 
+    localtime.c no longer mishandles TZif files that contain a single
+    transition into a DST regime.  Previously, it incorrectly assumed
+    DST was in effect before the transition too.
+
     zic now works again on Linux 2.6.16 and 2.6.17 (2006).
     (Problem reported by Rune Torgersen.)
 
diff --git a/localtime.c b/localtime.c
index 7cd43dc1..a54d3ae4 100644
--- a/localtime.c
+++ b/localtime.c
@@ -153,8 +153,7 @@ static int_fast32_t leapcorr(struct state const *, time_t);
 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 *, struct state *);
+static bool tzparse(char const *, struct state *, struct state const *);
 
 #ifdef ALL_STATE
 static struct state *	lclptr;
@@ -674,14 +673,18 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
 				       == sp->types[sp->timecnt - 2]))
 			      sp->timecnt--;
 
-			    for (i = 0;
-				 i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES;
-				 i++) {
+			    sp->goahead = ts->goahead;
+
+			    for (i = 0; i < ts->timecnt; i++) {
 			      time_t t = ts->ats[i];
 			      if (increment_overflow_time(&t, leapcorr(sp, t))
 				  || (0 < sp->timecnt
 				      && t <= sp->ats[sp->timecnt - 1]))
 				continue;
+			      if (TZ_MAX_TIMES <= sp->timecnt) {
+				sp->goahead = false;
+				break;
+			      }
 			      sp->ats[sp->timecnt] = t;
 			      sp->types[sp->timecnt] = (sp->typecnt
 							+ ts->types[i]);
@@ -694,28 +697,6 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
 	}
 	if (sp->typecnt == 0)
 	  return EINVAL;
-	if (sp->timecnt > 1) {
-	    if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) {
-		time_t repeatat = sp->ats[0] + SECSPERREPEAT;
-		int repeattype = sp->types[0];
-		for (i = 1; i < sp->timecnt; ++i)
-		  if (sp->ats[i] == repeatat
-		      && typesequiv(sp, sp->types[i], repeattype)) {
-					sp->goback = true;
-					break;
-		  }
-	    }
-	    if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) {
-		time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT;
-		int repeattype = sp->types[sp->timecnt - 1];
-		for (i = sp->timecnt - 2; i >= 0; --i)
-		  if (sp->ats[i] == repeatat
-		      && typesequiv(sp, sp->types[i], repeattype)) {
-					sp->goahead = true;
-					break;
-		  }
-	    }
-	}
 
 	/* Infer sp->defaulttype from the data.  Although this default
 	   type is always zero for data from recent tzdb releases,
@@ -792,31 +773,6 @@ tzload(char const *name, struct state *sp, bool doextend)
 #endif
 }
 
-static bool
-typesequiv(const struct state *sp, int a, int b)
-{
-	register bool result;
-
-	if (sp == NULL ||
-		a < 0 || a >= sp->typecnt ||
-		b < 0 || b >= sp->typecnt)
-			result = false;
-	else {
-		/* Compare the relevant members of *AP and *BP.
-		   Ignore tt_ttisstd and tt_ttisut, as they are
-		   irrelevant now and counting them could cause
-		   sp->goahead to mistakenly remain false.  */
-		register const struct ttinfo *	ap = &sp->ttis[a];
-		register const struct ttinfo *	bp = &sp->ttis[b];
-		result = (ap->tt_utoff == bp->tt_utoff
-			  && ap->tt_isdst == bp->tt_isdst
-			  && (strcmp(&sp->chars[ap->tt_desigidx],
-				     &sp->chars[bp->tt_desigidx])
-			      == 0));
-	}
-	return result;
-}
-
 static const int	mon_lengths[2][MONSPERYEAR] = {
 	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
 	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
@@ -1116,7 +1072,7 @@ transtime(const int year, register const struct rule *const rulep,
 */
 
 static bool
-tzparse(const char *name, struct state *sp, struct state *basep)
+tzparse(const char *name, struct state *sp, struct state const *basep)
 {
 	const char *			stdname;
 	const char *			dstname;
-- 
2.41.0




More information about the tz mailing list