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;
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@elsie.nci.nih.gov> Date: Thu, 25 Aug 2011 16:32:13 +0200 From: Alois Treindl <alois@astro.ch> Reply-To: <tz@elsie.nci.nih.gov> To: Arthur David Olson <olsona@lecserver.nci.nih.gov>, <tz@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;
participants (1)
-
Alois Treindl