>From ac4c1203c2e3f6062661503a1d7f7b05d5a590b5 Mon Sep 17 00:00:00 2001
From: Zefram <zefram@fysh.org>
Date: Sun, 14 Jul 2013 20:44:33 +0100
Subject: [PATCH] robustify the 400-year hack

This patch makes improvements to the hack whereby, for zone's whose
future behaviour cannot be expressed in the POSIX-TZ field, zic generates
400 years of explicit observations in the tzfile.  (The intention of
the hack is that if the zone operates by rules based on the Gregorian
calendar then the 400 years provides a complete repeating cycle that a
tzfile reader can apply to other 400-year cycles.)

Firstly, if the 400 years doesn't actually have any transitions then a
redundant observation is added on the end.  This provides an explicit
endpoint for the explicit observations.  (The tzfile format doesn't
include a slot to indicate the end of its validity, so the last listed
transition is the only possible cutoff for a reader that is being careful
or using the 400-year repeat trick.)

Where the zone data source lists explicit transitions up to 2037, this is
recognised as indicating that the behaviour after 2037 is not expressed
in the zone source, rather than being an explicit list of transitions
that actually ends in 2037.  zic already declines to fill the POSIX-TZ
field in this case; this behaviour is now extended to also decline to
add the redundant observation 400 years later.

Finally, the 400 years of explicit observations is extended to 402.
This avoids problems with the year of the last rule change being
contaminated by one-shot rules, which could make it an invalid example
of the cyclical rules.  It also avoids the problem of zic's internal
delimiter for the 400-year cycle, which is implicitly a year boundary,
not being visible to tzfile readers: only the transition times are
visible.  With this change, the last transition listed in the tzfile
can be correctly treated as the end of a 400-year repeatable cycle.
---
 zic.c |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 53 insertions(+), 7 deletions(-)

diff --git a/zic.c b/zic.c
index 1715c4a..c18cccc 100644
--- a/zic.c
+++ b/zic.c
@@ -1891,8 +1891,11 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
 		** presume this is a zone handled on a year-by-year basis;
 		** do not try to apply a rule to the zone.
 		*/
-		if (stdrp != NULL && stdrp->r_hiyear == 2037)
+		if (stdrp != NULL && stdrp->r_hiyear == 2037) {
+			result[0] = '!';
+			result[1] = '\0';
 			return;
+		}
 	}
 	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
 		return;
@@ -1944,6 +1947,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 	register int			max_abbr_len;
 	register int			max_envvar_len;
 	register int			prodstic; /* all rules are min to max */
+	register int			do_extend;
 
 	max_abbr_len = 2 + max_format_len + max_abbrvar_len;
 	max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -1993,6 +1997,9 @@ outzone(const struct zone * const zpfirst, const int zonecount)
 	** Generate lots of data if a rule can't cover all future times.
 	*/
 	stringzone(envvar, zpfirst, zonecount);
+	do_extend = envvar[0] == '\0';
+	if(envvar[0] == '!' && envvar[1] == '\0')
+		envvar[0] = '\0';
 	if (noise && envvar[0] == '\0') {
 		register char *	wp;
 
@@ -2002,12 +2009,12 @@ wp = ecpyalloc(_("no POSIX environment variable for zone"));
 		warning(wp);
 		free(wp);
 	}
-	if (envvar[0] == '\0') {
-		if (min_year >= ZIC_MIN + YEARSPERREPEAT)
-			min_year -= YEARSPERREPEAT;
+	if (do_extend) {
+		if (min_year >= ZIC_MIN + YEARSPERREPEAT + 2)
+			min_year -= YEARSPERREPEAT + 2;
 		else	min_year = ZIC_MIN;
-		if (max_year <= ZIC_MAX - YEARSPERREPEAT)
-			max_year += YEARSPERREPEAT;
+		if (max_year <= ZIC_MAX - YEARSPERREPEAT - 2)
+			max_year += YEARSPERREPEAT + 2;
 		else	max_year = ZIC_MAX;
 		/*
 		** Regardless of any of the above,
@@ -2017,7 +2024,7 @@ wp = ecpyalloc(_("no POSIX environment variable for zone"));
 		*/
 		if (prodstic) {
 			min_year = 1900;
-			max_year = min_year + YEARSPERREPEAT;
+			max_year = min_year + YEARSPERREPEAT + 2;
 		}
 	}
 	/*
@@ -2183,6 +2190,45 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 				starttime = tadd(starttime, -gmtoff);
 		}
 	}
+	if (do_extend) {
+		/*
+		** If we're extending the explicitly listed observations
+		** for 400 years because we can't fill the POSIX-TZ field,
+		** check whether we actually ended up explicitly listing
+		** observations through that period.  If there aren't any
+		** near the end of the 400-year period, add a redundant
+		** one at the end of the final year, to make it clear
+		** that we are claiming to have definite knowledge of
+		** the lack of transitions up to that point.
+		*/
+		struct rule xr;
+		int i;
+		struct attype *lastat;
+		xr.r_month = TM_JANUARY;
+		xr.r_dycode = DC_DOM;
+		xr.r_dayofmonth = 1;
+		xr.r_tod = 0;
+		for (lastat = &attypes[0], i = 1; i < timecnt; i++)
+			if (attypes[i].at > lastat->at)
+				lastat = &attypes[i];
+		if (lastat->at < rpytime(&xr, max_year - 1)) {
+			/*
+			** Create new type code for the redundant entry,
+			** to prevent it being optimised away.
+			*/
+			if (typecnt >= TZ_MAX_TYPES) {
+				error(_("too many local time types"));
+				exit(EXIT_FAILURE);
+			}
+			gmtoffs[typecnt] = gmtoffs[lastat->type];
+			isdsts[typecnt] = isdsts[lastat->type];
+			ttisstds[typecnt] = ttisstds[lastat->type];
+			ttisgmts[typecnt] = ttisgmts[lastat->type];
+			abbrinds[typecnt] = abbrinds[lastat->type];
+			++typecnt;
+			addtt(rpytime(&xr, max_year + 1), typecnt-1);
+		}
+	}
 	writezone(zpfirst->z_name, envvar);
 	free(startbuf);
 	free(ab);
-- 
1.7.10.4

