diff options
Diffstat (limited to 'timezone')
-rw-r--r-- | timezone/private.h | 37 | ||||
-rw-r--r-- | timezone/tzfile.h | 20 | ||||
-rw-r--r-- | timezone/version | 2 | ||||
-rw-r--r-- | timezone/zdump.c | 6 | ||||
-rw-r--r-- | timezone/zic.c | 814 |
5 files changed, 553 insertions, 326 deletions
diff --git a/timezone/private.h b/timezone/private.h index 1ead14793b..8513663036 100644 --- a/timezone/private.h +++ b/timezone/private.h @@ -132,11 +132,16 @@ ** Nested includes */ -/* Avoid clashes with NetBSD by renaming NetBSD's declarations. */ +/* Avoid clashes with NetBSD by renaming NetBSD's declarations. + If defining the 'timezone' variable, avoid a clash with FreeBSD's + 'timezone' function by renaming its declaration. */ #define localtime_rz sys_localtime_rz #define mktime_z sys_mktime_z #define posix2time_z sys_posix2time_z #define time2posix_z sys_time2posix_z +#if defined USG_COMPAT && USG_COMPAT == 2 +# define timezone sys_timezone +#endif #define timezone_t sys_timezone_t #define tzalloc sys_tzalloc #define tzfree sys_tzfree @@ -145,6 +150,9 @@ #undef mktime_z #undef posix2time_z #undef time2posix_z +#if defined USG_COMPAT && USG_COMPAT == 2 +# undef timezone +#endif #undef timezone_t #undef tzalloc #undef tzfree @@ -198,6 +206,14 @@ # endif #endif +#ifndef ALTZONE +# if defined __sun || defined _M_XENIX +# define ALTZONE 1 +# else +# define ALTZONE 0 +# endif +#endif + #ifndef R_OK #define R_OK 4 #endif /* !defined R_OK */ @@ -409,6 +425,10 @@ static time_t sys_time(time_t *x) { return time(x); } typedef time_tz tz_time_t; +# undef asctime +# define asctime tz_asctime +# undef asctime_r +# define asctime_r tz_asctime_r # undef ctime # define ctime tz_ctime # undef ctime_r @@ -473,11 +493,13 @@ typedef time_tz tz_time_t; # undef timezone # define timezone tz_timezone # endif -# ifdef ALTZONE +# if ALTZONE # undef altzone # define altzone tz_altzone # endif +char *asctime(struct tm const *); +char *asctime_r(struct tm const *restrict, char *restrict); char *ctime(time_t const *); char *ctime_r(time_t const *, char *); double difftime(time_t, time_t) ATTRIBUTE_CONST; @@ -512,17 +534,14 @@ extern char *asctime_r(struct tm const *restrict, char *restrict); extern char **environ; #endif -#if TZ_TIME_T || !HAVE_POSIX_DECLS -# if HAVE_TZNAME +#if 2 <= HAVE_TZNAME + (TZ_TIME_T || !HAVE_POSIX_DECLS) extern char *tzname[]; -# endif -# if USG_COMPAT +#endif +#if 2 <= USG_COMPAT + (TZ_TIME_T || !HAVE_POSIX_DECLS) extern long timezone; extern int daylight; -# endif #endif - -#ifdef ALTZONE +#if 2 <= ALTZONE + (TZ_TIME_T || !HAVE_POSIX_DECLS) extern long altzone; #endif diff --git a/timezone/tzfile.h b/timezone/tzfile.h index 27a38cc74d..ee91104443 100644 --- a/timezone/tzfile.h +++ b/timezone/tzfile.h @@ -33,6 +33,9 @@ #define TZDEFRULES "posixrules" #endif /* !defined TZDEFRULES */ + +/* See Internet RFC 8536 for more details about the following format. */ + /* ** Each file begins with. . . */ @@ -43,7 +46,7 @@ struct tzhead { char tzh_magic[4]; /* TZ_MAGIC */ 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_ttisutcnt[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 */ char tzh_timecnt[4]; /* coded number of transition times */ @@ -66,14 +69,15 @@ struct tzhead { ** one (char [4]) total correction after above ** tzh_ttisstdcnt (char)s indexed by type; if 1, transition ** time is standard time, if 0, -** transition time is wall clock time -** if absent, transition times are -** assumed to be wall clock time -** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition -** time is UT, if 0, -** transition time is local time -** if absent, transition times are +** transition time is local (wall clock) +** time; if absent, transition times are ** assumed to be local time +** tzh_ttisutcnt (char)s indexed by type; if 1, transition +** time is UT, if 0, transition time is +** local time; if absent, transition +** times are assumed to be local time. +** When this is 1, the corresponding +** std/wall indicator must also be 1. */ /* diff --git a/timezone/version b/timezone/version index 63f58006ee..7f680eec36 100644 --- a/timezone/version +++ b/timezone/version @@ -1 +1 @@ -2018i +2020a diff --git a/timezone/zdump.c b/timezone/zdump.c index 0fc8ced96a..b532fe3eae 100644 --- a/timezone/zdump.c +++ b/timezone/zdump.c @@ -328,12 +328,12 @@ abbrok(const char *const abbrp, const char *const zone) cp = abbrp; while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+') ++cp; - if (cp - abbrp < 3) + if (*cp) + wp = _("has characters other than ASCII alphanumerics, '-' or '+'"); + else if (cp - abbrp < 3) wp = _("has fewer than 3 characters"); else if (cp - abbrp > 6) wp = _("has more than 6 characters"); - else if (*cp) - wp = _("has characters other than ASCII alphanumerics, '-' or '+'"); else return; fflush(stdout); diff --git a/timezone/zic.c b/timezone/zic.c index 2ebc66a9af..2875b5544c 100644 --- a/timezone/zic.c +++ b/timezone/zic.c @@ -92,13 +92,10 @@ struct rule { int r_wday; zic_t r_tod; /* time from midnight */ - bool r_todisstd; /* above is standard time if 1 */ - /* or wall clock time if 0 */ - bool r_todisgmt; /* above is GMT if 1 */ - /* or local time if 0 */ + bool r_todisstd; /* is r_tod standard time? */ + bool r_todisut; /* is r_tod UT? */ bool r_isdst; /* is this daylight saving time? */ - zic_t r_stdoff; /* offset from default time (which is - usually standard time) */ + zic_t r_save; /* offset from standard time */ const char * r_abbrvar; /* variable part of abbreviation */ bool r_todo; /* a rule to do (used in outzone) */ @@ -118,13 +115,13 @@ struct zone { lineno z_linenum; const char * z_name; - zic_t z_gmtoff; + zic_t z_stdoff; char * z_rule; const char * z_format; char z_format_specifier; bool z_isdst; - zic_t z_stdoff; + zic_t z_save; struct rule * z_rules; ptrdiff_t z_nrules; @@ -156,13 +153,14 @@ extern int optind; static void addtt(zic_t starttime, int type); static int addtype(zic_t, char const *, bool, bool, bool); -static void leapadd(zic_t, bool, int, int); +static void leapadd(zic_t, int, int); static void adjleap(void); static void associate(void); static void dolink(const char *, const char *, bool); static char ** getfields(char * buf); static zic_t gethms(const char * string, const char * errstring); -static zic_t getstdoff(char *, bool *); +static zic_t getsave(char *, bool *); +static void inexpires(char **, int); static void infile(const char * filename); static void inleap(char ** fields, int nfields); static void inlink(char ** fields, int nfields); @@ -227,13 +225,14 @@ static int typecnt; #define LC_ZONE 1 #define LC_LINK 2 #define LC_LEAP 3 +#define LC_EXPIRES 4 /* ** Which fields are which on a Zone line. */ #define ZF_NAME 1 -#define ZF_GMTOFF 2 +#define ZF_STDOFF 2 #define ZF_RULE 3 #define ZF_FORMAT 4 #define ZF_TILYEAR 5 @@ -247,7 +246,7 @@ static int typecnt; ** Which fields are which on a Zone continuation line. */ -#define ZFC_GMTOFF 0 +#define ZFC_STDOFF 0 #define ZFC_RULE 1 #define ZFC_FORMAT 2 #define ZFC_TILYEAR 3 @@ -268,7 +267,7 @@ static int typecnt; #define RF_MONTH 5 #define RF_DAY 6 #define RF_TOD 7 -#define RF_STDOFF 8 +#define RF_SAVE 8 #define RF_ABBRVAR 9 #define RULE_FIELDS 10 @@ -292,6 +291,9 @@ static int typecnt; #define LP_ROLL 6 #define LEAP_FIELDS 7 +/* Expires lines are like Leap lines, except without CORR and ROLL fields. */ +#define EXPIRES_FIELDS 5 + /* ** Year synonyms. */ @@ -335,6 +337,7 @@ static struct lookup const zi_line_codes[] = { }; static struct lookup const leap_line_codes[] = { { "Leap", LC_LEAP }, + { "Expires", LC_EXPIRES }, { NULL, 0} }; @@ -409,11 +412,11 @@ static struct attype { bool dontmerge; unsigned char type; } * attypes; -static zic_t gmtoffs[TZ_MAX_TYPES]; +static zic_t utoffs[TZ_MAX_TYPES]; static char isdsts[TZ_MAX_TYPES]; -static unsigned char abbrinds[TZ_MAX_TYPES]; +static unsigned char desigidx[TZ_MAX_TYPES]; static bool ttisstds[TZ_MAX_TYPES]; -static bool ttisgmts[TZ_MAX_TYPES]; +static bool ttisuts[TZ_MAX_TYPES]; static char chars[TZ_MAX_CHARS]; static zic_t trans[TZ_MAX_LEAPS]; static zic_t corr[TZ_MAX_LEAPS]; @@ -574,8 +577,10 @@ usage(FILE *stream, int status) { fprintf(stream, _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n" - "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n" - "\t[ -t localtime-link ] [ -L leapseconds ] [ filename ... ]\n\n" + "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]" + " [ -L leapseconds ] \\\n" + "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -t localtime-link ] \\\n" + "\t[ filename ... ]\n\n" "Report bugs to %s.\n"), progname, progname, REPORT_BUGS_TO); if (status == EXIT_SUCCESS) @@ -603,6 +608,51 @@ change_directory (char const *dir) } } +#define TIME_T_BITS_IN_FILE 64 + +/* The minimum and maximum values representable in a TZif file. */ +static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); +static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); + +/* The minimum, and one less than the maximum, values specified by + the -r option. These default to MIN_TIME and MAX_TIME. */ +static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); +static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); + +/* The time specified by an Expires line, or negative if no such line. */ +static zic_t leapexpires = -1; + +/* The time specified by an #expires comment, or negative if no such line. */ +static zic_t comment_leapexpires = -1; + +/* Set the time range of the output to TIMERANGE. + Return true if successful. */ +static bool +timerange_option(char *timerange) +{ + intmax_t lo = min_time, hi = max_time; + char *lo_end = timerange, *hi_end; + if (*timerange == '@') { + errno = 0; + lo = strtoimax (timerange + 1, &lo_end, 10); + if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE)) + return false; + } + hi_end = lo_end; + if (lo_end[0] == '/' && lo_end[1] == '@') { + errno = 0; + hi = strtoimax (lo_end + 2, &hi_end, 10); + if (hi_end == lo_end + 2 || hi == INTMAX_MIN) + return false; + hi -= ! (hi == INTMAX_MAX && errno == ERANGE); + } + if (*hi_end || hi < lo || max_time < lo || hi < min_time) + return false; + lo_time = lo < min_time ? min_time : lo; + hi_time = max_time < hi ? max_time : hi; + return true; +} + static const char * psxrules; static const char * lcltime; static const char * directory; @@ -610,11 +660,27 @@ static const char * leapsec; static const char * tzdefault; static const char * yitcommand; +/* -1 if the TZif output file should be slim, 0 if default, 1 if the + output should be fat for backward compatibility. Currently the + default is fat, although this may change. */ +static int bloat; + +static bool +want_bloat(void) +{ + return 0 <= bloat; +} + +#ifndef ZIC_BLOAT_DEFAULT +# define ZIC_BLOAT_DEFAULT "fat" +#endif + int main(int argc, char **argv) { register int c, k; register ptrdiff_t i, j; + bool timerange_given = false; #ifdef S_IWGRP umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH)); @@ -640,10 +706,22 @@ main(int argc, char **argv) } else if (strcmp(argv[k], "--help") == 0) { usage(stdout, EXIT_SUCCESS); } - while ((c = getopt(argc, argv, "d:l:L:p:st:vy:")) != EOF && c != -1) + while ((c = getopt(argc, argv, "b:d:l:L:p:r:st:vy:")) != EOF && c != -1) switch (c) { default: usage(stderr, EXIT_FAILURE); + case 'b': + if (strcmp(optarg, "slim") == 0) { + if (0 < bloat) + error(_("incompatible -b options")); + bloat = -1; + } else if (strcmp(optarg, "fat") == 0) { + if (bloat < 0) + error(_("incompatible -b options")); + bloat = 1; + } else + error(_("invalid option: -b '%s'"), optarg); + break; case 'd': if (directory == NULL) directory = optarg; @@ -708,12 +786,29 @@ _("%s: More than one -L option specified\n"), case 'v': noise = true; break; + case 'r': + if (timerange_given) { + fprintf(stderr, +_("%s: More than one -r option specified\n"), + progname); + return EXIT_FAILURE; + } + if (! timerange_option(optarg)) { + fprintf(stderr, +_("%s: invalid time range: %s\n"), + progname, optarg); + return EXIT_FAILURE; + } + timerange_given = true; + break; case 's': warning(_("-s ignored")); break; } if (optind == argc - 1 && strcmp(argv[optind], "=") == 0) usage(stderr, EXIT_FAILURE); /* usage message by request */ + if (bloat == 0) + bloat = strcmp(ZIC_BLOAT_DEFAULT, "slim") == 0 ? -1 : 1; if (directory == NULL) directory = TZDIR; if (tzdefault == NULL) @@ -961,11 +1056,6 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink) } } -#define TIME_T_BITS_IN_FILE 64 - -static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); -static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); - /* Return true if NAME is a directory. */ static bool itsdir(char const *name) @@ -1072,7 +1162,7 @@ associate(void) ** Maybe we have a local standard time offset. */ eat(zp->z_filename, zp->z_linenum); - zp->z_stdoff = getstdoff(zp->z_rule, &zp->z_isdst); + zp->z_save = getsave(zp->z_rule, &zp->z_isdst); /* ** Note, though, that if there's no rule, ** a '%s' in the format is a bad thing. @@ -1128,7 +1218,8 @@ infile(const char *name) ++nfields; } if (nfields == 0) { - /* nothing to do */ + if (name == leapsec && *buf == '#') + sscanf(buf, "#expires %"SCNdZIC, &comment_leapexpires); } else if (wantcont) { wantcont = inzcont(fields, nfields); } else { @@ -1153,6 +1244,10 @@ infile(const char *name) inleap(fields, nfields); wantcont = false; break; + case LC_EXPIRES: + inexpires(fields, nfields); + wantcont = false; + break; default: /* "cannot happen" */ fprintf(stderr, _("%s: panic: Invalid l_value %d\n"), @@ -1230,10 +1325,10 @@ warning(_("values over 24 hours not handled by pre-2007 versions of zic")); } static zic_t -getstdoff(char *field, bool *isdst) +getsave(char *field, bool *isdst) { int dst = -1; - zic_t stdoff; + zic_t save; size_t fieldlen = strlen(field); if (fieldlen != 0) { char *ep = field + fieldlen - 1; @@ -1242,9 +1337,9 @@ getstdoff(char *field, bool *isdst) case 's': dst = 0; *ep = '\0'; break; } } - stdoff = gethms(field, _("invalid saved time")); - *isdst = dst < 0 ? stdoff != 0 : dst; - return stdoff; + save = gethms(field, _("invalid saved time")); + *isdst = dst < 0 ? save != 0 : dst; + return save; } static void @@ -1267,7 +1362,7 @@ inrule(char **fields, int nfields) } r.r_filename = filename; r.r_linenum = linenum; - r.r_stdoff = getstdoff(fields[RF_STDOFF], &r.r_isdst); + r.r_save = getsave(fields[RF_SAVE], &r.r_isdst); rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]); r.r_name = ecpyalloc(fields[RF_NAME]); @@ -1328,13 +1423,13 @@ inzsub(char **fields, int nfields, bool iscont) register char * cp; char * cp1; static struct zone z; - register int i_gmtoff, i_rule, i_format; + register int i_stdoff, i_rule, i_format; register int i_untilyear, i_untilmonth; register int i_untilday, i_untiltime; register bool hasuntil; if (iscont) { - i_gmtoff = ZFC_GMTOFF; + i_stdoff = ZFC_STDOFF; i_rule = ZFC_RULE; i_format = ZFC_FORMAT; i_untilyear = ZFC_TILYEAR; @@ -1345,7 +1440,7 @@ inzsub(char **fields, int nfields, bool iscont) } else if (!namecheck(fields[ZF_NAME])) return false; else { - i_gmtoff = ZF_GMTOFF; + i_stdoff = ZF_STDOFF; i_rule = ZF_RULE; i_format = ZF_FORMAT; i_untilyear = ZF_TILYEAR; @@ -1356,7 +1451,7 @@ inzsub(char **fields, int nfields, bool iscont) } z.z_filename = filename; z.z_linenum = linenum; - z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset")); + z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset")); if ((cp = strchr(fields[i_format], '%')) != 0) { if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%') || strchr(fields[i_format], '/')) { @@ -1410,8 +1505,8 @@ inzsub(char **fields, int nfields, bool iscont) return hasuntil; } -static void -inleap(char **fields, int nfields) +static zic_t +getleapdatetime(char **fields, int nfields, bool expire_line) { register const char * cp; register const struct lookup * lp; @@ -1422,10 +1517,6 @@ inleap(char **fields, int nfields) zic_t t; char xs; - if (nfields != LEAP_FIELDS) { - error(_("wrong number of fields on Leap line")); - return; - } dayoff = 0; cp = fields[LP_YEAR]; if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) { @@ -1433,13 +1524,15 @@ inleap(char **fields, int nfields) ** Leapin' Lizards! */ error(_("invalid leaping year")); - return; + return -1; } - if (!leapseen || leapmaxyear < year) + if (!expire_line) { + if (!leapseen || leapmaxyear < year) leapmaxyear = year; - if (!leapseen || leapminyear > year) + if (!leapseen || leapminyear > year) leapminyear = year; - leapseen = true; + leapseen = true; + } j = EPOCH_YEAR; while (j != year) { if (year > j) { @@ -1453,7 +1546,7 @@ inleap(char **fields, int nfields) } if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { error(_("invalid month name")); - return; + return -1; } month = lp->l_value; j = TM_JANUARY; @@ -1466,47 +1559,60 @@ inleap(char **fields, int nfields) if (sscanf(cp, "%d%c", &day, &xs) != 1 || day <= 0 || day > len_months[isleap(year)][month]) { error(_("invalid day of month")); - return; + return -1; } dayoff = oadd(dayoff, day - 1); if (dayoff < min_time / SECSPERDAY) { error(_("time too small")); - return; + return -1; } if (dayoff > max_time / SECSPERDAY) { error(_("time too large")); - return; + return -1; } t = dayoff * SECSPERDAY; tod = gethms(fields[LP_TIME], _("invalid time of day")); - cp = fields[LP_CORR]; - { - register bool positive; - int count; - - if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */ - positive = false; - count = 1; - } else if (strcmp(cp, "+") == 0) { - positive = true; - count = 1; - } else { - error(_("illegal CORRECTION field on Leap line")); - return; - } - if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) { - error(_( - "illegal Rolling/Stationary field on Leap line" - )); - return; - } - t = tadd(t, tod); - if (t < 0) { - error(_("leap second precedes Epoch")); - return; - } - leapadd(t, positive, lp->l_value, count); - } + t = tadd(t, tod); + if (t < 0) + error(_("leap second precedes Epoch")); + return t; +} + +static void +inleap(char **fields, int nfields) +{ + if (nfields != LEAP_FIELDS) + error(_("wrong number of fields on Leap line")); + else { + zic_t t = getleapdatetime(fields, nfields, false); + if (0 <= t) { + struct lookup const *lp = byword(fields[LP_ROLL], leap_types); + if (!lp) + error(_("invalid Rolling/Stationary field on Leap line")); + else { + int correction = 0; + if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */ + correction = -1; + else if (strcmp(fields[LP_CORR], "+") == 0) + correction = 1; + else + error(_("invalid CORRECTION field on Leap line")); + if (correction) + leapadd(t, correction, lp->l_value); + } + } + } +} + +static void +inexpires(char **fields, int nfields) +{ + if (nfields != EXPIRES_FIELDS) + error(_("wrong number of fields on Expires line")); + else if (0 <= leapexpires) + error(_("multiple Expires lines")); + else + leapexpires = getleapdatetime(fields, nfields, true); } static void @@ -1549,26 +1655,26 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, } rp->r_month = lp->l_value; rp->r_todisstd = false; - rp->r_todisgmt = false; + rp->r_todisut = false; dp = ecpyalloc(timep); if (*dp != '\0') { ep = dp + strlen(dp) - 1; switch (lowerit(*ep)) { case 's': /* Standard */ rp->r_todisstd = true; - rp->r_todisgmt = false; + rp->r_todisut = false; *ep = '\0'; break; case 'w': /* Wall */ rp->r_todisstd = false; - rp->r_todisgmt = false; + rp->r_todisut = false; *ep = '\0'; break; case 'g': /* Greenwich */ case 'u': /* Universal */ case 'z': /* Zulu */ rp->r_todisstd = true; - rp->r_todisgmt = true; + rp->r_todisut = true; *ep = '\0'; break; } @@ -1714,12 +1820,16 @@ puttzcode(const int_fast32_t val, FILE *const fp) } static void -puttzcode64(const zic_t val, FILE *const fp) +puttzcodepass(zic_t val, FILE *fp, int pass) { + if (pass == 1) + puttzcode(val, fp); + else { char buf[8]; convert64(val, buf); fwrite(buf, sizeof buf, 1, fp); + } } static int @@ -1731,15 +1841,34 @@ atcomp(const void *avp, const void *bvp) return (a < b) ? -1 : (a > b); } -static void -swaptypes(int i, int j) +struct timerange { + int defaulttype; + ptrdiff_t base, count; + int leapbase, leapcount; +}; + +static struct timerange +limitrange(struct timerange r, zic_t lo, zic_t hi, + zic_t const *ats, unsigned char const *types) { - { zic_t t = gmtoffs[i]; gmtoffs[i] = gmtoffs[j]; gmtoffs[j] = t; } - { char t = isdsts[i]; isdsts[i] = isdsts[j]; isdsts[j] = t; } - { unsigned char t = abbrinds[i]; abbrinds[i] = abbrinds[j]; - abbrinds[j] = t; } - { bool t = ttisstds[i]; ttisstds[i] = ttisstds[j]; ttisstds[j] = t; } - { bool t = ttisgmts[i]; ttisgmts[i] = ttisgmts[j]; ttisgmts[j] = t; } + while (0 < r.count && ats[r.base] < lo) { + r.defaulttype = types[r.base]; + r.count--; + r.base++; + } + while (0 < r.leapcount && trans[r.leapbase] < lo) { + r.leapcount--; + r.leapbase++; + } + + if (hi < ZIC_MAX) { + while (0 < r.count && hi + 1 < ats[r.base + r.count - 1]) + r.count--; + while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1]) + r.leapcount--; + } + + return r; } static void @@ -1748,8 +1877,6 @@ writezone(const char *const name, const char *const string, char version, { register FILE * fp; register ptrdiff_t i, j; - register int leapcnt32, leapi32; - register ptrdiff_t timecnt32, timei32; register int pass; static const struct tzhead tzh0; static struct tzhead tzh; @@ -1764,6 +1891,7 @@ writezone(const char *const name, const char *const string, char version, _Alignof(zic_t))); void *typesptr = ats + nats; unsigned char *types = typesptr; + struct timerange rangeall, range32, range64; /* ** Sort. @@ -1779,17 +1907,24 @@ writezone(const char *const name, const char *const string, char version, toi = 0; fromi = 0; for ( ; fromi < timecnt; ++fromi) { - if (toi != 0 && ((attypes[fromi].at + - gmtoffs[attypes[toi - 1].type]) <= - (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0 - : attypes[toi - 2].type]))) { + if (toi != 0 + && ((attypes[fromi].at + + utoffs[attypes[toi - 1].type]) + <= (attypes[toi - 1].at + + utoffs[toi == 1 ? 0 + : attypes[toi - 2].type]))) { attypes[toi - 1].type = attypes[fromi].type; continue; } if (toi == 0 || attypes[fromi].dontmerge - || attypes[toi - 1].type != attypes[fromi].type) + || (utoffs[attypes[toi - 1].type] + != utoffs[attypes[fromi].type]) + || (isdsts[attypes[toi - 1].type] + != isdsts[attypes[fromi].type]) + || (desigidx[attypes[toi - 1].type] + != desigidx[attypes[fromi].type])) attypes[toi++] = attypes[fromi]; } timecnt = toi; @@ -1832,39 +1967,20 @@ writezone(const char *const name, const char *const string, char version, seconds, as the idea is to insert a transition just before 32-bit time_t rolls around, and this occurs at a slightly different moment if transitions are leap-second corrected. */ - if (WORK_AROUND_QTBUG_53071 && timecnt != 0 + if (WORK_AROUND_QTBUG_53071 && timecnt != 0 && want_bloat() && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) { ats[timecnt] = y2038_boundary - 1; types[timecnt] = types[timecnt - 1]; timecnt++; } - /* - ** Figure out 32-bit-limited starts and counts. - */ - timecnt32 = timecnt; - timei32 = 0; - leapcnt32 = leapcnt; - leapi32 = 0; - while (0 < timecnt32 && INT32_MAX < ats[timecnt32 - 1]) - --timecnt32; - while (1 < timecnt32 && ats[timei32] < INT32_MIN - && ats[timei32 + 1] <= INT32_MIN) { - /* Discard too-low transitions, except keep any last too-low - transition if no transition is exactly at INT32_MIN. - The kept transition will be output as an INT32_MIN - "transition" appropriate for buggy 32-bit clients that do - not use time type 0 for timestamps before the first - transition; see below. */ - --timecnt32; - ++timei32; - } - while (0 < leapcnt32 && INT32_MAX < trans[leapcnt32 - 1]) - --leapcnt32; - while (0 < leapcnt32 && trans[leapi32] < INT32_MIN) { - --leapcnt32; - ++leapi32; - } + rangeall.defaulttype = defaulttype; + rangeall.base = rangeall.leapbase = 0; + rangeall.count = timecnt; + rangeall.leapcount = leapcnt; + range64 = limitrange(rangeall, lo_time, hi_time, ats, types); + range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types); + /* ** Remove old file, if any, to snap links. */ @@ -1894,43 +2010,84 @@ writezone(const char *const name, const char *const string, char version, for (pass = 1; pass <= 2; ++pass) { register ptrdiff_t thistimei, thistimecnt, thistimelim; register int thisleapi, thisleapcnt, thisleaplim; + int currenttype, thisdefaulttype; + bool locut, hicut; + zic_t lo; int old0; char omittype[TZ_MAX_TYPES]; int typemap[TZ_MAX_TYPES]; - register int thistypecnt; + int thistypecnt, stdcnt, utcnt; char thischars[TZ_MAX_CHARS]; int thischarcnt; bool toomanytimes; int indmap[TZ_MAX_CHARS]; if (pass == 1) { - thistimei = timei32; - thistimecnt = timecnt32; + /* Arguably the default time type in the 32-bit data + should be range32.defaulttype, which is suited for + timestamps just before INT32_MIN. However, zic + traditionally used the time type of the indefinite + past instead. Internet RFC 8532 says readers should + ignore 32-bit data, so this discrepancy matters only + to obsolete readers where the traditional type might + be more appropriate even if it's "wrong". So, use + the historical zic value, unless -r specifies a low + cutoff that excludes some 32-bit timestamps. */ + thisdefaulttype = (lo_time <= INT32_MIN + ? range64.defaulttype + : range32.defaulttype); + + thistimei = range32.base; + thistimecnt = range32.count; toomanytimes = thistimecnt >> 31 >> 1 != 0; - thisleapi = leapi32; - thisleapcnt = leapcnt32; + thisleapi = range32.leapbase; + thisleapcnt = range32.leapcount; + locut = INT32_MIN < lo_time; + hicut = hi_time < INT32_MAX; } else { - thistimei = 0; - thistimecnt = timecnt; + thisdefaulttype = range64.defaulttype; + thistimei = range64.base; + thistimecnt = range64.count; toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0; - thisleapi = 0; - thisleapcnt = leapcnt; + thisleapi = range64.leapbase; + thisleapcnt = range64.leapcount; + locut = min_time < lo_time; + hicut = hi_time < max_time; } if (toomanytimes) error(_("too many transition times")); + + /* Keep the last too-low transition if no transition is + exactly at LO. The kept transition will be output as + a LO "transition"; see "Output a LO_TIME transition" + below. This is needed when the output is truncated at + the start, and is also useful when catering to buggy + 32-bit clients that do not use time type 0 for + timestamps before the first transition. */ + if (0 < thistimei && ats[thistimei] != lo_time) { + thistimei--; + thistimecnt++; + locut = false; + } + thistimelim = thistimei + thistimecnt; thisleaplim = thisleapi + thisleapcnt; + if (thistimecnt != 0) { + if (ats[thistimei] == lo_time) + locut = false; + if (hi_time < ZIC_MAX && ats[thistimelim - 1] == hi_time + 1) + hicut = false; + } memset(omittype, true, typecnt); - omittype[defaulttype] = false; + omittype[thisdefaulttype] = false; for (i = thistimei; i < thistimelim; i++) omittype[types[i]] = false; - /* Reorder types to make DEFAULTTYPE type 0. - Use TYPEMAP to swap OLD0 and DEFAULTTYPE so that - DEFAULTTYPE appears as type 0 in the output instead + /* Reorder types to make THISDEFAULTTYPE type 0. + Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that + THISDEFAULTTYPE appears as type 0 in the output instead of OLD0. TYPEMAP also omits unused types. */ old0 = strlen(omittype); - swaptypes(old0, defaulttype); #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH /* @@ -1941,7 +2098,7 @@ writezone(const char *const name, const char *const string, char version, ** (to help get global "altzone" and "timezone" variables ** set correctly). */ - { + if (want_bloat()) { register int mrudst, mrustd, hidst, histd, type; hidst = histd = mrudst = mrustd = -1; @@ -1949,31 +2106,35 @@ writezone(const char *const name, const char *const string, char version, if (isdsts[types[i]]) mrudst = types[i]; else mrustd = types[i]; - for (i = old0; i < typecnt; i++) - if (!omittype[i]) { - if (isdsts[i]) - hidst = i; - else histd = i; - } + for (i = old0; i < typecnt; i++) { + int h = (i == old0 ? thisdefaulttype + : i == thisdefaulttype ? old0 : i); + if (!omittype[h]) { + if (isdsts[h]) + hidst = i; + else + histd = i; + } + } if (hidst >= 0 && mrudst >= 0 && hidst != mrudst && - gmtoffs[hidst] != gmtoffs[mrudst]) { + utoffs[hidst] != utoffs[mrudst]) { isdsts[mrudst] = -1; - type = addtype(gmtoffs[mrudst], - &chars[abbrinds[mrudst]], + type = addtype(utoffs[mrudst], + &chars[desigidx[mrudst]], true, ttisstds[mrudst], - ttisgmts[mrudst]); + ttisuts[mrudst]); isdsts[mrudst] = 1; omittype[type] = false; } if (histd >= 0 && mrustd >= 0 && histd != mrustd && - gmtoffs[histd] != gmtoffs[mrustd]) { + utoffs[histd] != utoffs[mrustd]) { isdsts[mrustd] = -1; - type = addtype(gmtoffs[mrustd], - &chars[abbrinds[mrustd]], + type = addtype(utoffs[mrustd], + &chars[desigidx[mrustd]], false, ttisstds[mrustd], - ttisgmts[mrustd]); + ttisuts[mrustd]); isdsts[mrustd] = 0; omittype[type] = false; } @@ -1982,21 +2143,25 @@ writezone(const char *const name, const char *const string, char version, thistypecnt = 0; for (i = old0; i < typecnt; i++) if (!omittype[i]) - typemap[i == old0 ? defaulttype - : i == defaulttype ? old0 : i] + typemap[i == old0 ? thisdefaulttype + : i == thisdefaulttype ? old0 : i] = thistypecnt++; for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i) indmap[i] = -1; - thischarcnt = 0; + thischarcnt = stdcnt = utcnt = 0; for (i = old0; i < typecnt; i++) { register char * thisabbr; if (omittype[i]) continue; - if (indmap[abbrinds[i]] >= 0) + if (ttisstds[i]) + stdcnt = thistypecnt; + if (ttisuts[i]) + utcnt = thistypecnt; + if (indmap[desigidx[i]] >= 0) continue; - thisabbr = &chars[abbrinds[i]]; + thisabbr = &chars[desigidx[i]]; for (j = 0; j < thischarcnt; ++j) if (strcmp(&thischars[j], thisabbr) == 0) break; @@ -2004,49 +2169,75 @@ writezone(const char *const name, const char *const string, char version, strcpy(&thischars[thischarcnt], thisabbr); thischarcnt += strlen(thisabbr) + 1; } - indmap[abbrinds[i]] = j; + indmap[desigidx[i]] = j; + } + if (pass == 1 && !want_bloat()) { + utcnt = stdcnt = thisleapcnt = 0; + thistimecnt = - (locut + hicut); + thistypecnt = thischarcnt = 1; + thistimelim = thistimei; } #define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp) tzh = tzh0; memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic); tzh.tzh_version[0] = version; - convert(thistypecnt, tzh.tzh_ttisgmtcnt); - convert(thistypecnt, tzh.tzh_ttisstdcnt); + convert(utcnt, tzh.tzh_ttisutcnt); + convert(stdcnt, tzh.tzh_ttisstdcnt); convert(thisleapcnt, tzh.tzh_leapcnt); - convert(thistimecnt, tzh.tzh_timecnt); + convert(locut + thistimecnt + hicut, tzh.tzh_timecnt); convert(thistypecnt, tzh.tzh_typecnt); convert(thischarcnt, tzh.tzh_charcnt); DO(tzh_magic); DO(tzh_version); DO(tzh_reserved); - DO(tzh_ttisgmtcnt); + DO(tzh_ttisutcnt); 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) - /* - ** Output an INT32_MIN "transition" - ** if appropriate; see above. - */ - puttzcode(((ats[i] < INT32_MIN) ? - INT32_MIN : ats[i]), fp); - else puttzcode64(ats[i], fp); + if (pass == 1 && !want_bloat()) { + /* Output a minimal data block with just one time type. */ + puttzcode(0, fp); /* utoff */ + putc(0, fp); /* dst */ + putc(0, fp); /* index of abbreviation */ + putc(0, fp); /* empty-string abbreviation */ + continue; + } + + /* Output a LO_TIME transition if needed; see limitrange. + But do not go below the minimum representable value + for this pass. */ + lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time; + + if (locut) + puttzcodepass(lo, fp, pass); + for (i = thistimei; i < thistimelim; ++i) { + zic_t at = ats[i] < lo ? lo : ats[i]; + puttzcodepass(at, fp, pass); + } + if (hicut) + puttzcodepass(hi_time + 1, fp, pass); + currenttype = 0; + if (locut) + putc(currenttype, fp); for (i = thistimei; i < thistimelim; ++i) { - unsigned char uc; + currenttype = typemap[types[i]]; + putc(currenttype, fp); + } + if (hicut) + putc(currenttype, fp); - uc = typemap[types[i]]; - fwrite(&uc, sizeof uc, 1, fp); + for (i = old0; i < typecnt; i++) { + int h = (i == old0 ? thisdefaulttype + : i == thisdefaulttype ? old0 : i); + if (!omittype[h]) { + puttzcode(utoffs[h], fp); + putc(isdsts[h], fp); + putc(indmap[desigidx[h]], fp); + } } - for (i = old0; i < typecnt; i++) - if (!omittype[i]) { - puttzcode(gmtoffs[i], fp); - putc(isdsts[i], fp); - putc((unsigned char) indmap[abbrinds[i]], fp); - } if (thischarcnt != 0) fwrite(thischars, sizeof thischars[0], thischarcnt, fp); @@ -2068,20 +2259,19 @@ writezone(const char *const name, const char *const string, char version, ++j; j = types[j - 1]; } - todo = tadd(trans[i], -gmtoffs[j]); + todo = tadd(trans[i], -utoffs[j]); } else todo = trans[i]; - if (pass == 1) - puttzcode(todo, fp); - else puttzcode64(todo, fp); + puttzcodepass(todo, fp, pass); puttzcode(corr[i], fp); } - for (i = old0; i < typecnt; i++) + if (stdcnt != 0) + for (i = old0; i < typecnt; i++) if (!omittype[i]) putc(ttisstds[i], fp); - for (i = old0; i < typecnt; i++) + if (utcnt != 0) + for (i = old0; i < typecnt; i++) if (!omittype[i]) - putc(ttisgmts[i], fp); - swaptypes(old0, defaulttype); + putc(ttisuts[i], fp); } fprintf(fp, "\n%s\n", string); close_file(fp, directory, name); @@ -2126,7 +2316,7 @@ abbroffset(char *buf, zic_t offset) static size_t doabbr(char *abbr, struct zone const *zp, char const *letters, - bool isdst, zic_t stdoff, bool doquotes) + bool isdst, zic_t save, bool doquotes) { register char * cp; register char * slashp; @@ -2137,7 +2327,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters, if (slashp == NULL) { char letterbuf[PERCENT_Z_LEN_BOUND + 1]; if (zp->z_format_specifier == 'z') - letters = abbroffset(letterbuf, zp->z_gmtoff + stdoff); + letters = abbroffset(letterbuf, zp->z_stdoff + save); else if (!letters) letters = "%s"; sprintf(abbr, format, letters); @@ -2202,8 +2392,7 @@ stringoffset(char *result, zic_t offset) } static int -stringrule(char *result, const struct rule *const rp, const zic_t dstoff, - const zic_t gmtoff) +stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff) { register zic_t tod = rp->r_tod; register int compat = 0; @@ -2250,10 +2439,10 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff, result += sprintf(result, "M%d.%d.%d", rp->r_month + 1, week, wday); } - if (rp->r_todisgmt) - tod += gmtoff; + if (rp->r_todisut) + tod += stdoff; if (rp->r_todisstd && !rp->r_isdst) - tod += dstoff; + tod += save; if (tod != 2 * SECSPERMIN * MINSPERHOUR) { *result++ = '/'; if (! stringoffset(result, tod)) @@ -2283,8 +2472,6 @@ rule_cmp(struct rule const *a, struct rule const *b) return a->r_dayofmonth - b->r_dayofmonth; } -enum { YEAR_BY_YEAR_ZONE = 1 }; - static int stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) { @@ -2301,6 +2488,12 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) struct rule stdr, dstr; result[0] = '\0'; + + /* Internet RFC 8536 section 5.1 says to use an empty TZ string if + future timestamps are truncated. */ + if (hi_time < max_time) + return -1; + zp = zpfirst + zonecount - 1; stdrp = dstrp = NULL; for (i = 0; i < zp->z_nrules; ++i) { @@ -2333,31 +2526,23 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) if (rule_cmp(stdrp, rp) < 0) stdrp = rp; } - /* - ** Horrid special case: if year is 2037, - ** presume this is a zone handled on a year-by-year basis; - ** do not try to apply a rule to the zone. - */ - if (stdrp != NULL && stdrp->r_hiyear == 2037) - return YEAR_BY_YEAR_ZONE; - if (stdrp != NULL && stdrp->r_isdst) { /* Perpetual DST. */ dstr.r_month = TM_JANUARY; dstr.r_dycode = DC_DOM; dstr.r_dayofmonth = 1; dstr.r_tod = 0; - dstr.r_todisstd = dstr.r_todisgmt = false; + dstr.r_todisstd = dstr.r_todisut = false; dstr.r_isdst = stdrp->r_isdst; - dstr.r_stdoff = stdrp->r_stdoff; + dstr.r_save = stdrp->r_save; dstr.r_abbrvar = stdrp->r_abbrvar; stdr.r_month = TM_DECEMBER; stdr.r_dycode = DC_DOM; stdr.r_dayofmonth = 31; - stdr.r_tod = SECSPERDAY + stdrp->r_stdoff; - stdr.r_todisstd = stdr.r_todisgmt = false; + stdr.r_tod = SECSPERDAY + stdrp->r_save; + stdr.r_todisstd = stdr.r_todisut = false; stdr.r_isdst = false; - stdr.r_stdoff = 0; + stdr.r_save = 0; stdr.r_abbrvar = (stdabbrrp ? stdabbrrp->r_abbrvar : ""); dstrp = &dstr; @@ -2368,7 +2553,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) return -1; abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar; len = doabbr(result, zp, abbrvar, false, 0, true); - offsetlen = stringoffset(result + len, -zp->z_gmtoff); + offsetlen = stringoffset(result + len, - zp->z_stdoff); if (! offsetlen) { result[0] = '\0'; return -1; @@ -2377,10 +2562,10 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) if (dstrp == NULL) return compat; len += doabbr(result + len, zp, dstrp->r_abbrvar, - dstrp->r_isdst, dstrp->r_stdoff, true); - if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) { + dstrp->r_isdst, dstrp->r_save, true); + if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) { offsetlen = stringoffset(result + len, - -(zp->z_gmtoff + dstrp->r_stdoff)); + - (zp->z_stdoff + dstrp->r_save)); if (! offsetlen) { result[0] = '\0'; return -1; @@ -2388,7 +2573,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) len += offsetlen; } result[len++] = ','; - c = stringrule(result + len, dstrp, dstrp->r_stdoff, zp->z_gmtoff); + c = stringrule(result + len, dstrp, dstrp->r_save, zp->z_stdoff); if (c < 0) { result[0] = '\0'; return -1; @@ -2397,7 +2582,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) compat = c; len += strlen(result + len); result[len++] = ','; - c = stringrule(result + len, stdrp, dstrp->r_stdoff, zp->z_gmtoff); + c = stringrule(result + len, stdrp, dstrp->r_save, zp->z_stdoff); if (c < 0) { result[0] = '\0'; return -1; @@ -2415,12 +2600,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) register ptrdiff_t i, j; register bool usestart, useuntil; register zic_t starttime, untiltime; - register zic_t gmtoff; register zic_t stdoff; + register zic_t save; register zic_t year; register zic_t startoff; register bool startttisstd; - register bool startttisgmt; + register bool startttisut; register int type; register char * startbuf; register char * ab; @@ -2456,7 +2641,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) ** for noting the need to unconditionally initialize startttisstd. */ startttisstd = false; - startttisgmt = false; + startttisut = false; min_year = max_year = EPOCH_YEAR; if (leapseen) { updateminmax(leapminyear); @@ -2481,13 +2666,13 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) */ compat = stringzone(envvar, zpfirst, zonecount); version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION; - do_extend = compat < 0 || compat == YEAR_BY_YEAR_ZONE; + do_extend = compat < 0; if (noise) { if (!*envvar) warning("%s %s", _("no POSIX environment variable for zone"), zpfirst->z_name); - else if (compat != 0 && compat != YEAR_BY_YEAR_ZONE) { + else if (compat != 0) { /* Circa-COMPAT clients, and earlier clients, might not work for this zone when given dates before 1970 or after 2038. */ @@ -2529,35 +2714,37 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) max_year = min_year + years_of_observations; } } - /* - ** For the benefit of older systems, - ** generate data from 1900 through 2038. - */ - if (min_year > 1900) - min_year = 1900; max_year0 = max_year; - if (max_year < 2038) + if (want_bloat()) { + /* For the benefit of older systems, + generate data from 1900 through 2038. */ + if (min_year > 1900) + min_year = 1900; + if (max_year < 2038) max_year = 2038; + } + for (i = 0; i < zonecount; ++i) { + struct rule *prevrp = NULL; /* ** A guess that may well be corrected later. */ - stdoff = 0; + save = 0; zp = &zpfirst[i]; usestart = i > 0 && (zp - 1)->z_untiltime > min_time; useuntil = i < (zonecount - 1); if (useuntil && zp->z_untiltime <= min_time) continue; - gmtoff = zp->z_gmtoff; + stdoff = zp->z_stdoff; eat(zp->z_filename, zp->z_linenum); *startbuf = '\0'; - startoff = zp->z_gmtoff; + startoff = zp->z_stdoff; if (zp->z_nrules == 0) { - stdoff = zp->z_stdoff; - doabbr(startbuf, zp, NULL, zp->z_isdst, stdoff, false); - type = addtype(oadd(zp->z_gmtoff, stdoff), + save = zp->z_save; + doabbr(startbuf, zp, NULL, zp->z_isdst, save, false); + type = addtype(oadd(zp->z_stdoff, save), startbuf, zp->z_isdst, startttisstd, - startttisgmt); + startttisut); if (usestart) { addtt(starttime, type); usestart = false; @@ -2593,16 +2780,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) if (useuntil) { /* ** Turn untiltime into UT - ** assuming the current gmtoff and - ** stdoff values. + ** assuming the current stdoff and + ** save values. */ untiltime = zp->z_untiltime; - if (!zp->z_untilrule.r_todisgmt) + if (!zp->z_untilrule.r_todisut) untiltime = tadd(untiltime, - -gmtoff); + -stdoff); if (!zp->z_untilrule.r_todisstd) untiltime = tadd(untiltime, - -stdoff); + -save); } /* ** Find the rule (of those to do, if any) @@ -2615,9 +2802,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) continue; eats(zp->z_filename, zp->z_linenum, rp->r_filename, rp->r_linenum); - offset = rp->r_todisgmt ? 0 : gmtoff; + offset = rp->r_todisut ? 0 : stdoff; if (!rp->r_todisstd) - offset = oadd(offset, stdoff); + offset = oadd(offset, save); jtime = rp->r_temp; if (jtime == min_time || jtime == max_time) @@ -2644,38 +2831,43 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) rp->r_todo = false; if (useuntil && ktime >= untiltime) break; - stdoff = rp->r_stdoff; + save = rp->r_save; if (usestart && ktime == starttime) usestart = false; if (usestart) { if (ktime < starttime) { - startoff = oadd(zp->z_gmtoff, - stdoff); + startoff = oadd(zp->z_stdoff, + save); doabbr(startbuf, zp, rp->r_abbrvar, rp->r_isdst, - rp->r_stdoff, + rp->r_save, false); continue; } - if (*startbuf == '\0' && - startoff == oadd(zp->z_gmtoff, - stdoff)) { + if (*startbuf == '\0' + && startoff == oadd(zp->z_stdoff, + save)) { doabbr(startbuf, zp, rp->r_abbrvar, rp->r_isdst, - rp->r_stdoff, + rp->r_save, false); } } eats(zp->z_filename, zp->z_linenum, rp->r_filename, rp->r_linenum); doabbr(ab, zp, rp->r_abbrvar, - rp->r_isdst, rp->r_stdoff, false); - offset = oadd(zp->z_gmtoff, rp->r_stdoff); + rp->r_isdst, rp->r_save, false); + offset = oadd(zp->z_stdoff, rp->r_save); + if (!want_bloat() && !useuntil && !do_extend + && prevrp + && rp->r_hiyear == ZIC_MAX + && prevrp->r_hiyear == ZIC_MAX) + break; type = addtype(offset, ab, rp->r_isdst, - rp->r_todisstd, rp->r_todisgmt); + rp->r_todisstd, rp->r_todisut); if (defaulttype < 0 && !rp->r_isdst) defaulttype = type; if (rp->r_hiyear == ZIC_MAX @@ -2683,6 +2875,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) && ktime < attypes[lastatmax].at)) lastatmax = timecnt; addtt(ktime, type); + prevrp = rp; } } if (usestart) { @@ -2695,9 +2888,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) if (*startbuf == '\0') error(_("can't determine time zone abbreviation to use just after until time")); else { - bool isdst = startoff != zp->z_gmtoff; + bool isdst = startoff != zp->z_stdoff; type = addtype(startoff, startbuf, isdst, - startttisstd, startttisgmt); + startttisstd, startttisut); if (defaulttype < 0 && !isdst) defaulttype = type; addtt(starttime, type); @@ -2708,12 +2901,12 @@ error(_("can't determine time zone abbreviation to use just after until time")); */ if (useuntil) { startttisstd = zp->z_untilrule.r_todisstd; - startttisgmt = zp->z_untilrule.r_todisgmt; + startttisut = zp->z_untilrule.r_todisut; starttime = zp->z_untiltime; if (!startttisstd) - starttime = tadd(starttime, -stdoff); - if (!startttisgmt) - starttime = tadd(starttime, -gmtoff); + starttime = tadd(starttime, -save); + if (!startttisut) + starttime = tadd(starttime, -stdoff); } } if (defaulttype < 0) @@ -2737,11 +2930,12 @@ error(_("can't determine time zone abbreviation to use just after until time")); xr.r_dycode = DC_DOM; xr.r_dayofmonth = 1; xr.r_tod = 0; - for (lastat = &attypes[0], i = 1; i < timecnt; i++) + for (lastat = attypes, i = 1; i < timecnt; i++) if (attypes[i].at > lastat->at) lastat = &attypes[i]; - if (lastat->at < rpytime(&xr, max_year - 1)) { - addtt(rpytime(&xr, max_year + 1), lastat->type); + if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) { + addtt(rpytime(&xr, max_year + 1), + lastat ? lastat->type : defaulttype); attypes[timecnt - 1].dontmerge = true; } } @@ -2762,20 +2956,28 @@ addtt(zic_t starttime, int type) } static int -addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt) +addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) { register int i, j; - /* - ** See if there's already an entry for this zone type. - ** If so, just return its index. - */ - for (i = 0; i < typecnt; ++i) { - if (gmtoff == gmtoffs[i] && isdst == isdsts[i] && - strcmp(abbr, &chars[abbrinds[i]]) == 0 && - ttisstd == ttisstds[i] && - ttisgmt == ttisgmts[i]) - return i; + if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) { + error(_("UT offset out of range")); + exit(EXIT_FAILURE); + } + if (!want_bloat()) + ttisstd = ttisut = false; + + for (j = 0; j < charcnt; ++j) + if (strcmp(&chars[j], abbr) == 0) + break; + if (j == charcnt) + newabbr(abbr); + else { + /* If there's already an entry, return its index. */ + for (i = 0; i < typecnt; i++) + if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i] + && ttisstd == ttisstds[i] && ttisut == ttisuts[i]) + return i; } /* ** There isn't one; add a new one, unless there are already too @@ -2785,48 +2987,34 @@ addtype(zic_t gmtoff, char const *abbr, bool isdst, bool ttisstd, bool ttisgmt) error(_("too many local time types")); exit(EXIT_FAILURE); } - if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) { - error(_("UT offset out of range")); - exit(EXIT_FAILURE); - } - gmtoffs[i] = gmtoff; + i = typecnt++; + utoffs[i] = utoff; isdsts[i] = isdst; ttisstds[i] = ttisstd; - ttisgmts[i] = ttisgmt; - - for (j = 0; j < charcnt; ++j) - if (strcmp(&chars[j], abbr) == 0) - break; - if (j == charcnt) - newabbr(abbr); - abbrinds[i] = j; - ++typecnt; + ttisuts[i] = ttisut; + desigidx[i] = j; return i; } static void -leapadd(zic_t t, bool positive, int rolling, int count) +leapadd(zic_t t, int correction, int rolling) { - register int i, j; + register int i; - if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) { + if (TZ_MAX_LEAPS <= leapcnt) { error(_("too many leap seconds")); exit(EXIT_FAILURE); } for (i = 0; i < leapcnt; ++i) if (t <= trans[i]) break; - do { - for (j = leapcnt; j > i; --j) { - trans[j] = trans[j - 1]; - corr[j] = corr[j - 1]; - roll[j] = roll[j - 1]; - } - trans[i] = t; - corr[i] = positive ? 1 : -count; - roll[i] = rolling; - ++leapcnt; - } while (positive && --count != 0); + memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans); + memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr); + memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll); + trans[i] = t; + corr[i] = correction; + roll[i] = rolling; + ++leapcnt; } static void @@ -2848,6 +3036,22 @@ adjleap(void) trans[i] = tadd(trans[i], last); last = corr[i] += last; } + + if (leapexpires < 0) { + leapexpires = comment_leapexpires; + if (0 <= leapexpires) + warning(_("\"#expires\" is obsolescent; use \"Expires\"")); + } + + if (0 <= leapexpires) { + leapexpires = oadd(leapexpires, last); + if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) { + error(_("last Leap time does not precede Expires time")); + exit(EXIT_FAILURE); + } + if (leapexpires <= hi_time) + hi_time = leapexpires - 1; + } } static char * @@ -3020,8 +3224,8 @@ byword(const char *word, const struct lookup *table) else return NULL; /* multiple inexact matches */ } - /* Warn about any backward-compatibility issue with pre-2017c zic. */ - if (foundlp) { + if (foundlp && noise) { + /* Warn about any backward-compatibility issue with pre-2017c zic. */ bool pre_2017c_match = false; for (lp = table; lp->l_word; lp++) if (itsabbr(word, lp->l_word)) { |