diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
index d004e5ebe2b4e92b605d09e58d8b48a01d45d83c..e6c1beaf96a8a5042e5d2cadd5ef3935149971ce 100644
--- a/src/timezone/localtime.c
+++ b/src/timezone/localtime.c
@@ -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;
diff --git a/src/timezone/private.h b/src/timezone/private.h
index 8480d389612243adc322dc3e80129d12afaa6a16..b8533d51e83a299ca9052a45068b8dda7623dbc8 100644
--- a/src/timezone/private.h
+++ b/src/timezone/private.h
@@ -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"
 
 /*
diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c
index 5630619321312c00fe59a008d0448f783c347e7c..4a0a01db651962c8e0b8c074f7a71cb540609107 100644
--- a/src/timezone/strftime.c
+++ b/src/timezone/strftime.c
@@ -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';
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index b546a1737299b803569102d4816d5b0bff844e31..52d8004ff73e437e5ec8146a878ff3ce822bfde2 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -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;
 }