Sync our copy of the timezone library with IANA release tzcode2019b.
authorTom Lane <[email protected]>
Wed, 17 Jul 2019 22:26:24 +0000 (18:26 -0400)
committerTom Lane <[email protected]>
Wed, 17 Jul 2019 22:26:24 +0000 (18:26 -0400)
A large fraction of this diff is just due to upstream's somewhat
random decision to rename a bunch of internal variables and struct
fields.  However, there is an interesting new feature in zic:
it's grown a "-b slim" option that emits zone files without 32-bit
data and other backwards-compatibility hacks.  We should consider
whether we wish to enable that.

src/timezone/README
src/timezone/localtime.c
src/timezone/pgtz.h
src/timezone/tzfile.h
src/timezone/zic.c

index 8fb2699327410bcb7d67737ab14dccc2b0e45ddd..d3dd7b7fa6da6c72478b55e77df9e876d1000923 100644 (file)
@@ -55,7 +55,7 @@ match properly on the old version.
 Time Zone code
 ==============
 
-The code in this directory is currently synced with tzcode release 2019a.
+The code in this directory is currently synced with tzcode release 2019b.
 There are many cosmetic (and not so cosmetic) differences from the
 original tzcode library, but diffs in the upstream version should usually
 be propagated to our version.  Here are some notes about that.
@@ -127,4 +127,7 @@ and then run them through pgindent.  (The first three sed patterns deal
 with conversion of their block comment style to something pgindent
 won't make a hash of; the remainder address other points noted above.)
 After that, the files can be diff'd directly against our corresponding
-files.
+files.  Also, it's typically helpful to diff against the previous tzcode
+release (after processing that the same way), and then try to apply the
+diff to our files.  This will take care of most of the changes
+mechanically.
index 96e62aff3b4372d8613dfc43a94a4d40a62389a1..0fc0a824ae4dfab052d3e0e4ac818b98824b9db1 100644 (file)
@@ -107,15 +107,15 @@ static bool typesequiv(struct state const *, int, int);
 
 static struct pg_tm tm;
 
-/* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND.  */
+/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX.  */
 static void
-init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind)
+init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
 {
-   s->tt_gmtoff = gmtoff;
+   s->tt_utoff = utoff;
    s->tt_isdst = isdst;
-   s->tt_abbrind = abbrind;
+   s->tt_desigidx = desigidx;
    s->tt_ttisstd = false;
-   s->tt_ttisgmt = false;
+   s->tt_ttisut = false;
 }
 
 static int32
@@ -251,7 +251,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
    for (stored = 4; stored <= 8; stored *= 2)
    {
        int32       ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
-       int32       ttisgmtcnt = detzcode(up->tzhead.tzh_ttisgmtcnt);
+       int32       ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
        int64       prevtr = 0;
        int32       prevcorr = 0;
        int32       leapcnt = detzcode(up->tzhead.tzh_leapcnt);
@@ -270,7 +270,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
              && 0 <= timecnt && timecnt < TZ_MAX_TIMES
              && 0 <= charcnt && charcnt < TZ_MAX_CHARS
              && (ttisstdcnt == typecnt || ttisstdcnt == 0)
-             && (ttisgmtcnt == typecnt || ttisgmtcnt == 0)))
+             && (ttisutcnt == typecnt || ttisutcnt == 0)))
            return EINVAL;
        if (nread
            < (tzheadsize       /* struct tzhead */
@@ -280,7 +280,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
               + charcnt        /* chars */
               + leapcnt * (stored + 4) /* lsinfos */
               + ttisstdcnt     /* ttisstds */
-              + ttisgmtcnt))   /* ttisgmts */
+              + ttisutcnt))    /* ttisuts */
            return EINVAL;
        sp->leapcnt = leapcnt;
        sp->timecnt = timecnt;
@@ -332,19 +332,19 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
        {
            struct ttinfo *ttisp;
            unsigned char isdst,
-                       abbrind;
+                       desigidx;
 
            ttisp = &sp->ttis[i];
-           ttisp->tt_gmtoff = detzcode(p);
+           ttisp->tt_utoff = detzcode(p);
            p += 4;
            isdst = *p++;
            if (!(isdst < 2))
                return EINVAL;
            ttisp->tt_isdst = isdst;
-           abbrind = *p++;
-           if (!(abbrind < sp->charcnt))
+           desigidx = *p++;
+           if (!(desigidx < sp->charcnt))
                return EINVAL;
-           ttisp->tt_abbrind = abbrind;
+           ttisp->tt_desigidx = desigidx;
        }
        for (i = 0; i < sp->charcnt; ++i)
            sp->chars[i] = *p++;
@@ -398,13 +398,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
            struct ttinfo *ttisp;
 
            ttisp = &sp->ttis[i];
-           if (ttisgmtcnt == 0)
-               ttisp->tt_ttisgmt = false;
+           if (ttisutcnt == 0)
+               ttisp->tt_ttisut = false;
            else
            {
                if (*p != true && *p != false)
                    return EINVAL;
-               ttisp->tt_ttisgmt = *p++;
+               ttisp->tt_ttisut = *p++;
            }
        }
 
@@ -438,13 +438,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
 
            for (i = 0; i < ts->typecnt; i++)
            {
-               char       *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
+               char       *tsabbr = ts->chars + ts->ttis[i].tt_desigidx;
                int         j;
 
                for (j = 0; j < charcnt; j++)
                    if (strcmp(sp->chars + j, tsabbr) == 0)
                    {
-                       ts->ttis[i].tt_abbrind = j;
+                       ts->ttis[i].tt_desigidx = j;
                        gotabbr++;
                        break;
                    }
@@ -456,7 +456,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                    {
                        strcpy(sp->chars + j, tsabbr);
                        charcnt = j + tsabbrlen + 1;
-                       ts->ttis[i].tt_abbrind = j;
+                       ts->ttis[i].tt_desigidx = j;
                        gotabbr++;
                    }
                }
@@ -614,12 +614,13 @@ typesequiv(const struct state *sp, int a, int b)
        const struct ttinfo *ap = &sp->ttis[a];
        const struct ttinfo *bp = &sp->ttis[b];
 
-       result = ap->tt_gmtoff == bp->tt_gmtoff &&
-           ap->tt_isdst == bp->tt_isdst &&
-           ap->tt_ttisstd == bp->tt_ttisstd &&
-           ap->tt_ttisgmt == bp->tt_ttisgmt &&
-           strcmp(&sp->chars[ap->tt_abbrind],
-                  &sp->chars[bp->tt_abbrind]) == 0;
+       result = (ap->tt_utoff == bp->tt_utoff
+                 && ap->tt_isdst == bp->tt_isdst
+                 && ap->tt_ttisstd == bp->tt_ttisstd
+                 && ap->tt_ttisut == bp->tt_ttisut
+                 && (strcmp(&sp->chars[ap->tt_desigidx],
+                            &sp->chars[bp->tt_desigidx])
+                     == 0));
    }
    return result;
 }
@@ -1176,7 +1177,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                if (!sp->ttis[j].tt_isdst)
                {
                    theirstdoffset =
-                       -sp->ttis[j].tt_gmtoff;
+                       -sp->ttis[j].tt_utoff;
                    break;
                }
            }
@@ -1187,7 +1188,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                if (sp->ttis[j].tt_isdst)
                {
                    theirdstoffset =
-                       -sp->ttis[j].tt_gmtoff;
+                       -sp->ttis[j].tt_utoff;
                    break;
                }
            }
@@ -1206,7 +1207,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
            {
                j = sp->types[i];
                sp->types[i] = sp->ttis[j].tt_isdst;
-               if (sp->ttis[j].tt_ttisgmt)
+               if (sp->ttis[j].tt_ttisut)
                {
                    /* No adjustment to transition time */
                }
@@ -1234,7 +1235,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                            theirstdoffset;
                    }
                }
-               theiroffset = -sp->ttis[j].tt_gmtoff;
+               theiroffset = -sp->ttis[j].tt_utoff;
                if (sp->ttis[j].tt_isdst)
                    theirdstoffset = theiroffset;
                else
@@ -1357,14 +1358,14 @@ localsub(struct state const *sp, pg_time_t const *timep,
 
    /*
     * To get (wrong) behavior that's compatible with System V Release 2.0
-    * you'd replace the statement below with t += ttisp->tt_gmtoff;
+    * you'd replace the statement below with t += ttisp->tt_utoff;
     * timesub(&t, 0L, sp, tmp);
     */
-   result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+   result = timesub(&t, ttisp->tt_utoff, sp, tmp);
    if (result)
    {
        result->tm_isdst = ttisp->tt_isdst;
-       result->tm_zone = (char *) &sp->chars[ttisp->tt_abbrind];
+       result->tm_zone = (char *) &sp->chars[ttisp->tt_desigidx];
    }
    return result;
 }
@@ -1647,7 +1648,7 @@ pg_next_dst_boundary(const pg_time_t *timep,
                break;
            }
        ttisp = &sp->ttis[i];
-       *before_gmtoff = ttisp->tt_gmtoff;
+       *before_gmtoff = ttisp->tt_utoff;
        *before_isdst = ttisp->tt_isdst;
        return 0;
    }
@@ -1700,7 +1701,7 @@ pg_next_dst_boundary(const pg_time_t *timep,
        /* No known transition > t, so use last known segment's type */
        i = sp->types[sp->timecnt - 1];
        ttisp = &sp->ttis[i];
-       *before_gmtoff = ttisp->tt_gmtoff;
+       *before_gmtoff = ttisp->tt_utoff;
        *before_isdst = ttisp->tt_isdst;
        return 0;
    }
@@ -1715,13 +1716,13 @@ pg_next_dst_boundary(const pg_time_t *timep,
                break;
            }
        ttisp = &sp->ttis[i];
-       *before_gmtoff = ttisp->tt_gmtoff;
+       *before_gmtoff = ttisp->tt_utoff;
        *before_isdst = ttisp->tt_isdst;
        *boundary = sp->ats[0];
        /* And for "after", use the first segment's type */
        i = sp->types[0];
        ttisp = &sp->ttis[i];
-       *after_gmtoff = ttisp->tt_gmtoff;
+       *after_gmtoff = ttisp->tt_utoff;
        *after_isdst = ttisp->tt_isdst;
        return 1;
    }
@@ -1743,12 +1744,12 @@ pg_next_dst_boundary(const pg_time_t *timep,
    }
    j = sp->types[i - 1];
    ttisp = &sp->ttis[j];
-   *before_gmtoff = ttisp->tt_gmtoff;
+   *before_gmtoff = ttisp->tt_utoff;
    *before_isdst = ttisp->tt_isdst;
    *boundary = sp->ats[i];
    j = sp->types[i];
    ttisp = &sp->ttis[j];
-   *after_gmtoff = ttisp->tt_gmtoff;
+   *after_gmtoff = ttisp->tt_utoff;
    *after_isdst = ttisp->tt_isdst;
    return 1;
 }
@@ -1832,9 +1833,9 @@ pg_interpret_timezone_abbrev(const char *abbrev,
    for (i = cutoff - 1; i >= 0; i--)
    {
        ttisp = &sp->ttis[sp->types[i]];
-       if (ttisp->tt_abbrind == abbrind)
+       if (ttisp->tt_desigidx == abbrind)
        {
-           *gmtoff = ttisp->tt_gmtoff;
+           *gmtoff = ttisp->tt_utoff;
            *isdst = ttisp->tt_isdst;
            return true;
        }
@@ -1846,9 +1847,9 @@ pg_interpret_timezone_abbrev(const char *abbrev,
    for (i = cutoff; i < sp->timecnt; i++)
    {
        ttisp = &sp->ttis[sp->types[i]];
-       if (ttisp->tt_abbrind == abbrind)
+       if (ttisp->tt_desigidx == abbrind)
        {
-           *gmtoff = ttisp->tt_gmtoff;
+           *gmtoff = ttisp->tt_utoff;
            *isdst = ttisp->tt_isdst;
            return true;
        }
@@ -1875,10 +1876,10 @@ pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff)
    sp = &tz->state;
    for (i = 1; i < sp->typecnt; i++)
    {
-       if (sp->ttis[i].tt_gmtoff != sp->ttis[0].tt_gmtoff)
+       if (sp->ttis[i].tt_utoff != sp->ttis[0].tt_utoff)
            return false;
    }
-   *gmtoff = sp->ttis[0].tt_gmtoff;
+   *gmtoff = sp->ttis[0].tt_utoff;
    return true;
 }
 
index 60258cec707b67bab72904574ff2e45a9492f881..2890493aadeb65118a29a8df4623a43846b24914 100644 (file)
 
 struct ttinfo
 {                              /* time type information */
-   int32       tt_gmtoff;      /* UT offset in seconds */
+   int32       tt_utoff;       /* UT offset in seconds */
    bool        tt_isdst;       /* used to set tm_isdst */
-   int         tt_abbrind;     /* abbreviation list index */
+   int         tt_desigidx;    /* abbreviation list index */
    bool        tt_ttisstd;     /* transition is std time */
-   bool        tt_ttisgmt;     /* transition is UT */
+   bool        tt_ttisut;      /* transition is UT */
 };
 
 struct lsinfo
index 562259b937e0e91bd479a03be8bfa4263ecc60a9..8f3eb6bd3f2c47e5220f58486fa8c4536e35c599 100644 (file)
@@ -41,7 +41,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 */
@@ -64,14 +64,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.
  */
 
 /*
index 76c78df0aab9fbcbfd860d8ffd1a5077d40b4b1c..5c81fcae0206a7e3b76252d2553322e73ab63443 100644 (file)
@@ -82,12 +82,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) */
@@ -108,13 +106,13 @@ struct zone
    lineno_t    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;
@@ -141,7 +139,7 @@ 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 infile(const char *filename);
 static void inleap(char **fields, int nfields);
 static void inlink(char **fields, int nfields);
@@ -218,7 +216,7 @@ static int  typecnt;
  */
 
 #define ZF_NAME        1
-#define ZF_GMTOFF  2
+#define ZF_STDOFF  2
 #define ZF_RULE        3
 #define ZF_FORMAT  4
 #define ZF_TILYEAR 5
@@ -232,7 +230,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
@@ -253,7 +251,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
 
@@ -397,11 +395,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];
@@ -548,8 +546,9 @@ usage(FILE *stream, int status)
 {
    fprintf(stream,
            _("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -P ] \\\n"
-             "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
-             "\t[ -t localtime-link ] [ -L leapseconds ] [ -r '[@lo][/@hi]' ] \\\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, PACKAGE_BUGREPORT);
@@ -633,6 +632,21 @@ 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)
 {
@@ -663,11 +677,27 @@ main(int argc, char **argv)
        {
            usage(stdout, EXIT_SUCCESS);
        }
-   while ((c = getopt(argc, argv, "d:l:L:p:Pr:st:vy:")) != EOF && c != -1)
+   while ((c = getopt(argc, argv, "b:d:l:L:p:Pr: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 = strdup(optarg);
@@ -767,6 +797,8 @@ main(int argc, char **argv)
        }
    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 = "data";
    if (tzdefault == NULL)
@@ -1191,7 +1223,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
@@ -1388,10 +1420,10 @@ gethms(char const *string, char const *errstring)
 }
 
 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)
@@ -1410,9 +1442,9 @@ getstdoff(char *field, bool *isdst)
                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
@@ -1451,7 +1483,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]);
@@ -1517,7 +1549,7 @@ inzsub(char **fields, int nfields, bool iscont)
    char       *cp;
    char       *cp1;
    static struct zone z;
-   int         i_gmtoff,
+   int         i_stdoff,
                i_rule,
                i_format;
    int         i_untilyear,
@@ -1528,7 +1560,7 @@ inzsub(char **fields, int nfields, bool iscont)
 
    if (iscont)
    {
-       i_gmtoff = ZFC_GMTOFF;
+       i_stdoff = ZFC_STDOFF;
        i_rule = ZFC_RULE;
        i_format = ZFC_FORMAT;
        i_untilyear = ZFC_TILYEAR;
@@ -1541,7 +1573,7 @@ inzsub(char **fields, int nfields, bool iscont)
        return false;
    else
    {
-       i_gmtoff = ZF_GMTOFF;
+       i_stdoff = ZF_STDOFF;
        i_rule = ZF_RULE;
        i_format = ZF_FORMAT;
        i_untilyear = ZF_TILYEAR;
@@ -1552,7 +1584,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], '%')) != NULL)
    {
        if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
@@ -1776,7 +1808,7 @@ 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')
    {
@@ -1785,19 +1817,19 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
        {
            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;
        }
@@ -1985,41 +2017,6 @@ atcomp(const void *avp, const void *bvp)
    return (a < b) ? -1 : (a > b);
 }
 
-static void
-swaptypes(int i, int j)
-{
-   {
-       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;
-   }
-}
-
 struct timerange
 {
    int         defaulttype;
@@ -2099,10 +2096,12 @@ writezone(const char *const name, const char *const string, char version,
        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;
@@ -2110,7 +2109,12 @@ writezone(const char *const name, const char *const string, char version,
            }
            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;
@@ -2159,7 +2163,7 @@ writezone(const char *const name, const char *const string, char version,
     * before 32-bit pg_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;
@@ -2221,7 +2225,9 @@ writezone(const char *const name, const char *const string, char version,
        int         old0;
        char        omittype[TZ_MAX_TYPES];
        int         typemap[TZ_MAX_TYPES];
-       int         thistypecnt;
+       int         thistypecnt,
+                   stdcnt,
+                   utcnt;
        char        thischars[TZ_MAX_CHARS];
        int         thischarcnt;
        bool        toomanytimes;
@@ -2301,7 +2307,6 @@ writezone(const char *const name, const char *const string, char version,
         * in the output instead of OLD0.  TYPEMAP also omits unused types.
         */
        old0 = strlen(omittype);
-       swaptypes(old0, thisdefaulttype);
 
 #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
 
@@ -2311,6 +2316,7 @@ writezone(const char *const name, const char *const string, char version,
         * offset, append an (unused) copy of the most recently used type (to
         * help get global "altzone" and "timezone" variables set correctly).
         */
+       if (want_bloat())
        {
            int         mrudst,
                        mrustd,
@@ -2325,34 +2331,39 @@ writezone(const char *const name, const char *const string, char version,
                else
                    mrustd = types[i];
            for (i = old0; i < typecnt; i++)
-               if (!omittype[i])
+           {
+               int         h = (i == old0 ? thisdefaulttype
+                                : i == thisdefaulttype ? old0 : i);
+
+               if (!omittype[h])
                {
-                   if (isdsts[i])
+                   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;
            }
@@ -2368,16 +2379,20 @@ writezone(const char *const name, const char *const string, char version,
 
        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++)
        {
            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;
@@ -2386,14 +2401,21 @@ 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(locut + thistimecnt + hicut, tzh.tzh_timecnt);
        convert(thistypecnt, tzh.tzh_typecnt);
@@ -2401,13 +2423,22 @@ writezone(const char *const name, const char *const string, char version,
        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
+       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;
+       }
 
        /* PG: print current timezone abbreviations if requested */
        if (print_abbrevs && pass == 2)
@@ -2418,14 +2449,14 @@ writezone(const char *const name, const char *const string, char version,
                if (i == thistimelim - 1 || ats[i + 1] > print_cutoff)
                {
                    unsigned char tm = types[i];
-                   char       *thisabbrev = &thischars[indmap[abbrinds[tm]]];
+                   char       *thisabbrev = &thischars[indmap[desigidx[tm]]];
 
                    /* filter out assorted junk entries */
                    if (strcmp(thisabbrev, GRANDPARENTED) != 0 &&
                        strcmp(thisabbrev, "zzz") != 0)
                        fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
                                thisabbrev,
-                               gmtoffs[tm],
+                               utoffs[tm],
                                isdsts[tm] ? "\tD" : "");
                }
            }
@@ -2433,14 +2464,14 @@ writezone(const char *const name, const char *const string, char version,
            if (thistimei >= thistimelim)
            {
                unsigned char tm = defaulttype;
-               char       *thisabbrev = &thischars[indmap[abbrinds[tm]]];
+               char       *thisabbrev = &thischars[indmap[desigidx[tm]]];
 
                /* filter out assorted junk entries */
                if (strcmp(thisabbrev, GRANDPARENTED) != 0 &&
                    strcmp(thisabbrev, "zzz") != 0)
                    fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
                            thisabbrev,
-                           gmtoffs[tm],
+                           utoffs[tm],
                            isdsts[tm] ? "\tD" : "");
            }
        }
@@ -2473,12 +2504,17 @@ writezone(const char *const name, const char *const string, char version,
            putc(currenttype, fp);
 
        for (i = old0; i < typecnt; i++)
-           if (!omittype[i])
+       {
+           int         h = (i == old0 ? thisdefaulttype
+                            : i == thisdefaulttype ? old0 : i);
+
+           if (!omittype[h])
            {
-               puttzcode(gmtoffs[i], fp);
-               putc(isdsts[i], fp);
-               putc((unsigned char) indmap[abbrinds[i]], fp);
+               puttzcode(utoffs[h], fp);
+               putc(isdsts[h], fp);
+               putc(indmap[desigidx[h]], fp);
            }
+       }
        if (thischarcnt != 0)
            fwrite(thischars, sizeof thischars[0],
                   thischarcnt, fp);
@@ -2506,20 +2542,21 @@ 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];
            puttzcodepass(todo, fp, pass);
            puttzcode(corr[i], fp);
        }
-       for (i = old0; i < typecnt; i++)
-           if (!omittype[i])
-               putc(ttisstds[i], fp);
-       for (i = old0; i < typecnt; i++)
-           if (!omittype[i])
-               putc(ttisgmts[i], fp);
-       swaptypes(old0, thisdefaulttype);
+       if (stdcnt != 0)
+           for (i = old0; i < typecnt; i++)
+               if (!omittype[i])
+                   putc(ttisstds[i], fp);
+       if (utcnt != 0)
+           for (i = old0; i < typecnt; i++)
+               if (!omittype[i])
+                   putc(ttisuts[i], fp);
    }
    fprintf(fp, "\n%s\n", string);
    close_file(fp, directory, name);
@@ -2572,7 +2609,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)
 {
    char       *cp;
    char       *slashp;
@@ -2585,7 +2622,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
        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);
@@ -2657,8 +2694,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)
 {
    zic_t       tod = rp->r_tod;
    int         compat = 0;
@@ -2715,10 +2751,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++ = '/';
@@ -2752,10 +2788,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)
 {
@@ -2821,15 +2853,6 @@ 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.  */
@@ -2837,17 +2860,17 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
            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;
@@ -2858,7 +2881,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';
@@ -2868,11 +2891,11 @@ 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';
@@ -2881,7 +2904,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';
@@ -2891,7 +2914,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';
@@ -2913,12 +2936,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                useuntil;
    zic_t       starttime,
                untiltime;
-   zic_t       gmtoff;
    zic_t       stdoff;
+   zic_t       save;
    zic_t       year;
    zic_t       startoff;
    bool        startttisstd;
-   bool        startttisgmt;
+   bool        startttisut;
    int         type;
    char       *startbuf;
    char       *ab;
@@ -2956,7 +2979,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
     * startttisstd.
     */
    startttisstd = false;
-   startttisgmt = false;
+   startttisut = false;
    min_year = max_year = EPOCH_YEAR;
    if (leapseen)
    {
@@ -2985,14 +3008,14 @@ 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
@@ -3041,37 +3064,43 @@ 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)
-       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);
@@ -3117,16 +3146,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                    if (useuntil)
                    {
                        /*
-                        * Turn untiltime into UT assuming the current gmtoff
-                        * and stdoff values.
+                        * Turn untiltime into UT 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);
                    }
 
                    /*
@@ -3141,9 +3170,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)
@@ -3174,41 +3203,46 @@ 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
@@ -3216,6 +3250,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                             && ktime < attypes[lastatmax].at))
                        lastatmax = timecnt;
                    addtt(ktime, type);
+                   prevrp = rp;
                }
            }
        if (usestart)
@@ -3230,10 +3265,10 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                error(_("cannot 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);
@@ -3246,12 +3281,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
        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, -save);
+           if (!startttisut)
                starttime = tadd(starttime, -stdoff);
-           if (!startttisgmt)
-               starttime = tadd(starttime, -gmtoff);
        }
    }
    if (defaulttype < 0)
@@ -3303,22 +3338,31 @@ 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)
 {
    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 (!(-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 (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
-           strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
-           ttisstd == ttisstds[i] &&
-           ttisgmt == ttisgmts[i])
-           return i;
+       /* 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;
    }
 
    /*
@@ -3329,23 +3373,12 @@ 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;
 }
 
@@ -3679,9 +3712,9 @@ byword(const char *word, const struct lookup *table)
                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++)