[tz] [PATCH] Support time stamps past 2038 in zones like America/Santiago.
Paul Eggert
eggert at cs.ucla.edu
Fri Sep 6 01:31:13 UTC 2013
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
More information about the tz
mailing list