From 284ebde202e6f6cfd60db0099daf91cda2ac6c64 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Sat, 30 Dec 2023 01:50:10 -0800
Subject: [PROPOSED] Fix localtime.c bug: America/Ciudad_Juarez in 2422

Problem reported by Gilmore Davidson in:
https://mm.icann.org/pipermail/tz/2023-December/033392.html
* NEWS: Mention this.
* localtime.c (tzparse): Use a window that is one year longer,
for the same reason zic.c uses a window like that.
* private.h (years_of_observations): Move here ...
* zic.c (outzone): ... from here.
---
 NEWS        | 14 ++++++++++++++
 localtime.c |  6 +++---
 private.h   | 12 ++++++++++++
 zic.c       | 15 ---------------
 4 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/NEWS b/NEWS
index 031ba6a8..d0cffb2b 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,19 @@
 News for the tz database
 
+Unreleased, experimental changes
+
+  Briefly:
+    localtime no longer mishandles Ciudad Juárez in 2422.
+
+  Changes to code
+
+    localtime and related functions no longer mishandle some
+    timestamps that occur about 400 years after a switch to a time
+    zone with a DST schedule.  In 2023d data this problem was visible
+    only for some November 2422 timestamps in America/Ciudad_Juarez.
+    (Problem reported by Gilmore Davidson.)
+
+
 Release 2023d - 2023-12-21 20:02:24 -0800
 
   Briefly:
diff --git a/localtime.c b/localtime.c
index c2cebfde..8837c345 100644
--- a/localtime.c
+++ b/localtime.c
@@ -1204,7 +1204,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
 			}
 
 			yearlim = yearbeg;
-			if (increment_overflow(&yearlim, YEARSPERREPEAT + 1))
+			if (increment_overflow(&yearlim, years_of_observations))
 			  yearlim = INT_MAX;
 			for (year = yearbeg; year < yearlim; year++) {
 				int_fast32_t
@@ -1241,7 +1241,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
 				if (endtime < leaplo) {
 				  yearlim = year;
 				  if (increment_overflow(&yearlim,
-							 YEARSPERREPEAT + 1))
+							 years_of_observations))
 				    yearlim = INT_MAX;
 				}
 				if (increment_overflow_time
@@ -1253,7 +1253,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
 			if (! timecnt) {
 				sp->ttis[0] = sp->ttis[1];
 				sp->typecnt = 1;	/* Perpetual DST.  */
-			} else if (YEARSPERREPEAT < year - yearbeg)
+			} else if (years_of_observations <= year - yearbeg)
 				sp->goback = sp->goahead = true;
 		} else {
 			register int_fast32_t	theirstdoffset;
diff --git a/private.h b/private.h
index 52e8b1f5..07316289 100644
--- a/private.h
+++ b/private.h
@@ -957,6 +957,18 @@ enum {
 #define SECSPERREPEAT		((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
 #define AVGSECSPERYEAR		(SECSPERREPEAT / YEARSPERREPEAT)
 
+/* How many years to generate (in zic.c) or search through (in localtime.c).
+   This is two years larger than the obvious 400, to avoid edge cases.
+   E.g., suppose a non-POSIX rule applies from 2012 on and has transitions
+   in March and September, plus one-off transitions in November 2013.
+   If zic looked only at the last 400 years, it would set max_year=2413,
+   with the intent that the 400 years 2014 through 2413 will be repeated.
+   The last transition listed in the tzfile would be in 2413-09,
+   less than 400 years after the last one-off transition in 2013-11.
+   Two years is not overkill for localtime.c, as a one-year bump
+   would mishandle 2023d's America/Ciudad_Juarez for November 2422.  */
+enum { years_of_observations = YEARSPERREPEAT + 2 };
+
 enum {
   TM_SUNDAY,
   TM_MONDAY,
diff --git a/zic.c b/zic.c
index b260851f..3f5b0094 100644
--- a/zic.c
+++ b/zic.c
@@ -3198,21 +3198,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 		}
 	}
 	if (do_extend) {
-		/*
-		** Search through a couple of extra years past the obvious
-		** 400, to avoid edge cases.  For example, suppose a non-POSIX
-		** rule applies from 2012 onwards and has transitions in March
-		** and September, plus some one-off transitions in November
-		** 2013.  If zic looked only at the last 400 years, it would
-		** set max_year=2413, with the intent that the 400 years 2014
-		** through 2413 will be repeated.  The last transition listed
-		** in the tzfile would be in 2413-09, less than 400 years
-		** after the last one-off transition in 2013-11.  Two years
-		** might be overkill, but with the kind of edge cases
-		** available we're not sure that one year would suffice.
-		*/
-		enum { years_of_observations = YEARSPERREPEAT + 2 };
-
 		if (min_year >= ZIC_MIN + years_of_observations)
 			min_year -= years_of_observations;
 		else	min_year = ZIC_MIN;
-- 
2.40.1

