Sync our copy of the timezone library with IANA release tzcode2016g.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Oct 2016 22:55:52 +0000 (18:55 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Oct 2016 22:55:57 +0000 (18:55 -0400)
This is mostly to absorb some corner-case fixes in zic for year-2037
timestamps.  The other changes that have been made are unlikely to affect
our usage, but nonetheless we may as well take 'em.

src/timezone/localtime.c
src/timezone/private.h
src/timezone/strftime.c
src/timezone/zic.c

index a14215d6bde177847c005020431d7df809d7fbd4..ae9c2d23e631e5cff2981f0131a3b72df4319d59 100644 (file)
@@ -1280,9 +1280,8 @@ gmtsub(pg_time_t const * timep, int32 offset, struct pg_tm * tmp)
    result = timesub(timep, offset, gmtptr, tmp);
 
    /*
-    * Could get fancy here and deliver something such as "UT+xxxx" or
-    * "UT-xxxx" if offset is non-zero, but this is no time for a treasure
-    * hunt.
+    * Could get fancy here and deliver something such as "+xx" or "-xx" if
+    * offset is non-zero, but this is no time for a treasure hunt.
     */
    if (offset != 0)
        tmp->tm_zone = wildabbr;
index 8480d389612243adc322dc3e80129d12afaa6a16..b8533d51e83a299ca9052a45068b8dda7623dbc8 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "pgtime.h"
 
+/* This string was in the Factory zone through version 2016f.  */
 #define GRANDPARENTED  "Local time zone must be set--see zic manual page"
 
 /*
index 5630619321312c00fe59a008d0448f783c347e7c..4a0a01db651962c8e0b8c074f7a71cb540609107 100644 (file)
@@ -128,7 +128,7 @@ pg_strftime(char *s, size_t maxsize, const char *format,
    int         warn;
 
    warn = IN_NONE;
-   p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
+   p = _fmt(format, t, s, s + maxsize, &warn);
    if (p == s + maxsize)
        return 0;
    *p = '\0';
index b546a1737299b803569102d4816d5b0bff844e31..52d8004ff73e437e5ec8146a878ff3ce822bfde2 100644 (file)
@@ -105,7 +105,7 @@ static int  addtype(zic_t, char const *, bool, bool, bool);
 static void leapadd(zic_t, bool, int, int);
 static void adjleap(void);
 static void associate(void);
-static void dolink(const char *fromfield, const char *tofield);
+static void dolink(const char *, const char *, bool);
 static char **getfields(char *buf);
 static zic_t gethms(const char *string, const char *errstring,
       bool);
@@ -119,7 +119,7 @@ static bool inzsub(char **, int, bool);
 static int itsdir(const char *name);
 static bool is_alpha(char a);
 static char lowerit(char);
-static bool mkdirs(char *);
+static void mkdirs(char const *, bool);
 static void newabbr(const char *abbr);
 static zic_t oadd(zic_t t1, zic_t t2);
 static void outzone(const struct zone * zp, int ntzones);
@@ -136,6 +136,15 @@ 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
+   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.  */
+enum
+{
+WORK_AROUND_QTBUG_53071 = true};
+
 static int charcnt;
 static bool errors;
 static bool warnings;
@@ -346,6 +355,7 @@ static const int len_years[2] = {
 static struct attype
 {
    zic_t       at;
+   bool        dontmerge;
    unsigned char type;
 }  *attypes;
 static zic_t gmtoffs[TZ_MAX_TYPES];
@@ -410,7 +420,8 @@ growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
        return ptr;
    else
    {
-       int         amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
+       int         nitems_max = INT_MAX - WORK_AROUND_QTBUG_53071;
+       int         amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
 
        if ((amax - 1) / 3 * 2 < *nitems_alloc)
            memory_exhausted(_("int overflow"));
@@ -478,17 +489,17 @@ warning(const char *string,...)
 }
 
 static void
-close_file(FILE *stream, char const * name)
+close_file(FILE *stream, char const * dir, char const * name)
 {
    char const *e = (ferror(stream) ? _("I/O error")
                     : fclose(stream) != 0 ? strerror(errno) : NULL);
 
    if (e)
    {
-       fprintf(stderr, "%s: ", progname);
-       if (name)
-           fprintf(stderr, "%s: ", name);
-       fprintf(stderr, "%s\n", e);
+       fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
+               dir ? dir : "", dir ? "/" : "",
+               name ? name : "", name ? ": " : "",
+               e);
        exit(EXIT_FAILURE);
    }
 }
@@ -503,10 +514,34 @@ usage(FILE *stream, int status)
              "Report bugs to %s.\n"),
            progname, progname, PACKAGE_BUGREPORT);
    if (status == EXIT_SUCCESS)
-       close_file(stream, NULL);
+       close_file(stream, NULL, NULL);
    exit(status);
 }
 
+/* Change the working directory to DIR, possibly creating DIR and its
+   ancestors.  After this is done, all files are accessed with names
+   relative to DIR.  */
+static void
+change_directory(char const * dir)
+{
+   if (chdir(dir) != 0)
+   {
+       int         chdir_errno = errno;
+
+       if (chdir_errno == ENOENT)
+       {
+           mkdirs(dir, false);
+           chdir_errno = chdir(dir) == 0 ? 0 : errno;
+       }
+       if (chdir_errno != 0)
+       {
+           fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
+                   progname, dir, strerror(chdir_errno));
+           exit(EXIT_FAILURE);
+       }
+   }
+}
+
 static const char *psxrules;
 static const char *lcltime;
 static const char *directory;
@@ -534,7 +569,7 @@ main(int argc, char *argv[])
        if (strcmp(argv[i], "--version") == 0)
        {
            printf("zic %s\n", PG_VERSION);
-           close_file(stdout, NULL);
+           close_file(stdout, NULL, NULL);
            return EXIT_SUCCESS;
        }
        else if (strcmp(argv[i], "--help") == 0)
@@ -630,6 +665,7 @@ main(int argc, char *argv[])
    if (errors)
        return EXIT_FAILURE;
    associate();
+   change_directory(directory);
    for (i = 0; i < nzones; i = j)
    {
        /*
@@ -646,7 +682,7 @@ main(int argc, char *argv[])
    for (i = 0; i < nlinks; ++i)
    {
        eat(links[i].l_filename, links[i].l_linenum);
-       dolink(links[i].l_from, links[i].l_to);
+       dolink(links[i].l_from, links[i].l_to, false);
        if (noise)
            for (j = 0; j < nlinks; ++j)
                if (strcmp(links[i].l_to,
@@ -656,12 +692,12 @@ main(int argc, char *argv[])
    if (lcltime != NULL)
    {
        eat(_("command line"), 1);
-       dolink(lcltime, TZDEFAULT);
+       dolink(lcltime, TZDEFAULT, true);
    }
    if (psxrules != NULL)
    {
        eat(_("command line"), 1);
-       dolink(psxrules, TZDEFRULES);
+       dolink(psxrules, TZDEFRULES, true);
    }
    if (warnings && (ferror(stderr) || fclose(stderr) != 0))
        return EXIT_FAILURE;
@@ -751,131 +787,117 @@ namecheck(const char *name)
    return componentcheck(name, component, cp);
 }
 
-static char *
-relname(char const * dir, char const * base)
-{
-   if (*base == '/')
-       return ecpyalloc(base);
-   else
-   {
-       size_t      dir_len = strlen(dir);
-       bool        needs_slash = dir_len && dir[dir_len - 1] != '/';
-       char       *result = emalloc(dir_len + needs_slash + strlen(base) + 1);
-
-       result[dir_len] = '/';
-       strcpy(result + dir_len + needs_slash, base);
-       return memcpy(result, dir, dir_len);
-   }
-}
-
 static void
-dolink(char const * fromfield, char const * tofield)
+dolink(char const * fromfield, char const * tofield, bool staysymlink)
 {
-   char       *fromname;
-   char       *toname;
    int         fromisdir;
-
-   fromname = relname(directory, fromfield);
-   toname = relname(directory, tofield);
+   bool        todirs_made = false;
+   int         link_errno;
 
    /*
     * We get to be careful here since there's a fair chance of root running
     * us.
     */
-   fromisdir = itsdir(fromname);
+   fromisdir = itsdir(fromfield);
    if (fromisdir)
    {
        char const *e = strerror(fromisdir < 0 ? errno : EPERM);
 
-       fprintf(stderr, _("%s: link from %s failed: %s"),
-               progname, fromname, e);
+       fprintf(stderr, _("%s: link from %s/%s failed: %s\n"),
+               progname, directory, fromfield, e);
        exit(EXIT_FAILURE);
    }
-   if (link(fromname, toname) != 0)
+   if (staysymlink)
+       staysymlink = itsdir(tofield) == 2;
+   if (remove(tofield) == 0)
+       todirs_made = true;
+   else if (errno != ENOENT)
    {
-       int         link_errno = errno;
-       bool        retry_if_link_supported = false;
+       char const *e = strerror(errno);
 
-       if (link_errno == ENOENT || link_errno == ENOTSUP)
+       fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
+               progname, directory, tofield, e);
+       exit(EXIT_FAILURE);
+   }
+   link_errno = (staysymlink ? ENOTSUP
+                 : link(fromfield, tofield) == 0 ? 0 : errno);
+   if (link_errno == ENOENT && !todirs_made)
+   {
+       mkdirs(tofield, true);
+       todirs_made = true;
+       link_errno = link(fromfield, tofield) == 0 ? 0 : errno;
+   }
+   if (link_errno != 0)
+   {
+#ifdef HAVE_SYMLINK
+       const char *s = fromfield;
+       const char *t;
+       char       *p;
+       size_t      dotdots = 0;
+       char       *symlinkcontents;
+       int         symlink_errno;
+
+       do
+           t = s;
+       while ((s = strchr(s, '/'))
+              && strncmp(fromfield, tofield, ++s - fromfield) == 0);
+
+       for (s = tofield + (t - fromfield); *s; s++)
+           dotdots += *s == '/';
+       symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1);
+       for (p = symlinkcontents; dotdots-- != 0; p += 3)
+           memcpy(p, "../", 3);
+       strcpy(p, t);
+       symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
+       if (symlink_errno == ENOENT && !todirs_made)
        {
-           if (!mkdirs(toname))
-               exit(EXIT_FAILURE);
-           retry_if_link_supported = true;
+           mkdirs(tofield, true);
+           symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
        }
-       if ((link_errno == EEXIST || link_errno == ENOTSUP)
-           && itsdir(toname) == 0
-           && (remove(toname) == 0 || errno == ENOENT))
-           retry_if_link_supported = true;
-       if (retry_if_link_supported && link_errno != ENOTSUP)
-           link_errno = link(fromname, toname) == 0 ? 0 : errno;
-       if (link_errno != 0)
+       free(symlinkcontents);
+       if (symlink_errno == 0)
        {
-#ifdef HAVE_SYMLINK
-           const char *s = fromfield;
-           const char *t;
-           char       *p;
-           size_t      dotdots = 0;
-           char       *symlinkcontents;
-           int         symlink_result;
-
-           do
-               t = s;
-           while ((s = strchr(s, '/'))
-                  && strncmp(fromfield, tofield, ++s - fromfield) == 0);
-
-           for (s = tofield + (t - fromfield); *s; s++)
-               dotdots += *s == '/';
-           symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1);
-           for (p = symlinkcontents; dotdots-- != 0; p += 3)
-               memcpy(p, "../", 3);
-           strcpy(p, t);
-           symlink_result = symlink(symlinkcontents, toname);
-           free(symlinkcontents);
-           if (symlink_result == 0)
-           {
-               if (link_errno != ENOTSUP)
-                   warning(_("symbolic link used because hard link failed: %s"),
-                           strerror(link_errno));
-           }
-           else
+           if (link_errno != ENOTSUP)
+               warning(_("symbolic link used because hard link failed: %s"),
+                       strerror(link_errno));
+       }
+       else
 #endif   /* HAVE_SYMLINK */
-           {
-               FILE       *fp,
-                          *tp;
-               int         c;
+       {
+           FILE       *fp,
+                      *tp;
+           int         c;
 
-               fp = fopen(fromname, "rb");
-               if (!fp)
-               {
-                   const char *e = strerror(errno);
+           fp = fopen(fromfield, "rb");
+           if (!fp)
+           {
+               char const *e = strerror(errno);
 
-                   fprintf(stderr,
-                           _("%s: Can't read %s: %s\n"),
-                           progname, fromname, e);
-                   exit(EXIT_FAILURE);
-               }
-               tp = fopen(toname, "wb");
-               if (!tp)
-               {
-                   const char *e = strerror(errno);
+               fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
+                       progname, directory, fromfield, e);
+               exit(EXIT_FAILURE);
+           }
+           tp = fopen(tofield, "wb");
+           if (!tp)
+           {
+               char const *e = strerror(errno);
 
-                   fprintf(stderr,
-                           _("%s: Can't create %s: %s\n"),
-                           progname, toname, e);
-                   exit(EXIT_FAILURE);
-               }
-               while ((c = getc(fp)) != EOF)
-                   putc(c, tp);
-               close_file(fp, fromname);
-               close_file(tp, toname);
-               if (link_errno != ENOTSUP)
-                   warning(_("copy used because hard link failed: %s"),
-                           strerror(link_errno));
+               fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
+                       progname, directory, tofield, e);
+               exit(EXIT_FAILURE);
            }
+           while ((c = getc(fp)) != EOF)
+               putc(c, tp);
+           close_file(fp, directory, fromfield);
+           close_file(tp, directory, tofield);
+           if (link_errno != ENOTSUP)
+               warning(_("copy used because hard link failed: %s"),
+                       strerror(link_errno));
+           else if (symlink_errno != ENOTSUP)
+               warning(_("copy used because symbolic link failed: %s"),
+                       strerror(symlink_errno));
        }
    }
-   free(fromname);
-   free(toname);
 }
 
 #define TIME_T_BITS_IN_FILE 64
@@ -888,10 +910,6 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
  * rounded downward to the negation of a power of two that is
  * comfortably outside the error bounds.
  *
- * zic does not output time stamps before this, partly because they
- * are physically suspect, and partly because GNOME mishandles them; see
- * GNOME bug 730332 <https://bugzilla.gnome.org/show_bug.cgi?id=730332>.
- *
  * For the time of the Big Bang, see:
  *
  * Ade PAR, Aghanim N, Armitage-Caplan C et al.  Planck 2013 results.
@@ -913,26 +931,45 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
 #define BIG_BANG (- (((zic_t) 1) << 59))
 #endif
 
-static const zic_t big_bang_time = BIG_BANG;
+/* 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.
 
-/* Return 1 if NAME is a directory, 0 if it's something else, -1 if trouble.  */
+   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 1 if NAME is a directory, 2 if a symbolic link, 0 if
+   something else, -1 (setting errno) if trouble.  */
 static int
 itsdir(char const * name)
 {
    struct stat st;
-   int         res = stat(name, &st);
+   int         res = lstat(name, &st);
 
-#ifdef S_ISDIR
    if (res == 0)
-       return S_ISDIR(st.st_mode) != 0;
-#endif
-   if (res == 0 || errno == EOVERFLOW)
    {
-       char       *nameslashdot = relname(name, ".");
-       bool        dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW;
+#ifdef S_ISDIR
+       return S_ISDIR(st.st_mode) ? 1 : S_ISLNK(st.st_mode) ? 2 : 0;
+#else
+       size_t      n = strlen(name);
+       char       *nameslashdot = emalloc(n + 3);
+       bool        dir;
 
+       memcpy(nameslashdot, name, n);
+       strcpy(&nameslashdot[n], &"/."[!(n && name[n - 1] != '/')]);
+       dir = lstat(nameslashdot, &st) == 0;
        free(nameslashdot);
        return dir;
+#endif
    }
    return -1;
 }
@@ -1129,7 +1166,7 @@ infile(const char *name)
        }
        free(fields);
    }
-   close_file(fp, filename);
+   close_file(fp, NULL, filename);
    if (wantcont)
        error(_("expected continuation line not found"));
 }
@@ -1313,7 +1350,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);
-   if ((cp = strchr(fields[i_format], '%')) != 0)
+   if ((cp = strchr(fields[i_format], '%')) != NULL)
    {
        if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
            || strchr(fields[i_format], '/'))
@@ -1491,7 +1528,7 @@ inleap(char **fields, int nfields)
            return;
        }
        t = tadd(t, tod);
-       if (t < big_bang_time)
+       if (t < early_time)
        {
            error(_("leap second precedes Big Bang"));
            return;
@@ -1764,11 +1801,14 @@ writezone(const char *const name, const char *const string, char version)
    int         timecnt32,
                timei32;
    int         pass;
-   char       *fullname;
    static const struct tzhead tzh0;
    static struct tzhead tzh;
-   zic_t      *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
-   void       *typesptr = ats + timecnt;
+   bool        dir_checked = false;
+   zic_t       one = 1;
+   zic_t       y2038_boundary = one << 31;
+   int         nats = timecnt + WORK_AROUND_QTBUG_53071;
+   zic_t      *ats = emalloc(size_product(nats, sizeof *ats + 1));
+   void       *typesptr = ats + nats;
    unsigned char *types = typesptr;
 
    /*
@@ -1786,7 +1826,7 @@ writezone(const char *const name, const char *const string, char version)
 
        toi = 0;
        fromi = 0;
-       while (fromi < timecnt && attypes[fromi].at < big_bang_time)
+       while (fromi < timecnt && attypes[fromi].at < early_time)
            ++fromi;
        for (; fromi < timecnt; ++fromi)
        {
@@ -1799,8 +1839,9 @@ writezone(const char *const name, const char *const string, char version)
                    attypes[fromi].type;
                continue;
            }
-           if (toi == 0 ||
-               attypes[toi - 1].type != attypes[fromi].type)
+           if (toi == 0
+               || attypes[fromi].dontmerge
+               || attypes[toi - 1].type != attypes[fromi].type)
                attypes[toi++] = attypes[fromi];
        }
        timecnt = toi;
@@ -1818,6 +1859,20 @@ writezone(const char *const name, const char *const string, char version)
        types[i] = attypes[i].type;
    }
 
+   /*
+    * 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
+    * only for timestamps before the boundary, which should be good enough in
+    * practice as QTBUG-53071 should be long-dead by 2038.
+    */
+   if (WORK_AROUND_QTBUG_53071 && timecnt != 0
+       && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<'))
+   {
+       ats[timecnt] = y2038_boundary - 1;
+       types[timecnt] = types[timecnt - 1];
+       timecnt++;
+   }
+
    /*
     * Correct for leap seconds.
     */
@@ -1862,29 +1917,35 @@ writezone(const char *const name, const char *const string, char version)
        --leapcnt32;
        ++leapi32;
    }
-   fullname = relname(directory, name);
 
    /*
     * Remove old file, if any, to snap links.
     */
-   if (itsdir(fullname) == 0 && remove(fullname) != 0 && errno != ENOENT)
+   if (remove(name) == 0)
+       dir_checked = true;
+   else if (errno != ENOENT)
    {
        const char *e = strerror(errno);
 
-       fprintf(stderr, _("%s: Cannot remove %s: %s\n"),
-               progname, fullname, e);
+       fprintf(stderr, _("%s: Cannot remove %s/%s: %s\n"),
+               progname, directory, name, e);
        exit(EXIT_FAILURE);
    }
-   if ((fp = fopen(fullname, "wb")) == NULL)
+   fp = fopen(name, "wb");
+   if (!fp)
    {
-       if (!mkdirs(fullname))
-           exit(EXIT_FAILURE);
-       if ((fp = fopen(fullname, "wb")) == NULL)
-       {
-           const char *e = strerror(errno);
+       int         fopen_errno = errno;
 
-           fprintf(stderr, _("%s: Cannot create %s: %s\n"),
-                   progname, fullname, e);
+       if (fopen_errno == ENOENT && !dir_checked)
+       {
+           mkdirs(name, true);
+           fp = fopen(name, "wb");
+           fopen_errno = errno;
+       }
+       if (!fp)
+       {
+           fprintf(stderr, _("%s: Cannot create %s/%s: %s\n"),
+                   progname, directory, name, strerror(fopen_errno));
            exit(EXIT_FAILURE);
        }
    }
@@ -2130,9 +2191,8 @@ writezone(const char *const name, const char *const string, char version)
                putc(ttisgmts[i], fp);
    }
    fprintf(fp, "\n%s\n", string);
-   close_file(fp, fullname);
+   close_file(fp, directory, name);
    free(ats);
-   free(fullname);
 }
 
 static char const *
@@ -2527,6 +2587,7 @@ outzone(const struct zone * zpfirst, int zonecount)
    int         compat;
    bool        do_extend;
    char        version;
+   int         lastatmax = -1;
 
    max_abbr_len = 2 + max_format_len + max_abbrvar_len;
    max_envvar_len = 2 * max_abbr_len + 5 * 9;
@@ -2649,9 +2710,9 @@ outzone(const struct zone * zpfirst, int zonecount)
         */
        stdoff = 0;
        zp = &zpfirst[i];
-       usestart = i > 0 && (zp - 1)->z_untiltime > big_bang_time;
+       usestart = i > 0 && (zp - 1)->z_untiltime > early_time;
        useuntil = i < (zonecount - 1);
-       if (useuntil && zp->z_untiltime <= big_bang_time)
+       if (useuntil && zp->z_untiltime <= early_time)
            continue;
        gmtoff = zp->z_gmtoff;
        eat(zp->z_filename, zp->z_linenum);
@@ -2670,7 +2731,7 @@ outzone(const struct zone * zpfirst, int zonecount)
                usestart = false;
            }
            else
-               addtt(big_bang_time, type);
+               addtt(early_time, type);
        }
        else
            for (year = min_year; year <= max_year; ++year)
@@ -2792,6 +2853,10 @@ outzone(const struct zone * zpfirst, int zonecount)
                    offset = oadd(zp->z_gmtoff, rp->r_stdoff);
                    type = addtype(offset, ab, rp->r_stdoff != 0,
                                   rp->r_todisstd, rp->r_todisgmt);
+                   if (rp->r_hiyear == ZIC_MAX
+                       && !(0 <= lastatmax
+                            && ktime < attypes[lastatmax].at))
+                       lastatmax = timecnt;
                    addtt(ktime, type);
                }
            }
@@ -2827,6 +2892,8 @@ outzone(const struct zone * zpfirst, int zonecount)
                starttime = tadd(starttime, -gmtoff);
        }
    }
+   if (0 <= lastatmax)
+       attypes[lastatmax].dontmerge = true;
    if (do_extend)
    {
        /*
@@ -2850,22 +2917,8 @@ outzone(const struct zone * zpfirst, int zonecount)
                lastat = &attypes[i];
        if (lastat->at < rpytime(&xr, max_year - 1))
        {
-           /*
-            * Create new type code for the redundant entry, to prevent it
-            * being optimized away.
-            */
-           if (typecnt >= TZ_MAX_TYPES)
-           {
-               error(_("too many local time types"));
-               exit(EXIT_FAILURE);
-           }
-           gmtoffs[typecnt] = gmtoffs[lastat->type];
-           isdsts[typecnt] = isdsts[lastat->type];
-           ttisstds[typecnt] = ttisstds[lastat->type];
-           ttisgmts[typecnt] = ttisgmts[lastat->type];
-           abbrinds[typecnt] = abbrinds[lastat->type];
-           ++typecnt;
            addtt(rpytime(&xr, max_year + 1), typecnt - 1);
+           attypes[timecnt - 1].dontmerge = true;
        }
    }
    writezone(zpfirst->z_name, envvar, version);
@@ -2877,8 +2930,8 @@ outzone(const struct zone * zpfirst, int zonecount)
 static void
 addtt(zic_t starttime, int type)
 {
-   if (starttime <= big_bang_time ||
-       (timecnt == 1 && attypes[0].at < big_bang_time))
+   if (starttime <= early_time
+       || (timecnt == 1 && attypes[0].at < early_time))
    {
        gmtoffs[0] = gmtoffs[type];
        isdsts[0] = isdsts[type];
@@ -2894,6 +2947,7 @@ addtt(zic_t starttime, int type)
    }
    attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
    attypes[timecnt].at = starttime;
+   attypes[timecnt].dontmerge = false;
    attypes[timecnt].type = type;
    ++timecnt;
 }
@@ -3441,53 +3495,51 @@ newabbr(const char *string)
    charcnt += i;
 }
 
-static bool
-mkdirs(char *argname)
+/* Ensure that the directories of ARGNAME exist, by making any missing
+   ones.  If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
+   do it for ARGNAME too.  Exit with failure if there is trouble.  */
+static void
+mkdirs(char const * argname, bool ancestors)
 {
    char       *name;
    char       *cp;
 
-   if (argname == NULL || *argname == '\0')
-       return true;
    cp = name = ecpyalloc(argname);
-   while ((cp = strchr(cp + 1, '/')) != NULL)
-   {
-       *cp = '\0';
+
+   /* Do not mkdir a root directory, as it must exist.  */
 #ifdef WIN32
+   if (is_alpha(name[0]) && name[1] == ':')
+       cp += 2;
+#endif
+   while (*cp == '/')
+       cp++;
 
-       /*
-        * DOS drive specifier?
-        */
-       if (is_alpha(name[0]) && name[1] == ':' && name[2] == '\0')
-       {
-           *cp = '/';
-           continue;
-       }
-#endif   /* WIN32 */
+   while (cp && ((cp = strchr(cp, '/')) || !ancestors))
+   {
+       if (cp)
+           *cp = '\0';
 
        /*
         * Try to create it.  It's OK if creation fails because the directory
         * already exists, perhaps because some other process just created it.
+        * For simplicity do not check first whether it already exists, as
+        * that is checked anyway if the mkdir fails.
         */
        if (mkdir(name, MKDIR_UMASK) != 0)
        {
            int         err = errno;
 
-           if (itsdir(name) <= 0)
+           if (err != EEXIST && itsdir(name) < 0)
            {
-               char const *e = strerror(err);
-
-               warning(_("%s: Can't create directory"
-                         " %s: %s"),
-                       progname, name, e);
-               free(name);
-               return false;
+               error(_("%s: Cannot create directory %s: %s"),
+                     progname, name, strerror(err));
+               exit(EXIT_FAILURE);
            }
        }
-       *cp = '/';
+       if (cp)
+           *cp++ = '/';
    }
    free(name);
-   return true;
 }