aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--time/tzfile.c2
-rw-r--r--timezone/private.h37
-rw-r--r--timezone/tzfile.h20
-rw-r--r--timezone/version2
-rw-r--r--timezone/zdump.c6
-rw-r--r--timezone/zic.c814
6 files changed, 554 insertions, 327 deletions
diff --git a/time/tzfile.c b/time/tzfile.c
index 013b3d0..af6da1b 100644
--- a/time/tzfile.c
+++ b/time/tzfile.c
@@ -189,7 +189,7 @@ __tzfile_read (const char *file, size_t extra, char **extrap)
chars = (size_t) decode (tzhead.tzh_charcnt);
num_leaps = (size_t) decode (tzhead.tzh_leapcnt);
num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
- num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
+ num_isgmt = (size_t) decode (tzhead.tzh_ttisutcnt);
if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types))
goto lose;
diff --git a/timezone/private.h b/timezone/private.h
index 1ead147..8513663 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 27a38cc..ee91104 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 63f5800..7f680ee 100644
--- a/timezone/version
+++ b/timezone/version
@@ -1 +1 @@
-2018i
+2020a
diff --git a/timezone/zdump.c b/timezone/zdump.c
index 0fc8ced..b532fe3 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 2ebc66a..2875b55 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)) {