Below are diffs to the time zone code to cope with 64-bit time_t systems; these reflect suggestions I've received to date. Absent negative feedback, I'll update the ftp versions on May 2, 2005. --ado diff -c -r tz/localtime.c tzexp/localtime.c *** tz/localtime.c Thu Apr 21 15:04:15 2005 --- tzexp/localtime.c Mon Apr 4 15:39:41 2005 *************** *** 5,11 **** #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)localtime.c 7.91"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 5,11 ---- #ifndef lint #ifndef NOID ! static char elsieid[] = "@(#)localtime.c 7.92"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 98,103 **** --- 98,105 ---- int timecnt; int typecnt; int charcnt; + int goback; + int goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; *************** *** 123,128 **** --- 125,131 ---- */ static long detzcode P((const char * codep)); + static time_t detzcode64 P((const char * codep)); static const char * getzname P((const char * strp)); static const char * getnum P((const char * strp, int * nump, int min, int max)); *************** *** 221,226 **** --- 224,242 ---- return result; } + static time_t + detzcode64(codep) + const char * const codep; + { + register signed64_t result; + register int i; + + result = (codep[0] & 0x80) ? ~0L : 0L; + for (i = 0; i < 8; ++i) + result = (result << 8) | (codep[i] & 0xff); + return (time_t) result; + } + static void settzname P((void)) { *************** *** 276,284 **** register const char * name; register struct state * const sp; { ! register const char * p; ! register int i; ! register int fid; if (name == NULL && (name = TZDEFAULT) == NULL) return -1; --- 292,308 ---- register const char * name; register struct state * const sp; { ! register const char * p; ! register int i; ! register int fid; ! register int stored; ! register int nread; ! union { ! struct tzhead tzhead; ! char buf[2 * sizeof(struct tzhead) + ! 2 * sizeof *sp + ! 4 * TZ_MAX_TIMES]; ! } u; if (name == NULL && (name = TZDEFAULT) == NULL) return -1; *************** *** 316,333 **** if ((fid = open(name, OPEN_MODE)) == -1) return -1; } ! { ! struct tzhead * tzhp; ! union { ! struct tzhead tzhead; ! char buf[sizeof *sp + sizeof *tzhp]; ! } u; int ttisstdcnt; int ttisgmtcnt; - i = read(fid, u.buf, sizeof u.buf); - if (close(fid) != 0) - return -1; ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); --- 340,352 ---- if ((fid = open(name, OPEN_MODE)) == -1) return -1; } ! nread = read(fid, u.buf, sizeof u.buf); ! if (close(fid) < 0 || nread <= 0) ! return -1; ! for (stored = 4; stored <= 8; stored *= 2) { int ttisstdcnt; int ttisgmtcnt; ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt); ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt); sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt); *************** *** 342,358 **** (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; ! if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */ sp->timecnt + /* types */ ! sp->typecnt * (4 + 2) + /* ttinfos */ sp->charcnt + /* chars */ ! sp->leapcnt * (4 + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ return -1; for (i = 0; i < sp->timecnt; ++i) { ! sp->ats[i] = detzcode(p); ! p += 4; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; --- 361,379 ---- (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) return -1; ! if (nread - (p - u.buf) < ! sp->timecnt * stored + /* ats */ sp->timecnt + /* types */ ! sp->typecnt * 6 + /* ttinfos */ sp->charcnt + /* chars */ ! sp->leapcnt * (stored + 4) + /* lsinfos */ ttisstdcnt + /* ttisstds */ ttisgmtcnt) /* ttisgmts */ return -1; for (i = 0; i < sp->timecnt; ++i) { ! sp->ats[i] = (stored == 4) ? ! detzcode(p) : detzcode64(p); ! p += stored; } for (i = 0; i < sp->timecnt; ++i) { sp->types[i] = (unsigned char) *p++; *************** *** 380,387 **** register struct lsinfo * lsisp; lsisp = &sp->lsis[i]; ! lsisp->ls_trans = detzcode(p); ! p += 4; lsisp->ls_corr = detzcode(p); p += 4; } --- 401,409 ---- register struct lsinfo * lsisp; lsisp = &sp->lsis[i]; ! lsisp->ls_trans = (stored == 4) ? ! detzcode(p) : detzcode64(p); ! p += stored; lsisp->ls_corr = detzcode(p); p += 4; } *************** *** 438,444 **** --- 460,488 ---- } break; } + /* + ** If this is an old file, we're done. + */ + if (u.tzhead.tzh_version[0] == '\0') + break; + /* + ** If this is a narrow integer time_t system, we're done. + */ + if (stored >= sizeof(time_t) && TYPE_INTEGRAL(time_t)) + break; + nread -= p - u.buf; + for (i = 0; i < nread; ++i) + u.buf[i] = p[i]; } + i = 2 * YEARSPERREPEAT; + sp->goback = sp->goahead = sp->timecnt > i; + sp->goback &= sp->types[i] == sp->types[0] && + sp->ats[i] - sp->ats[0] == SECSPERREPEAT; + sp->goahead &= + sp->types[sp->timecnt - 1] == + sp->types[sp->timecnt - 1 - i] && + sp->ats[sp->timecnt - 1] - sp->ats[sp->timecnt - 1 - i] == + SECSPERREPEAT; return 0; } *************** *** 1046,1051 **** --- 1090,1133 ---- if (sp == NULL) return gmtsub(timep, offset, tmp); #endif /* defined ALL_STATE */ + if ((sp->goback && t < sp->ats[0]) || + (sp->goahead && t > sp->ats[sp->timecnt - 1])) { + time_t newt = t; + register time_t seconds; + register time_t tcycles; + register signed64_t icycles; + + if (t < sp->ats[0]) + seconds = sp->ats[0] - t; + else seconds = t - sp->ats[sp->timecnt - 1]; + --seconds; + tcycles = seconds / SECSPERREPEAT; + ++tcycles; + icycles = tcycles; + if (tcycles - icycles >= 1 || icycles - tcycles >= 1) + return NULL; + seconds = icycles; + seconds *= SECSPERREPEAT; + if (t < sp->ats[0]) + newt += seconds; + else newt -= seconds; + if (newt < sp->ats[0] || + newt > sp->ats[sp->timecnt - 1]) + return NULL; /* "cannot happen" */ + result = localsub(&newt, offset, tmp); + if (result == tmp) { + register time_t newy; + + newy = tmp->tm_year; + if (t < sp->ats[0]) + newy -= icycles * YEARSPERREPEAT; + else newy += icycles * YEARSPERREPEAT; + tmp->tm_year = newy; + if (tmp->tm_year != newy) + return NULL; + } + return result; + } if (sp->timecnt == 0 || t < sp->ats[0]) { i = 0; while (sp->ttis[i].tt_isdst) *** tz/private.h Thu Apr 21 15:20:23 2005 --- tzexp/private.h Thu Apr 21 15:21:15 2005 *************** *** 21,30 **** #ifndef lint #ifndef NOID ! static char privatehid[] = "@(#)private.h 7.55"; #endif /* !defined NOID */ #endif /* !defined lint */ /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. --- 21,36 ---- #ifndef lint #ifndef NOID ! static char privatehid[] = "@(#)private.h 7.62"; #endif /* !defined NOID */ #endif /* !defined lint */ + #ifndef SIGNED64_T + #define SIGNED64_T long long + #endif /* !defined SIGNED64_T */ + + typedef SIGNED64_T signed64_t; + /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. *************** *** 308,313 **** --- 314,327 ---- char *ctime_r P((time_t const *, char *)); #endif /* HAVE_INCOMPATIBLE_CTIME_R */ + #ifndef YEARSPERREPEAT + #define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ + #endif /* !defined YEARSPERREPEAT */ + + #ifndef SECSPERREPEAT + #define SECSPERREPEAT 12622780800 + #endif /* !defined SECSPERREPEAT */ + /* ** UNIX was a registered trademark of The Open Group in 2003. */ diff -c -r tz/tzfile.5 tzexp/tzfile.5 *** tz/tzfile.5 Thu Apr 21 15:04:14 2005 --- tzexp/tzfile.5 Thu Apr 21 15:09:16 2005 *************** *** 9,15 **** .IR tzset (3) begin with the magic characters "TZif" to identify then as time zone information files, ! followed by sixteen bytes reserved for future use, followed by six four-byte values of type .BR long , written in a ``standard'' byte order --- 9,17 ---- .IR tzset (3) begin with the magic characters "TZif" to identify then 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') ! followed by fifteen bytes containing zeroes reserved for future use, followed by six four-byte values of type .BR long , written in a ``standard'' byte order *************** *** 131,138 **** .I tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file. .SH SEE ALSO newctime(3) ! .\" @(#)tzfile.5 7.11 .\" This file is in the public domain, so clarified as of .\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). --- 133,145 ---- .I tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file. + .PP + For version-2-format time zone files, + the above header and data is followed by a second header and data, + identical in format except that + eight bytes are used for each transition time or leap second time. .SH SEE ALSO newctime(3) ! .\" @(#)tzfile.5 7.13 .\" This file is in the public domain, so clarified as of .\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). diff -c -r tz/tzfile.h tzexp/tzfile.h *** tz/tzfile.h Thu Apr 21 15:04:14 2005 --- tzexp/tzfile.h Thu Apr 21 15:09:17 2005 *************** *** 21,27 **** #ifndef lint #ifndef NOID ! static char tzfilehid[] = "@(#)tzfile.h 7.17"; #endif /* !defined NOID */ #endif /* !defined lint */ --- 21,27 ---- #ifndef lint #ifndef NOID ! static char tzfilehid[] = "@(#)tzfile.h 7.19"; #endif /* !defined NOID */ #endif /* !defined lint */ *************** *** 49,55 **** struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ ! char tzh_reserved[16]; /* reserved for future use */ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_leapcnt[4]; /* coded number of leap seconds */ --- 49,56 ---- struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ ! char tzh_version[1]; /* '\0' or '2' as of 2005 */ ! 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 */ char tzh_leapcnt[4]; /* coded number of leap seconds */ *************** *** 84,101 **** */ /* ** In the current implementation, "tzset()" refuses to deal with files that ** exceed any of the limits below. */ #ifndef TZ_MAX_TIMES ! /* ! ** The TZ_MAX_TIMES value below is enough to handle a bit more than a ! ** year's worth of solar time (corrected daily to the nearest second) or ! ** 138 years of Pacific Presidential Election time ! ** (where there are three time zone transitions every fourth year). ! */ ! #define TZ_MAX_TIMES 370 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES --- 85,102 ---- */ /* + ** If tzh_version is '2' or greater, the above is followed by a second instance + ** of tzhead and a second instance of the data in which each coded transition + ** time uses 8 rather than 4 chars. + */ + + /* ** In the current implementation, "tzset()" refuses to deal with files that ** exceed any of the limits below. */ #ifndef TZ_MAX_TIMES ! #define TZ_MAX_TIMES 1000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES diff -c -r tz/zic.8 tzexp/zic.8 *** tz/zic.8 Thu Apr 21 15:04:14 2005 --- tzexp/zic.8 Mon Apr 4 15:39:43 2005 *************** *** 21,28 **** .B \-L .I leapsecondfilename ] [ - .B \-s - ] [ .B \-y .I command ] [ --- 21,26 ---- *************** *** 86,96 **** .IR zic ) appears in the input. .TP - .B \-s - Limit time values stored in output files to values that are the same - whether they're taken to be signed or unsigned. - You can use this option to generate SVVS-compatible files. - .TP .BI "\-y " command Use the given .I command --- 84,89 ---- *************** *** 421,424 **** /usr/local/etc/zoneinfo standard directory used for created files .SH "SEE ALSO" newctime(3), tzfile(5), zdump(8) ! .\" @(#)zic.8 7.22 --- 414,417 ---- /usr/local/etc/zoneinfo standard directory used for created files .SH "SEE ALSO" newctime(3), tzfile(5), zdump(8) ! .\" @(#)zic.8 7.23 diff -c -r tz/zic.c tzexp/zic.c *** tz/zic.c Thu Apr 21 15:04:15 2005 --- tzexp/zic.c Mon Apr 4 15:39:43 2005 *************** *** 1,15 **** ! static char elsieid[] = "@(#)zic.c 7.122"; - /* - ** Regardless of the type of time_t, we do our work using this type. - */ - - typedef int zic_t; - #include "private.h" #include "locale.h" #include "tzfile.h" #if HAVE_SYS_STAT_H #include "sys/stat.h" #endif --- 1,13 ---- ! static char elsieid[] = "@(#)zic.c 7.127"; #include "private.h" #include "locale.h" #include "tzfile.h" + #define ZIC_VERSION '2' + + typedef signed64_t zic_t; + #if HAVE_SYS_STAT_H #include "sys/stat.h" #endif *************** *** 40,45 **** --- 38,45 ---- int r_loyear; /* for example, 1986 */ int r_hiyear; /* for example, 1986 */ const char * r_yrtype; + int r_lowasnum; + int r_hiwasnum; int r_month; /* 0..11 */ *************** *** 99,104 **** --- 99,105 ---- static void associate P((void)); static int ciequal P((const char * ap, const char * bp)); static void convert P((long val, char * buf)); + static void convert64 P((zic_t val, char * buf)); static void dolink P((const char * fromfile, const char * tofile)); static void doabbr P((char * abbr, const char * format, const char * letters, int isdst)); *************** *** 117,122 **** --- 118,124 ---- static int inzcont P((char ** fields, int nfields)); static int inzone P((char ** fields, int nfields)); static int inzsub P((char ** fields, int nfields, int iscont)); + static int is32 P((zic_t x)); static int itsabbr P((const char * abbr, const char * word)); static int itsdir P((const char * name)); static int lowerit P((int c)); *************** *** 126,131 **** --- 128,134 ---- static long oadd P((long t1, long t2)); static void outzone P((const struct zone * zp, int ntzones)); static void puttzcode P((long code, FILE * fp)); + static void puttzcode64 P((zic_t code, FILE * fp)); static int rcomp P((const void * leftp, const void * rightp)); static zic_t rpytime P((const struct rule * rp, int wantedy)); static void rulesub P((struct rule * rp, *************** *** 146,158 **** static int errors; static const char * filename; static int leapcnt; static int linenum; static zic_t max_time; static int max_year; - static int max_year_representable; static zic_t min_time; static int min_year; - static int min_year_representable; static int noise; static const char * rfilename; static int rlinenum; --- 149,162 ---- static int errors; static const char * filename; static int leapcnt; + static int leapseen; + static int leapminyear; + static int leapmaxyear; static int linenum; static zic_t max_time; static int max_year; static zic_t min_time; static int min_year; static int noise; static const char * rfilename; static int rlinenum; *************** *** 449,455 **** usage P((void)) { (void) fprintf(stderr, _("%s: usage is %s \ ! [ --version ] [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\ \t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"), progname, progname); (void) exit(EXIT_FAILURE); --- 453,459 ---- usage P((void)) { (void) fprintf(stderr, _("%s: usage is %s \ ! [ --version ] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\ \t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"), progname, progname); (void) exit(EXIT_FAILURE); *************** *** 460,466 **** static const char * directory; static const char * leapsec; static const char * yitcommand; - static int sflag = FALSE; int main(argc, argv) --- 464,469 ---- *************** *** 482,487 **** --- 485,495 ---- (void) textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ progname = argv[0]; + if (8 > sizeof(signed64_t)) { + (void) fprintf(stderr, + "%s: wild compilation-time specification of SIGNED64_T\n", progname); + exit(EXIT_FAILURE); + } for (i = 1; i < argc; ++i) if (strcmp(argv[i], "--version") == 0) { (void) printf("%s\n", elsieid); *************** *** 545,551 **** noise = TRUE; break; case 's': ! sflag = TRUE; break; } if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) --- 553,559 ---- noise = TRUE; break; case 's': ! (void) printf("%s: -s ignored\n", progname); break; } if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) *************** *** 667,720 **** ifree(toname); } ! #ifndef INT_MAX ! #define INT_MAX ((int) (((unsigned)~0)>>1)) ! #endif /* !defined INT_MAX */ - #ifndef INT_MIN - #define INT_MIN ((int) ~(((unsigned)~0)>>1)) - #endif /* !defined INT_MIN */ - - /* - ** The tz file format currently allows at most 32-bit quantities. - ** This restriction should be removed before signed 32-bit values - ** wrap around in 2038, but unfortunately this will require a - ** change to the tz file format. - */ - - #define MAX_BITS_IN_FILE 32 - #define TIME_T_BITS_IN_FILE ((TYPE_BIT(zic_t) < MAX_BITS_IN_FILE) ? \ - TYPE_BIT(zic_t) : MAX_BITS_IN_FILE) - static void setboundaries P((void)) { register int i; ! if (TYPE_SIGNED(zic_t)) { ! min_time = -1; ! for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) ! min_time *= 2; ! max_time = -(min_time + 1); ! if (sflag) ! min_time = 0; ! } else { ! min_time = 0; ! max_time = 2 - sflag; ! for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) ! max_time *= 2; ! --max_time; ! } ! { ! time_t t; ! ! t = (time_t) min_time; ! min_year = TM_YEAR_BASE + gmtime(&t)->tm_year; ! t = (time_t) max_time; ! max_year = TM_YEAR_BASE + gmtime(&t)->tm_year; ! } ! min_year_representable = min_year; ! max_year_representable = max_year; } static int --- 675,691 ---- ifree(toname); } ! #define TIME_T_BITS_IN_FILE 64 static void setboundaries P((void)) { register int i; ! min_time = -1; ! for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i) ! min_time *= 2; ! max_time = -(min_time + 1); } static int *************** *** 1155,1160 **** --- 1126,1136 ---- error(_("invalid leaping year")); return; } + if (!leapseen || leapmaxyear < year) + leapmaxyear = year; + if (!leapseen || leapminyear < year) + leapminyear = year; + leapseen = TRUE; j = EPOCH_YEAR; while (j != year) { if (year > j) { *************** *** 1309,1315 **** */ cp = loyearp; lp = byword(cp, begin_years); ! if (lp != NULL) switch ((int) lp->l_value) { case YR_MINIMUM: rp->r_loyear = INT_MIN; break; --- 1285,1292 ---- */ cp = loyearp; lp = byword(cp, begin_years); ! rp->r_lowasnum = lp == NULL; ! if (!rp->r_lowasnum) switch ((int) lp->l_value) { case YR_MINIMUM: rp->r_loyear = INT_MIN; break; *************** *** 1324,1337 **** } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { error(_("invalid starting year")); return; - } else if (noise) { - if (rp->r_loyear < min_year_representable) - warning(_("starting year too low to be represented")); - else if (rp->r_loyear > max_year_representable) - warning(_("starting year too high to be represented")); } cp = hiyearp; ! if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) { case YR_MINIMUM: rp->r_hiyear = INT_MIN; break; --- 1301,1311 ---- } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) { error(_("invalid starting year")); return; } cp = hiyearp; ! lp = byword(cp, end_years); ! rp->r_hiwasnum = lp == NULL; ! if (!rp->r_hiwasnum) switch ((int) lp->l_value) { case YR_MINIMUM: rp->r_hiyear = INT_MIN; break; *************** *** 1349,1359 **** } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) { error(_("invalid ending year")); return; - } else if (noise) { - if (rp->r_loyear < min_year_representable) - warning(_("ending year too low to be represented")); - else if (rp->r_loyear > max_year_representable) - warning(_("ending year too high to be represented")); } if (rp->r_loyear > rp->r_hiyear) { error(_("starting year greater than ending year")); --- 1323,1328 ---- *************** *** 1368,1375 **** } rp->r_yrtype = ecpyalloc(typep); } - if (rp->r_loyear < min_year && rp->r_loyear > 0) - min_year = rp->r_loyear; /* ** Day work. ** Accept things such as: --- 1337,1342 ---- *************** *** 1423,1429 **** char * const buf; { register int i; ! register long shift; for (i = 0, shift = 24; i < 4; ++i, shift -= 8) buf[i] = val >> shift; --- 1390,1396 ---- char * const buf; { register int i; ! register int shift; for (i = 0, shift = 24; i < 4; ++i, shift -= 8) buf[i] = val >> shift; *************** *** 1430,1435 **** --- 1397,1414 ---- } static void + convert64(val, buf) + const zic_t val; + char * const buf; + { + register int i; + register int shift; + + for (i = 0, shift = 56; i < 8; ++i, shift -= 8) + buf[i] = val >> shift; + } + + static void puttzcode(val, fp) const long val; FILE * const fp; *************** *** 1440,1445 **** --- 1419,1435 ---- (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); } + static void + puttzcode64(val, fp) + const zic_t val; + FILE * const fp; + { + char buf[8]; + + convert64(val, buf); + (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp); + } + static int atcomp(avp, bvp) void * avp; *************** *** 1452,1467 **** else return 0; } static void writezone(name) const char * const name; { ! register FILE * fp; ! register int i, j; ! static char * fullname; ! static struct tzhead tzh; ! zic_t ats[TZ_MAX_TIMES]; ! unsigned char types[TZ_MAX_TIMES]; /* ** Sort. --- 1442,1469 ---- else return 0; } + static int + is32(x) + zic_t x; + { + x >>= 31; + return x == 0 || x == ~0; + } + static void writezone(name) const char * const name; { ! register FILE * fp; ! register int i, j; ! register int leapcnt32, leapi32; ! register int timecnt32, timei32; ! register int pass; ! static char * fullname; ! static const struct tzhead tzh0; ! static struct tzhead tzh; ! zic_t ats[TZ_MAX_TIMES]; ! unsigned char types[TZ_MAX_TIMES]; /* ** Sort. *************** *** 1505,1510 **** --- 1507,1542 ---- ats[i] = attypes[i].at; types[i] = attypes[i].type; } + /* + ** Correct for leap seconds. + */ + for (i = 0; i < timecnt; ++i) { + j = leapcnt; + while (--j >= 0) + if (ats[i] >= trans[j]) { + ats[i] = tadd(ats[i], corr[j]); + break; + } + } + /* + ** Figure out 32-bit-limited starts and counts. + */ + timecnt32 = timecnt; + timei32 = 0; + leapcnt32 = leapcnt; + leapi32 = 0; + while (timecnt32 > 0 && !is32(ats[timecnt32 - 1])) + --timecnt32; + while (timecnt32 > 0 && !is32(ats[timei32])) { + --timecnt32; + ++timei32; + } + while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1])) + --leapcnt32; + while (leapcnt32 > 0 && !is32(trans[leapi32])) { + --leapcnt32; + ++leapi32; + } fullname = erealloc(fullname, (int) (strlen(directory) + 1 + strlen(name) + 1)); (void) sprintf(fullname, "%s/%s", directory, name); *************** *** 1529,1595 **** (void) exit(EXIT_FAILURE); } } ! convert(eitol(typecnt), tzh.tzh_ttisgmtcnt); ! convert(eitol(typecnt), tzh.tzh_ttisstdcnt); ! convert(eitol(leapcnt), tzh.tzh_leapcnt); ! convert(eitol(timecnt), tzh.tzh_timecnt); ! convert(eitol(typecnt), tzh.tzh_typecnt); ! convert(eitol(charcnt), tzh.tzh_charcnt); ! (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); #define DO(field) (void) fwrite((void *) tzh.field, \ (size_t) sizeof tzh.field, (size_t) 1, fp) ! DO(tzh_magic); ! DO(tzh_reserved); ! DO(tzh_ttisgmtcnt); ! DO(tzh_ttisstdcnt); ! DO(tzh_leapcnt); ! DO(tzh_timecnt); ! DO(tzh_typecnt); ! DO(tzh_charcnt); #undef DO ! for (i = 0; i < timecnt; ++i) { ! j = leapcnt; ! while (--j >= 0) ! if (ats[i] >= trans[j]) { ! ats[i] = tadd(ats[i], corr[j]); ! break; ! } ! puttzcode((long) ats[i], fp); } - if (timecnt > 0) - (void) fwrite((void *) types, (size_t) sizeof types[0], - (size_t) timecnt, fp); - for (i = 0; i < typecnt; ++i) { - puttzcode((long) gmtoffs[i], fp); - (void) putc(isdsts[i], fp); - (void) putc(abbrinds[i], fp); - } - if (charcnt != 0) - (void) fwrite((void *) chars, (size_t) sizeof chars[0], - (size_t) charcnt, fp); - for (i = 0; i < leapcnt; ++i) { - if (roll[i]) { - if (timecnt == 0 || trans[i] < ats[0]) { - j = 0; - while (isdsts[j]) - if (++j >= typecnt) { - j = 0; - break; - } - } else { - j = 1; - while (j < timecnt && trans[i] >= ats[j]) - ++j; - j = types[j - 1]; - } - puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp); - } else puttzcode((long) trans[i], fp); - puttzcode((long) corr[i], fp); - } - for (i = 0; i < typecnt; ++i) - (void) putc(ttisstds[i], fp); - for (i = 0; i < typecnt; ++i) - (void) putc(ttisgmts[i], fp); if (ferror(fp) || fclose(fp)) { (void) fprintf(stderr, _("%s: Error writing %s\n"), progname, fullname); --- 1561,1652 ---- (void) exit(EXIT_FAILURE); } } ! for (pass = 1; pass <= 2; ++pass) { ! register int thistimei, thistimecnt; ! register int thisleapi, thisleapcnt; ! register int thistimelim, thisleaplim; ! ! if (pass == 1) { ! thistimei = timei32; ! thistimecnt = timecnt32; ! thisleapi = leapi32; ! thisleapcnt = leapcnt32; ! } else { ! thistimei = 0; ! thistimecnt = timecnt; ! thisleapi = 0; ! thisleapcnt = leapcnt; ! } ! thistimelim = thistimei + thistimecnt; ! thisleaplim = thisleapi + thisleapcnt; #define DO(field) (void) fwrite((void *) tzh.field, \ (size_t) sizeof tzh.field, (size_t) 1, fp) ! tzh = tzh0; ! (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); ! tzh.tzh_version[0] = ZIC_VERSION; ! convert(eitol(typecnt), tzh.tzh_ttisgmtcnt); ! convert(eitol(typecnt), tzh.tzh_ttisstdcnt); ! convert(eitol(thisleapcnt), tzh.tzh_leapcnt); ! convert(eitol(thistimecnt), tzh.tzh_timecnt); ! convert(eitol(typecnt), tzh.tzh_typecnt); ! convert(eitol(charcnt), tzh.tzh_charcnt); ! DO(tzh_magic); ! DO(tzh_version); ! DO(tzh_reserved); ! DO(tzh_ttisgmtcnt); ! DO(tzh_ttisstdcnt); ! DO(tzh_leapcnt); ! DO(tzh_timecnt); ! DO(tzh_typecnt); ! DO(tzh_charcnt); #undef DO ! for (i = thistimei; i < thistimelim; ++i) ! if (pass == 1) ! puttzcode((long) ats[i], fp); ! else puttzcode64(ats[i], fp); ! if (thistimecnt > 0) ! (void) fwrite((void *) &types[thistimei], ! (size_t) sizeof types[0], ! (size_t) thistimecnt, ! fp); ! for (i = 0; i < typecnt; ++i) { ! puttzcode(gmtoffs[i], fp); ! (void) putc(isdsts[i], fp); ! (void) putc(abbrinds[i], fp); ! } ! if (charcnt != 0) ! (void) fwrite((void *) chars, (size_t) sizeof chars[0], ! (size_t) charcnt, fp); ! for (i = thisleapi; i < thisleaplim; ++i) { ! register zic_t todo; ! ! if (roll[i]) { ! if (timecnt == 0 || trans[i] < ats[0]) { ! j = 0; ! while (isdsts[j]) ! if (++j >= typecnt) { ! j = 0; ! break; ! } ! } else { ! j = 1; ! while (j < timecnt && ! trans[i] >= ats[j]) ! ++j; ! j = types[j - 1]; ! } ! todo = tadd(trans[i], -gmtoffs[j]); ! } else todo = trans[i]; ! if (pass == 1) ! puttzcode(todo, fp); ! else puttzcode64(todo, fp); ! puttzcode(corr[i], fp); ! } ! for (i = 0; i < typecnt; ++i) ! (void) putc(ttisstds[i], fp); ! for (i = 0; i < typecnt; ++i) ! (void) putc(ttisgmts[i], fp); } if (ferror(fp) || fclose(fp)) { (void) fprintf(stderr, _("%s: Error writing %s\n"), progname, fullname); *************** *** 1617,1622 **** --- 1674,1689 ---- } static void + updateminmax(x) + const int x; + { + if (min_year > x) + min_year = x; + if (max_year < x) + max_year = x; + } + + static void outzone(zpfirst, zonecount) const struct zone * const zpfirst; const int zonecount; *************** *** 1649,1655 **** --- 1716,1744 ---- */ startttisstd = FALSE; startttisgmt = FALSE; + min_year = max_year = EPOCH_YEAR; + if (leapseen) { + updateminmax(leapminyear); + updateminmax(leapmaxyear); + } for (i = 0; i < zonecount; ++i) { + zp = &zpfirst[i]; + updateminmax(zp->z_untilrule.r_loyear); + for (j = 0; j < zp->z_nrules; ++j) { + rp = &zp->z_rules[j]; + if (rp->r_lowasnum) + updateminmax(rp->r_loyear); + if (rp->r_hiwasnum) + updateminmax(rp->r_hiyear); + } + } + if (min_year >= INT_MIN + YEARSPERREPEAT) + min_year -= YEARSPERREPEAT; + else min_year = INT_MIN; + if (max_year <= INT_MAX - YEARSPERREPEAT) + max_year += YEARSPERREPEAT; + else max_year = INT_MAX; + for (i = 0; i < zonecount; ++i) { /* ** A guess that may well be corrected later. */ *************** *** 2180,2187 **** will not work with pre-2004 versions of zic")); } } - if (dayoff < 0 && !TYPE_SIGNED(zic_t)) - return min_time; if (dayoff < min_time / SECSPERDAY) return min_time; if (dayoff > max_time / SECSPERDAY) --- 2269,2274 ----
Arthur David Olson <olsona@lecserver.nci.nih.gov> writes:
+ For version-2-format time zone files, + the above header and data is followed by a second header and data, + identical in format except that + eight bytes are used for each transition time or leap second time.
This would indicate that version-2-format zone files should be at most 3 times the size of old-format files, as there's a separate copy and the second copy should be at most twice the size of the first. But that's not what I observe. For example, for America/Los_Angeles the old file is 1017 bytes, whereas the new file is 9107 bytes. Why did it grow by a factor of 9? Also, while we're on the subject, how about if we modify the format so that the second copy contains only the information that's not in the first copy? This should help prevent bloat.
Here are a few detailed comments about porting issues involved in the current draft. To be honest I don't really follow the code, and I haven't had time to check it out with outlandish time stamps, but I figure I should send what I have. First, the type name "signed64_t" is a bit misleading, since long long is not necessarily a 64-bit type (it might be longer). I suggest using the standard name "int_fast64_t" instead: this is the type of an integer that is at least 64 bits wide, and may be wider, and is reasonably fast. C99 defines this type, and the code can use that if available, falling back to long long (or long) otherwise. In a few places in localtime.c, the code uses signed64_t when it could use time_t (even if time_t is floating). "#define SECSPERREPEAT 12622780800" doesn't work on 32-bit C89 compilers: they report a compile-time error when that constant is used, since it's out of range. gcc sometimes warns if you compare an integer value to an unsigned value. (A cast silences this.) zic.c's wild-compilation check assumes CHAR_BIT == 8. The call to qsort uses types that don't conform to C89, because the comparison function is supposed to take const void * args, not void * args. (This is a problem in the current code too, come to think of it.) is32 assumes that integers use two's complement representation. It's easy to remove this assumption here. (I realize the assumption is probably elsewhere too.) There's a missing cast in the call to puttzcode, that would be necessary on K&R compilers. (Are we still worried about K&R compilers?) Here's a proposed patch: =================================================================== RCS file: RCS/Makefile,v retrieving revision 2005.8 diff -pu -r2005.8 Makefile --- Makefile 2005/04/04 15:24:31 2005.8 +++ Makefile 2005/04/22 19:36:45 @@ -95,6 +95,7 @@ LDLIBS= # -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4) # -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD) # -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD) +# -DHAVE_STDINT_H=1 if you have a pre-C99 compiler with "stdint.h" # -DHAVE_STRERROR=0 if your system lacks the strerror function # -DHAVE_SYMLINK=0 if your system lacks the symlink function # -DHAVE_SYS_STAT_H=0 if your compiler lacks a "sys/stat.h" =================================================================== RCS file: RCS/private.h,v retrieving revision 2005.5.1.1 diff -pu -r2005.5.1.1 private.h --- private.h 2005/04/21 19:24:10 2005.5.1.1 +++ private.h 2005/04/22 20:58:10 @@ -25,12 +25,6 @@ static char privatehid[] = "@(#)private. #endif /* !defined NOID */ #endif /* !defined lint */ -#ifndef SIGNED64_T -#define SIGNED64_T long long -#endif /* !defined SIGNED64_T */ - -typedef SIGNED64_T signed64_t; - /* ** Defaults for preprocessor symbols. ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'. @@ -52,6 +46,10 @@ typedef SIGNED64_T signed64_t; #define HAVE_SETTIMEOFDAY 3 #endif /* !defined HAVE_SETTIMEOFDAY */ +#ifndef HAVE_STDINT_H +#define HAVE_STDINT_H (199901 <= __STDC_VERSION__) +#endif /* !defined HAVE_STDINT_H */ + #ifndef HAVE_STRERROR #define HAVE_STRERROR 1 #endif /* !defined HAVE_STRERROR */ @@ -93,7 +91,7 @@ typedef SIGNED64_T signed64_t; #include "stdio.h" #include "errno.h" #include "string.h" -#include "limits.h" /* for CHAR_BIT */ +#include "limits.h" #include "time.h" #include "stdlib.h" @@ -128,6 +126,25 @@ typedef SIGNED64_T signed64_t; /* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #define is_digit(c) ((unsigned)(c) - '0' <= 9) +#if HAVE_STDINT_H +#include <stdint.h> +#endif /* !HAVE_STDINT_H */ + +#ifndef INT_FAST64_MAX +#ifdef LLONG_MAX +#define int_fast64_t long long int +#else /* !defined LLONG_MAX */ +#define int_fast64_t long int +#endif /* !defined LLONG_MAX */ +#endif /* !defined INT_FAST64_MAX */ + +#ifndef INT32_MIN +#define INT32_MIN (-1 - 2147483647) +#endif /* !defined INT32_MIN */ +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif /* !defined INT32_MAX */ + /* ** Workarounds for compilers/systems. */ @@ -318,10 +335,21 @@ char *ctime_r P((time_t const *, char *) #define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ #endif /* !defined YEARSPERREPEAT */ +/* +** The Gregorian year averages 365.2425 days, which is 31556952 seconds. +*/ +#ifndef AVGSECSPERYEAR +#define AVGSECSPERYEAR 31556952 +#endif /* !defined AVGSECSPERYEAR */ + #ifndef SECSPERREPEAT -#define SECSPERREPEAT 12622780800 +#define SECSPERREPEAT (YEARSPERREPEAT * (time_t) AVGSECSPERYEAR) #endif /* !defined SECSPERREPEAT */ +#ifndef SECSPERREPEAT_BITS +#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */ +#endif /* !defined LG_SECSPERREPEAT */ + /* ** UNIX was a registered trademark of The Open Group in 2003. */ =================================================================== RCS file: RCS/localtime.c,v retrieving revision 2005.5.1.1 diff -pu -r2005.5.1.1 localtime.c --- localtime.c 2005/04/21 19:24:10 2005.5.1.1 +++ localtime.c 2005/04/22 20:46:19 @@ -126,6 +126,7 @@ struct rule { static long detzcode P((const char * codep)); static time_t detzcode64 P((const char * codep)); +static int differ_by_repeat P((time_t t1, time_t t0)); static const char * getzname P((const char * strp)); static const char * getnum P((const char * strp, int * nump, int min, int max)); @@ -220,7 +221,7 @@ const char * const codep; result = (codep[0] & 0x80) ? ~0L : 0L; for (i = 0; i < 4; ++i) - result = (result << 8) | (codep[i] & 0xff); + result = 256 * result + (codep[i] & 0xff); return result; } @@ -228,13 +229,13 @@ static time_t detzcode64(codep) const char * const codep; { - register signed64_t result; + register time_t result; register int i; result = (codep[0] & 0x80) ? ~0L : 0L; for (i = 0; i < 8; ++i) - result = (result << 8) | (codep[i] & 0xff); - return (time_t) result; + result = 256 * result + (codep[i] & 0xff); + return result; } static void @@ -288,6 +289,17 @@ settzname P((void)) } static int +differ_by_repeat(t1, t0) +time_t t1; +time_t t0; +{ + return + ! (TYPE_INTEGRAL(time_t) && + TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS) && + t1 - t0 == SECSPERREPEAT; +} + +static int tzload(name, sp) register const char * name; register struct state * const sp; @@ -468,7 +480,7 @@ register struct state * const sp; /* ** If this is a narrow integer time_t system, we're done. */ - if (stored >= sizeof(time_t) && TYPE_INTEGRAL(time_t)) + if (stored >= (int)sizeof(time_t) && TYPE_INTEGRAL(time_t)) break; nread -= p - u.buf; for (i = 0; i < nread; ++i) @@ -477,12 +489,12 @@ register struct state * const sp; i = 2 * YEARSPERREPEAT; sp->goback = sp->goahead = sp->timecnt > i; sp->goback &= sp->types[i] == sp->types[0] && - sp->ats[i] - sp->ats[0] == SECSPERREPEAT; + differ_by_repeat(sp->ats[i], sp->ats[0]); sp->goahead &= sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] && - sp->ats[sp->timecnt - 1] - sp->ats[sp->timecnt - 1 - i] == - SECSPERREPEAT; + differ_by_repeat(sp->ats[sp->timecnt - 1], + sp->ats[sp->timecnt - 1 - i]); return 0; } @@ -1095,19 +1107,20 @@ struct tm * const tmp; time_t newt = t; register time_t seconds; register time_t tcycles; - register signed64_t icycles; + register int_fast64_t icycles; if (t < sp->ats[0]) seconds = sp->ats[0] - t; else seconds = t - sp->ats[sp->timecnt - 1]; --seconds; - tcycles = seconds / SECSPERREPEAT; + tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR; ++tcycles; icycles = tcycles; if (tcycles - icycles >= 1 || icycles - tcycles >= 1) return NULL; seconds = icycles; - seconds *= SECSPERREPEAT; + seconds *= YEARSPERREPEAT; + seconds *= AVGSECSPERYEAR; if (t < sp->ats[0]) newt += seconds; else newt -= seconds; =================================================================== RCS file: RCS/zic.c,v retrieving revision 2005.5.1.1 diff -pu -r2005.5.1.1 zic.c --- zic.c 2005/04/21 19:24:10 2005.5.1.1 +++ zic.c 2005/04/22 20:58:23 @@ -6,7 +6,7 @@ static char elsieid[] = "@(#)zic.c 7.127 #define ZIC_VERSION '2' -typedef signed64_t zic_t; +typedef int_fast64_t zic_t; #if HAVE_SYS_STAT_H #include "sys/stat.h" @@ -485,9 +485,9 @@ char * argv[]; (void) textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ progname = argv[0]; - if (8 > sizeof(signed64_t)) { + if (TYPE_BIT(zic_t) < 64) { (void) fprintf(stderr, -"%s: wild compilation-time specification of SIGNED64_T\n", progname); +"%s: wild compilation-time specification of zic_t\n", progname); exit(EXIT_FAILURE); } for (i = 1; i < argc; ++i) @@ -1432,22 +1432,20 @@ FILE * const fp; static int atcomp(avp, bvp) -void * avp; -void * bvp; +const void * avp; +const void * bvp; { - if (((struct attype *) avp)->at < ((struct attype *) bvp)->at) - return -1; - else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at) - return 1; - else return 0; + zic_t a = ((const struct attype *) avp)->at; + zic_t b = ((const struct attype *) bvp)->at; + + return a < b ? -1 : a > b; } static int is32(x) zic_t x; { - x >>= 31; - return x == 0 || x == ~0; + return INT32_MIN <= x && x <= INT32_MAX; } static void @@ -1638,7 +1636,7 @@ const char * const name; todo = tadd(trans[i], -gmtoffs[j]); } else todo = trans[i]; if (pass == 1) - puttzcode(todo, fp); + puttzcode((long) todo, fp); else puttzcode64(todo, fp); puttzcode(corr[i], fp); }
participants (2)
-
Arthur David Olson -
Paul Eggert