[tz] [PATCH 2/2] Bump tzfile format to version 3.
Paul Eggert
eggert at cs.ucla.edu
Mon Sep 9 06:17:58 UTC 2013
Also, improve the documentation and diagnostics in this area.
Suggested by Arthur David Olson in
<http://mm.icann.org/pipermail/tz/2013-September/020064.html>.
* tzfile.5, tzfile.h: Bump tzfile format to version 3.
* zic.8: Document -v better.
* zic.c (ZIC_VERSION): Bump from '2' to '3'.
(stringrule, stringzone, outzone): Report compatibility issues
more carefully, mentioning client dates.
---
tzfile.5 | 9 +++++----
tzfile.h | 9 ++++++++-
zic.8 | 38 ++++++++++++++++++++++++++++++-----
zic.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++------------------
4 files changed, 97 insertions(+), 29 deletions(-)
diff --git a/tzfile.5 b/tzfile.5
index b2d1a4d..ff1ec63 100644
--- a/tzfile.5
+++ b/tzfile.5
@@ -10,7 +10,7 @@ The time zone information files used by
begin with the magic characters "TZif" to identify them as
time zone information files,
followed by a character identifying the version of the file's format
-(as of 2005, either an ASCII NUL or a '2')
+(as of 2013, either an ASCII NUL, or '2', or '3')
followed by fifteen bytes containing zeroes reserved for future use,
followed by six four-byte values of type
.BR long ,
@@ -145,9 +145,10 @@ POSIX-TZ-environment-variable-style string for use in handling instants
after the last transition time stored in the file
(with nothing between the newlines if there is no POSIX representation for
such instants).
-As described in
-.IR newtzset (3),
-this string may use two minor extensions to the POSIX TZ format.
+.PP
+For version-3-format time zone files, the POSIX-TZ-style string may
+use two minor extensions to the POSIX TZ format, as described in
+.IR newtzset (3).
First, the hours part of its transition times may be signed and range from
\(mi167 through 167 instead of the POSIX-required unsigned values
from 0 through 24. Second, DST is in effect all year if it starts
diff --git a/tzfile.h b/tzfile.h
index 0cf2943..63db98e 100644
--- a/tzfile.h
+++ b/tzfile.h
@@ -39,7 +39,7 @@
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' as of 2005 */
+ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
char tzh_reserved[15]; /* reserved--must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
@@ -82,6 +82,13 @@ struct tzhead {
** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for
** such instants).
+**
+** If tz_version is '3' or greatar, the above is extended as follows.
+** First, the POSIX TZ string's hour offset may range from -167
+** through 167 as compared to the POSIX-required 0 through 24.
+** Second, its DST start time may be January 1 at 00:00 and its stop
+** time December 31 at 24:00 plus the difference between DST and
+** standard time, indicating DST all year.
*/
/*
diff --git a/zic.8 b/zic.8
index 5c8b59c..602c3c9 100644
--- a/zic.8
+++ b/zic.8
@@ -77,14 +77,42 @@ If this option is not used,
no leap second information appears in output files.
.TP
.B \-v
-Complain if a year that appears in a data file is outside the range
+Be more verbose, and complain about the following situations:
+.RS
+.PP
+The input data specifies a link to a link.
+.PP
+A year that appears in a data file is outside the range
of years representable by
.IR time (2)
values.
-Also complain if a time of 24:00
-(which cannot be handled by pre-1998 versions of
-.IR zic )
-appears in the input.
+.PP
+A time of 24:00 or more appears in the input.
+Pre-1998 versions of
+.I zic
+prohibit 24:00, and pre-2007 versions prohibit times greater than 24:00.
+.PP
+A rule goes past the start or end of the month.
+Pre-2004 versions of
+.I zic
+prohibit this.
+.PP
+The output file does not contain all the information about the
+long-term future of a zone, because the future cannot be summarized as
+an extended POSIX TZ string. For example, as of 2013 this problem
+occurs for Iran's daylight-saving rules for the predicted future, as
+these rules are based on the Iranian calendar, which cannot be
+represented.
+.PP
+The output contains data that may not be handled properly by client
+code designed for older
+.I zic
+output formats. These compatibility issues affect only time stamps
+before 1970 or after the start of 2038.
+.PP
+A time zone abbreviation has fewer than 3 characters.
+POSIX requires at least 3.
+.RE
.TP
.B \-s
Limit time values stored in output files to values that are the same
diff --git a/zic.c b/zic.c
index 55afb40..dcab3aa 100644
--- a/zic.c
+++ b/zic.c
@@ -10,7 +10,7 @@
#include <stdarg.h>
-#define ZIC_VERSION '2'
+#define ZIC_VERSION '3'
typedef int_fast64_t zic_t;
#define ZIC_MIN INT_FAST64_MIN
@@ -1795,6 +1795,7 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
const zic_t gmtoff)
{
register zic_t tod = rp->r_tod;
+ register int compat = 0;
result = end(result);
if (rp->r_dycode == DC_DOM) {
@@ -1817,6 +1818,8 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
if (rp->r_dycode == DC_DOWGEQ) {
wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
wday -= wdayoff;
tod += wdayoff * SECSPERDAY;
week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
@@ -1825,6 +1828,8 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
week = 5;
else {
wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
wday -= wdayoff;
tod += wdayoff * SECSPERDAY;
week = rp->r_dayofmonth / DAYSPERWEEK;
@@ -1843,8 +1848,15 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
(void) strcat(result, "/");
if (stringoffset(end(result), tod) != 0)
return -1;
+ if (tod < 0) {
+ if (compat < 2013)
+ compat = 2013;
+ } else if (SECSPERDAY <= tod) {
+ if (compat < 1994)
+ compat = 1994;
+ }
}
- return 0;
+ return compat;
}
static int
@@ -1861,7 +1873,7 @@ rule_cmp(struct rule const *a, struct rule const *b)
return a->r_dayofmonth - b->r_dayofmonth;
}
-static void
+static int
stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
{
register const struct zone * zp;
@@ -1870,6 +1882,8 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
register struct rule * dstrp;
register int i;
register const char * abbrvar;
+ register int compat = 0;
+ register int c;
struct rule stdr, dstr;
result[0] = '\0';
@@ -1884,11 +1898,11 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
if (rp->r_stdoff == 0) {
if (stdrp == NULL)
stdrp = rp;
- else return;
+ else return -1;
} else {
if (dstrp == NULL)
dstrp = rp;
- else return;
+ else return -1;
}
}
if (stdrp == NULL && dstrp == NULL) {
@@ -1911,7 +1925,7 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
** do not try to apply a rule to the zone.
*/
if (stdrp != NULL && stdrp->r_hiyear == 2037)
- return;
+ return -1;
if (stdrp != NULL && stdrp->r_stdoff != 0) {
/* Perpetual DST. */
@@ -1935,32 +1949,39 @@ stringzone(char *result, const struct zone *const zpfirst, const int zonecount)
}
}
if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
- return;
+ return -1;
abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
result[0] = '\0';
- return;
+ return -1;
}
if (dstrp == NULL)
- return;
+ return compat;
doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
if (stringoffset(end(result),
-(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
result[0] = '\0';
- return;
+ return -1;
}
(void) strcat(result, ",");
- if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+ c = stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff);
+ if (c < 0) {
result[0] = '\0';
- return;
+ return -1;
}
+ if (compat < c)
+ compat = c;
(void) strcat(result, ",");
- if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+ c = stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff);
+ if (c < 0) {
result[0] = '\0';
- return;
+ return -1;
}
+ if (compat < c)
+ compat = c;
+ return compat;
}
static void
@@ -1984,6 +2005,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 compat;
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2032,11 +2054,21 @@ 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);
- if (noise && envvar[0] == '\0')
- warning("%s %s",
- _("no POSIX environment variable for zone"),
- zpfirst->z_name);
+ compat = stringzone(envvar, zpfirst, zonecount);
+ if (noise && compat != 0) {
+ if (compat < 0)
+ warning("%s %s",
+ _("no POSIX environment variable for zone"),
+ zpfirst->z_name);
+ else {
+ /* Circa-COMPAT clients, and earlier clients, might
+ not work for this zone when given dates before
+ 1970 or after 2038. */
+ warning(_("%s: pre-%d clients may mishandle"
+ " distant timestamps"),
+ zpfirst->z_name, compat);
+ }
+ }
if (envvar[0] == '\0') {
if (min_year >= ZIC_MIN + YEARSPERREPEAT)
min_year -= YEARSPERREPEAT;
--
1.8.1.2
More information about the tz
mailing list