about summary refs log tree commit diff
path: root/timezone/zic.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2018-12-10 13:11:58 +0000
committerJoseph Myers <joseph@codesourcery.com>2018-12-10 13:11:58 +0000
commit0afcb29d50312409f0b698001d1ee2b535d06a3c (patch)
tree810bf1bbcabd4ad6cc535fb15de39c22b7aeac2e /timezone/zic.c
parentb93f4052fc171f456c8f594d4ae4aeb02ec5b3b6 (diff)
downloadglibc-0afcb29d50312409f0b698001d1ee2b535d06a3c.tar.gz
glibc-0afcb29d50312409f0b698001d1ee2b535d06a3c.tar.xz
glibc-0afcb29d50312409f0b698001d1ee2b535d06a3c.zip
Update timezone code from tzcode 2018g.
This patch updates files coming from tzcode to the versions in tzcode
2018g.  No changes elsewhere in glibc were needed.

Tested for x86_64.

	* timezone/private.h: Update from tzcode 2018g.
	* timezone/tzfile.h: Likewise.
	* timezone/tzselect.ksh: Likewise.
	* timezone/zdump.c: Likewise.
	* timezone/zic.c: Likewise.
Diffstat (limited to 'timezone/zic.c')
-rw-r--r--timezone/zic.c530
1 files changed, 322 insertions, 208 deletions
diff --git a/timezone/zic.c b/timezone/zic.c
index e738386600..cb1bf28bfb 100644
--- a/timezone/zic.c
+++ b/timezone/zic.c
@@ -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.
@@ -11,6 +13,7 @@
 #include <locale.h>
 #include <stdarg.h>
 #include <stddef.h>
+#include <stdio.h>
 
 #define	ZIC_VERSION_PRE_2013 '2'
 #define	ZIC_VERSION	'3'
@@ -40,12 +43,32 @@ typedef int_fast64_t	zic_t;
 #else
 #define MKDIR_UMASK 0755
 #endif
+/* Port to native MS-Windows and to ancient UNIX.  */
+#if !defined S_ISDIR && defined S_IFDIR && defined S_IFMT
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>	/* for WIFEXITED and WEXITSTATUS */
+#endif /* HAVE_SYS_WAIT_H */
+
+#ifndef WIFEXITED
+#define WIFEXITED(status)	(((status) & 0xff) == 0)
+#endif /* !defined WIFEXITED */
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status)	(((status) >> 8) & 0xff)
+#endif /* !defined WEXITSTATUS */
 
 /* The maximum ptrdiff_t value, for pre-C99 platforms.  */
 #ifndef PTRDIFF_MAX
 static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
 #endif
 
+/* The minimum alignment of a type, for pre-C11 platforms.  */
+#if __STDC_VERSION__ < 201112
+# define _Alignof(type) offsetof(struct { char a; type b; }, b)
+#endif
+
 /* The type for line numbers.  Use PRIdMAX to format them; formerly
    there was also "#define PRIdLINENO PRIdMAX" and formats used
    PRIdLINENO, but xgettext cannot grok that.  */
@@ -73,7 +96,9 @@ struct rule {
 					/* or wall clock time if 0 */
 	bool		r_todisgmt;	/* above is GMT if 1 */
 					/* or local time if 0 */
-	zic_t		r_stdoff;	/* offset from standard time */
+	bool		r_isdst;	/* is this daylight saving time? */
+	zic_t		r_stdoff;	/* offset from default time (which is
+					   usually standard time) */
 	const char *	r_abbrvar;	/* variable part of abbreviation */
 
 	bool		r_todo;		/* a rule to do (used in outzone) */
@@ -94,10 +119,11 @@ struct zone {
 
 	const char *	z_name;
 	zic_t		z_gmtoff;
-	const char *	z_rule;
+	char *		z_rule;
 	const char *	z_format;
 	char		z_format_specifier;
 
+	bool		z_isdst;
 	zic_t		z_stdoff;
 
 	struct rule *	z_rules;
@@ -135,8 +161,8 @@ 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);
 static void	inlink(char ** fields, int nfields);
@@ -164,11 +190,13 @@ static bool	yearistype(zic_t year, const char * type);
 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.  */
+#ifndef WORK_AROUND_QTBUG_53071
 enum { WORK_AROUND_QTBUG_53071 = true };
+#endif
 
 static int		charcnt;
 static bool		errors;
@@ -299,10 +327,13 @@ struct lookup {
 static struct lookup const *	byword(const char * string,
 					const struct lookup * lp);
 
-static struct lookup const	line_codes[] = {
+static struct lookup const zi_line_codes[] = {
 	{ "Rule",	LC_RULE },
 	{ "Zone",	LC_ZONE },
 	{ "Link",	LC_LINK },
+	{ NULL,		0 }
+};
+static struct lookup const leap_line_codes[] = {
 	{ "Leap",	LC_LEAP },
 	{ NULL,		0}
 };
@@ -407,6 +438,16 @@ size_product(size_t nitems, size_t itemsize)
 	return nitems * itemsize;
 }
 
+static ATTRIBUTE_PURE size_t
+align_to(size_t size, size_t alignment)
+{
+  size_t aligned_size = size + alignment - 1;
+  aligned_size -= aligned_size % alignment;
+  if (aligned_size < size)
+    memory_exhausted(_("alignment overflow"));
+  return aligned_size;
+}
+
 #if !HAVE_STRDUP
 static char *
 strdup(char const *str)
@@ -416,7 +457,7 @@ strdup(char const *str)
 }
 #endif
 
-static ATTRIBUTE_PURE void *
+static void *
 memcheck(void *ptr)
 {
 	if (ptr == NULL)
@@ -424,7 +465,7 @@ memcheck(void *ptr)
 	return ptr;
 }
 
-static void *
+static void * ATTRIBUTE_MALLOC
 emalloc(size_t size)
 {
   return memcheck(malloc(size));
@@ -436,7 +477,7 @@ erealloc(void *ptr, size_t size)
   return memcheck(realloc(ptr, size));
 }
 
-static char *
+static char * ATTRIBUTE_MALLOC
 ecpyalloc (char const *str)
 {
   return memcheck(strdup(str));
@@ -534,7 +575,7 @@ usage(FILE *stream, int status)
   fprintf(stream,
 	  _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
 	    "\t[ -l localtime ] [ -p posixrules ] [ -d directory ] \\\n"
-	    "\t[ -L leapseconds ] [ filename ... ]\n\n"
+	    "\t[ -t localtime-link ] [ -L leapseconds ] [ filename ... ]\n\n"
 	    "Report bugs to %s.\n"),
 	  progname, progname, REPORT_BUGS_TO);
   if (status == EXIT_SUCCESS)
@@ -566,6 +607,7 @@ static const char *	psxrules;
 static const char *	lcltime;
 static const char *	directory;
 static const char *	leapsec;
+static const char *	tzdefault;
 static const char *	yitcommand;
 
 int
@@ -598,7 +640,7 @@ main(int argc, char **argv)
 		} else if (strcmp(argv[k], "--help") == 0) {
 			usage(stdout, EXIT_SUCCESS);
 		}
-	while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF && c != -1)
+	while ((c = getopt(argc, argv, "d:l:L:p:st:vy:")) != EOF && c != -1)
 		switch (c) {
 			default:
 				usage(stderr, EXIT_FAILURE);
@@ -632,10 +674,21 @@ _("%s: More than one -p option specified\n"),
 					return EXIT_FAILURE;
 				}
 				break;
+			case 't':
+				if (tzdefault != NULL) {
+				  fprintf(stderr,
+					  _("%s: More than one -t option"
+					    " specified\n"),
+					  progname);
+				  return EXIT_FAILURE;
+				}
+				tzdefault = optarg;
+				break;
 			case 'y':
-				if (yitcommand == NULL)
+				if (yitcommand == NULL) {
+					warning(_("-y is obsolescent"));
 					yitcommand = optarg;
-				else {
+				} else {
 					fprintf(stderr,
 _("%s: More than one -y option specified\n"),
 						progname);
@@ -663,6 +716,8 @@ _("%s: More than one -L option specified\n"),
 		usage(stderr, EXIT_FAILURE);	/* usage message by request */
 	if (directory == NULL)
 		directory = TZDIR;
+	if (tzdefault == NULL)
+		tzdefault = TZDEFAULT;
 	if (yitcommand == NULL)
 		yitcommand = "yearistype";
 
@@ -699,7 +754,7 @@ _("%s: More than one -L option specified\n"),
 	}
 	if (lcltime != NULL) {
 		eat(_("command line"), 1);
-		dolink(lcltime, TZDEFAULT, true);
+		dolink(lcltime, tzdefault, true);
 	}
 	if (psxrules != NULL) {
 		eat(_("command line"), 1);
@@ -864,9 +919,11 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink)
 	  char *linkalloc = absolute ? NULL : relname(fromfield, tofield);
 	  char const *contents = absolute ? fromfield : linkalloc;
 	  int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
-	  if (symlink_errno == ENOENT && !todirs_made) {
+	  if (!todirs_made
+	      && (symlink_errno == ENOENT || symlink_errno == ENOTSUP)) {
 	    mkdirs(tofield, true);
-	    symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
+	    if (symlink_errno == ENOENT)
+	      symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
 	  }
 	  free(linkalloc);
 	  if (symlink_errno == 0) {
@@ -909,44 +966,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
-   <http://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 (- (1LL << 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)
@@ -1053,8 +1072,7 @@ associate(void)
 			** Maybe we have a local standard time offset.
 			*/
 			eat(zp->z_filename, zp->z_linenum);
-			zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
-				true);
+			zp->z_stdoff = getstdoff(zp->z_rule, &zp->z_isdst);
 			/*
 			** Note, though, that if there's no rule,
 			** a '%s' in the format is a bad thing.
@@ -1114,6 +1132,8 @@ infile(const char *name)
 		} else if (wantcont) {
 			wantcont = inzcont(fields, nfields);
 		} else {
+			struct lookup const *line_codes
+			  = name == leapsec ? leap_line_codes : zi_line_codes;
 			lp = byword(fields[0], line_codes);
 			if (lp == NULL)
 				error(_("input line of unknown type"));
@@ -1130,11 +1150,7 @@ infile(const char *name)
 					wantcont = false;
 					break;
 				case LC_LEAP:
-					if (name != leapsec)
-					  warning(_("%s: Leap line in non leap"
-						    " seconds file %s"),
-						  progname, name);
-					else	inleap(fields, nfields);
+					inleap(fields, nfields);
 					wantcont = false;
 					break;
 				default:	/* "cannot happen" */
@@ -1160,26 +1176,38 @@ _("%s: panic: Invalid l_value %d\n"),
 */
 
 static zic_t
-gethms(char const *string, char const *errstring, bool signable)
+gethms(char const *string, char const *errstring)
 {
 	zic_t	hh;
-	int	mm, ss, sign;
-	char xs;
+	int sign, mm = 0, ss = 0;
+	char hhx, mmx, ssx, xr = '0', xs;
+	int tenths = 0;
+	bool ok = true;
 
 	if (string == NULL || *string == '\0')
 		return 0;
-	if (!signable)
-		sign = 1;
-	else if (*string == '-') {
+	if (*string == '-') {
 		sign = -1;
 		++string;
 	} else	sign = 1;
-	if (sscanf(string, "%"SCNdZIC"%c", &hh, &xs) == 1)
-		mm = ss = 0;
-	else if (sscanf(string, "%"SCNdZIC":%d%c", &hh, &mm, &xs) == 2)
-		ss = 0;
-	else if (sscanf(string, "%"SCNdZIC":%d:%d%c", &hh, &mm, &ss, &xs)
-		 != 3) {
+	switch (sscanf(string,
+		       "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+		       &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) {
+	  default: ok = false; break;
+	  case 8:
+	    ok = '0' <= xr && xr <= '9';
+	    /* fallthrough */
+	  case 7:
+	    ok &= ssx == '.';
+	    if (ok && noise)
+	      warning(_("fractional seconds rejected by"
+			" pre-2018 versions of zic"));
+	    /* fallthrough */
+	  case 5: ok &= mmx == ':'; /* fallthrough */
+	  case 3: ok &= hhx == ':'; /* fallthrough */
+	  case 1: break;
+	}
+	if (!ok) {
 			error("%s", errstring);
 			return 0;
 	}
@@ -1193,6 +1221,7 @@ gethms(char const *string, char const *errstring, bool signable)
 		error(_("time overflow"));
 		return 0;
 	}
+	ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even.  */
 	if (noise && (hh > HOURSPERDAY ||
 		(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
 warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
@@ -1200,6 +1229,24 @@ warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
 		    sign * (mm * SECSPERMIN + ss));
 }
 
+static zic_t
+getstdoff(char *field, bool *isdst)
+{
+  int dst = -1;
+  zic_t stdoff;
+  size_t fieldlen = strlen(field);
+  if (fieldlen != 0) {
+    char *ep = field + fieldlen - 1;
+    switch (*ep) {
+      case 'd': dst = 1; *ep = '\0'; break;
+      case 's': dst = 0; *ep = '\0'; break;
+    }
+  }
+  stdoff = gethms(field, _("invalid saved time"));
+  *isdst = dst < 0 ? stdoff != 0 : dst;
+  return stdoff;
+}
+
 static void
 inrule(char **fields, int nfields)
 {
@@ -1209,13 +1256,18 @@ inrule(char **fields, int nfields)
 		error(_("wrong number of fields on Rule line"));
 		return;
 	}
-	if (*fields[RF_NAME] == '\0') {
-		error(_("nameless rule"));
+	switch (*fields[RF_NAME]) {
+	  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;
-	r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), true);
+	r.r_stdoff = getstdoff(fields[RF_STDOFF], &r.r_isdst);
 	rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
 		fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
 	r.r_name = ecpyalloc(fields[RF_NAME]);
@@ -1235,10 +1287,10 @@ inzone(char **fields, int nfields)
 		error(_("wrong number of fields on Zone line"));
 		return false;
 	}
-	if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
+	if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
 		error(
 _("\"Zone %s\" line and -l option are mutually exclusive"),
-			TZDEFAULT);
+			tzdefault);
 		return false;
 	}
 	if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
@@ -1304,7 +1356,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], '%')) != 0) {
 		if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
 		    || strchr(fields[i_format], '/')) {
@@ -1426,7 +1478,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];
 	{
 		register bool	positive;
@@ -1435,15 +1487,9 @@ inleap(char **fields, int nfields)
 		if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
 			positive = false;
 			count = 1;
-		} else if (strcmp(cp, "--") == 0) {
-			positive = false;
-			count = 2;
 		} else if (strcmp(cp, "+") == 0) {
 			positive = true;
 			count = 1;
-		} else if (strcmp(cp, "++") == 0) {
-			positive = true;
-			count = 2;
 		} else {
 			error(_("illegal CORRECTION field on Leap line"));
 			return;
@@ -1455,8 +1501,8 @@ inleap(char **fields, int nfields)
 			return;
 		}
 		t = tadd(t, tod);
-		if (t < early_time) {
-			error(_("leap second precedes Big Bang"));
+		if (t < 0) {
+			error(_("leap second precedes Epoch"));
 			return;
 		}
 		leapadd(t, positive, lp->l_value, count);
@@ -1527,7 +1573,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);
 	/*
 	** Year work.
@@ -1584,13 +1630,16 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
 			error(_("typed single year"));
 			return;
 		}
+		warning(_("year type \"%s\" is obsolete; use \"-\" instead"),
+			typep);
 		rp->r_yrtype = ecpyalloc(typep);
 	}
 	/*
 	** Day work.
 	** Accept things such as:
 	**	1
-	**	last-Sunday
+	**	lastSunday
+	**	last-Sunday (undocumented; warn about this)
 	**	Sun<=20
 	**	Sun>=7
 	*/
@@ -1682,14 +1731,20 @@ atcomp(const void *avp, const void *bvp)
 	return (a < b) ? -1 : (a > b);
 }
 
-static bool
-is32(const zic_t x)
+static void
+swaptypes(int i, int j)
 {
-	return INT32_MIN <= x && x <= INT32_MAX;
+  { 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)
+writezone(const char *const name, const char *const string, char version,
+	  int defaulttype)
 {
 	register FILE *			fp;
 	register ptrdiff_t		i, j;
@@ -1702,7 +1757,11 @@ writezone(const char *const name, const char *const string, char version)
 	zic_t one = 1;
 	zic_t y2038_boundary = one << 31;
 	ptrdiff_t nats = timecnt + WORK_AROUND_QTBUG_53071;
-	zic_t *ats = emalloc(size_product(nats, sizeof *ats + 1));
+
+	/* Allocate the ATS and TYPES arrays via a single malloc,
+	   as this is a bit faster.  */
+	zic_t *ats = emalloc(align_to(size_product(nats, sizeof *ats + 1),
+				      _Alignof(zic_t)));
 	void *typesptr = ats + nats;
 	unsigned char *types = typesptr;
 
@@ -1719,13 +1778,11 @@ 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 +
+			if (toi != 0 && ((attypes[fromi].at +
 				gmtoffs[attypes[toi - 1].type]) <=
-				(attypes[toi - 1].at +
-				gmtoffs[attypes[toi - 2].type]))) {
+				(attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
+				: attypes[toi - 2].type]))) {
 					attypes[toi - 1].type =
 						attypes[fromi].type;
 					continue;
@@ -1755,18 +1812,6 @@ 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.
 	*/
@@ -1778,6 +1823,22 @@ writezone(const char *const name, const char *const string, char version)
 				break;
 			}
 	}
+
+	/* 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.  Do this after correcting for leap
+	   seconds, as the idea is to insert a transition just before
+	   32-bit time_t rolls around, and this occurs at a slightly
+	   different moment if transitions are leap-second corrected.  */
+	if (WORK_AROUND_QTBUG_53071 && timecnt != 0
+	    && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<')) {
+	  ats[timecnt] = y2038_boundary - 1;
+	  types[timecnt] = types[timecnt - 1];
+	  timecnt++;
+	}
+
 	/*
 	** Figure out 32-bit-limited starts and counts.
 	*/
@@ -1785,22 +1846,22 @@ writezone(const char *const name, const char *const string, char version)
 	timei32 = 0;
 	leapcnt32 = leapcnt;
 	leapi32 = 0;
-	while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
+	while (0 < timecnt32 && INT32_MAX < ats[timecnt32 - 1])
 		--timecnt32;
-	while (timecnt32 > 0 && !is32(ats[timei32])) {
+	while (1 < timecnt32 && ats[timei32] < INT32_MIN
+	       && ats[timei32 + 1] <= INT32_MIN) {
+		/* Discard too-low transitions, except keep any last too-low
+		   transition if no transition is exactly at INT32_MIN.
+		   The kept transition will be output as an INT32_MIN
+		   "transition" appropriate for buggy 32-bit clients that do
+		   not use time type 0 for timestamps before the first
+		   transition; see below.  */
 		--timecnt32;
 		++timei32;
 	}
-	/*
-	** Output an INT32_MIN "transition" if appropriate; see below.
-	*/
-	if (timei32 > 0 && ats[timei32] > INT32_MIN) {
-		--timei32;
-		++timecnt32;
-	}
-	while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
+	while (0 < leapcnt32 && INT32_MAX < trans[leapcnt32 - 1])
 		--leapcnt32;
-	while (leapcnt32 > 0 && !is32(trans[leapi32])) {
+	while (0 < leapcnt32 && trans[leapi32] < INT32_MIN) {
 		--leapcnt32;
 		++leapi32;
 	}
@@ -1833,7 +1894,8 @@ writezone(const char *const name, const char *const string, char version)
 	for (pass = 1; pass <= 2; ++pass) {
 		register ptrdiff_t thistimei, thistimecnt, thistimelim;
 		register int	thisleapi, thisleapcnt, thisleaplim;
-		int		writetype[TZ_MAX_TYPES];
+		int old0;
+		char		omittype[TZ_MAX_TYPES];
 		int		typemap[TZ_MAX_TYPES];
 		register int	thistypecnt;
 		char		thischars[TZ_MAX_CHARS];
@@ -1858,25 +1920,18 @@ 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;
-			/*
-			** For America/Godthab and Antarctica/Palmer
-			*/
-			if (thistimei == 0)
-				writetype[0] = 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);
+
 #ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
 		/*
 		** For some pre-2011 systems: if the last-to-be-written
@@ -1894,8 +1949,8 @@ writezone(const char *const name, const char *const string, char version)
 				if (isdsts[types[i]])
 					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;
 					else	histd = i;
@@ -1909,7 +1964,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]) {
@@ -1920,20 +1975,24 @@ 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++) {
 			register char *	thisabbr;
 
-			if (!writetype[i])
+			if (omittype[i])
 				continue;
 			if (indmap[abbrinds[i]] >= 0)
 				continue;
@@ -1982,8 +2041,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);
 				putc((unsigned char) indmap[abbrinds[i]], fp);
@@ -2016,12 +2075,13 @@ writezone(const char *const name, const char *const string, char version)
 			else	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);
@@ -2044,7 +2104,7 @@ abbroffset(char *buf, zic_t offset)
   minutes = offset % MINSPERHOUR;
   offset /= MINSPERHOUR;
   if (100 <= offset) {
-    error(_("%%z UTC offset magnitude exceeds 99:59:59"));
+    error(_("%%z UT offset magnitude exceeds 99:59:59"));
     return "%z";
   } else {
     char *p = buf;
@@ -2066,7 +2126,7 @@ abbroffset(char *buf, zic_t offset)
 
 static size_t
 doabbr(char *abbr, struct zone const *zp, char const *letters,
-       zic_t stdoff, bool doquotes)
+       bool isdst, zic_t stdoff, bool doquotes)
 {
 	register char *	cp;
 	register char *	slashp;
@@ -2081,7 +2141,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
 	  else if (!letters)
 	    letters = "%s";
 	  sprintf(abbr, format, letters);
-	} else if (stdoff != 0) {
+	} else if (isdst) {
 		strcpy(abbr, slashp + 1);
 	} else {
 		memcpy(abbr, format, slashp - format);
@@ -2192,7 +2252,7 @@ stringrule(char *result, const struct rule *const rp, const zic_t dstoff,
 	}
 	if (rp->r_todisgmt)
 		tod += gmtoff;
-	if (rp->r_todisstd && rp->r_stdoff == 0)
+	if (rp->r_todisstd && !rp->r_isdst)
 		tod += dstoff;
 	if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
 		*result++ = '/';
@@ -2249,7 +2309,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 			continue;
 		if (rp->r_yrtype != NULL)
 			continue;
-		if (rp->r_stdoff == 0) {
+		if (!rp->r_isdst) {
 			if (stdrp == NULL)
 				stdrp = rp;
 			else	return -1;
@@ -2268,7 +2328,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 		register struct rule *stdabbrrp = NULL;
 		for (i = 0; i < zp->z_nrules; ++i) {
 			rp = &zp->z_rules[i];
-			if (rp->r_stdoff == 0 && rule_cmp(stdabbrrp, rp) < 0)
+			if (!rp->r_isdst && rule_cmp(stdabbrrp, rp) < 0)
 				stdabbrrp = rp;
 			if (rule_cmp(stdrp, rp) < 0)
 				stdrp = rp;
@@ -2281,13 +2341,14 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 		if (stdrp != NULL && stdrp->r_hiyear == 2037)
 			return YEAR_BY_YEAR_ZONE;
 
-		if (stdrp != NULL && stdrp->r_stdoff != 0) {
+		if (stdrp != NULL && stdrp->r_isdst) {
 			/* Perpetual DST.  */
 			dstr.r_month = TM_JANUARY;
 			dstr.r_dycode = DC_DOM;
 			dstr.r_dayofmonth = 1;
 			dstr.r_tod = 0;
 			dstr.r_todisstd = dstr.r_todisgmt = false;
+			dstr.r_isdst = stdrp->r_isdst;
 			dstr.r_stdoff = stdrp->r_stdoff;
 			dstr.r_abbrvar = stdrp->r_abbrvar;
 			stdr.r_month = TM_DECEMBER;
@@ -2295,6 +2356,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 			stdr.r_dayofmonth = 31;
 			stdr.r_tod = SECSPERDAY + stdrp->r_stdoff;
 			stdr.r_todisstd = stdr.r_todisgmt = false;
+			stdr.r_isdst = false;
 			stdr.r_stdoff = 0;
 			stdr.r_abbrvar
 			  = (stdabbrrp ? stdabbrrp->r_abbrvar : "");
@@ -2302,10 +2364,10 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 			stdrp = &stdr;
 		}
 	}
-	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
+	if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_isdst))
 		return -1;
 	abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
-	len = doabbr(result, zp, abbrvar, 0, true);
+	len = doabbr(result, zp, abbrvar, false, 0, true);
 	offsetlen = stringoffset(result + len, -zp->z_gmtoff);
 	if (! offsetlen) {
 		result[0] = '\0';
@@ -2314,7 +2376,8 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 	len += offsetlen;
 	if (dstrp == NULL)
 		return compat;
-	len += doabbr(result + len, zp, dstrp->r_abbrvar, dstrp->r_stdoff, true);
+	len += doabbr(result + len, zp, dstrp->r_abbrvar,
+		      dstrp->r_isdst, dstrp->r_stdoff, true);
 	if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR) {
 	  offsetlen = stringoffset(result + len,
 				   -(zp->z_gmtoff + dstrp->r_stdoff));
@@ -2372,6 +2435,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;
@@ -2480,9 +2544,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);
@@ -2490,14 +2554,15 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 		startoff = zp->z_gmtoff;
 		if (zp->z_nrules == 0) {
 			stdoff = zp->z_stdoff;
-			doabbr(startbuf, zp, NULL, stdoff, false);
+			doabbr(startbuf, zp, NULL, zp->z_isdst, stdoff, false);
 			type = addtype(oadd(zp->z_gmtoff, stdoff),
-				startbuf, stdoff != 0, startttisstd,
+				startbuf, zp->z_isdst, startttisstd,
 				startttisgmt);
 			if (usestart) {
 				addtt(starttime, type);
 				usestart = false;
-			} else	addtt(early_time, type);
+			} else
+				defaulttype = type;
 		} else for (year = min_year; year <= max_year; ++year) {
 			if (useuntil && year > zp->z_untilrule.r_hiyear)
 				break;
@@ -2588,6 +2653,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 							stdoff);
 						doabbr(startbuf, zp,
 							rp->r_abbrvar,
+							rp->r_isdst,
 							rp->r_stdoff,
 							false);
 						continue;
@@ -2598,6 +2664,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 							doabbr(startbuf,
 								zp,
 								rp->r_abbrvar,
+								rp->r_isdst,
 								rp->r_stdoff,
 								false);
 					}
@@ -2605,10 +2672,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 				eats(zp->z_filename, zp->z_linenum,
 					rp->r_filename, rp->r_linenum);
 				doabbr(ab, zp, rp->r_abbrvar,
-				       rp->r_stdoff, false);
+				       rp->r_isdst, rp->r_stdoff, false);
 				offset = oadd(zp->z_gmtoff, rp->r_stdoff);
-				type = addtype(offset, ab, rp->r_stdoff != 0,
+				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))
@@ -2625,11 +2694,14 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
 			eat(zp->z_filename, zp->z_linenum);
 			if (*startbuf == '\0')
 error(_("can't determine time zone abbreviation to use just after until time"));
-			else	addtt(starttime,
-					addtype(startoff, startbuf,
-						startoff != zp->z_gmtoff,
-						startttisstd,
-						startttisgmt));
+			else {
+			  bool isdst = startoff != zp->z_gmtoff;
+			  type = addtype(startoff, startbuf, isdst,
+					 startttisstd, startttisgmt);
+			  if (defaulttype < 0 && !isdst)
+			    defaulttype = type;
+			  addtt(starttime, type);
+			}
 		}
 		/*
 		** Now we may get to set starttime for the next zone line.
@@ -2644,6 +2716,8 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 				starttime = tadd(starttime, -gmtoff);
 		}
 	}
+	if (defaulttype < 0)
+	  defaulttype = 0;
 	if (0 <= lastatmax)
 	  attypes[lastatmax].dontmerge = true;
 	if (do_extend) {
@@ -2671,7 +2745,7 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 			attypes[timecnt - 1].dontmerge = true;
 		}
 	}
-	writezone(zpfirst->z_name, envvar, version);
+	writezone(zpfirst->z_name, envvar, version, defaulttype);
 	free(startbuf);
 	free(ab);
 	free(envvar);
@@ -2680,20 +2754,6 @@ error(_("can't determine time zone abbreviation to use just after until time"));
 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;
@@ -2754,13 +2814,8 @@ leapadd(zic_t t, bool positive, int rolling, int count)
 		exit(EXIT_FAILURE);
 	}
 	for (i = 0; i < leapcnt; ++i)
-		if (t <= trans[i]) {
-			if (t == trans[i]) {
-				error(_("repeated leap second moment"));
-				exit(EXIT_FAILURE);
-			}
+		if (t <= trans[i])
 			break;
-		}
 	do {
 		for (j = leapcnt; j > i; --j) {
 			trans[j] = trans[j - 1];
@@ -2779,11 +2834,17 @@ adjleap(void)
 {
 	register int	i;
 	register zic_t	last = 0;
+	register zic_t	prevtrans = 0;
 
 	/*
 	** propagate leap seconds forward
 	*/
 	for (i = 0; i < leapcnt; ++i) {
+		if (trans[i] - prevtrans < 28 * SECSPERDAY) {
+		  error(_("Leap seconds too close together"));
+		  exit(EXIT_FAILURE);
+		}
+		prevtrans = trans[i];
 		trans[i] = tadd(trans[i], last);
 		last = corr[i] += last;
 	}
@@ -2884,7 +2945,7 @@ lowerit(char a)
 }
 
 /* case-insensitive equality */
-static ATTRIBUTE_PURE bool
+static bool
 ciequal(register const char *ap, register const char *bp)
 {
 	while (lowerit(*ap) == lowerit(*bp++))
@@ -2893,7 +2954,7 @@ ciequal(register const char *ap, register const char *bp)
 	return false;
 }
 
-static ATTRIBUTE_PURE bool
+static bool
 itsabbr(register const char *abbr, register const char *word)
 {
 	if (lowerit(*abbr) != lowerit(*word))
@@ -2907,7 +2968,20 @@ itsabbr(register const char *abbr, register const char *word)
 	return true;
 }
 
-static ATTRIBUTE_PURE const struct lookup *
+/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case.  */
+
+static bool
+ciprefix(char const *abbr, char const *word)
+{
+  do
+    if (!*abbr)
+      return true;
+  while (lowerit(*abbr++) == lowerit(*word++));
+
+  return false;
+}
+
+static const struct lookup *
 byword(const char *word, const struct lookup *table)
 {
 	register const struct lookup *	foundlp;
@@ -2915,6 +2989,20 @@ byword(const char *word, const struct lookup *table)
 
 	if (word == NULL || table == NULL)
 		return NULL;
+
+	/* If TABLE is LASTS and the word starts with "last" followed
+	   by a non-'-', skip the "last" and look in WDAY_NAMES instead.
+	   Warn about any usage of the undocumented prefix "last-".  */
+	if (table == lasts && ciprefix("last", word) && word[4]) {
+	  if (word[4] == '-')
+	    warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
+		    word, word + 5);
+	  else {
+	    word += 4;
+	    table = wday_names;
+	  }
+	}
+
 	/*
 	** Look for exact match.
 	*/
@@ -2926,11 +3014,25 @@ byword(const char *word, const struct lookup *table)
 	*/
 	foundlp = NULL;
 	for (lp = table; lp->l_word != NULL; ++lp)
-		if (itsabbr(word, lp->l_word)) {
+		if (ciprefix(word, lp->l_word)) {
 			if (foundlp == NULL)
 				foundlp = lp;
 			else	return NULL;	/* multiple inexact matches */
 		}
+
+	/* Warn about any backward-compatibility issue with pre-2017c zic.  */
+	if (foundlp) {
+	  bool pre_2017c_match = false;
+	  for (lp = table; lp->l_word; lp++)
+	    if (itsabbr(word, lp->l_word)) {
+	      if (pre_2017c_match) {
+		warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
+		break;
+	      }
+	      pre_2017c_match = true;
+	    }
+	}
+
 	return foundlp;
 }
 
@@ -3023,6 +3125,15 @@ 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) {
 			i = len_years[isleap(y)];
@@ -3134,11 +3245,14 @@ mkdirs(char const *argname, bool ancestors)
 
 	cp = name = ecpyalloc(argname);
 
+	/* On MS-Windows systems, do not worry about drive letters or
+	   backslashes, as this should suffice in practice.  Time zone
+	   names do not use drive letters and backslashes.  If the -d
+	   option of zic does not name an already-existing directory,
+	   it can use slashes to separate the already-existing
+	   ancestor prefix from the to-be-created subdirectories.  */
+
 	/* Do not mkdir a root directory, as it must exist.  */
-#ifdef HAVE_DOS_FILE_NAMES
-	if (is_alpha(name[0]) && name[1] == ':')
-	  cp += 2;
-#endif
 	while (*cp == '/')
 	  cp++;