]> granicus.if.org Git - postgresql/commitdiff
Sync our copy of the timezone library with IANA release tzcode2019b.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 17 Jul 2019 22:26:24 +0000 (18:26 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
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 8191346b219763cb668c804c99a9c1c2dbb20c15..e5499460e475f1e272153efbf22788e199217497 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 de8daaee5c9fb5d1c4455944fe3fbec3318ecbd6..f440065bd91f73eee46d63df94bf9f37d977834c 100644 (file)
@@ -77,12 +77,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) */
@@ -103,13 +101,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;
@@ -133,7 +131,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);
@@ -210,7 +208,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
@@ -224,7 +222,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
@@ -245,7 +243,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
 
@@ -389,11 +387,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];
@@ -540,8 +538,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);
@@ -625,6 +624,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)
 {
@@ -655,11 +669,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);
@@ -759,6 +789,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)
@@ -1183,7 +1215,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
@@ -1380,10 +1412,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)
@@ -1402,9 +1434,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
@@ -1443,7 +1475,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]);
@@ -1509,7 +1541,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,
@@ -1520,7 +1552,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;
@@ -1533,7 +1565,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;
@@ -1544,7 +1576,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, '%')
@@ -1768,7 +1800,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')
        {
@@ -1777,19 +1809,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;
                }
@@ -1977,41 +2009,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;
@@ -2091,10 +2088,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;
@@ -2102,7 +2101,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;
@@ -2151,7 +2155,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;
@@ -2213,7 +2217,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;
@@ -2293,7 +2299,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
 
@@ -2303,6 +2308,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,
@@ -2317,34 +2323,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;
                        }
@@ -2360,16 +2371,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;
@@ -2378,14 +2393,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);
@@ -2393,13 +2415,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)
@@ -2410,14 +2441,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" : "");
                                }
                        }
@@ -2425,14 +2456,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" : "");
                        }
                }
@@ -2465,12 +2496,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);
@@ -2498,20 +2534,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);
@@ -2564,7 +2601,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;
@@ -2577,7 +2614,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);
@@ -2649,8 +2686,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;
@@ -2707,10 +2743,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++ = '/';
@@ -2744,10 +2780,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)
 {
@@ -2813,15 +2845,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.  */
@@ -2829,17 +2852,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;
@@ -2850,7 +2873,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';
@@ -2860,11 +2883,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';
@@ -2873,7 +2896,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';
@@ -2883,7 +2906,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';
@@ -2905,12 +2928,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;
@@ -2948,7 +2971,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
         * startttisstd.
         */
        startttisstd = false;
-       startttisgmt = false;
+       startttisut = false;
        min_year = max_year = EPOCH_YEAR;
        if (leapseen)
        {
@@ -2977,14 +3000,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
@@ -3033,37 +3056,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);
@@ -3109,16 +3138,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);
                                        }
 
                                        /*
@@ -3133,9 +3162,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)
@@ -3166,41 +3195,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
@@ -3208,6 +3242,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                                                         && ktime < attypes[lastatmax].at))
                                                lastatmax = timecnt;
                                        addtt(ktime, type);
+                                       prevrp = rp;
                                }
                        }
                if (usestart)
@@ -3222,10 +3257,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);
@@ -3238,12 +3273,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)
@@ -3295,22 +3330,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;
        }
 
        /*
@@ -3321,23 +3365,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;
 }
 
@@ -3671,9 +3704,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++)