This implements a suggestion by Arthur David Olson. Without this change, zic -v diagnoses problems with several zones where it cannot compute a POSIX-equivalent TZ setting for time stamps past 2038, which means these time stamps may be mishandled. This entails a minor change to the binary tz file format, to allow a minor extension to the POSIX TZ setting in the binary file, instead of requiring a pure POSIX TZ setting. The zones fixed by this change are America/Godthab, America/Santiago, Antarctica/Palmer, Asia/Gaza, Asia/Hebron, Asia/Jerusalem, Pacific/Easter, and Pacific/Fiji. The only zone that remains unfixed is Asia/Tehran, which schedules clock transitions via the Iranian calendar, something that even the extended TZ setting cannot represent. * localtime.c (getrule): Allow transition times to be signed. * newtzset.3: Describe the extensions to POSIX TZ strings. Some of these extensions (e.g., hours == 26) were already implemented but were not documented. Give examples. * tzfile.5: Document the relaxed restriction on the stored TZ string; its hours component can be in the range -167..167 rather than the POSIX-required 0..24. Refer to newtzset(5). * zic.c (stringoffset): Allow hours to go up to 167. (stringrule): Be willing to generate hours in the range -167 through 167. --- localtime.c | 2 +- newtzset.3 | 37 ++++++++++++++++++++++++++++++++++--- tzfile.5 | 6 +++++- zic.c | 27 ++++++++++++++------------- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/localtime.c b/localtime.c index 619a656..91a3171 100644 --- a/localtime.c +++ b/localtime.c @@ -835,7 +835,7 @@ getrule(const char *strp, register struct rule *const rulep) ** Time specified. */ ++strp; - strp = getsecs(strp, &rulep->r_time); + strp = getoffset(strp, &rulep->r_time); } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */ return strp; } diff --git a/newtzset.3 b/newtzset.3 index 3689e50..bb40c01 100644 --- a/newtzset.3 +++ b/newtzset.3 @@ -177,16 +177,47 @@ The .I time has the same format as .I offset -except that no leading sign +except that POSIX does not allow a leading sign .RB (`` \(mi '' or -.RB `` \(pl '') -is allowed. The default, if +.RB `` \(pl ''). +As an extension to POSIX, the hours part of +.I time +can range from \(mi167 to 167; this allows for unusual rules such +as "the Saturday before the first Sunday of March". The default, if .I time is not given, is .BR 02:00:00 . .RE .LP +Here are some examples of +.B TZ +values that directly specify the time zone rules; they use some of the +extensions to POSIX. +.TP +.B EST5 +stands for US eastern +time (EST), 5 hours behind UTC, without daylight saving. +.TP +.B FJT\(mi12FJST,M10.3.4/74,M1.3.4/75 +stands for Fiji Time (FJT) and Fiji Summer Time (FJST), 12 hours ahead +of UTC, where clocks spring forward at 74:00 on the third Thursday in +October (i.e., 02:00 on the first Sunday on or after October 18), and +fall back at 75:00 on the third Thursday in January (i.e., 03:00 on +the first Sunday on or after January 18). +.TP +.B IST\(mi2IDT,M3.4.4/26,M10.5.0 +stands for Israel standard time (IST) and Israel daylight time (IDT), +2 hours ahead of UTC, where clocks spring forward at 26:00 on the +fourth Thursday in March (i.e., 02:00 on the first Friday on or after +March 23), and fall back at 02:00 on the last Sunday in October. +.TP +.B WGT3WGST,M3.5.0/\(mi2,M10.5.0/\(mi1 +stands for Western Greenland Time (WGT) and Western Greenland Summer +Time (WGST), 3 hours behind UTC, where clocks follow the EU rules of +springing forward the last Sunday in March at \(mi02:00 (i.e., 01:00 UTC) +and falling back the last Sunday in October at \(mi01:00 (i.e., 01:00 UTC). +.PP If no .I rule is present in diff --git a/tzfile.5 b/tzfile.5 index e92eaed..c7bd40e 100644 --- a/tzfile.5 +++ b/tzfile.5 @@ -145,7 +145,11 @@ 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). +This string may use a minor extension to the POSIX TZ format: 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. .SH SEE ALSO -newctime(3) +newctime(3), newtzset(3) .\" This file is in the public domain, so clarified as of .\" 1996-06-05 by Arthur David Olson. diff --git a/zic.c b/zic.c index 97786ae..260dc2e 100644 --- a/zic.c +++ b/zic.c @@ -1776,7 +1776,7 @@ stringoffset(char *result, zic_t offset) minutes = offset % MINSPERHOUR; offset /= MINSPERHOUR; hours = offset; - if (hours > HOURSPERDAY) { + if (hours >= HOURSPERDAY * DAYSPERWEEK) { result[0] = '\0'; return -1; } @@ -1793,7 +1793,7 @@ static int stringrule(char *result, const struct rule *const rp, const zic_t dstoff, const zic_t gmtoff) { - register zic_t tod; + register zic_t tod = rp->r_tod; result = end(result); if (rp->r_dycode == DC_DOM) { @@ -1807,32 +1807,33 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff, (void) sprintf(result, "J%d", total + rp->r_dayofmonth); } else { register int week; + register int wday = rp->r_wday; + register int wdayoff; if (rp->r_dycode == DC_DOWGEQ) { - if ((rp->r_dayofmonth % DAYSPERWEEK) != 1) - return -1; - week = 1 + rp->r_dayofmonth / DAYSPERWEEK; + wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK; + wday -= wdayoff; + tod += wdayoff * SECSPERDAY; + week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK; } else if (rp->r_dycode == DC_DOWLEQ) { if (rp->r_dayofmonth == len_months[1][rp->r_month]) week = 5; else { - if ((rp->r_dayofmonth % DAYSPERWEEK) != 0) - return -1; + wdayoff = rp->r_dayofmonth % DAYSPERWEEK; + wday -= wdayoff; + tod += wdayoff * SECSPERDAY; week = rp->r_dayofmonth / DAYSPERWEEK; } } else return -1; /* "cannot happen" */ + if (wday < 0) + wday += DAYSPERWEEK; (void) sprintf(result, "M%d.%d.%d", - rp->r_month + 1, week, rp->r_wday); + rp->r_month + 1, week, wday); } - tod = rp->r_tod; if (rp->r_todisgmt) tod += gmtoff; if (rp->r_todisstd && rp->r_stdoff == 0) tod += dstoff; - if (tod < 0) { - result[0] = '\0'; - return -1; - } if (tod != 2 * SECSPERMIN * MINSPERHOUR) { (void) strcat(result, "/"); if (stringoffset(end(result), tod) != 0) -- 1.8.1.2