[PATCH] Update timezone code from tzcode 2020a

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] Update timezone code from tzcode 2020a

Paul Eggert
This patch updates files coming from tzcode to tzcode 2020a.
This is mostly for better support for Internet RFC 8536, by adding
support to zic for the Expires line (new to tzcode 2020a), the -b
option (new to 2019b) and the -r option (new to 2019a).
One trivial change to other glibc was needed.
* time/tzfile.c (__tzfile_read): Adjust to tzcode private.h renaming.
* timezone/private.h, timezone/tzfile.h, timezone/version:
* timezone/zdump.c, timezone/zic.c: Update from tzcode 2020a.
---
 time/tzfile.c      |   2 +-
 timezone/private.h |  37 ++-
 timezone/tzfile.h  |  20 +-
 timezone/version   |   2 +-
 timezone/zdump.c   |   6 +-
 timezone/zic.c     | 814 ++++++++++++++++++++++++++++-----------------
 6 files changed, 554 insertions(+), 327 deletions(-)

diff --git a/time/tzfile.c b/time/tzfile.c
index 013b3d03f3..af6da1bf00 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 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..8e4c88ab81 100644
--- a/timezone/version
+++ b/timezone/version
@@ -1 +1 @@
-2018i
+2020a-dirty
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)) {
--
2.17.1


Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Update timezone code from tzcode 2020a

Paul Eggert
On 4/25/20 10:27 AM, Paul Eggert wrote:
> --- a/timezone/version
> +++ b/timezone/version
> @@ -1 +1 @@
> -2018i
> +2020a-dirty

That's a typo; the line should be just '2020a'. I'll fix that before installing.
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Update timezone code from tzcode 2020a

Sourceware - libc-alpha mailing list
In reply to this post by Paul Eggert
Paul Eggert wrote:

> This patch updates files coming from tzcode to tzcode 2020a.
> This is mostly for better support for Internet RFC 8536, by adding
> support to zic for the Expires line (new to tzcode 2020a), the -b
> option (new to 2019b) and the -r option (new to 2019a).
> One trivial change to other glibc was needed.
> * time/tzfile.c (__tzfile_read): Adjust to tzcode private.h renaming.
> * timezone/private.h, timezone/tzfile.h, timezone/version:
> * timezone/zdump.c, timezone/zic.c: Update from tzcode 2020a.
> ---
>  time/tzfile.c      |   2 +-
>  timezone/private.h |  37 ++-
>  timezone/tzfile.h  |  20 +-
>  timezone/version   |   2 +-
>  timezone/zdump.c   |   6 +-
>  timezone/zic.c     | 814 ++++++++++++++++++++++++++++-----------------
>  6 files changed, 554 insertions(+), 327 deletions(-)

With the version tweak you mentioned already,

Reviewed-by: Jonathan Nieder <[hidden email]>
Thanks.