[tz] [PROPOSED] Support SAVE suffixes, for Namibia 1994-2017

Paul Eggert eggert at cs.ucla.edu
Sun Apr 8 01:21:03 UTC 2018


[Changing the Subject: line from [PROPOSED] Namibia was on DST until 2017-10-24"".]

Michael H Deckers via tz wrote:
>      See [www.lac.org.na/laws/1994/811.pdf].
> 
>      So both summer and winter time are called
>      "standard" (which differs from the use in Ireland),

Thanks, good catch. So: not only was the proposed patch incorrect for Namibia, 
tzdb has been wrong since tzdata1997i, since it said that Namibian summer time 
was daylight saving time, and this wasn't correct either.

zic does not have a convenient notation for Namibia's timekeeping practices in 
1994-2017, when standard time was changed twice per year in a regular pattern. 
Although tzdb can explicitly list each transition, that's awkward and 
error-prone; it's better to use Rule lines. Attached is a proposed patch to do 
that, by slightly extending the syntax of Rule lines so that an entry in their 
SAVE columns can have an 's' suffix if you want the corresponding rule to 
specify standard time. For completeness, the new 'd' suffix specifies daylight 
saving time.

Since this is an extension to zic input format, it's a vanguard feature, which 
means that the patch causes the new feature to be used in vanguard.zi but not in 
the traditional 'africa' file. We will wait a while before migrating it into 
'africa'. As usual, people can test their tzdata parsers on bleeding-edge-format 
data by running the parsers on 'vanguard.zi'.
-------------- next part --------------
From c1c0c110f820e21433603e46d81f05eaa9169430 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Sat, 7 Apr 2018 17:51:07 -0700
Subject: [PROPOSED] Support SAVE suffixes, for Namibia 1994-2017
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

From 1994 through 2017, Nambia officially alternated between two
standard times, and did not observe daylight saving time.
(Problem reported by Michael Deckers.)  Add a new feature to zic,
so that this behavior can be expressed compactly in Rule lines.
For now, use the new feature only in vanguard format.  In the main
and rearguard formats, explicitly list two transitions for every
year affected instead.
* NEWS, zic.8: Document this.
* africa (Namibia): Use this rule only in vanguard format, and use
the new ‘s’ suffix so that winter and summer time are both standard.
(Africa/Windhoek): In vanguard format, simply use the Namibia rule.
Otherwise, explicitly list each transition from 1994 through 2017.
* zic.c (struct rule): New member r_isdst.
All uses of r_stdoff changed to use this member instead of
assuming that nonzero r_stdoff iff daylight-saving.
(inrule): Parse new SAVE suffixes.
(doabbr): New isdst arg.  All uses changed.
(stringzone): Initialize r_isdst too.
* ziguard.awk (Zone_using_Namibia_rule):
Adjust to ‘africa’ changes.
---
 NEWS        | 23 +++++++++++++++---
 africa      | 81 +++++++++++++++++++++++++++++++++++++++++++++++++------------
 zic.8       | 20 ++++++++++-----
 zic.c       | 40 +++++++++++++++++++++---------
 ziguard.awk |  8 +++---
 5 files changed, 131 insertions(+), 41 deletions(-)

diff --git a/NEWS b/NEWS
index 3bbb829..13197f5 100644
--- a/NEWS
+++ b/NEWS
@@ -2,12 +2,27 @@ News for the tz database
 
 Unreleased, experimental changes
 
+  Briefly:
+
+    New 's' and 'd' suffixes in SAVE columns of Rule lines.
+
+  Changes to data format and to code
+
+    The SAVE column in Rule lines can now be followed by an 's' or 'd'
+    suffix, which specifies whether the adjusted time is standard time
+    or daylight saving time.  If no suffix is given, daylight saving
+    time is used if and only if the SAVE column is nonzero; this is
+    the longstanding behavior.  This new feature can be used to
+    represent Namibia's 1994-2017 timekeeping, as described below.
+
   Changes to past time stamps
 
-    For Namibia's transition from +01/+02 to plain +02, change the
-    transition date from 2017-09-03 at 03:00 to 2017-10-24 at 00:00.
-    This does not affect UTC offsets, only whether a DST regime was
-    in place.
+    From 1994 through 2017 Nambia did not observe DST.  Instead, it
+    changed standard time twice per year, and summer and winter time
+    were both considered standard.  This change does not affect UTC
+    offsets; it affects only the tm_isdst flag and the abbreviation
+    that was used during summer, which is now CAT rather than WAST.
+    (Problem reported by Michael Deckers.)
 
 
 Release 2018d - 2018-03-22 07:05:46 -0700
diff --git a/africa b/africa
index e564906..88dc2a5 100644
--- a/africa
+++ b/africa
@@ -967,6 +967,10 @@ Link Africa/Maputo Africa/Lusaka	# Zambia
 # commence at OOhOO on Monday 21 March 1994 and shall end at 02h00 on
 # Sunday 4 September 1994.
 
+# From Michael Deckers (2017-04-06):
+# ... both summer and winter time are called "standard"
+# (which differs from the use in Ireland) ...
+
 # From Petronella Sibeene (2007-03-30):
 # http://allafrica.com/stories/200703300178.html
 # While the entire country changes its time, Katima Mulilo and other
@@ -992,34 +996,79 @@ Link Africa/Maputo Africa/Lusaka	# Zambia
 # the same time they would normally start DST, the first Sunday in September:
 # https://www.timeanddate.com/news/time/namibia-new-time-zone.html
 
-# From Paul Eggert (2017-04-05):
-# The official date of the time zone change was 2017-10-24.  See:
+# From Paul Eggert (2017-04-07):
+# The official date of the rule change was 2017-10-24.  See:
 # http://www.lac.org.na/laws/annoSTAT/Namibian%20Time%20Act%209%20of%202017.pdf
 
+# Vanguard section, for newer tzdata parsers that support 's' suffixes
+# in the SAVE columns of Rule lines.
 # RULE	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
-# The following has standard time in summer and negative daylight
-# saving time in winter.  It is for when negative SAVE values are used.
-# For more about this, see the comments in 'europe' for Rule Eire.
-#Rule	Namibia	1994	only	-	Mar	21	0:00	-1:00	+01
+#Rule	Namibia	1994	only	-	Mar	21	0:00	-1:00s	WAT
 #Rule	Namibia	1994	2017	-	Sep	Sun>=1	2:00	0	CAT
-#Rule	Namibia	1995	2017	-	Apr	Sun>=1	2:00	-1:00	+01
-# The following is for when SAVE values are always nonnegative.
-Rule	Namibia	1994	only	-	Mar	21	0:00	0	-
-Rule	Namibia	1994	2017	-	Sep	Sun>=1	2:00	1:00	S
-Rule	Namibia	1995	2017	-	Apr	Sun>=1	2:00	0	-
+#Rule	Namibia	1995	2017	-	Apr	Sun>=1	2:00	-1:00s	WAT
+# End of vanguard section.
+
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Africa/Windhoek	1:08:24 -	LMT	1892 Feb 8
 			1:30	-	+0130	1903 Mar
 			2:00	-	SAST	1942 Sep 20  2:00
 			2:00	1:00	SAST	1943 Mar 21  2:00
 			2:00	-	SAST	1990 Mar 21 # independence
+# Vanguard section, for newer tzdata parsers that support 's' suffixes
+# in the SAVE columns of Rule lines.
+#			2:00	Namibia	%s
+# Rearguard section, for older tzdata parsers that do not support 's'
+# suffixes in the SAVE columns of Rule lines.
 			2:00	-	CAT	1994 Mar 21  0:00
-# The next line is for when negative SAVE values are used.
-#			2:00	Namibia	%s	2017 Oct 24
-# The next line is for when SAVE values are always nonnegative.
-			1:00	Namibia	WA%sT   2017 Oct 24
-# End of daylight saving.
+			1:00	-	WAT	1994 Sep Sun>=1 2:00
+			2:00	-	CAT	1995 Apr Sun>=1 2:00
+			1:00	-	WAT	1995 Sep Sun>=1 2:00
+			2:00	-	CAT	1996 Apr Sun>=1 2:00
+			1:00	-	WAT	1996 Sep Sun>=1 2:00
+			2:00	-	CAT	1997 Apr Sun>=1 2:00
+			1:00	-	WAT	1997 Sep Sun>=1 2:00
+			2:00	-	CAT	1998 Apr Sun>=1 2:00
+			1:00	-	WAT	1998 Sep Sun>=1 2:00
+			2:00	-	CAT	1999 Apr Sun>=1 2:00
+			1:00	-	WAT	1999 Sep Sun>=1 2:00
+			2:00	-	CAT	2000 Apr Sun>=1 2:00
+			1:00	-	WAT	2000 Sep Sun>=1 2:00
+			2:00	-	CAT	2001 Apr Sun>=1 2:00
+			1:00	-	WAT	2001 Sep Sun>=1 2:00
+			2:00	-	CAT	2002 Apr Sun>=1 2:00
+			1:00	-	WAT	2002 Sep Sun>=1 2:00
+			2:00	-	CAT	2003 Apr Sun>=1 2:00
+			1:00	-	WAT	2003 Sep Sun>=1 2:00
+			2:00	-	CAT	2004 Apr Sun>=1 2:00
+			1:00	-	WAT	2004 Sep Sun>=1 2:00
+			2:00	-	CAT	2005 Apr Sun>=1 2:00
+			1:00	-	WAT	2005 Sep Sun>=1 2:00
+			2:00	-	CAT	2006 Apr Sun>=1 2:00
+			1:00	-	WAT	2006 Sep Sun>=1 2:00
+			2:00	-	CAT	2007 Apr Sun>=1 2:00
+			1:00	-	WAT	2007 Sep Sun>=1 2:00
+			2:00	-	CAT	2008 Apr Sun>=1 2:00
+			1:00	-	WAT	2008 Sep Sun>=1 2:00
+			2:00	-	CAT	2009 Apr Sun>=1 2:00
+			1:00	-	WAT	2009 Sep Sun>=1 2:00
+			2:00	-	CAT	2010 Apr Sun>=1 2:00
+			1:00	-	WAT	2010 Sep Sun>=1 2:00
+			2:00	-	CAT	2011 Apr Sun>=1 2:00
+			1:00	-	WAT	2011 Sep Sun>=1 2:00
+			2:00	-	CAT	2012 Apr Sun>=1 2:00
+			1:00	-	WAT	2012 Sep Sun>=1 2:00
+			2:00	-	CAT	2013 Apr Sun>=1 2:00
+			1:00	-	WAT	2013 Sep Sun>=1 2:00
+			2:00	-	CAT	2014 Apr Sun>=1 2:00
+			1:00	-	WAT	2014 Sep Sun>=1 2:00
+			2:00	-	CAT	2015 Apr Sun>=1 2:00
+			1:00	-	WAT	2015 Sep Sun>=1 2:00
+			2:00	-	CAT	2016 Apr Sun>=1 2:00
+			1:00	-	WAT	2016 Sep Sun>=1 2:00
+			2:00	-	CAT	2017 Apr Sun>=1 2:00
+			1:00	-	WAT	2017 Sep Sun>=1 2:00
 			2:00	-	CAT
+# End of rearguard section.
 
 # Niger
 # See Africa/Lagos.
diff --git a/zic.8 b/zic.8
index 634bc84..4251868 100644
--- a/zic.8
+++ b/zic.8
@@ -165,14 +165,14 @@ abbreviation must be unambiguous in context.
 A rule line has the form
 .nf
 .ti +.5i
-.ta \w'Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
+.ta \w'Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00s\0\0'u +\w'1:00d\0\0'u
 .sp
 Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 .sp
 For example:
 .ti +.5i
 .sp
-Rule	US	1967	1973	\*-	Apr	lastSun	2:00	1:00	D
+Rule	US	1967	1973	\*-	Apr	lastSun	2:00s	1:00d	D
 .sp
 .fi
 The fields that make up a rule line are:
@@ -295,16 +295,24 @@ field would show the specified date and time of day.
 .TP
 .B SAVE
 Gives the amount of time to be added to local standard time when the rule is in
-effect.
+effect, and whether the resulting time is standard or daylight saving.
 This field has the same format as the
 .B AT
 field
-(although, of course, the
-suffixes are not used).
+except with a different set of suffix letters:
+.B s
+for standard time and
+.B d
+for daylight saving time.
+The suffix letter is typically omitted, and defaults to
+.B s
+if the offset is zero and to
+.B d
+otherwise.
 Negative offsets are allowed; in Ireland, for example, daylight saving
 time is observed in winter and has a negative offset relative to
 Irish Standard Time.
-Only the sum of standard time and this amount matters; for example,
+The offset is merely added to standard time; for example,
 .I zic
 does not distinguish a 10:30 standard time plus an 0:30
 .B SAVE
diff --git a/zic.c b/zic.c
index 6ad31f9..d271bfc 100644
--- a/zic.c
+++ b/zic.c
@@ -89,7 +89,9 @@ struct rule {
 					/* or wall clock time if 0 */
 	bool		r_todisgmt;	/* above is GMT if 1 */
 					/* or local time if 0 */
-	zic_t		r_stdoff;	/* offset from standard time */
+	bool		r_isdst;	/* is this daylight saving time? */
+	zic_t		r_stdoff;	/* offset from default time (which is
+					   usually standard time) */
 	const char *	r_abbrvar;	/* variable part of abbreviation */
 
 	bool		r_todo;		/* a rule to do (used in outzone) */
@@ -1252,6 +1254,7 @@ static void
 inrule(char **fields, int nfields)
 {
 	static struct rule	r;
+	int isdst = -1;
 
 	if (nfields != RULE_FIELDS) {
 		error(_("wrong number of fields on Rule line"));
@@ -1263,7 +1266,15 @@ inrule(char **fields, int nfields)
 	}
 	r.r_filename = filename;
 	r.r_linenum = linenum;
+	if (fields[RF_STDOFF][0]) {
+	  char *ep = fields[RF_STDOFF] + strlen(fields[RF_STDOFF]) - 1;
+	  switch (*ep) {
+	    case 'd': isdst = 1; *ep = '\0'; break;
+	    case 's': isdst = 0; *ep = '\0'; break;
+	  }
+	}
 	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
+	r.r_isdst = isdst < 0 ? r.r_stdoff != 0 : isdst;
 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
 	r.r_name = ecpyalloc(fields[RF_NAME]);
@@ -2111,7 +2122,7 @@ abbroffset(char *buf, zic_t offset)
 
 static size_t
 doabbr(char *abbr, struct zone const *zp, char const *letters,
-       zic_t stdoff, bool doquotes)
+       bool isdst, zic_t stdoff, bool doquotes)
 {
 	register char *	cp;
 	register char *	slashp;
@@ -2126,7 +2137,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
 	  else if (!letters)
 	    letters = "%s";
 	  sprintf(abbr, format, letters);
-	} else if (stdoff != 0) {
+	} else if (isdst) {
 		strcpy(abbr, slashp + 1);
 	} else {
 		memcpy(abbr, format, slashp - format);
@@ -2237,7 +2248,7 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
 	}
 	if (rp->r_todisgmt)
 		tod += gmtoff;
-	if (rp->r_todisstd && rp->r_stdoff == 0)
+	if (rp->r_todisstd && !rp->r_isdst)
 		tod += dstoff;
 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
 		*result++ = '/';
@@ -2294,7 +2305,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 			continue;
 		if (rp->r_yrtype != NULL)
 			continue;
-		if (rp->r_stdoff == 0) {
+		if (!rp->r_isdst) {
 			if (stdrp == NULL)
 				stdrp = rp;
 			else	return -1;
@@ -2313,7 +2324,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 		register struct rule *stdabbrrp = NULL;
 		for (i = 0; i < zp->z_nrules; ++i) {
 			rp = &zp->z_rules[i];
-			if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
+			if (!rp->r_isdst && rule_cmp(stdabbrrp, rp) < 0)
 				stdabbrrp = rp;
 			if (rule_cmp(stdrp, rp) < 0)
 				stdrp = rp;
@@ -2326,13 +2337,14 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 		if (stdrp != NULL && stdrp->r_hiyear == 2037)
 			return YEAR_BY_YEAR_ZONE;
 
-		if (stdrp != NULL && stdrp->r_stdoff != 0) {
+		if (stdrp != NULL && stdrp->r_isdst) {
 			/* Perpetual DST.  */
 			dstr.r_month = TM_JANUARY;
 			dstr.r_dycode = DC_DOM;
 			dstr.r_dayofmonth = 1;
 			dstr.r_tod = 0;
 			dstr.r_todisstd = dstr.r_todisgmt = false;
+			dstr.r_isdst = stdrp->r_isdst;
 			dstr.r_stdoff = stdrp->r_stdoff;
 			dstr.r_abbrvar = stdrp->r_abbrvar;
 			stdr.r_month = TM_DECEMBER;
@@ -2340,6 +2352,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 			stdr.r_dayofmonth = 31;
 			stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
 			stdr.r_todisstd = stdr.r_todisgmt = false;
+			stdr.r_isdst = false;
 			stdr.r_stdoff = 0;
 			stdr.r_abbrvar
 			  = (stdabbrrp ? stdabbrrp->r_abbrvar : "");
@@ -2350,7 +2363,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
 		return -1;
 	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
-	len = doabbr(result, zp, abbrvar, 0, true);
+	len = doabbr(result, zp, abbrvar, false, 0, true);
 	offsetlen = stringoffset(result + len, -zp->z_gmtoff);
 	if (! offsetlen) {
 		result[0] = '\0';
@@ -2359,7 +2372,8 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 	len += offsetlen;
 	if (dstrp == NULL)
 		return compat;
-	len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true);
+	len += doabbr(result + len, zp, dstrp->r_abbrvar,
+		      dstrp->r_isdst, dstrp->r_stdoff, true);
 	if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) {
 	  offsetlen = stringoffset(result + len,
 				   -(zp->z_gmtoff + dstrp->r_stdoff));
@@ -2535,7 +2549,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 		startoff = zp->z_gmtoff;
 		if (zp->z_nrules == 0) {
 			stdoff = zp->z_stdoff;
-			doabbr(startbuf, zp, NULL, stdoff, false);
+			doabbr(startbuf, zp, NULL, stdoff != 0, stdoff, false);
 			type = addtype(oadd(zp->z_gmtoff, stdoff),
 				startbuf, stdoff != 0, startttisstd,
 				startttisgmt);
@@ -2633,6 +2647,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 							stdoff);
 						doabbr(startbuf, zp,
 							rp->r_abbrvar,
+							rp->r_isdst,
 							rp->r_stdoff,
 							false);
 						continue;
@@ -2643,6 +2658,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 							doabbr(startbuf,
 								zp,
 								rp->r_abbrvar,
+								rp->r_isdst,
 								rp->r_stdoff,
 								false);
 					}
@@ -2650,9 +2666,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 				eats(zp->z_filename, zp->z_linenum,
 					rp->r_filename, rp->r_linenum);
 				doabbr(ab, zp, rp->r_abbrvar,
-				       rp->r_stdoff, false);
+				       rp->r_isdst, rp->r_stdoff, false);
 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
-				type = addtype(offset, ab, rp->r_stdoff != 0,
+				type = addtype(offset, ab, rp->r_isdst,
 					rp->r_todisstd, rp->r_todisgmt);
 				if (rp->r_hiyear == ZIC_MAX
 				    && ! (0 <= lastatmax
diff --git a/ziguard.awk b/ziguard.awk
index 710b630..5bfaebb 100644
--- a/ziguard.awk
+++ b/ziguard.awk
@@ -44,12 +44,14 @@ outfile != "main.zi" {
     }
   }
 
-  # If this line should differ due to Namibia using negative SAVE values,
+  # If this line should differ due to Namibia using Rule SAVE suffixes,
   # uncomment the desired version and comment out the undesired one.
   Rule_Namibia = /^#?Rule[\t ]+Namibia[\t ]/
   Zone_using_Namibia_rule \
-    = (zone == "Africa/Windhoek" && /^#?[\t ]+[12]:00[\t ]/ \
-       && $(in_comment + 2) == "Namibia")
+    = (zone == "Africa/Windhoek" \
+       && ($(in_comment + 2) == "Namibia" \
+	   || (1994 <= $(in_comment + 4) && $(in_comment + 4) <= 2017) \
+	   || in_comment + 3 == NF))
   if (Rule_Namibia || Zone_using_Namibia_rule) {
     if (in_comment == vanguard) {
       uncomment = in_comment
-- 
2.7.4



More information about the tz mailing list