Ulrich Drepper is on the time zone mailing list,
but at a different address than the one below.
Direct replies carefully.
--ado
-----Original Message-----
From: Ulrich Drepper [mailto:drepper@redhat.com]
Sent: Thursday, July 24, 2003 8:53 PM
To: tz(a)lecserver.nci.nih.gov
Subject: better overflow check
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
The code at the end of zic.c:rpytime has the comment
Cheap overflow check.
Indeed, cheap. And unfortunately insufficient. If the code is executed
on a machine with 64 bit time_t the result differs from that you get on
a machine with 32 bit time_t. The following patch does a better job.
- --- timezone/zic.c 6 Apr 2002 03:39:57 -0000 1.14
+++ timezone/zic.c 25 Jul 2003 00:48:48 -0000 1.15
@@ -2152,12 +2152,13 @@
}
if (dayoff < 0 && !TYPE_SIGNED(time_t))
return min_time;
+ if (dayoff < min_time / SECSPERDAY)
+ return min_time;
+ if (dayoff > max_time / SECSPERDAY)
+ return max_time;
t = (time_t) dayoff * SECSPERDAY;
- - /*
- - ** Cheap overflow check.
- - */
- - if (t / SECSPERDAY != dayoff)
- - return (dayoff > 0) ? max_time : min_time;
+ if (t > 0 && max_time - t < rp->r_tod)
+ return max_time;
return tadd(t, rp->r_tod);
}
- --
- --------------. ,-. 444 Castro Street
Ulrich Drepper \ ,-----------------' \ Mountain View, CA 94041 USA
Red Hat `--' drepper at redhat.com `---------------------------
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)
iD8DBQE/IH8K2ijCOnn/RHQRAgSOAKCGEBJ9z2ToBRnKeXzLgPDMP4m1EgCfSos7
JcgAEplXNFZy95IqU50gsFM=
=mZY1
-----END PGP SIGNATURE-----
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
The code at the end of zic.c:rpytime has the comment
Cheap overflow check.
Indeed, cheap. And unfortunately insufficient. If the code is executed
on a machine with 64 bit time_t the result differs from that you get on
a machine with 32 bit time_t. The following patch does a better job.
- --- timezone/zic.c 6 Apr 2002 03:39:57 -0000 1.14
+++ timezone/zic.c 25 Jul 2003 00:48:48 -0000 1.15
@@ -2152,12 +2152,13 @@
}
if (dayoff < 0 && !TYPE_SIGNED(time_t))
return min_time;
+ if (dayoff < min_time / SECSPERDAY)
+ return min_time;
+ if (dayoff > max_time / SECSPERDAY)
+ return max_time;
t = (time_t) dayoff * SECSPERDAY;
- - /*
- - ** Cheap overflow check.
- - */
- - if (t / SECSPERDAY != dayoff)
- - return (dayoff > 0) ? max_time : min_time;
+ if (t > 0 && max_time - t < rp->r_tod)
+ return max_time;
return tadd(t, rp->r_tod);
}
- --
- --------------. ,-. 444 Castro Street
Ulrich Drepper \ ,-----------------' \ Mountain View, CA 94041 USA
Red Hat `--' drepper at redhat.com `---------------------------
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)
iD8DBQE/IH8K2ijCOnn/RHQRAgSOAKCGEBJ9z2ToBRnKeXzLgPDMP4m1EgCfSos7
JcgAEplXNFZy95IqU50gsFM=
=mZY1
-----END PGP SIGNATURE-----
Dave Rolsky's interpretation of what the "asia" file *says* is happening
is correct, as witness output from "zdump -v Asia/Aqtau | grep 1995":
Asia/Aqtau Sat Mar 25 20:59:59 1995 UTC = Sun Mar 26 01:59:59 1995
AQTT isdst=0
Asia/Aqtau Sat Mar 25 21:00:00 1995 UTC = Sun Mar 26 03:00:00 1995
AQTST isdst=1
Asia/Aqtau Sat Sep 23 17:59:59 1995 UTC = Sat Sep 23 23:59:59 1995
AQTST isdst=1
Asia/Aqtau Sat Sep 23 18:00:00 1995 UTC = Sat Sep 23 23:00:00 1995
AQTST isdst=1
Asia/Aqtau Sat Sep 23 21:59:59 1995 UTC = Sun Sep 24 02:59:59 1995
AQTST isdst=1
Asia/Aqtau Sat Sep 23 22:00:00 1995 UTC = Sun Sep 24 02:00:00 1995
AQTT isdst=0
(Recall that for each time change zic produces a "before" and "after" line;
six lines above means there were three changes in 1995).
I don't know *really* happened in Asia/Aqtau in 1995;
if there was indeed just one change to clocks, the "1995 Sep lastSun" line
needs tweaking.
--ado
-----Original Message-----
From: Dave Rolsky [mailto:autarch@urth.org]
Sent: Friday, July 18, 2003 1:34 AM
To: tz(a)lecserver.nci.nih.gov
Subject: My confusion with Asia/Aqtau
I'm trying to implement a parse/code generator based on the Olson DB
_text_ files (not binary).
I think I've got it mostly working, but there's some strange edge cases.
One of them involves Asia/Aqtau, and I'm trying to figure out if I'm
_really_ understanding what the file indicates.
The observances for this zone include the following:
5:00 RussiaAsia AQT%sT 1995 Sep lastSun # Aqtau
Time
4:00 RussiaAsia AQT%sT
The RussiaAsia rules include the following rules which apply to those
observances:
Rule RussiaAsia 1993 max - Mar lastSun 2:00s 1:00
S
Rule RussiaAsia 1993 1995 - Sep lastSun 2:00s 0
-
Rule RussiaAsia 1996 max - Oct lastSun 2:00s 0
-
By my reading, this indicates the following transitions:
* On Sep 24, 1995 at 00:00:00 _local_ time, the offset from UTC changes
from 05:00 to 04:00. However, the DST rule is still in effect, so the
total offset goes from 06:00 to 05:00.
* On Sep 24, 1995 at 01:00:00 local time (2:00s == 1:00 wall when DST is
in effect), the rule changes so that DST is no longer in effect, and the
cumulative offset goes from 05:00 to 04:00.
Is this _really_ correct? It seems awfully strange to have an observance
change so close to a DST change. And if cases like these are possible,
it's really going to complicate my code. But if it is correct, I do want
to get it right in my code.
OTOH, if it's wrong, and such things don't happen, I'd be just as happy to
leave me code as is ;)
-dave
/*=======================
House Absolute Consulting
www.houseabsolute.com
=======================*/
The data for Africa/Abidjan contains this:
Zone Africa/Abidjan -0:16:08 - LMT 1912
0:00 - GMT
But zdump gives me this:
Africa/Abidjan Mon Jan 1 00:16:07 1912 UTC = Sun Dec 31 23:59:59 1911 GMT isdst=0 gmtoff=-968
Africa/Abidjan Mon Jan 1 00:16:08 1912 UTC = Mon Jan 1 00:16:08 1912 GMT isdst=0 gmtoff=0
Africa/Abidjan Mon Jan 18 03:14:07 2038 UTC = Mon Jan 18 03:14:07 2038 GMT isdst=0 gmtoff=0
Africa/Abidjan Tue Jan 19 03:14:07 2038 UTC = Tue Jan 19 03:14:07 2038 GMT isdst=0 gmtoff=0
The version of the time zone data installed on my system (GNU/Linux Debian
unstable) may not match the version of the files I'm looking at (2003a)
but I didn't think that the LMT name was something terribly recent. If
look directly at the binary data for this zone on my system, I see that it
does indeed contain the string "LMT".
Any idea where this is getting munged?
I'm trying to generate tests for my code based on the output of zdump, but
I'm getting weird failures because of this problem.
-dave
/*=======================
House Absolute Consulting
www.houseabsolute.com
=======================*/
The zdump manual page notes the output to be expected when using the -v
option:
-v For each zonename on the command line, print the time
at the lowest possible time value, the time one day
after the lowest possible time value, the times both
one second before and exactly at each detected time
discontinuity, the time at one day less than the
highest possible time value, and the time at the
highest possible time value, Each line ends with
isdst=1 if the given time is Daylight Saving Time or
isdst=0 otherwise.
So the two 1901 lines and the two 2038 lines are to be expected.
--ado
-----Original Message-----
From: Dave Rolsky [mailto:autarch@urth.org]
Sent: Saturday, July 19, 2003 4:34 PM
To: tz(a)lecserver.nci.nih.gov
Subject: Re: Strange output from zdump for 2038
On Sat, 19 Jul 2003, Dave Rolsky wrote:
> autarch@houseabsolute:~/DateTime/modules/DateTime-TimeZone$ zdump -v
Asia/Aqtau | grep 2038
>
> Asia/Aqtau Mon Jan 18 03:14:07 2038 UTC = Mon Jan 18 07:14:07 2038 AQTT
isdst=0 gmtoff=14400
> Asia/Aqtau Tue Jan 19 03:14:07 2038 UTC = Tue Jan 19 07:14:07 2038 AQTT
isdst=0 gmtoff=14400
>
> Surely that's not right ;) I'd suggest that it's better to simply not
> output anything rather than output bad data. Obviously this is related to
> the 32-bit-ness of time_t, and is probably really a zic problem.
The same problem occurs at the other end of integer:
Africa/Abidjan Fri Dec 13 20:45:52 1901 UTC = Fri Dec 13 20:29:44 1901 GMT
isdst=0 gmtoff=-968
Africa/Abidjan Sat Dec 14 20:45:52 1901 UTC = Sat Dec 14 20:29:44 1901 GMT
isdst=0 gmtoff=-968
-dave
/*=======================
House Absolute Consulting
www.houseabsolute.com
=======================*/
> The point is, however, that the gmtoff values are rubbish, the result of
> using signed 32-bit values to model mathematical integers.
The gmtoff values being output do seem to be the difference (in seconds)
between UTC and local time. (It's true that the appearance of "GMT" is bogus
but,
as noted earlier, that seems to be a GNU challenge rather than a
timeezone-package-as distributed problem.)
--ado
-----Original Message-----
From: John Cowan [mailto:cowan@mercury.ccil.org]
Sent: Monday, July 21, 2003 9:01 AM
To: Olson, Arthur David (NIH/NCI)
Cc: tz(a)lecserver.nci.nih.gov
Subject: Re: Strange output from zdump for 2038
Olson, Arthur David (NIH/NCI) scripsit:
> So the two 1901 lines and the two 2038 lines are to be expected.
The point is, however, that the gmtoff values are rubbish, the result of
using signed 32-bit values to model mathematical integers.
> > Asia/Aqtau Mon Jan 18 03:14:07 2038 UTC = Mon Jan 18 07:14:07 2038 AQTT
> isdst=0 gmtoff=14400
> > Asia/Aqtau Tue Jan 19 03:14:07 2038 UTC = Tue Jan 19 07:14:07 2038 AQTT
> isdst=0 gmtoff=14400
> Africa/Abidjan Fri Dec 13 20:45:52 1901 UTC = Fri Dec 13 20:29:44 1901
GMT
> isdst=0 gmtoff=-968
> Africa/Abidjan Sat Dec 14 20:45:52 1901 UTC = Sat Dec 14 20:29:44 1901
GMT
> isdst=0 gmtoff=-968
--
My confusion is rapidly waxing John Cowan
For XML Schema's too taxing: jcowan(a)reutershealth.com
I'd use DTDs http://www.reutershealth.com
If they had local trees -- http://www.ccil.org/~cowan
I think I best switch to RELAX NG.
When one sees code like ...
long test = 0x01020304;
if ((char *)&test == "\0x01\0x02\0x03\0x04") return s;
it is a fair indication that the rest of it isn't going to
be worth bothering about ...
kre
autarch@houseabsolute:~/DateTime/modules/DateTime-TimeZone$ zdump -v Asia/Aqtau | grep 2038
Asia/Aqtau Mon Jan 18 03:14:07 2038 UTC = Mon Jan 18 07:14:07 2038 AQTT isdst=0 gmtoff=14400
Asia/Aqtau Tue Jan 19 03:14:07 2038 UTC = Tue Jan 19 07:14:07 2038 AQTT isdst=0 gmtoff=14400
Surely that's not right ;) I'd suggest that it's better to simply not
output anything rather than output bad data. Obviously this is related to
the 32-bit-ness of time_t, and is probably really a zic problem.
-dave
/*=======================
House Absolute Consulting
www.houseabsolute.com
=======================*/
I am submitting the following short program for possible inclusion in the
next release of tzcode. Zrd (zone reader) is a program that dumps the
contents of a binary zoneinfo file. It does not depend on the existing
library in any way (except that it calls gmtime, but any gmtime will do),
and provides a direct view into the contents of zoneinfo files.
I have dedicated the program to the public domain.
===== cut here ====
/* zrd - a routine to dump the contents of a binary timezone file
* Define ZONEINFO to be the directory where the timezone files are,
* typically "/usr/share/zoneinfo/".
* This program requires only stdio and "gmtime".
* Public domain source by John Cowan <cowan(a)ccil.org>
*/
# define ZONEINFO "/usr/share/zoneinfo/"
# include <stdio.h>
# include <time.h>
/* A handy routine that converts from network order
* (big-endian) 32-bit values to local order.
* It tests what local order is, and is not the most
* efficient conceivable routine, but works OK
* for this application.
*/
long my_ntohl(long s) {
long r;
char *ps;
char *pr;
long test = 0x01020304;
if ((char *)&test == "\0x01\0x02\0x03\0x04") return s;
ps = (char *) &s;
pr = (char *) &r;
pr[0] = ps[3];
pr[1] = ps[2];
pr[2] = ps[1];
pr[3] = ps[0];
return r;
}
/* The header of a timezone file.
* It is followed by:
* tzh_timecnt 32-bit values representing time transitions;
* tzh_timecnt 1-byte indexes into the time type table;
* the time type table, containing tzh_typecnt "struct ttinfo" elements;
* the leap-second table, containing tzh_leapcnt "struct ttleap" elements;
* tzh.isstdcnt 1-byte flags indicating if transitions are
* based on local standard time or local current ("wall") time
* tz.isgmtcnt 1-byte flags indicating if transitions are
* based on UTC or local time
*/
struct tzhead {
char tzh_magic[4]; /* magic number */
char tzh_reserved[16]; /* reserved for future use */
long tzh_ttisgmtcnt; /* coded number of trans. time flags */
long tzh_ttisstdcnt; /* coded number of trans. time flags */
long tzh_leapcnt; /* coded number of leap seconds */
long tzh_timecnt; /* coded number of transition times */
long tzh_typecnt; /* coded number of local time types */
long tzh_charcnt; /* coded number of abbr. chars */
};
/* A time type object
*/
struct ttinfo {
long tt_gmtoff; /* offset of this time type from UTC */
char tt_isdst; /* whether this is a DST time type */
char tt_abbrind; /* index into the timezone name abbreviations */
};
/* A leap second object
*/
struct ttleap {
long tt_time; /* The leap second */
long tt_seconds; /* New value of TAI - UTC - 10 */
};
char tzfilename[1024]; /* Filename of the timezone file */
FILE *tz; /* File stream for reading the file */
struct tzhead t; /* The file's header */
char tzdata[4096]; /* The rest of the file */
long tzsize; /* Size of tzdata in use */
/* Pointers to different parts of tzdata
*/
long *timep;
char *timetypep;
char *xtypep;
struct ttinfo *typep;
char *charp;
struct ttleap *leapp;
char *stdp;
char *gmtp;
main(argc, argv)
int argc;
char *argv[];
{
int i;
long gmtoff;
int gmtsign;
long clock;
struct tm *d;
char magic[5];
/* Validate command line and load header */
if (argc != 2) {
fprintf(stderr, "usage: %s continent/location\n", argv[0]);
exit(1);
}
strcpy(tzfilename, ZONEINFO);
strcat(tzfilename, argv[1]);
tz = fopen(tzfilename, "rb");
if (!tz) {
fprintf(stderr, "%s: %s unknown\n", argv[0], argv[1]);
exit(1);
}
fread(&t, sizeof(t), 1, tz);
/* Localize values in the header */
strncpy(magic, t.tzh_magic, 4);
magic[4] = 0;
t.tzh_ttisgmtcnt = my_ntohl(t.tzh_ttisgmtcnt);
t.tzh_ttisstdcnt = my_ntohl(t.tzh_ttisstdcnt);
t.tzh_leapcnt = my_ntohl(t.tzh_leapcnt);
t.tzh_typecnt = my_ntohl(t.tzh_typecnt);
t.tzh_timecnt = my_ntohl(t.tzh_timecnt);
t.tzh_charcnt = my_ntohl(t.tzh_charcnt);
/* Read the rest of the file */
tzsize = fread(&tzdata, 1, sizeof(tzdata), tz);
fclose(tz);
/* Set up pointers to various sections */
timep = (long *)tzdata;
timetypep = (char *)(timep + t.tzh_timecnt);
xtypep = timetypep + t.tzh_timecnt;
charp = xtypep + 6 * t.tzh_typecnt; /* alignment problem */
leapp = (struct ttleap *)(charp + t.tzh_charcnt);
stdp = (char *)(leapp + t.tzh_leapcnt);
gmtp = stdp + t.tzh_ttisstdcnt;
/* Check for invalid file size */
if (tzdata + tzsize != gmtp + t.tzh_ttisgmtcnt)
fprintf(stderr, "Warning: file is %ld bytes, expected %ld\n",
tzsize + sizeof(t),
(gmtp + t.tzh_ttisgmtcnt) - tzdata);
/* Print magic number */
if (magic[0])
printf("TZ magic number is \"%s\" (normally \"TZif\")\n",
magic);
/* Print transition time table */
printf("%ld transition times:\n", t.tzh_timecnt);
for (i = 0; i < t.tzh_timecnt; i++) {
clock = my_ntohl(timep[i]);
/* printf("\tat %ld use type %d\n", clock, timetypep[i]); */
d = gmtime(&clock);
printf("\tat %4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d UTC (%ld) use type %d\n",
d->tm_year + 1900, d->tm_mon+1, d->tm_mday,
d->tm_hour, d->tm_min, d->tm_sec, clock, timetypep[i]);
}
/* Print time types table */
printf("%ld local time types:\n", t.tzh_typecnt);
for (i = 0; i < t.tzh_typecnt; i++) {
typep = (struct ttinfo *)(xtypep + i * 6);
gmtoff = my_ntohl(typep->tt_gmtoff);
if (gmtoff < 0) {
gmtsign = -1;
gmtoff = -gmtoff;
}
else if (gmtoff > 0)
gmtsign = 1;
else
gmtsign = 0;
printf("\ttype %d offset %2.2ld:%2.2ld:%2.2ld %s name (%d) %s",
i, gmtsign*(gmtoff/3600), (gmtoff%3600)/60, gmtoff%60,
typep->tt_isdst ? "isdst " : "notdst",
typep->tt_abbrind,
charp + typep->tt_abbrind);
if (i < t.tzh_ttisstdcnt)
printf(" %s", stdp[i] ? "std" : "wall");
if (i < t.tzh_ttisgmtcnt)
printf(" %s", gmtp[i] ? "gmt" : "local");
printf("\n");
}
/* Print time zone abbreviations */
printf("%ld bytes of abbreviations: [", t.tzh_charcnt);
for (i = 0; i < t.tzh_charcnt; i++)
if (charp[i])
putchar(charp[i]);
else if (i != t.tzh_charcnt - 1)
putchar('|');
printf("]\n");
/* Print leap second table */
printf("%ld leap second entries:\n", t.tzh_leapcnt);
for (i = 0; i < t.tzh_leapcnt; i++) {
clock = my_ntohl(leapp[i].tt_time);
d = gmtime(&clock);
printf("\tat %4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d UTC (%ld), TAI - UTC = %ld seconds\n",
d->tm_year + 1900, d->tm_mon+1, d->tm_mday,
d->tm_hour, d->tm_min, d->tm_sec, clock,
my_ntohl(leapp[i].tt_seconds) + 10);
}
/* Print counts of gmt/local and std/wall flags (contents
* have already been printed)
*/
printf("%ld std/wall flags\n", t.tzh_ttisstdcnt);
printf("%ld gmt/local flags\n", t.tzh_ttisgmtcnt);
exit(0);
}
===== cut here =====
--
He made the Legislature meet at one-horse John Cowan
tank-towns out in the alfalfa belt, so that jcowan(a)reutershealth.com
hardly nobody could get there and most of http://www.reutershealth.com
the leaders would stay home and let him go http://www.ccil.org/~cowan
to work and do things as he pleased. --Mencken, _Declaration of Independence_
I'm trying to implement a parse/code generator based on the Olson DB
_text_ files (not binary).
I think I've got it mostly working, but there's some strange edge cases.
One of them involves Asia/Aqtau, and I'm trying to figure out if I'm
_really_ understanding what the file indicates.
The observances for this zone include the following:
5:00 RussiaAsia AQT%sT 1995 Sep lastSun # Aqtau Time
4:00 RussiaAsia AQT%sT
The RussiaAsia rules include the following rules which apply to those
observances:
Rule RussiaAsia 1993 max - Mar lastSun 2:00s 1:00 S
Rule RussiaAsia 1993 1995 - Sep lastSun 2:00s 0 -
Rule RussiaAsia 1996 max - Oct lastSun 2:00s 0 -
By my reading, this indicates the following transitions:
* On Sep 24, 1995 at 00:00:00 _local_ time, the offset from UTC changes
from 05:00 to 04:00. However, the DST rule is still in effect, so the
total offset goes from 06:00 to 05:00.
* On Sep 24, 1995 at 01:00:00 local time (2:00s == 1:00 wall when DST is
in effect), the rule changes so that DST is no longer in effect, and the
cumulative offset goes from 05:00 to 04:00.
Is this _really_ correct? It seems awfully strange to have an observance
change so close to a DST change. And if cases like these are possible,
it's really going to complicate my code. But if it is correct, I do want
to get it right in my code.
OTOH, if it's wrong, and such things don't happen, I'd be just as happy to
leave me code as is ;)
-dave
/*=======================
House Absolute Consulting
www.houseabsolute.com
=======================*/