]> granicus.if.org Git - postgresql/commitdiff
Sync our copy of the timezone library with IANA release tzcode2018f.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 19 Oct 2018 23:36:34 +0000 (19:36 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 19 Oct 2018 23:36:34 +0000 (19:36 -0400)
About half of this is purely cosmetic changes to reduce the diff between
our code and theirs, like inserting "const" markers where they have them.

The other half is tracking actual code changes in zic.c and localtime.c.
I don't think any of these represent near-term compatibility hazards, but
it seems best to stay up to date.

I also fixed longstanding bugs in our code for producing the
known_abbrevs.txt list, which by chance hadn't been exposed before,
but which resulted in some garbage output after applying the upstream
changes in zic.c.  Notably, because upstream removed their old phony
transitions at the Big Bang, it's now necessary to cope with TZif files
containing no DST transition times at all.

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

index c33c26143f244a9c4d08e24dbd9d287371e93790..4a6170101a7e9e3ffe4651f99fdcb09e9e526a83 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 2018e.
+The code in this directory is currently synced with tzcode release 2018f.
 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.
index e3029d841d7b1727aabb2b2f67af3d4902395748..96e62aff3b4372d8613dfc43a94a4d40a62389a1 100644 (file)
@@ -1,3 +1,5 @@
+/* Convert timestamp from pg_time_t to struct pg_tm.  */
+
 /*
  * This file is in the public domain, so clarified as of
  * 1996-06-05 by Arthur David Olson.
@@ -117,7 +119,7 @@ init_ttinfo(struct ttinfo *s, int32 gmtoff, bool isdst, int abbrind)
 }
 
 static int32
-detzcode(const char *codep)
+detzcode(const char *const codep)
 {
        int32           result;
        int                     i;
@@ -143,7 +145,7 @@ detzcode(const char *codep)
 }
 
 static int64
-detzcode64(const char *codep)
+detzcode64(const char *const codep)
 {
        uint64          result;
        int                     i;
@@ -258,8 +260,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                int32           charcnt = detzcode(up->tzhead.tzh_charcnt);
                char const *p = up->buf + tzheadsize;
 
+               /*
+                * Although tzfile(5) currently requires typecnt to be nonzero,
+                * support future formats that may allow zero typecnt in files that
+                * have a TZ string and no transitions.
+                */
                if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
-                         && 0 < typecnt && typecnt < TZ_MAX_TYPES
+                         && 0 <= typecnt && typecnt < TZ_MAX_TYPES
                          && 0 <= timecnt && timecnt < TZ_MAX_TIMES
                          && 0 <= charcnt && charcnt < TZ_MAX_CHARS
                          && (ttisstdcnt == typecnt || ttisstdcnt == 0)
@@ -416,8 +423,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                struct state *ts = &lsp->u.st;
 
                up->buf[nread - 1] = '\0';
-               if (tzparse(&up->buf[1], ts, false)
-                       && ts->typecnt == 2)
+               if (tzparse(&up->buf[1], ts, false))
                {
                        /*
                         * Attempt to reuse existing abbreviations. Without this,
@@ -430,7 +436,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                        int                     gotabbr = 0;
                        int                     charcnt = sp->charcnt;
 
-                       for (i = 0; i < 2; i++)
+                       for (i = 0; i < ts->typecnt; i++)
                        {
                                char       *tsabbr = ts->chars + ts->ttis[i].tt_abbrind;
                                int                     j;
@@ -455,7 +461,7 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                                        }
                                }
                        }
-                       if (gotabbr == 2)
+                       if (gotabbr == ts->typecnt)
                        {
                                sp->charcnt = charcnt;
 
@@ -470,7 +476,8 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                                        sp->timecnt--;
 
                                for (i = 0; i < ts->timecnt; i++)
-                                       if (sp->ats[sp->timecnt - 1] < ts->ats[i])
+                                       if (sp->timecnt == 0
+                                               || sp->ats[sp->timecnt - 1] < ts->ats[i])
                                                break;
                                while (i < ts->timecnt
                                           && sp->timecnt < TZ_MAX_TIMES)
@@ -481,11 +488,13 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                                        sp->timecnt++;
                                        i++;
                                }
-                               sp->ttis[sp->typecnt++] = ts->ttis[0];
-                               sp->ttis[sp->typecnt++] = ts->ttis[1];
+                               for (i = 0; i < ts->typecnt; i++)
+                                       sp->ttis[sp->typecnt++] = ts->ttis[i];
                        }
                }
        }
+       if (sp->typecnt == 0)
+               return EINVAL;
        if (sp->timecnt > 1)
        {
                for (i = 1; i < sp->timecnt; ++i)
@@ -506,6 +515,18 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                        }
        }
 
+       /*
+        * Infer sp->defaulttype from the data.  Although this default type is
+        * always zero for data from recent tzdb releases, things are trickier for
+        * data from tzdb 2018e or earlier.
+        *
+        * The first set of heuristics work around bugs in 32-bit data generated
+        * by tzdb 2013c or earlier.  The workaround is for zones like
+        * Australia/Macquarie where timestamps before the first transition have a
+        * time type that is not the earliest standard-time type.  See:
+        * https://mm.icann.org/pipermail/tz/2013-May/019368.html
+        */
+
        /*
         * If type 0 is unused in transitions, it's the type to use for early
         * times.
@@ -528,6 +549,11 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                                break;
        }
 
+       /*
+        * The next heuristics are for data generated by tzdb 2018e or earlier,
+        * for zones like EST5EDT where the first transition is to DST.
+        */
+
        /*
         * If no result yet, find the first standard type. If there is none, punt
         * to type zero.
@@ -542,7 +568,14 @@ tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
                                break;
                        }
        }
+
+       /*
+        * A simple 'sp->defaulttype = 0;' would suffice here if we didn't have to
+        * worry about 2018e-or-earlier data.  Even simpler would be to remove the
+        * defaulttype member and just use 0 in its place.
+        */
        sp->defaulttype = i;
+
        return 0;
 }
 
@@ -601,10 +634,11 @@ static const int year_lengths[2] = {
 };
 
 /*
- * Given a pointer into a time zone string, scan until a character that is not
- * a valid character in a zone name is found. Return a pointer to that
- * character.
+ * Given a pointer into a timezone string, scan until a character that is not
+ * a valid character in a time zone abbreviation is found.
+ * Return a pointer to that character.
  */
+
 static const char *
 getzname(const char *strp)
 {
@@ -617,15 +651,17 @@ getzname(const char *strp)
 }
 
 /*
- * Given a pointer into an extended time zone string, scan until the ending
- * delimiter of the zone name is located. Return a pointer to the delimiter.
+ * Given a pointer into an extended timezone string, scan until the ending
+ * delimiter of the time zone abbreviation is located.
+ * Return a pointer to the delimiter.
  *
  * As with getzname above, the legal character set is actually quite
  * restricted, with other characters producing undefined results.
  * We don't do any checking here; checking is done later in common-case code.
  */
+
 static const char *
-getqzname(const char *strp, int delim)
+getqzname(const char *strp, const int delim)
 {
        int                     c;
 
@@ -635,13 +671,14 @@ getqzname(const char *strp, int delim)
 }
 
 /*
- * Given a pointer into a time zone string, extract a number from that string.
+ * Given a pointer into a timezone string, extract a number from that string.
  * Check that the number is within a specified range; if it is not, return
  * NULL.
  * Otherwise, return a pointer to the first character not part of the number.
  */
+
 static const char *
-getnum(const char *strp, int *nump, int min, int max)
+getnum(const char *strp, int *const nump, const int min, const int max)
 {
        char            c;
        int                     num;
@@ -663,14 +700,15 @@ getnum(const char *strp, int *nump, int min, int max)
 }
 
 /*
- * Given a pointer into a time zone string, extract a number of seconds,
+ * Given a pointer into a timezone string, extract a number of seconds,
  * in hh[:mm[:ss]] form, from the string.
  * If any error occurs, return NULL.
  * Otherwise, return a pointer to the first character not part of the number
  * of seconds.
  */
+
 static const char *
-getsecs(const char *strp, int32 *secsp)
+getsecs(const char *strp, int32 *const secsp)
 {
        int                     num;
 
@@ -704,13 +742,14 @@ getsecs(const char *strp, int32 *secsp)
 }
 
 /*
- * Given a pointer into a time zone string, extract an offset, in
+ * Given a pointer into a timezone string, extract an offset, in
  * [+-]hh[:mm[:ss]] form, from the string.
  * If any error occurs, return NULL.
  * Otherwise, return a pointer to the first character not part of the time.
  */
+
 static const char *
-getoffset(const char *strp, int32 *offsetp)
+getoffset(const char *strp, int32 *const offsetp)
 {
        bool            neg = false;
 
@@ -730,13 +769,14 @@ getoffset(const char *strp, int32 *offsetp)
 }
 
 /*
- * Given a pointer into a time zone string, extract a rule in the form
+ * Given a pointer into a timezone string, extract a rule in the form
  * date[/time]. See POSIX section 8 for the format of "date" and "time".
  * If a valid rule is not found, return NULL.
  * Otherwise, return a pointer to the first character not part of the rule.
  */
+
 static const char *
-getrule(const char *strp, struct rule *rulep)
+getrule(const char *strp, struct rule *const rulep)
 {
        if (*strp == 'J')
        {
@@ -795,9 +835,10 @@ getrule(const char *strp, struct rule *rulep)
  * Given a year, a rule, and the offset from UT at the time that rule takes
  * effect, calculate the year-relative time that rule takes effect.
  */
+
 static int32
-transtime(int year, const struct rule *rulep,
-                 int32 offset)
+transtime(const int year, const struct rule *const rulep,
+                 const int32 offset)
 {
        bool            leapyear;
        int32           value;
@@ -967,7 +1008,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                {
                        dstname = name;
                        name = getzname(name);
-                       dstlen = name - dstname;        /* length of DST zone name */
+                       dstlen = name - dstname;        /* length of DST abbr. */
                }
                if (!dstlen)
                        return false;
@@ -1039,8 +1080,8 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                        /*
                         * Two transitions per year, from EPOCH_YEAR forward.
                         */
-                       init_ttinfo(&sp->ttis[0], -dstoffset, true, stdlen + 1);
-                       init_ttinfo(&sp->ttis[1], -stdoffset, false, 0);
+                       init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+                       init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
                        sp->defaulttype = 0;
                        timecnt = 0;
                        janfirst = 0;
@@ -1089,19 +1130,15 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                                        if (!increment_overflow_time
                                                (&sp->ats[timecnt],
                                                 janoffset + starttime))
-                                               sp->types[timecnt++] = reversed;
-                                       else if (janoffset)
-                                               sp->defaulttype = reversed;
+                                               sp->types[timecnt++] = !reversed;
                                        sp->ats[timecnt] = janfirst;
                                        if (!increment_overflow_time
                                                (&sp->ats[timecnt],
                                                 janoffset + endtime))
                                        {
-                                               sp->types[timecnt++] = !reversed;
+                                               sp->types[timecnt++] = reversed;
                                                yearlim = year + YEARSPERREPEAT + 1;
                                        }
-                                       else if (janoffset)
-                                               sp->defaulttype = !reversed;
                                }
                                if (increment_overflow_time
                                        (&janfirst, janoffset + yearsecs))
@@ -1110,7 +1147,10 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                        }
                        sp->timecnt = timecnt;
                        if (!timecnt)
+                       {
+                               sp->ttis[0] = sp->ttis[1];
                                sp->typecnt = 1;        /* Perpetual DST.  */
+                       }
                        else if (YEARSPERREPEAT < year - yearbeg)
                                sp->goback = sp->goahead = true;
                }
@@ -1179,7 +1219,6 @@ tzparse(const char *name, struct state *sp, bool lastditch)
                                         * otherwise, add the standard time offset to the
                                         * transition time.
                                         */
-
                                        /*
                                         * Transitions from DST to DDST will effectively disappear
                                         * since POSIX provides for only one DST offset.
@@ -1233,7 +1272,7 @@ tzparse(const char *name, struct state *sp, bool lastditch)
 }
 
 static void
-gmtload(struct state *sp)
+gmtload(struct state *const sp)
 {
        if (tzload(gmt, NULL, sp, true) != 0)
                tzparse(gmt, sp, true);
@@ -1248,7 +1287,7 @@ gmtload(struct state *sp)
  */
 static struct pg_tm *
 localsub(struct state const *sp, pg_time_t const *timep,
-                struct pg_tm *tmp)
+                struct pg_tm *const tmp)
 {
        const struct ttinfo *ttisp;
        int                     i;
@@ -1316,6 +1355,11 @@ localsub(struct state const *sp, pg_time_t const *timep,
        }
        ttisp = &sp->ttis[i];
 
+       /*
+        * To get (wrong) behavior that's compatible with System V Release 2.0
+        * you'd replace the statement below with t += ttisp->tt_gmtoff;
+        * timesub(&t, 0L, sp, tmp);
+        */
        result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
        if (result)
        {
@@ -1338,8 +1382,10 @@ pg_localtime(const pg_time_t *timep, const pg_tz *tz)
  *
  * Except we have a private "struct state" for GMT, so no sp is passed in.
  */
+
 static struct pg_tm *
-gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp)
+gmtsub(pg_time_t const *timep, int32 offset,
+          struct pg_tm *tmp)
 {
        struct pg_tm *result;
 
@@ -1354,6 +1400,7 @@ gmtsub(pg_time_t const *timep, int32 offset, struct pg_tm *tmp)
                        return NULL;            /* errno should be set by malloc */
                gmtload(gmtptr);
        }
+
        result = timesub(timep, offset, gmtptr, tmp);
 
        /*
@@ -1378,6 +1425,7 @@ pg_gmtime(const pg_time_t *timep)
  * Return the number of leap years through the end of the given year
  * where, to make the math easy, the answer for year zero is defined as zero.
  */
+
 static int
 leaps_thru_end_of_nonneg(int y)
 {
index 1e94d66d49046f163ec68028f754e004c2679d3f..a07dced583e6c3a79ca7fc74799c16e7548be3c1 100644 (file)
@@ -49,10 +49,16 @@ struct state
        pg_time_t       ats[TZ_MAX_TIMES];
        unsigned char types[TZ_MAX_TIMES];
        struct ttinfo ttis[TZ_MAX_TYPES];
-       char            chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 3 /* sizeof gmt */ ),
+       char            chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 4 /* sizeof gmt */ ),
                                                          (2 * (TZ_STRLEN_MAX + 1)))];
        struct lsinfo lsis[TZ_MAX_LEAPS];
-       int                     defaulttype;    /* for early times or if no transitions */
+
+       /*
+        * The time type to use for early times or if no transitions. It is always
+        * zero for recent tzdb releases. It might be nonzero for data from tzdb
+        * 2018e or earlier.
+        */
+       int                     defaulttype;
 };
 
 
index 701112ec5b80b4ddfbd4f736bf42affcfeca4bb8..a952c4dc30d05d0c71f6c11ed4d968445349e72f 100644 (file)
@@ -1,4 +1,7 @@
+/* Private header for tzdb code.  */
+
 #ifndef PRIVATE_H
+
 #define PRIVATE_H
 
 /*
index 275b5f477c5f9cb556c1545c24908f43dd4db3d9..217dd375f365b29265af17979126f2226630903d 100644 (file)
@@ -1,4 +1,4 @@
-/* Convert a broken-down timestamp to a string. */
+/* Convert a broken-down timestamp to a string.  */
 
 /*
  * Copyright 1989 The Regents of the University of California.
@@ -115,7 +115,7 @@ static char *_add(const char *, char *, const char *);
 static char *_conv(int, const char *, char *, const char *);
 static char *_fmt(const char *, const struct pg_tm *, char *, const char *,
         enum warn *);
-static char *_yconv(int, int, bool, bool, char *, const char *);
+static char *_yconv(int, int, bool, bool, char *, char const *);
 
 
 size_t
@@ -441,7 +441,8 @@ _fmt(const char *format, const struct pg_tm *t, char *pt,
 
                                        /*
                                         * C99 and later say that %Z must be replaced by the empty
-                                        * string if the time zone is not determinable.
+                                        * string if the time zone abbreviation is not
+                                        * determinable.
                                         */
                                        continue;
                                case 'z':
@@ -519,6 +520,7 @@ _add(const char *str, char *pt, const char *ptlim)
  * same output as %Y, and that %Y contains at least 4 bytes,
  * with more only if necessary.
  */
+
 static char *
 _yconv(int a, int b, bool convert_top, bool convert_yy,
           char *pt, const char *ptlim)
@@ -526,7 +528,7 @@ _yconv(int a, int b, bool convert_top, bool convert_yy,
        int                     lead;
        int                     trail;
 
-#define DIVISOR 100
+#define DIVISOR        100
        trail = a % DIVISOR + b % DIVISOR;
        lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
        trail %= DIVISOR;
index 25ca3074034e2639eb6f11e73be8e79b41f207d4..6c97808397fca18ebf85f3f0224edc4a1eb31269 100644 (file)
@@ -1,4 +1,7 @@
+/* Layout and location of TZif files.  */
+
 #ifndef TZFILE_H
+
 #define TZFILE_H
 
 /*
  * Information about time zone files.
  */
 
-#define TZDEFAULT      "localtime"
+#define TZDEFAULT      "/etc/localtime"
 #define TZDEFRULES     "posixrules"
 
 /*
  * Each file begins with. . .
  */
 
-#define TZ_MAGIC       "TZif"
+#define        TZ_MAGIC        "TZif"
 
 struct tzhead
 {
index 66c56829257e9ee2bc475eb23658303123abe435..ae61264364cf298514d3afc7a70d82e46e908499 100644 (file)
@@ -1,3 +1,5 @@
+/* Compile .zi time zone data into TZif binary files.  */
+
 /*
  * This file is in the public domain, so clarified as of
  * 2006-07-17 by Arthur David Olson.
@@ -130,8 +132,7 @@ static void adjleap(void);
 static void associate(void);
 static void dolink(const char *, const char *, bool);
 static char **getfields(char *buf);
-static zic_t gethms(const char *string, const char *errstring,
-          bool);
+static zic_t gethms(const char *string, const char *errstring);
 static zic_t getstdoff(char *, bool *);
 static void infile(const char *filename);
 static void inleap(char **fields, int nfields);
@@ -162,7 +163,7 @@ enum
 PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1};
 
 /* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles
-   tz binary files whose POSIX-TZ-style strings contain '<'; see
+   TZif files whose POSIX-TZ-style strings contain '<'; see
    QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>.  This
    workaround will no longer be needed when Qt 5.6.1 and earlier are
    obsolete, say in the year 2021.  */
@@ -211,7 +212,7 @@ static int  typecnt;
 #define ZF_RULE                3
 #define ZF_FORMAT      4
 #define ZF_TILYEAR     5
-#define ZF_TILMONTH 6
+#define ZF_TILMONTH    6
 #define ZF_TILDAY      7
 #define ZF_TILTIME     8
 #define ZONE_MINFIELDS 5
@@ -224,12 +225,12 @@ static int        typecnt;
 #define ZFC_GMTOFF     0
 #define ZFC_RULE       1
 #define ZFC_FORMAT     2
-#define ZFC_TILYEAR 3
+#define ZFC_TILYEAR    3
 #define ZFC_TILMONTH   4
 #define ZFC_TILDAY     5
-#define ZFC_TILTIME 6
-#define ZONEC_MINFIELDS 3
-#define ZONEC_MAXFIELDS 7
+#define ZFC_TILTIME    6
+#define ZONEC_MINFIELDS        3
+#define ZONEC_MAXFIELDS        7
 
 /*
  * Which files are which on a Rule line.
@@ -244,7 +245,7 @@ static int  typecnt;
 #define RF_TOD         7
 #define RF_STDOFF      8
 #define RF_ABBRVAR     9
-#define RULE_FIELDS 10
+#define RULE_FIELDS    10
 
 /*
  * Which fields are which on a Link line.
@@ -252,7 +253,7 @@ static int  typecnt;
 
 #define LF_FROM                1
 #define LF_TO          2
-#define LINK_FIELDS 3
+#define LINK_FIELDS    3
 
 /*
  * Which fields are which on a Leap line.
@@ -264,7 +265,7 @@ static int  typecnt;
 #define LP_TIME                4
 #define LP_CORR                5
 #define LP_ROLL                6
-#define LEAP_FIELDS 7
+#define LEAP_FIELDS    7
 
 /*
  * Year synonyms.
@@ -998,48 +999,6 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
 static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
 static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
 
-/*
- * Estimated time of the Big Bang, in seconds since the POSIX epoch.
- * rounded downward to the negation of a power of two that is
- * comfortably outside the error bounds.
- *
- * For the time of the Big Bang, see:
- *
- * Ade PAR, Aghanim N, Armitage-Caplan C et al.  Planck 2013 results.
- * I. Overview of products and scientific results.
- * arXiv:1303.5062 2013-03-20 20:10:01 UTC
- * <https://arxiv.org/pdf/1303.5062v1> [PDF]
- *
- * Page 36, Table 9, row Age/Gyr, column Planck+WP+highL+BAO 68% limits
- * gives the value 13.798 plus-or-minus 0.037 billion years.
- * Multiplying this by 1000000000 and then by 31557600 (the number of
- * seconds in an astronomical year) gives a value that is comfortably
- * less than 2**59, so BIG_BANG is - 2**59.
- *
- * BIG_BANG is approximate, and may change in future versions.
- * Please do not rely on its exact value.
- */
-
-#ifndef BIG_BANG
-#define BIG_BANG (- (((zic_t) 1) << 59))
-#endif
-
-/* If true, work around GNOME bug 730332
-   <https://bugzilla.gnome.org/show_bug.cgi?id=730332>
-   by refusing to output time stamps before BIG_BANG.
-   Such time stamps are physically suspect anyway.
-
-   The GNOME bug is scheduled to be fixed in GNOME 3.22, and if so
-   this workaround will no longer be needed when GNOME 3.21 and
-   earlier are obsolete, say in the year 2021.  */
-enum
-{
-WORK_AROUND_GNOME_BUG_730332 = true};
-
-static const zic_t early_time = (WORK_AROUND_GNOME_BUG_730332
-                                                                ? BIG_BANG
-                                                                : MINVAL(zic_t, TIME_T_BITS_IN_FILE));
-
 /* Return true if NAME is a directory.  */
 static bool
 itsdir(char const *name)
@@ -1281,8 +1240,9 @@ infile(const char *name)
  * A null string maps to zero.
  * Call error with errstring and return zero on errors.
  */
+
 static zic_t
-gethms(char const *string, char const *errstring, bool signable)
+gethms(char const *string, char const *errstring)
 {
        /* PG: make hh be int not zic_t to avoid sscanf portability issues */
        int                     hh;
@@ -1299,9 +1259,7 @@ gethms(char const *string, char const *errstring, bool signable)
 
        if (string == NULL || *string == '\0')
                return 0;
-       if (!signable)
-               sign = 1;
-       else if (*string == '-')
+       if (*string == '-')
        {
                sign = -1;
                ++string;
@@ -1384,7 +1342,7 @@ getstdoff(char *field, bool *isdst)
                                break;
                }
        }
-       stdoff = gethms(field, _("invalid saved time"), true);
+       stdoff = gethms(field, _("invalid saved time"));
        *isdst = dst < 0 ? stdoff != 0 : dst;
        return stdoff;
 }
@@ -1399,10 +1357,29 @@ inrule(char **fields, int nfields)
                error(_("wrong number of fields on Rule line"));
                return;
        }
-       if (*fields[RF_NAME] == '\0')
+       switch (*fields[RF_NAME])
        {
-               error(_("nameless rule"));
-               return;
+               case '\0':
+               case ' ':
+               case '\f':
+               case '\n':
+               case '\r':
+               case '\t':
+               case '\v':
+               case '+':
+               case '-':
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
+                       return;
        }
        r.r_filename = filename;
        r.r_linenum = linenum;
@@ -1507,7 +1484,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"), true);
+       z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UT offset"));
        if ((cp = strchr(fields[i_format], '%')) != NULL)
        {
                if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
@@ -1649,7 +1626,7 @@ inleap(char **fields, int nfields)
                return;
        }
        t = dayoff * SECSPERDAY;
-       tod = gethms(fields[LP_TIME], _("invalid time of day"), false);
+       tod = gethms(fields[LP_TIME], _("invalid time of day"));
        cp = fields[LP_CORR];
        {
                bool            positive;
@@ -1757,7 +1734,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
                                break;
                }
        }
-       rp->r_tod = gethms(dp, _("invalid time of day"), false);
+       rp->r_tod = gethms(dp, _("invalid time of day"));
        free(dp);
 
        /*
@@ -1942,7 +1919,43 @@ is32(const zic_t x)
 }
 
 static void
-writezone(const char *const name, const char *const string, char version)
+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;
+       }
+}
+
+static void
+writezone(const char *const name, const char *const string, char version,
+                 int defaulttype)
 {
        FILE       *fp;
        ptrdiff_t       i,
@@ -1977,14 +1990,12 @@ writezone(const char *const name, const char *const string, char version)
 
                toi = 0;
                fromi = 0;
-               while (fromi < timecnt && attypes[fromi].at < early_time)
-                       ++fromi;
                for (; fromi < timecnt; ++fromi)
                {
-                       if (toi > 1 && ((attypes[fromi].at +
-                                                        gmtoffs[attypes[toi - 1].type]) <=
-                                                       (attypes[toi - 1].at +
-                                                        gmtoffs[attypes[toi - 2].type])))
+                       if (toi != 0 && ((attypes[fromi].at +
+                                                         gmtoffs[attypes[toi - 1].type]) <=
+                                                        (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
+                                                                                                                       : attypes[toi - 2].type])))
                        {
                                attypes[toi - 1].type =
                                        attypes[fromi].type;
@@ -2019,8 +2030,8 @@ writezone(const char *const name, const char *const string, char version)
        }
 
        /*
-        * Work around QTBUG-53071 for time stamps less than y2038_boundary - 1,
-        * by inserting a no-op transition at time y2038_boundary - 1. This works
+        * Work around QTBUG-53071 for timestamps less than y2038_boundary - 1, by
+        * inserting a no-op transition at time y2038_boundary - 1. This works
         * only for timestamps before the boundary, which should be good enough in
         * practice as QTBUG-53071 should be long-dead by 2038.
         */
@@ -2116,7 +2127,8 @@ writezone(const char *const name, const char *const string, char version)
                int                     thisleapi,
                                        thisleapcnt,
                                        thisleaplim;
-               int                     writetype[TZ_MAX_TYPES];
+               int                     old0;
+               char            omittype[TZ_MAX_TYPES];
                int                     typemap[TZ_MAX_TYPES];
                int                     thistypecnt;
                char            thischars[TZ_MAX_CHARS];
@@ -2144,28 +2156,19 @@ writezone(const char *const name, const char *const string, char version)
                        error(_("too many transition times"));
                thistimelim = thistimei + thistimecnt;
                thisleaplim = thisleapi + thisleapcnt;
-               for (i = 0; i < typecnt; ++i)
-                       writetype[i] = thistimecnt == timecnt;
-               if (thistimecnt == 0)
-               {
-                       /*
-                        * No transition times fall in the current (32- or 64-bit) window.
-                        */
-                       if (typecnt != 0)
-                               writetype[typecnt - 1] = true;
-               }
-               else
-               {
-                       for (i = thistimei - 1; i < thistimelim; ++i)
-                               if (i >= 0)
-                                       writetype[types[i]] = true;
+               memset(omittype, true, typecnt);
+               omittype[defaulttype] = false;
+               for (i = thistimei; i < thistimelim; i++)
+                       omittype[types[i]] = false;
+
+               /*
+                * Reorder types to make DEFAULTTYPE type 0. Use TYPEMAP to swap OLD0
+                * and DEFAULTTYPE so that DEFAULTTYPE appears as type 0 in the output
+                * instead of OLD0.  TYPEMAP also omits unused types.
+                */
+               old0 = strlen(omittype);
+               swaptypes(old0, defaulttype);
 
-                       /*
-                        * For America/Godthab and Antarctica/Palmer
-                        */
-                       if (thistimei == 0)
-                               writetype[0] = true;
-               }
 #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
 
                /*
@@ -2187,8 +2190,8 @@ writezone(const char *const name, const char *const string, char version)
                                        mrudst = types[i];
                                else
                                        mrustd = types[i];
-                       for (i = 0; i < typecnt; ++i)
-                               if (writetype[i])
+                       for (i = old0; i < typecnt; i++)
+                               if (!omittype[i])
                                {
                                        if (isdsts[i])
                                                hidst = i;
@@ -2205,7 +2208,7 @@ writezone(const char *const name, const char *const string, char version)
                                                           ttisstds[mrudst],
                                                           ttisgmts[mrudst]);
                                isdsts[mrudst] = 1;
-                               writetype[type] = true;
+                               omittype[type] = false;
                        }
                        if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
                                gmtoffs[histd] != gmtoffs[mrustd])
@@ -2217,22 +2220,26 @@ writezone(const char *const name, const char *const string, char version)
                                                           ttisstds[mrustd],
                                                           ttisgmts[mrustd]);
                                isdsts[mrustd] = 0;
-                               writetype[type] = true;
+                               omittype[type] = false;
                        }
                }
 #endif                                                 /* !defined
                                                                 * LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
                thistypecnt = 0;
-               for (i = 0; i < typecnt; ++i)
-                       typemap[i] = writetype[i] ? thistypecnt++ : -1;
+               for (i = old0; i < typecnt; i++)
+                       if (!omittype[i])
+                               typemap[i == old0 ? defaulttype
+                                               : i == defaulttype ? old0 : i]
+                                       = thistypecnt++;
+
                for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
                        indmap[i] = -1;
                thischarcnt = 0;
-               for (i = 0; i < typecnt; ++i)
+               for (i = old0; i < typecnt; i++)
                {
                        char       *thisabbr;
 
-                       if (!writetype[i])
+                       if (omittype[i])
                                continue;
                        if (indmap[abbrinds[i]] >= 0)
                                continue;
@@ -2267,23 +2274,16 @@ writezone(const char *const name, const char *const string, char version)
                DO(tzh_typecnt);
                DO(tzh_charcnt);
 #undef DO
-               for (i = thistimei; i < thistimelim; ++i)
-                       if (pass == 1)
 
-                               /*
-                                * Output an INT32_MIN "transition" if appropriate; see above.
-                                */
-                               puttzcode(((ats[i] < PG_INT32_MIN) ?
-                                                  PG_INT32_MIN : ats[i]), fp);
-                       else
+               /* PG: print current timezone abbreviations if requested */
+               if (print_abbrevs && pass == 2)
+               {
+                       /* Print "type" data for periods ending after print_cutoff */
+                       for (i = thistimei; i < thistimelim; ++i)
                        {
-                               puttzcode64(ats[i], fp);
-
-                               /* Print current timezone abbreviations if requested */
-                               if (print_abbrevs &&
-                                       (i == thistimelim - 1 || ats[i + 1] > print_cutoff))
+                               if (i == thistimelim - 1 || ats[i + 1] > print_cutoff)
                                {
-                                       unsigned char tm = typemap[types[i]];
+                                       unsigned char tm = types[i];
                                        char       *thisabbrev = &thischars[indmap[abbrinds[tm]]];
 
                                        /* filter out assorted junk entries */
@@ -2295,6 +2295,32 @@ writezone(const char *const name, const char *const string, char version)
                                                                isdsts[tm] ? "\tD" : "");
                                }
                        }
+                       /* Print the default type if we have no transitions at all */
+                       if (thistimei >= thistimelim)
+                       {
+                               unsigned char tm = defaulttype;
+                               char       *thisabbrev = &thischars[indmap[abbrinds[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],
+                                                       isdsts[tm] ? "\tD" : "");
+                       }
+               }
+
+               for (i = thistimei; i < thistimelim; ++i)
+                       if (pass == 1)
+
+                               /*
+                                * Output an INT32_MIN "transition" if appropriate; see above.
+                                */
+                               puttzcode(((ats[i] < PG_INT32_MIN) ?
+                                                  PG_INT32_MIN : ats[i]), fp);
+                       else
+                               puttzcode64(ats[i], fp);
                for (i = thistimei; i < thistimelim; ++i)
                {
                        unsigned char uc;
@@ -2302,8 +2328,8 @@ writezone(const char *const name, const char *const string, char version)
                        uc = typemap[types[i]];
                        fwrite(&uc, sizeof uc, 1, fp);
                }
-               for (i = 0; i < typecnt; ++i)
-                       if (writetype[i])
+               for (i = old0; i < typecnt; i++)
+                       if (!omittype[i])
                        {
                                puttzcode(gmtoffs[i], fp);
                                putc(isdsts[i], fp);
@@ -2346,12 +2372,13 @@ writezone(const char *const name, const char *const string, char version)
                                puttzcode64(todo, fp);
                        puttzcode(corr[i], fp);
                }
-               for (i = 0; i < typecnt; ++i)
-                       if (writetype[i])
+               for (i = old0; i < typecnt; i++)
+                       if (!omittype[i])
                                putc(ttisstds[i], fp);
-               for (i = 0; i < typecnt; ++i)
-                       if (writetype[i])
+               for (i = old0; i < typecnt; i++)
+                       if (!omittype[i])
                                putc(ttisgmts[i], fp);
+               swaptypes(old0, defaulttype);
        }
        fprintf(fp, "\n%s\n", string);
        close_file(fp, directory, name);
@@ -2757,6 +2784,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
        zic_t           one = 1;
        zic_t           y2038_boundary = one << 31;
        zic_t           max_year0;
+       int                     defaulttype = -1;
 
        max_abbr_len = 2 + max_format_len + max_abbrvar_len;
        max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2880,9 +2908,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                 */
                stdoff = 0;
                zp = &zpfirst[i];
-               usestart = i > 0 && (zp - 1)->z_untiltime > early_time;
+               usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
                useuntil = i < (zonecount - 1);
-               if (useuntil && zp->z_untiltime <= early_time)
+               if (useuntil && zp->z_untiltime <= min_time)
                        continue;
                gmtoff = zp->z_gmtoff;
                eat(zp->z_filename, zp->z_linenum);
@@ -2901,7 +2929,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                                usestart = false;
                        }
                        else
-                               addtt(early_time, type);
+                               defaulttype = type;
                }
                else
                        for (year = min_year; year <= max_year; ++year)
@@ -3032,6 +3060,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                                        offset = oadd(zp->z_gmtoff, rp->r_stdoff);
                                        type = addtype(offset, ab, rp->r_isdst,
                                                                   rp->r_todisstd, rp->r_todisgmt);
+                                       if (defaulttype < 0 && !rp->r_isdst)
+                                               defaulttype = type;
                                        if (rp->r_hiyear == ZIC_MAX
                                                && !(0 <= lastatmax
                                                         && ktime < attypes[lastatmax].at))
@@ -3050,11 +3080,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                        if (*startbuf == '\0')
                                error(_("cannot determine time zone abbreviation to use just after until time"));
                        else
-                               addtt(starttime,
-                                         addtype(startoff, startbuf,
-                                                         startoff != zp->z_gmtoff,
-                                                         startttisstd,
-                                                         startttisgmt));
+                       {
+                               bool            isdst = startoff != zp->z_gmtoff;
+
+                               type = addtype(startoff, startbuf, isdst,
+                                                          startttisstd, startttisgmt);
+                               if (defaulttype < 0 && !isdst)
+                                       defaulttype = type;
+                               addtt(starttime, type);
+                       }
                }
 
                /*
@@ -3071,6 +3105,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                                starttime = tadd(starttime, -gmtoff);
                }
        }
+       if (defaulttype < 0)
+               defaulttype = 0;
        if (0 <= lastatmax)
                attypes[lastatmax].dontmerge = true;
        if (do_extend)
@@ -3100,7 +3136,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
                        attypes[timecnt - 1].dontmerge = true;
                }
        }
-       writezone(zpfirst->z_name, envvar, version);
+       writezone(zpfirst->z_name, envvar, version, defaulttype);
        free(startbuf);
        free(ab);
        free(envvar);
@@ -3109,21 +3145,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 static void
 addtt(zic_t starttime, int type)
 {
-       if (starttime <= early_time
-               || (timecnt == 1 && attypes[0].at < early_time))
-       {
-               gmtoffs[0] = gmtoffs[type];
-               isdsts[0] = isdsts[type];
-               ttisstds[0] = ttisstds[type];
-               ttisgmts[0] = ttisgmts[type];
-               if (abbrinds[type] != 0)
-                       strcpy(chars, &chars[abbrinds[type]]);
-               abbrinds[0] = 0;
-               charcnt = strlen(chars) + 1;
-               typecnt = 1;
-               timecnt = 0;
-               type = 0;
-       }
        attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
        attypes[timecnt].at = starttime;
        attypes[timecnt].dontmerge = false;
@@ -3361,7 +3382,7 @@ is_alpha(char a)
 }
 
 /* If A is an uppercase character in the C locale, return its lowercase
* counterpart.  Otherwise, return A.  */
  counterpart.  Otherwise, return A.  */
 static char
 lowerit(char a)
 {
@@ -3628,6 +3649,18 @@ rpytime(const struct rule *rp, zic_t wantedy)
        dayoff = 0;
        m = TM_JANUARY;
        y = EPOCH_YEAR;
+       if (y < wantedy)
+       {
+               wantedy -= y;
+               dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
+               wantedy %= YEARSPERREPEAT;
+               wantedy += y;
+       }
+       else if (wantedy < 0)
+       {
+               dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
+               wantedy %= YEARSPERREPEAT;
+       }
        while (wantedy != y)
        {
                if (wantedy > y)
@@ -3706,7 +3739,6 @@ will not work with pre-2004 versions of zic"));
        if (dayoff > max_time / SECSPERDAY)
                return max_time;
        t = (zic_t) dayoff * SECSPERDAY;
-
        return tadd(t, rp->r_tod);
 }