[tz] Fwd: a patch for zic.c to allow 'follow rules' - reminder

Alois Treindl alois at astro.ch
Tue Sep 10 14:28:17 UTC 2013


Russ Albery wrote:
Here, incidentally, is exactly where it would be nice to be able to link
to another entry.  Assuming for the sake of argument that we want to
retain the current pre-standardized-time behavior for Europe/Vaduz, we
could change:

Zone    Europe/Vaduz    0:38:04 -       LMT     1894 Jun
                         1:00    -       CET     1981
                         1:00    EU      CE%sT

to:

Zone    Europe/Vaduz    0:38:04 -       LMT     1894 Jun
                         => Europe/Zurich

where the "=> Europe/Zurich" syntax (that I just made up, and which
doubtless could be made much better) says to apply the rules of that zone
for the time period of that rule.  If you apply a winnowing threshold
after June of 1894, the zone would then just become a link; if you don't,
it would be a separate zone but would just mirror Europe/Zurich.

You could even potentially allow such a line to have an end date, so that
you could directly represent "followed all the rules of another zone
during ths time range" without duplicating those rules.

Without that, applying the DST corrections from 1941 to 1942 to
Europe/Vaduz as well requires copying them from Europe/Zurich, which loses
the piece of information that they're not confirmed rules specific for
Liechtenstein but rather copied rules from Europe/Zurich under the
assumption that they were probably the same.  You can put that information
back in comments, of course, but it still would be nice to be able to
represent it in the data format.
-----------

I made a similar proposal two years ago, allowing a zone file to switch 
to another from a given date, by ending it with a line in a special syntax.
	0	follow Euorpe/Zurich

This is the same as Russ Albery proposed with his syntax.
	=> Europe/Zürich

I had already written the necessary patch for zic.c (based on the latest 
code at that time, August 2011)

-------- Original Message --------
Subject: a patch for zic.c to allow 'follow rules'
Resent-Date: Thu, 25 Aug 2011 10:32:26 -0400
Resent-From: <tz at elsie.nci.nih.gov>
Date: Thu, 25 Aug 2011 16:32:13 +0200
From: Alois Treindl <alois at astro.ch>
Reply-To: <tz at elsie.nci.nih.gov>
To: Arthur David Olson <olsona at lecserver.nci.nih.gov>, 
<tz at lecserver.nci.nih.gov>

Dear Arthur,
I have been preparing a small modification for zic.c which will allow it
to understand a 'follow rule' in zone files, like this:

# experimental table for Geneva, to test the 'follow' mechanism
Zone    Europe/Geneva   0:24:37 -       LMT     1848 Sep 12
                          0:24:37 -       GET     1894 Jun # Geneva MTime
                          0       follow  Europe/Zurich

For test purposes, one can simply insert this zone in the europe file.

The feature is helpful in the extension of tzdata to cover complete
timezone history. For this purpose, about 1000 additional zone files
need to be written, and to be maintained.
The process to do so has started in the astrological community, as it
needs a complete pre-1970 timezone history.
I will not bother you with details.

I intend to help creating and maintaining a set of files like
'europe_hist', 'asia_hist' etc which contain the additional zones.

The 'follow rule' will allow to keep these extra zone files very small,
as usually the additional zones will all have a cross-over moment into a
regular tzdata zone.

I have for the moment called the modified zic source file zicx.c
and prepared a patch file for zic.c to implement the 'follow rule' feature.

I have tried to keep the modification very small.

Please have a look at the patch, and check for the errors I have
probably made.

Description of the code:
------------------------
The follow rule must be the last line in a zone, and recursion is not
allowed. It is parsed in inzsub() which reacts on GMTOFF = 0 and the
word 'follow' in the RULE field.

The zone line is stored in zones[] with a special value z_gmtoff =
FOLLOW_MARK and the zone_name to follow in field z_rule (which is abused
for this purpose).
Before the call to outzone() the two sets of zone lines are patched
together in a temporary array, so that outzone() and all functions
deeper down() need not be modified.

Patch
-----
--- zic.c       2011-03-09 20:41:27.000000000 +0100
+++ zicx.c      2011-08-25 16:09:04.000000000 +0200
@@ -262,6 +262,17 @@ static int         typecnt;
   #define YR_MAXIMUM     1
   #define YR_ONLY                2

+/*
+** follow rules, added by Alois Treindl August 2011
+** Zones may end with a line: 0  follow  zone_name
+** which the parser will represent with FOLLOW_MARK in z_gmtoff,
+** and the zone name in z_rule.
+** Before the call to outzone() the zone and its follow zone will be
+** patched together, so that outzone() sees a regular zone array.
+** To keep zone files readable, follow rules cannot be used recursively.
+*/
+#define FOLLOW_MARK    999999
+
   static struct rule *   rules;
   static int             nrules; /* number of rules */

@@ -589,7 +600,48 @@ _("%s: More than one -L option specified
                  */
                  for (j = i + 1; j < nzones && zones[j].z_name == NULL; 
++j)
                          continue;
-               outzone(&zones[i], j - i);
+               if (zones[j -1].z_gmtoff == FOLLOW_MARK) {
+                 /* copy zone and follow zone into one new array */
+                 int   k,n,m, nrows;
+                  struct zone *xzones, *zp;
+                 nrows = j - i - 1;
+                 zp = &zones[j - 1];   /* line with the follow rule */
+                 xzones = (struct zone *) (void *) emalloc((int) (nrows
* sizeof *xzones));
+                 for (k = 0; k < nrows; k++)
+                   xzones[k] = zones[i + k];
+                  /* now find the follow zone in array zones[] */
+                 for (m = 0; m < nzones; m++)  {
+                   if (zones[m].z_name != NULL && strcmp(zp->z_rule,
zones[m].z_name) == 0) break;
+                 }
+                 if (m == nzones) {  /* error, follow zone not found */
+                   char *cp;
+                   cp = emalloc(80 + strlen(zp->z_rule) +
strlen(zones[i].z_name));;
+                   sprintf(cp, "For zone %s follow zone %s not found,
no zonefile created.", zones[i].z_name, zp->z_rule);
+                   eat(zp->z_filename, zp->z_linenum);
+                   error(cp);
+                   ifree(cp);
+                   ifree((void *)xzones);
+                 }
+                 /* now find the next date in the follow zone */
+                 for (n = m + 1; n < nzones; n++) {
+                   if (zp->z_untiltime < zones[n].z_untiltime) break;
+                 }
+                 /* missing errorcode for case n == nzones,
+                  * assume we are still in same follow zone.
+                   * The follow zone part we need starts at zones[n].
+                  * Now find where follow zone ends, will be m.
+                  */
+                 for (m = n; m < nzones && zones[m].z_name == NULL; ++m)
+                   continue;
+                 xzones = (struct zone *) (void *) erealloc((char *)
xzones, (int) ((nrows + m - n) * sizeof *xzones));
+                 for (k = 0; k <  m - n; k++)
+                   xzones[nrows + k] = zones[n + k];
+                  nrows += m - n;
+                 outzone(&xzones[0], nrows);
+                 ifree((void *) xzones);
+               } else {
+                 outzone(&zones[i], j - i);
+               }
          }
          /*
          ** Make links.
@@ -784,7 +836,7 @@ associate(void)
          }
          for (i = 0; i < nzones; ++i) {
                  zp = &zones[i];
-               if (zp->z_nrules == 0) {
+               if (zp->z_nrules == 0 && zp->z_gmtoff != FOLLOW_MARK) {
                          /*
                          ** Maybe we have a local standard time offset.
                          */
@@ -1079,11 +1131,23 @@ const int               iscont;
                          return FALSE;
                  }
          }
-       z.z_rule = ecpyalloc(fields[i_rule]);
-       z.z_format = ecpyalloc(fields[i_format]);
-       if (max_format_len < strlen(z.z_format))
-               max_format_len = strlen(z.z_format);
-       hasuntil = nfields > i_untilyear;
+       /* check for 0 follow zonename
+        ** zonename must contain at least one /, and follow must be
+       * 'follow' or an abbreviation thereof.
+        */
+       if (z.z_gmtoff == 0 && strchr(fields[i_format], '/') != NULL
+       && itsabbr(fields[i_rule], "follow")) {
+               z.z_gmtoff = FOLLOW_MARK;
+               z.z_rule = ecpyalloc(fields[i_format]);
+               z.z_format = "";
+               hasuntil = FALSE;       /* no continue of zone now */
+       } else {
+               z.z_rule = ecpyalloc(fields[i_rule]);
+               z.z_format = ecpyalloc(fields[i_format]);
+               if (max_format_len < strlen(z.z_format))
+                       max_format_len = strlen(z.z_format);
+               hasuntil = nfields > i_untilyear;
+       }
          if (hasuntil) {
                  z.z_untilrule.r_filename = filename;
                  z.z_untilrule.r_linenum = linenum;





-------------- next part --------------
--- zic.c	2011-03-09 20:41:27.000000000 +0100
+++ zicx.c	2011-08-25 16:09:04.000000000 +0200
@@ -262,6 +262,17 @@ static int		typecnt;
 #define YR_MAXIMUM	1
 #define YR_ONLY		2
 
+/*
+** follow rules, added by Alois Treindl August 2011
+** Zones may end with a line: 0  follow  zone_name
+** which the parser will represent with FOLLOW_MARK in z_gmtoff,
+** and the zone name in z_rule.
+** Before the call to outzone() the zone and its follow zone will be
+** patched together, so that outzone() sees a regular zone array.
+** To keep zone files readable, follow rules cannot be used recursively.
+*/
+#define FOLLOW_MARK	999999
+
 static struct rule *	rules;
 static int		nrules;	/* number of rules */
 
@@ -589,7 +600,48 @@ _("%s: More than one -L option specified
 		*/
 		for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
 			continue;
-		outzone(&zones[i], j - i);
+		if (zones[j -1].z_gmtoff == FOLLOW_MARK) {
+		  /* copy zone and follow zone into one new array */
+		  int	k,n,m, nrows;
+                  struct zone *xzones, *zp;
+		  nrows = j - i - 1;
+		  zp = &zones[j - 1];	/* line with the follow rule */
+	          xzones = (struct zone *) (void *) emalloc((int) (nrows * sizeof *xzones));
+		  for (k = 0; k < nrows; k++)
+		    xzones[k] = zones[i + k];
+                  /* now find the follow zone in array zones[] */
+		  for (m = 0; m < nzones; m++)  {
+		    if (zones[m].z_name != NULL && strcmp(zp->z_rule, zones[m].z_name) == 0) break;
+		  }
+		  if (m == nzones) {  /* error, follow zone not found */
+		    char *cp;
+		    cp = emalloc(80 + strlen(zp->z_rule) + strlen(zones[i].z_name));;
+		    sprintf(cp, "For zone %s follow zone %s not found, no zonefile created.", zones[i].z_name, zp->z_rule);
+		    eat(zp->z_filename, zp->z_linenum);
+		    error(cp);
+		    ifree(cp);
+		    ifree((void *)xzones);
+		  }
+		  /* now find the next date in the follow zone */
+		  for (n = m + 1; n < nzones; n++) {
+		    if (zp->z_untiltime < zones[n].z_untiltime) break;
+		  }
+		  /* missing errorcode for case n == nzones,
+		   * assume we are still in same follow zone.
+                   * The follow zone part we need starts at zones[n].
+		   * Now find where follow zone ends, will be m.
+		   */
+		  for (m = n; m < nzones && zones[m].z_name == NULL; ++m)
+		    continue;
+	          xzones = (struct zone *) (void *) erealloc((char *) xzones, (int) ((nrows + m - n) * sizeof *xzones));
+		  for (k = 0; k <  m - n; k++)
+		    xzones[nrows + k] = zones[n + k];
+                  nrows += m - n;
+		  outzone(&xzones[0], nrows);
+		  ifree((void *) xzones);
+		} else {
+		  outzone(&zones[i], j - i);
+		}
 	}
 	/*
 	** Make links.
@@ -784,7 +836,7 @@ associate(void)
 	}
 	for (i = 0; i < nzones; ++i) {
 		zp = &zones[i];
-		if (zp->z_nrules == 0) {
+		if (zp->z_nrules == 0 && zp->z_gmtoff != FOLLOW_MARK) {
 			/*
 			** Maybe we have a local standard time offset.
 			*/
@@ -1079,11 +1131,23 @@ const int		iscont;
 			return FALSE;
 		}
 	}
-	z.z_rule = ecpyalloc(fields[i_rule]);
-	z.z_format = ecpyalloc(fields[i_format]);
-	if (max_format_len < strlen(z.z_format))
-		max_format_len = strlen(z.z_format);
-	hasuntil = nfields > i_untilyear;
+	/* check for 0 follow zonename
+        ** zonename must contain at least one /, and follow must be
+	* 'follow' or an abbreviation thereof.
+	 */
+	if (z.z_gmtoff == 0 && strchr(fields[i_format], '/') != NULL
+	&& itsabbr(fields[i_rule], "follow")) {
+		z.z_gmtoff = FOLLOW_MARK;
+		z.z_rule = ecpyalloc(fields[i_format]);
+		z.z_format = "";
+		hasuntil = FALSE;	/* no continue of zone now */
+	} else {
+		z.z_rule = ecpyalloc(fields[i_rule]);
+		z.z_format = ecpyalloc(fields[i_format]);
+		if (max_format_len < strlen(z.z_format))
+			max_format_len = strlen(z.z_format);
+		hasuntil = nfields > i_untilyear;
+	}
 	if (hasuntil) {
 		z.z_untilrule.r_filename = filename;
 		z.z_untilrule.r_linenum = linenum;


More information about the tz mailing list