[tz] [PROPOSED 3/4] Fix zic bug with Palestine after 2075

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


The bug can be observed when processing the following .zi data,
adapted from the current ‘asia’ file:
	Rule Palestine 2075 max  - Mar Sat<=30 2:00 1:00 S
	Rule Palestine 2075 max  - Oct Sat<=30 2:00    0 -
	Rule Palestine 2076 only - Jul      25 2:00    0 -
	Rule Palestine 2076 only - Sep       5 2:00 1:00 S
	Zone Asia/Gaza 2:00 -         EET   2012
	               2:00 Palestine EE%sT
Without the fix, zic generates an incorrect TZif file, in which
the special-case 2076 transitions are omitted.  This causes ‘zdump
-ic 2076,2077 Asia/Gaza’ to mistakenly omit the lines:
	2076-07-25	01	+02	EET
	2076-09-05	03	+03	EEST	1
* zic.c (outzone): Redo algorithm to work even when the effect of
a Rule that never ends (TO="max") is interspersed with the effect
of a one-shot rule (TO="only").
---
 NEWS  |  4 ++++
 zic.c | 57 +++++++++++++++++++++++++++++++++++++++------------------
 2 files changed, 43 insertions(+), 18 deletions(-)

diff --git a/NEWS b/NEWS
index 44ed20f3..115aff7b 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,10 @@ Unreleased, experimental changes
     transition into a DST regime.  Previously, it incorrectly assumed
     DST was in effect before the transition too.
 
+    zic no longer mishandles data for Palestine after the year 2075.
+    Previously, it incorrectly omitted post-2075 transitions that are
+    predicted for just before and just after Ramadan.
+
     zic now works again on Linux 2.6.16 and 2.6.17 (2006).
     (Problem reported by Rune Torgersen.)
 
diff --git a/zic.c b/zic.c
index 4293637d..38685f62 100644
--- a/zic.c
+++ b/zic.c
@@ -2980,6 +2980,10 @@ rule_cmp(struct rule const *a, struct rule const *b)
 	return a->r_dayofmonth - b->r_dayofmonth;
 }
 
+/* Store into RESULT a POSIX TZ string that represent the future
+   predictions for the zone ZPFIRST with ZONECOUNT entries.  Return a
+   compatibility indicator (a TZDB release year) if successful, a
+   negative integer if no such TZ string exissts.  */
 static int
 stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 {
@@ -3119,7 +3123,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 	register int			compat;
 	register bool			do_extend;
 	register char			version;
-	ptrdiff_t lastatmax = -1;
+	zic_t nonTZlimtime = ZIC_MIN;
+	int nonTZlimtype = -1;
 	zic_t max_year0;
 	int defaulttype = -1;
 
@@ -3235,7 +3240,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 	  unspecifiedtype = addtype(0, "-00", false, false, false);
 
 	for (i = 0; i < zonecount; ++i) {
-		struct rule *prevrp = NULL;
 		/*
 		** A guess that may well be corrected later.
 		*/
@@ -3245,8 +3249,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 		bool useuntil = i < (zonecount - 1);
 		zic_t stdoff = zp->z_stdoff;
 		zic_t startoff = stdoff;
-		zic_t prevktime;
-		INITIALIZE(prevktime);
 		if (useuntil && zp->z_untiltime <= min_time)
 			continue;
 		eat(zp->z_filenum, zp->z_linenum);
@@ -3260,6 +3262,10 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 				startttisut);
 			if (usestart) {
 				addtt(starttime, type);
+				if (useuntil && nonTZlimtime < starttime) {
+				  nonTZlimtime = starttime;
+				  nonTZlimtype = type;
+				}
 				usestart = false;
 			} else
 				defaulttype = type;
@@ -3387,23 +3393,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 				doabbr(ab, zp, rp->r_abbrvar,
 				       rp->r_isdst, rp->r_save, false);
 				offset = oadd(zp->z_stdoff, rp->r_save);
-				if (!want_bloat() && !useuntil && !do_extend
-				    && prevrp && lo_time <= prevktime
-				    && redundant_time <= ktime
-				    && rp->r_hiyear == ZIC_MAX
-				    && prevrp->r_hiyear == ZIC_MAX)
-				  break;
 				type = addtype(offset, ab, rp->r_isdst,
 					rp->r_todisstd, rp->r_todisut);
 				if (defaulttype < 0 && !rp->r_isdst)
 				  defaulttype = type;
-				if (rp->r_hiyear == ZIC_MAX
-				    && ! (0 <= lastatmax
-					  && ktime < attypes[lastatmax].at))
-				  lastatmax = timecnt;
 				addtt(ktime, type);
-				prevrp = rp;
-				prevktime = ktime;
+				if (nonTZlimtime < ktime
+				    && (useuntil || rp->r_hiyear != ZIC_MAX)) {
+				  nonTZlimtime = ktime;
+				  nonTZlimtype = type;
+				}
 			}
 		  }
 		}
@@ -3438,8 +3437,30 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 	}
 	if (defaulttype < 0)
 	  defaulttype = 0;
-	if (0 <= lastatmax)
-	  attypes[lastatmax].dontmerge = true;
+	if (!do_extend && !want_bloat()) {
+	  /* The earliest transition into a time governed by the TZ string.  */
+	  zic_t TZstarttime = ZIC_MAX;
+	  for (i = 0; i < timecnt; i++) {
+	    zic_t at = attypes[i].at;
+	    if (nonTZlimtime < at && at < TZstarttime)
+	      TZstarttime = at;
+	  }
+	  if (TZstarttime == ZIC_MAX)
+	    TZstarttime = nonTZlimtime;
+
+	  /* Omit trailing transitions deducible from the TZ string.  */
+	  for (i = j = 0; i < timecnt; i++)
+	    if (redundant_time <= attypes[i].at
+		&& attypes[i].at <= TZstarttime) {
+	      attypes[j].at = attypes[i].at;
+	      attypes[j].dontmerge = (attypes[i].at == TZstarttime
+				      && (nonTZlimtype != attypes[i].type
+					  || strchr(envvar, ',')));
+	      attypes[j].type = attypes[i].type;
+	      j++;
+	    }
+	  timecnt = j;
+	}
 	if (do_extend) {
 		/*
 		** If we're extending the explicitly listed observations
-- 
2.41.0




More information about the tz mailing list