Update to tzcode1996o + our fixes. Fixes problem pointed out
authormillert <millert@openbsd.org>
Tue, 14 Jan 1997 03:16:42 +0000 (03:16 +0000)
committermillert <millert@openbsd.org>
Tue, 14 Jan 1997 03:16:42 +0000 (03:16 +0000)
by cross@va.pubnix.com.

15 files changed:
lib/libc/time/asctime.c
lib/libc/time/ctime.3
lib/libc/time/difftime.c
lib/libc/time/ialloc.c
lib/libc/time/localtime.c
lib/libc/time/private.h
lib/libc/time/scheck.c
lib/libc/time/time2posix.3
lib/libc/time/tzfile.5
lib/libc/time/tzfile.h
lib/libc/time/tzset.3
lib/libc/time/zdump.8
lib/libc/time/zdump.c
lib/libc/time/zic.8
lib/libc/time/zic.c

index 0db9e34..e31c614 100644 (file)
@@ -1,5 +1,14 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: asctime.c,v 1.2 1996/08/19 08:34:47 tholo Exp $";
+#if 0
+static char    elsieid[] = "@(#)asctime.c      7.8";
+#else
+static char rcsid[] = "$OpenBSD: asctime.c,v 1.3 1997/01/14 03:16:42 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 /*LINTLIBRARY*/
index 88eb9e8..81c6c59 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: ctime.3,v 1.5 1996/08/19 08:34:48 tholo Exp $
+.\"    $OpenBSD: ctime.3,v 1.6 1997/01/14 03:16:43 millert Exp $
 .TH CTIME 3
 .SH NAME
 asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time to ASCII
@@ -31,13 +31,14 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time to
 .B time_t mktime(tm)
 .B struct tm *tm;
 .PP
+.B cc ... -lz
 .fi
 .SH DESCRIPTION
 .I Ctime\^
 converts a long integer, pointed to by
 .IR clock ,
 representing the time in seconds since
-00:00:00 UTC, January 1, 1970,
+00:00:00 UTC, 1970-01-01,
 and returns a pointer to a
 26-character string
 of the form
@@ -211,4 +212,4 @@ will also be overwritten at the next call
 Avoid using out-of-range values with
 .I mktime
 when setting up lunch with promptness sticklers in Riyadh.
-.\" @(#)newctime.3     7.12
+.\" @(#)newctime.3     7.13
index f61af9a..6cfddc6 100644 (file)
@@ -1,5 +1,14 @@
+/*
+** This file is in the public domain, so clarified as of
+** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: difftime.c,v 1.3 1996/08/19 08:34:48 tholo Exp $";
+#if 0
+static char    elsieid[] = "@(#)difftime.c     7.7";
+#else
+static char rcsid[] = "$OpenBSD: difftime.c,v 1.4 1997/01/14 03:16:44 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 /*LINTLIBRARY*/
index e912ead..5590dd1 100644 (file)
@@ -1,5 +1,9 @@
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: ialloc.c,v 1.2 1996/08/19 08:34:49 tholo Exp $";
+#if 0
+static char    elsieid[] = "@(#)ialloc.c       8.28";
+#else
+static char rcsid[] = "$OpenBSD: ialloc.c,v 1.3 1997/01/14 03:16:45 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 /*LINTLIBRARY*/
index c25b435..a7cdae4 100644 (file)
@@ -1,5 +1,14 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: localtime.c,v 1.7 1996/10/30 00:20:14 tholo Exp $";
+#if 0
+static char    elsieid[] = "@(#)localtime.c    7.59";
+#else
+static char rcsid[] = "$OpenBSD: localtime.c,v 1.8 1997/01/14 03:16:47 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 /*
@@ -285,7 +294,7 @@ register struct state * const       sp;
                if (!doaccess && issetugid() == 0) {
                        if ((p = TZDIR) == NULL)
                                return -1;
-                       if (strlen(p) + 1 + strlen(name) + 1 >= sizeof fullname)
+                       if ((strlen(p) + strlen(name) + 2) >= sizeof fullname)
                                return -1;
                        (void) strcpy(fullname, p);
                        (void) strcat(fullname, "/");
@@ -878,6 +887,7 @@ const int                   lastditch;
                        sp->ttis[1].tt_gmtoff = -dstoffset;
                        sp->ttis[1].tt_isdst = TRUE;
                        sp->ttis[1].tt_abbrind = stdlen + 1;
+                       sp->typecnt = 2;
                }
        } else {
                dstlen = 0;
@@ -1223,7 +1233,8 @@ const time_t * const      timep;
 /*
 ** Adapted from code provided by Robert Elz, who writes:
 **     The "best" way to do mktime I think is based on an idea of Bob
-**     Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
+**     Kridle's (so its said...) from a long time ago.
+**     [kridle@xinet.com as of 1996-01-16.]
 **     It does a binary search of the time_t space.  Since time_t's are
 **     just 32 bits, its a max of 32 iterations (even at 64 bits it
 **     would still be very reasonable).
@@ -1313,10 +1324,12 @@ int * const             okayp;
        while (yourtm.tm_mday <= 0) {
                if (increment_overflow(&yourtm.tm_year, -1))
                        return WRONG;
-               yourtm.tm_mday += year_lengths[isleap(yourtm.tm_year)];
+               i = yourtm.tm_year + (1 < yourtm.tm_mon);
+               yourtm.tm_mday += year_lengths[isleap(i)];
        }
        while (yourtm.tm_mday > DAYSPERLYEAR) {
-               yourtm.tm_mday -= year_lengths[isleap(yourtm.tm_year)];
+               i = yourtm.tm_year + (1 < yourtm.tm_mon);
+               yourtm.tm_mday -= year_lengths[isleap(i)];
                if (increment_overflow(&yourtm.tm_year, 1))
                        return WRONG;
        }
index a90fc20..98d09df 100644 (file)
@@ -1,14 +1,21 @@
-/*     $OpenBSD: private.h,v 1.5 1996/10/30 00:20:12 tholo Exp $       */
+/*     $OpenBSD: private.h,v 1.6 1997/01/14 03:16:48 millert Exp $     */
 
 #ifndef PRIVATE_H
+
 #define PRIVATE_H
 
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
 /* OpenBSD defaults */
-#define TM_GMTOFF      tm_gmtoff
-#define TM_ZONE                tm_zone
-#define PCTS           1
-#define STD_INSPIRED   1
-#define HAVE_LONG_DOUBLE 1
+#define TM_GMTOFF              tm_gmtoff
+#define TM_ZONE                        tm_zone
+#define PCTS                   1
+#define STD_INSPIRED           1
+#define HAVE_LONG_DOUBLE       1
+#define HAVE_STRERROR          1
 
 /*
 ** This header is for use ONLY with the time conversion code.
 ** Thank you!
 */
 
+/*
+** ID
+*/
+
+#if 0
+#ifndef lint
+#ifndef NOID
+static char    privatehid[] = "@(#)private.h   7.44";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+#endif
+
 /*
 ** Defaults for preprocessor symbols.
 ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
 #define HAVE_ADJTIME           1
 #endif /* !defined HAVE_ADJTIME */
 
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT           0
+#endif /* !defined HAVE_GETTEXT */
+
 #ifndef HAVE_SETTIMEOFDAY
 #define HAVE_SETTIMEOFDAY      3
 #endif /* !defined HAVE_SETTIMEOFDAY */
 
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR          0
+#endif /* !defined HAVE_STRERROR */
+
 #ifndef HAVE_UNISTD_H
 #define HAVE_UNISTD_H          1
 #endif /* !defined HAVE_UNISTD_H */
 #include "time.h"
 #include "stdlib.h"
 
+#if HAVE_GETTEXT - 0
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
 #if HAVE_UNISTD_H - 0
 #include "unistd.h"    /* for F_OK and R_OK */
 #endif /* HAVE_UNISTD_H - 0 */
@@ -144,6 +175,17 @@ extern int unlink P((const char * filename));
 #define remove unlink
 #endif /* !defined remove */
 
+#if 0
+/*
+** Some ancient errno.h implementations don't declare errno.
+** But some newer errno.h implementations define it as a macro.
+** Fix the former without affecting the latter.
+*/
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+#endif
+
 /*
 ** Finally, some convenience items.
 */
@@ -199,6 +241,24 @@ extern int unlink P((const char * filename));
 #endif /* !defined GNUC_or_lint */
 #endif /* !defined INITIALIZE */
 
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
 /*
 ** UNIX was a registered trademark of UNIX System Laboratories in 1993.
 */
index 5fd5b09..e7455da 100644 (file)
@@ -1,5 +1,9 @@
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: scheck.c,v 1.3 1996/08/19 08:34:51 tholo Exp $";
+#if 0
+static char    elsieid[] = "@(#)scheck.c       8.13";
+#else
+static char rcsid[] = "$OpenBSD: scheck.c,v 1.4 1997/01/14 03:16:49 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 /*LINTLIBRARY*/
index 9ad2b87..2f2f9aa 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: time2posix.3,v 1.3 1996/08/19 08:34:52 tholo Exp $
+.\"    $OpenBSD: time2posix.3,v 1.4 1997/01/14 03:16:50 millert Exp $
 .TH TIME2POSIX 3
 .SH NAME
 time2posix, posix2time \- convert seconds since the Epoch
@@ -13,6 +13,7 @@ time2posix, posix2time \- convert seconds since the Epoch
 .B time_t posix2time(t)
 .B time_t t
 .PP
+.B cc ... -lz
 .fi
 .SH DESCRIPTION
 IEEE Standard 1003.1
@@ -116,4 +117,6 @@ difftime(3),
 localtime(3),
 mktime(3),
 time(3)
-.\" @(#)time2posix.3   7.3
+.\" @(#)time2posix.3   7.5
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
index 58705b2..bafaf75 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: tzfile.5,v 1.3 1996/08/19 08:34:52 tholo Exp $
+.\"    $OpenBSD: tzfile.5,v 1.4 1997/01/14 03:16:51 millert Exp $
 .TH TZFILE 5
 .SH NAME
 tzfile \- time zone information
@@ -44,7 +44,7 @@ four-byte values of type
 sorted in ascending order.
 These values are written in ``standard'' byte order.
 Each is used as a transition time (as returned by
-.IR time (2))
+.IR time (3))
 at which the rules for computing local time change.
 Next come
 .I tzh_timecnt
@@ -132,4 +132,6 @@ is zero or the time argument is less than the first transition time recorded
 in the file.
 .SH SEE ALSO
 ctime(3)
-.\" @(#)tzfile.5       7.3
+.\" @(#)tzfile.5       7.5
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
index ce1c7cf..82e6852 100644 (file)
@@ -1,8 +1,14 @@
-/*     $OpenBSD: tzfile.h,v 1.3 1996/08/19 08:34:53 tholo Exp $        */
+/*     $OpenBSD: tzfile.h,v 1.4 1997/01/14 03:16:52 millert Exp $      */
 
 #ifndef TZFILE_H
+
 #define TZFILE_H
 
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
 /*
 ** This header is for use ONLY with the time conversion code.
 ** There is no guarantee that it will remain unchanged,
 ** Thank you!
 */
 
+/*
+** ID
+*/
+
+#if 0
+#ifndef lint
+#ifndef NOID
+static char    tzfilehid[] = "@(#)tzfile.h     7.9";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+#endif
+
 /*
 ** Information about time zone files.
 */
 
-#ifndef TZDIR          /* Time zone object file directory */
-#define TZDIR          "/usr/share/zoneinfo"
+#ifndef TZDIR
+#define TZDIR  "/usr/share/zoneinfo" /* Time zone object file directory */
 #endif /* !defined TZDIR */
 
 #ifndef TZDEFAULT
index aa27f05..9ff0575 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: tzset.3,v 1.4 1996/08/19 08:34:53 tholo Exp $
+.\"    $OpenBSD: tzset.3,v 1.5 1997/01/14 03:16:54 millert Exp $
 .TH TZSET 3
 .SH NAME
 tzset \- initialize time conversion information
@@ -6,6 +6,7 @@ tzset \- initialize time conversion information
 .nf
 .B void tzset()
 .PP
+.B cc ... -lz
 .fi
 .SH DESCRIPTION
 .I Tzset
index 59e0304..7126d4e 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: zdump.8,v 1.2 1996/08/19 08:34:54 tholo Exp $
+.\"    $OpenBSD: zdump.8,v 1.3 1997/01/14 03:16:54 millert Exp $
 .TH ZDUMP 8
 .SH NAME
 zdump \- time zone dumper
@@ -21,8 +21,7 @@ These options are available:
 For each
 .I zonename
 on the command line,
-print the current time,
-the time at the lowest possible time value,
+print the time at the lowest possible time value,
 the time one day after the lowest possible time value,
 the times both one second before and exactly at
 each detected time discontinuity,
@@ -38,4 +37,4 @@ otherwise.
 Cut off the verbose output near the start of the given year.
 .SH "SEE ALSO"
 ctime(3), tzfile(5), zic(8)
-.\" @(#)zdump.8        7.2
+.\" @(#)zdump.8        7.3
index e0f76d1..2df4b3e 100644 (file)
@@ -1,5 +1,9 @@
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: zdump.c,v 1.3 1996/08/27 03:36:57 deraadt Exp $";
+#if 0
+static char    elsieid[] = "@(#)zdump.c        7.24";
+#else
+static char rcsid[] = "$OpenBSD: zdump.c,v 1.4 1997/01/14 03:16:56 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 /*
@@ -66,6 +70,11 @@ static char rcsid[] = "$OpenBSD: zdump.c,v 1.3 1996/08/27 03:36:57 deraadt Exp $
 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
 #endif /* !defined isleap */
 
+#if HAVE_GETTEXT - 0
+#include "locale.h"    /* for setlocale */
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
 #ifndef GNUC_or_lint
 #ifdef lint
 #define GNUC_or_lint
@@ -86,6 +95,24 @@ static char rcsid[] = "$OpenBSD: zdump.c,v 1.3 1996/08/27 03:36:57 deraadt Exp $
 #endif /* !defined GNUC_or_lint */
 #endif /* !defined INITIALIZE */
 
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
 extern char ** environ;
 extern int     getopt();
 extern char *  optarg;
@@ -120,6 +147,13 @@ char *     argv[];
        struct tm               newtm;
 
        INITIALIZE(cuttime);
+#if HAVE_GETTEXT - 0
+       (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+       (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined(TEXTDOMAINDIR) */
+       (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
        progname = argv[0];
        vflag = 0;
        cutoff = NULL;
@@ -130,7 +164,7 @@ char *      argv[];
        if (c != EOF ||
                (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
                        (void) fprintf(stderr,
-"%s: usage is %s [ -v ] [ -c cutoff ] zonename ...\n",
+_("%s: usage is %s [ -v ] [ -c cutoff ] zonename ...\n"),
                                argv[0], argv[0]);
                        (void) exit(EXIT_FAILURE);
        }
@@ -189,8 +223,8 @@ char *      argv[];
                t += SECSPERHOUR * HOURSPERDAY;
                show(argv[i], t, TRUE);
                tm = *localtime(&t);
-               (void) strncpy(buf, abbr(&tm), sizeof(buf) - 1);
-               buf[sizeof(buf) - 1] = '\0';
+               (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
+               buf[(sizeof buf) - 1] = '\0';
                for ( ; ; ) {
                        if (cutoff != NULL && t >= cuttime)
                                break;
@@ -206,8 +240,8 @@ char *      argv[];
                                        newt = hunt(argv[i], t, newt);
                                        newtm = *localtime(&newt);
                                        (void) strncpy(buf, abbr(&newtm),
-                                               sizeof(buf) - 1);
-                                       buf[sizeof(buf) - 1] = '\0';
+                                               (sizeof buf) - 1);
+                                       buf[(sizeof buf) - 1] = '\0';
                        }
                        t = newt;
                        tm = newtm;
@@ -224,9 +258,9 @@ char *      argv[];
                show(argv[i], t, TRUE);
        }
        if (fflush(stdout) || ferror(stdout)) {
-               (void) fprintf(stderr, "%s: Error writing standard output ",
+               (void) fprintf(stderr, _("%s: Error writing standard output "),
                        argv[0]);
-               (void) perror("standard output");
+               (void) perror(_("standard output"));
                (void) exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
@@ -248,8 +282,8 @@ time_t      hit;
        static char     loab[MAX_STRING_LENGTH];
 
        lotm = *localtime(&lot);
-       (void) strncpy(loab, abbr(&lotm), sizeof(loab) - 1);
-       loab[sizeof(loab) - 1] = '\0';
+       (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
+       loab[(sizeof loab) - 1] = '\0';
        while ((hit - lot) >= 2) {
                t = lot / 2 + hit / 2;
                if (t <= lot)
index 256dfbe..004ff09 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: zic.8,v 1.3 1996/08/19 08:34:55 tholo Exp $
+.\"    $OpenBSD: zic.8,v 1.4 1997/01/14 03:16:57 millert Exp $
 .TH ZIC 8
 .SH NAME
 zic \- time zone compiler
@@ -74,7 +74,7 @@ no leap second information appears in output files.
 .B \-v
 Complain if a year that appears in a data file is outside the range
 of years representable by
-.IR time (2)
+.IR time (3)
 values.
 .TP
 .B \-s
@@ -305,6 +305,9 @@ It is specified as a year, a month, a day, and a time of day.
 If this is specified,
 the time zone information is generated from the given GMT offset
 and rule change until the time specified.
+The month, day, and time of day have the same format as the IN, ON, and AT
+columns of a rule; trailing columns can be omitted, and default to the
+earliest possible value for the missing columns.
 .IP
 The next line must be a
 .q continuation
@@ -406,4 +409,4 @@ the earliest transition time recorded in the compiled file is correct.
 /usr/share/zoneinfo    standard directory used for created files
 .SH "SEE ALSO"
 ctime(3), tzfile(5), zdump(8)
-.\" @(#)zic.8  7.12
+.\" @(#)zic.8  7.14
index 6205e8f..98e063e 100644 (file)
@@ -1,8 +1,13 @@
 #if defined(LIBC_SCCS) && !defined(lint)
-static char rcsid[] = "$OpenBSD: zic.c,v 1.4 1996/08/19 08:34:56 tholo Exp $";
+#if 0
+static char    elsieid[] = "@(#)zic.c  7.80";
+#else
+static char rcsid[] = "$OpenBSD: zic.c,v 1.5 1997/01/14 03:16:58 millert Exp $";
+#endif
 #endif /* LIBC_SCCS and not lint */
 
 #include "private.h"
+#include "locale.h"
 #include "tzfile.h"
 #ifdef unix
 #include "sys/stat.h"                  /* for umask manifest constants */
@@ -133,6 +138,10 @@ static void        usage P((void));
 static void    writezone P((const char * name));
 static int     yearistype P((int year, const char * type));
 
+#if !(HAVE_STRERROR - 0)
+static char *  strerror P((int));
+#endif /* !(HAVE_STRERROR - 0) */
+
 static int             charcnt;
 static int             errors;
 static const char *    filename;
@@ -328,8 +337,10 @@ static const int   len_years[2] = {
        DAYSPERNYEAR, DAYSPERLYEAR
 };
 
-static time_t          ats[TZ_MAX_TIMES];
-static unsigned char   types[TZ_MAX_TIMES];
+static struct attype {
+       time_t          at;
+       unsigned char   type;
+}                      attypes[TZ_MAX_TIMES];
 static long            gmtoffs[TZ_MAX_TYPES];
 static char            isdsts[TZ_MAX_TYPES];
 static unsigned char   abbrinds[TZ_MAX_TYPES];
@@ -349,7 +360,10 @@ memcheck(ptr)
 char * const   ptr;
 {
        if (ptr == NULL) {
-               (void) perror(progname);
+               const char *e = strerror(errno);
+
+               (void) fprintf(stderr, _("%s: Memory exhausted: %s\n"),
+                       progname, e);
                (void) exit(EXIT_FAILURE);
        }
        return ptr;
@@ -364,6 +378,19 @@ char * const       ptr;
 ** Error handling.
 */
 
+#if !(HAVE_STRERROR - 0)
+static char *
+strerror(errnum)
+int    errnum;
+{
+       extern char *   sys_errlist[];
+       extern int      sys_nerr;
+
+       return (errnum > 0 && errnum <= sys_nerr) ?
+               sys_errlist[errnum] : "Unknown system error";
+}
+#endif /* !(HAVE_STRERROR - 0) */
+
 static void
 eats(name, num, rname, rnum)
 const char * const     name;
@@ -394,21 +421,32 @@ const char * const        string;
        **      zic ... 2>&1 | error -t "*" -v
        ** on BSD systems.
        */
-       (void) fprintf(stderr, "\"%s\", line %d: %s",
+       (void) fprintf(stderr, _("\"%s\", line %d: %s"),
                filename, linenum, string);
        if (rfilename != NULL)
-               (void) fprintf(stderr, " (rule from \"%s\", line %d)",
+               (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
                        rfilename, rlinenum);
        (void) fprintf(stderr, "\n");
        ++errors;
 }
 
+static void
+warning(string)
+const char * const     string;
+{
+       char *  cp;
+
+       cp = ecpyalloc("warning: ");
+       cp = ecatalloc(cp, string);
+       error(string);
+       ifree(cp);
+       --errors;
+}
+
 static void
 usage P((void))
 {
-       (void) fprintf(stderr, "%s: usage is %s \
-[ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n\
-\t[ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n",
+       (void) fprintf(stderr, _("%s: usage is %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ]\n\t[ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n"),
                progname, progname);
        (void) exit(EXIT_FAILURE);
 }
@@ -432,6 +470,13 @@ char *     argv[];
 #ifdef unix
        (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
 #endif /* defined unix */
+#if HAVE_GETTEXT - 0
+       (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+       (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined TEXTDOMAINDIR */
+       (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
        progname = argv[0];
        while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF)
                switch (c) {
@@ -442,7 +487,7 @@ char *      argv[];
                                        directory = optarg;
                                else {
                                        (void) fprintf(stderr,
-"%s: More than one -d option specified\n",
+_("%s: More than one -d option specified\n"),
                                                progname);
                                        (void) exit(EXIT_FAILURE);
                                }
@@ -452,7 +497,7 @@ char *      argv[];
                                        lcltime = optarg;
                                else {
                                        (void) fprintf(stderr,
-"%s: More than one -l option specified\n",
+_("%s: More than one -l option specified\n"),
                                                progname);
                                        (void) exit(EXIT_FAILURE);
                                }
@@ -462,7 +507,7 @@ char *      argv[];
                                        psxrules = optarg;
                                else {
                                        (void) fprintf(stderr,
-"%s: More than one -p option specified\n",
+_("%s: More than one -p option specified\n"),
                                                progname);
                                        (void) exit(EXIT_FAILURE);
                                }
@@ -472,7 +517,7 @@ char *      argv[];
                                        yitcommand = optarg;
                                else {
                                        (void) fprintf(stderr,
-"%s: More than one -y option specified\n",
+_("%s: More than one -y option specified\n"),
                                                progname);
                                        (void) exit(EXIT_FAILURE);
                                }
@@ -482,7 +527,7 @@ char *      argv[];
                                        leapsec = optarg;
                                else {
                                        (void) fprintf(stderr,
-"%s: More than one -L option specified\n",
+_("%s: More than one -L option specified\n"),
                                                progname);
                                        (void) exit(EXIT_FAILURE);
                                }
@@ -565,9 +610,11 @@ const char * const tofile;
                if (mkdirs(toname) != 0)
                        (void) exit(EXIT_FAILURE);
                if (link(fromname, toname) != 0) {
-                       (void) fprintf(stderr, "%s: Can't link from %s to ",
-                               progname, fromname);
-                       (void) perror(toname);
+                       const char *e = strerror(errno);
+
+                       (void) fprintf(stderr,
+                               _("%s: Can't link from %s to %s: %s\n"),
+                               progname, fromname, toname, e);
                        (void) exit(EXIT_FAILURE);
                }
        }
@@ -591,8 +638,7 @@ const char * const  tofile;
 */
 
 #define MAX_BITS_IN_FILE       32
-#define TIME_T_BITS_IN_FILE    ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? \
-                                       TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
+#define TIME_T_BITS_IN_FILE    ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
 
 static void
 setboundaries P((void))
@@ -650,11 +696,37 @@ associate P((void))
        register struct zone *  zp;
        register struct rule *  rp;
        register int            base, out;
-       register int            i;
+       register int            i, j;
 
-       if (nrules != 0)
+       if (nrules != 0) {
                (void) qsort((void *) rules, (size_t) nrules,
                        (size_t) sizeof *rules, rcomp);
+               for (i = 0; i < nrules - 1; ++i) {
+                       if (strcmp(rules[i].r_name,
+                               rules[i + 1].r_name) != 0)
+                                       continue;
+                       if (strcmp(rules[i].r_filename,
+                               rules[i + 1].r_filename) == 0)
+                                       continue;
+                       eat(rules[i].r_filename, rules[i].r_linenum);
+                       warning(_("same rule name in multiple files"));
+                       eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
+                       warning(_("same rule name in multiple files"));
+                       for (j = i + 2; j < nrules; ++j) {
+                               if (strcmp(rules[i].r_name,
+                                       rules[j].r_name) != 0)
+                                               break;
+                               if (strcmp(rules[i].r_filename,
+                                       rules[j].r_filename) == 0)
+                                               continue;
+                               if (strcmp(rules[i + 1].r_filename,
+                                       rules[j].r_filename) == 0)
+                                               continue;
+                               break;
+                       }
+                       i = j - 1;
+               }
+       }
        for (i = 0; i < nzones; ++i) {
                zp = &zones[i];
                zp->z_rules = NULL;
@@ -680,13 +752,14 @@ associate P((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 = gethms(zp->z_rule, _("unruly zone"),
+                                             TRUE);
                        /*
                        ** Note, though, that if there's no rule,
                        ** a '%s' in the format is a bad thing.
                        */
                        if (strchr(zp->z_format, '%') != 0)
-                               error("%s in ruleless zone");
+                               error(_("%s in ruleless zone"));
                }
        }
        if (errors)
@@ -707,11 +780,13 @@ const char *      name;
        char                            buf[BUFSIZ];
 
        if (strcmp(name, "-") == 0) {
-               name = "standard input";
+               name = _("standard input");
                fp = stdin;
        } else if ((fp = fopen(name, "r")) == NULL) {
-               (void) fprintf(stderr, "%s: Can't open ", progname);
-               (void) perror(name);
+               const char *e = strerror(errno);
+
+               (void) fprintf(stderr, _("%s: Can't open %s: %s\n"),
+                       progname, name, e);
                (void) exit(EXIT_FAILURE);
        }
        wantcont = FALSE;
@@ -721,7 +796,7 @@ const char *        name;
                        break;
                cp = strchr(buf, '\n');
                if (cp == NULL) {
-                       error("line too long");
+                       error(_("line too long"));
                        (void) exit(EXIT_FAILURE);
                }
                *cp = '\0';
@@ -741,7 +816,7 @@ const char *        name;
                } else {
                        lp = byword(fields[0], line_codes);
                        if (lp == NULL)
-                               error("input line of unknown type");
+                               error(_("input line of unknown type"));
                        else switch ((int) (lp->l_value)) {
                                case LC_RULE:
                                        inrule(fields, nfields);
@@ -757,14 +832,14 @@ const char *      name;
                                case LC_LEAP:
                                        if (name != leapsec)
                                                (void) fprintf(stderr,
-"%s: Leap line in non leap seconds file %s\n",
+_("%s: Leap line in non leap seconds file %s\n"),
                                                        progname, name);
                                        else    inleap(fields, nfields);
                                        wantcont = FALSE;
                                        break;
                                default:        /* "cannot happen" */
                                        (void) fprintf(stderr,
-"%s: panic: Invalid l_value %d\n",
+_("%s: panic: Invalid l_value %d\n"),
                                                progname, lp->l_value);
                                        (void) exit(EXIT_FAILURE);
                        }
@@ -772,17 +847,19 @@ const char *      name;
                ifree((char *) fields);
        }
        if (ferror(fp)) {
-               (void) fprintf(stderr, "%s: Error reading ", progname);
-               (void) perror(filename);
+               (void) fprintf(stderr, _("%s: Error reading %s\n"),
+                       progname, filename);
                (void) exit(EXIT_FAILURE);
        }
        if (fp != stdin && fclose(fp)) {
-               (void) fprintf(stderr, "%s: Error closing ", progname);
-               (void) perror(filename);
+               const char *e = strerror(errno);
+
+               (void) fprintf(stderr, _("%s: Error closing %s: %s\n"),
+                       progname, filename, e);
                (void) exit(EXIT_FAILURE);
        }
        if (wantcont)
-               error("expected continuation line not found");
+               error(_("expected continuation line not found"));
 }
 
 /*
@@ -837,16 +914,16 @@ const int         nfields;
        static struct rule      r;
 
        if (nfields != RULE_FIELDS) {
-               error("wrong number of fields on Rule line");
+               error(_("wrong number of fields on Rule line"));
                return;
        }
        if (*fields[RF_NAME] == '\0') {
-               error("nameless rule");
+               error(_("nameless rule"));
                return;
        }
        r.r_filename = filename;
        r.r_linenum = linenum;
-       r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE);
+       r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
        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]);
@@ -865,13 +942,13 @@ const int         nfields;
        static char *   buf;
 
        if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
-               error("wrong number of fields on Zone line");
+               error(_("wrong number of fields on Zone line"));
                return FALSE;
        }
        if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
                buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
                (void) sprintf(buf,
-"\"Zone %s\" line and -l option are mutually exclusive",
+_("\"Zone %s\" line and -l option are mutually exclusive"),
                        TZDEFAULT);
                error(buf);
                return FALSE;
@@ -879,7 +956,7 @@ const int           nfields;
        if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
                buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
                (void) sprintf(buf,
-"\"Zone %s\" line and -p option are mutually exclusive",
+_("\"Zone %s\" line and -p option are mutually exclusive"),
                        TZDEFRULES);
                error(buf);
                return FALSE;
@@ -891,7 +968,7 @@ const int           nfields;
                                        strlen(fields[ZF_NAME]) +
                                        strlen(zones[i].z_filename)));
                                (void) sprintf(buf,
-"duplicate zone name %s (file \"%s\", line %d)",
+_("duplicate zone name %s (file \"%s\", line %d)"),
                                        fields[ZF_NAME],
                                        zones[i].z_filename,
                                        zones[i].z_linenum);
@@ -907,7 +984,7 @@ register char ** const      fields;
 const int              nfields;
 {
        if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
-               error("wrong number of fields on Zone continuation line");
+               error(_("wrong number of fields on Zone continuation line"));
                return FALSE;
        }
        return inzsub(fields, nfields, TRUE);
@@ -947,10 +1024,10 @@ const int                iscont;
        }
        z.z_filename = filename;
        z.z_linenum = linenum;
-       z.z_gmtoff = gethms(fields[i_gmtoff], "invalid GMT offset", TRUE);
+       z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid GMT offset"), TRUE);
        if ((cp = strchr(fields[i_format], '%')) != 0) {
                if (*++cp != 's' || strchr(cp, '%') != 0) {
-                       error("invalid abbreviation format");
+                       error(_("invalid abbreviation format"));
                        return FALSE;
                }
        }
@@ -976,8 +1053,7 @@ const int          iscont;
                        zones[nzones - 1].z_untiltime > min_time &&
                        zones[nzones - 1].z_untiltime < max_time &&
                        zones[nzones - 1].z_untiltime >= z.z_untiltime) {
-                               error("Zone continuation line end time is not \
-after end time of previous line");
+                               error(_("Zone continuation line end time is not after end time of previous line"));
                                return FALSE;
                }
        }
@@ -1004,7 +1080,7 @@ const int         nfields;
        time_t                          t;
 
        if (nfields != LEAP_FIELDS) {
-               error("wrong number of fields on Leap line");
+               error(_("wrong number of fields on Leap line"));
                return;
        }
        dayoff = 0;
@@ -1013,7 +1089,7 @@ const int         nfields;
                        /*
                         * Leapin' Lizards!
                         */
-                       error("invalid leaping year");
+                       error(_("invalid leaping year"));
                        return;
        }
        j = EPOCH_YEAR;
@@ -1028,7 +1104,7 @@ const int         nfields;
                dayoff = oadd(dayoff, eitol(i));
        }
        if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
-               error("invalid month name");
+               error(_("invalid month name"));
                return;
        }
        month = lp->l_value;
@@ -1041,12 +1117,12 @@ const int               nfields;
        cp = fields[LP_DAY];
        if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
                day <= 0 || day > len_months[isleap(year)][month]) {
-                       error("invalid day of month");
+                       error(_("invalid day of month"));
                        return;
        }
        dayoff = oadd(dayoff, eitol(day - 1));
        if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
-               error("time before zero");
+               error(_("time before zero"));
                return;
        }
        t = (time_t) dayoff * SECSPERDAY;
@@ -1054,10 +1130,10 @@ const int               nfields;
        ** Cheap overflow check.
        */
        if (t / SECSPERDAY != dayoff) {
-               error("time overflow");
+               error(_("time overflow"));
                return;
        }
-       tod = gethms(fields[LP_TIME], "invalid time of day", FALSE);
+       tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
        cp = fields[LP_CORR];
        {
                register int    positive;
@@ -1076,11 +1152,11 @@ const int               nfields;
                        positive = TRUE;
                        count = 2;
                } else {
-                       error("illegal CORRECTION field on Leap line");
+                       error(_("illegal CORRECTION field on Leap line"));
                        return;
                }
                if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
-                       error("illegal Rolling/Stationary field on Leap line");
+                       error(_("illegal Rolling/Stationary field on Leap line"));
                        return;
                }
                leapadd(tadd(t, tod), positive, lp->l_value, count);
@@ -1095,15 +1171,15 @@ const int               nfields;
        struct link     l;
 
        if (nfields != LINK_FIELDS) {
-               error("wrong number of fields on Link line");
+               error(_("wrong number of fields on Link line"));
                return;
        }
        if (*fields[LF_FROM] == '\0') {
-               error("blank FROM field on Link line");
+               error(_("blank FROM field on Link line"));
                return;
        }
        if (*fields[LF_TO] == '\0') {
-               error("blank TO field on Link line");
+               error(_("blank TO field on Link line"));
                return;
        }
        l.l_filename = filename;
@@ -1131,7 +1207,7 @@ const char * const                timep;
        register char *                 ep;
 
        if ((lp = byword(monthp, mon_names)) == NULL) {
-               error("invalid month name");
+               error(_("invalid month name"));
                return;
        }
        rp->r_month = lp->l_value;
@@ -1159,7 +1235,7 @@ const char * const                timep;
                                break;
                }
        }
-       rp->r_tod = gethms(dp, "invalid time of day", FALSE);
+       rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
        ifree(dp);
        /*
        ** Year work.
@@ -1175,11 +1251,11 @@ const char * const              timep;
                        break;
                default:        /* "cannot happen" */
                        (void) fprintf(stderr,
-                               "%s: panic: Invalid l_value %d\n",
+                               _("%s: panic: Invalid l_value %d\n"),
                                progname, lp->l_value);
                        (void) exit(EXIT_FAILURE);
        } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
-               error("invalid starting year");
+               error(_("invalid starting year"));
                return;
        }
        cp = hiyearp;
@@ -1195,22 +1271,22 @@ const char * const              timep;
                        break;
                default:        /* "cannot happen" */
                        (void) fprintf(stderr,
-                               "%s: panic: Invalid l_value %d\n",
+                               _("%s: panic: Invalid l_value %d\n"),
                                progname, lp->l_value);
                        (void) exit(EXIT_FAILURE);
        } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
-               error("invalid ending year");
+               error(_("invalid ending year"));
                return;
        }
        if (rp->r_loyear > rp->r_hiyear) {
-               error("starting year greater than ending year");
+               error(_("starting year greater than ending year"));
                return;
        }
        if (*typep == '\0')
                rp->r_yrtype = NULL;
        else {
                if (rp->r_loyear == rp->r_hiyear) {
-                       error("typed single year");
+                       error(_("typed single year"));
                        return;
                }
                rp->r_yrtype = ecpyalloc(typep);
@@ -1240,12 +1316,12 @@ const char * const              timep;
                if (rp->r_dycode != DC_DOM) {
                        *ep++ = 0;
                        if (*ep++ != '=') {
-                               error("invalid day of month");
+                               error(_("invalid day of month"));
                                ifree(dp);
                                return;
                        }
                        if ((lp = byword(dp, wday_names)) == NULL) {
-                               error("invalid weekday name");
+                               error(_("invalid weekday name"));
                                ifree(dp);
                                return;
                        }
@@ -1254,7 +1330,7 @@ const char * const                timep;
                if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
                        rp->r_dayofmonth <= 0 ||
                        (rp->r_dayofmonth > len_months[1][rp->r_month])) {
-                               error("invalid day of month");
+                               error(_("invalid day of month"));
                                ifree(dp);
                                return;
                }
@@ -1285,6 +1361,18 @@ FILE * const     fp;
        (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
 }
 
+static int
+atcomp(avp, bvp)
+void * avp;
+void * bvp;
+{
+       if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
+               return -1;
+       else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
+               return 1;
+       else    return 0;
+}
+
 static void
 writezone(name)
 const char * const     name;
@@ -1293,16 +1381,71 @@ const char * const      name;
        register int            i, j;
        static char *           fullname;
        static struct tzhead    tzh;
+       time_t                  ats[TZ_MAX_TIMES];
+       unsigned char           types[TZ_MAX_TIMES];
 
+       /*
+       ** Sort.
+       */
+       if (timecnt > 1)
+               (void) qsort((void *) attypes, (size_t) timecnt,
+                       (size_t) sizeof *attypes, atcomp);
+       /*
+       ** Optimize.
+       */
+       {
+               int     fromi;
+               int     toi;
+
+               toi = 0;
+               fromi = 0;
+               if (isdsts[0] == 0)
+                       while (attypes[fromi].type == 0)
+                               ++fromi;        /* handled by default rule */
+               for ( ; fromi < timecnt; ++fromi) {
+                       if (toi != 0
+                           && ((attypes[fromi].at
+                                + gmtoffs[attypes[toi - 1].type])
+                               <= (attypes[toi - 1].at
+                                   + gmtoffs[toi == 1 ? 0
+                                             : attypes[toi - 2].type]))) {
+                               attypes[toi - 1].type = attypes[fromi].type;
+                               continue;
+                       }
+                       if (toi == 0 ||
+                               attypes[toi - 1].type != attypes[fromi].type)
+                                       attypes[toi++] = attypes[fromi];
+               }
+               timecnt = toi;
+       }
+       /*
+       ** Transfer.
+       */
+       for (i = 0; i < timecnt; ++i) {
+               ats[i] = attypes[i].at;
+               types[i] = attypes[i].type;
+       }
        fullname = erealloc(fullname,
                (int) (strlen(directory) + 1 + strlen(name) + 1));
        (void) sprintf(fullname, "%s/%s", directory, name);
+       /*
+       ** Remove old file, if any, to snap links.
+       */
+       if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT) {
+               const char *e = strerror(errno);
+
+               (void) fprintf(stderr, _("%s: Can't remove %s: %s\n"),
+                       progname, fullname, e);
+               (void) exit(EXIT_FAILURE);
+       }
        if ((fp = fopen(fullname, "wb")) == NULL) {
                if (mkdirs(fullname) != 0)
                        (void) exit(EXIT_FAILURE);
                if ((fp = fopen(fullname, "wb")) == NULL) {
-                       (void) fprintf(stderr, "%s: Can't create ", progname);
-                       (void) perror(fullname);
+                       const char *e = strerror(errno);
+
+                       (void) fprintf(stderr, _("%s: Can't create %s: %s\n"),
+                               progname, fullname, e);
                        (void) exit(EXIT_FAILURE);
                }
        }
@@ -1312,8 +1455,7 @@ const char * const        name;
        convert(eitol(timecnt), tzh.tzh_timecnt);
        convert(eitol(typecnt), tzh.tzh_typecnt);
        convert(eitol(charcnt), tzh.tzh_charcnt);
-#define DO(field)      (void) fwrite((void *) tzh.field, \
-               (size_t) sizeof tzh.field, (size_t) 1, fp)
+#define DO(field)      (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
        DO(tzh_reserved);
        DO(tzh_ttisgmtcnt);
        DO(tzh_ttisstdcnt);
@@ -1366,8 +1508,8 @@ const char * const        name;
        for (i = 0; i < typecnt; ++i)
                (void) putc(ttisgmts[i], fp);
        if (ferror(fp) || fclose(fp)) {
-               (void) fprintf(stderr, "%s: Write error on ", progname);
-               (void) perror(fullname);
+               (void) fprintf(stderr, _("%s: Error writing %s\n"),
+                       progname, fullname);
                (void) exit(EXIT_FAILURE);
        }
 }
@@ -1405,7 +1547,6 @@ const int                 zonecount;
        register long                   stdoff;
        register int                    year;
        register long                   startoff;
-       register int                    startisdst;
        register int                    startttisstd;
        register int                    startttisgmt;
        register int                    type;
@@ -1413,7 +1554,6 @@ const int                 zonecount;
 
        INITIALIZE(untiltime);
        INITIALIZE(starttime);
-       INITIALIZE(startoff);
        /*
        ** Now. . .finally. . .generate some useful data!
        */
@@ -1438,7 +1578,8 @@ const int                 zonecount;
                        continue;
                gmtoff = zp->z_gmtoff;
                eat(zp->z_filename, zp->z_linenum);
-               startisdst = -1;
+               *startbuf = '\0';
+               startoff = zp->z_gmtoff;
                if (zp->z_nrules == 0) {
                        stdoff = zp->z_stdoff;
                        doabbr(startbuf, zp->z_format,
@@ -1446,8 +1587,10 @@ const int                        zonecount;
                        type = addtype(oadd(zp->z_gmtoff, stdoff),
                                startbuf, stdoff != 0, startttisstd,
                                startttisgmt);
-                       if (usestart)
+                       if (usestart) {
                                addtt(starttime, type);
+                               usestart = FALSE;
+                       }
                        else if (stdoff != 0)
                                addtt(min_time, type);
                } else for (year = min_year; year <= max_year; ++year) {
@@ -1518,36 +1661,25 @@ const int                       zonecount;
                                rp->r_todo = FALSE;
                                if (useuntil && ktime >= untiltime)
                                        break;
+                               stdoff = rp->r_stdoff;
+                               if (usestart && ktime == starttime)
+                                       usestart = FALSE;
                                if (usestart) {
-                                   if (ktime < starttime) {
-                                       stdoff = rp->r_stdoff;
-                                       startoff = oadd(zp->z_gmtoff,
-                                               rp->r_stdoff);
-                                       doabbr(startbuf, zp->z_format,
-                                               rp->r_abbrvar,
-                                               rp->r_stdoff != 0);
-                                       startisdst = rp->r_stdoff != 0;
-                                       continue;
-                                   }
-                                   usestart = FALSE;
-                                   if (ktime != starttime) {
-                                       if (startisdst < 0 &&
-                                           zp->z_gmtoff !=
-                                           (zp - 1)->z_gmtoff) {
-                                               type = (timecnt == 0) ? 0 :
-                                                       types[timecnt - 1];
-                                               startoff = oadd(gmtoffs[type],
-                                                       -(zp - 1)->z_gmtoff);
-                                               startisdst = startoff != 0;
-                                               startoff = oadd(startoff,
-                                                       zp->z_gmtoff);
-                                               (void) strcpy(startbuf,
-                                                   &chars[abbrinds[type]]);
+                                       if (ktime < starttime) {
+                                               startoff = oadd(zp->z_gmtoff,
+                                                       stdoff);
+                                               doabbr(startbuf, zp->z_format,
+                                                       rp->r_abbrvar,
+                                                       rp->r_stdoff != 0);
+                                               continue;
+                                       }
+                                       if (*startbuf == '\0' &&
+                                           startoff == oadd(zp->z_gmtoff,
+                                           stdoff)) {
+                                               doabbr(startbuf, zp->z_format,
+                                                       rp->r_abbrvar,
+                                                       rp->r_stdoff != 0);
                                        }
-                                       if (startisdst >= 0)
-addtt(starttime, addtype(startoff, startbuf, startisdst, startttisstd,
-       startttisgmt));
-                                   }
                                }
                                eats(zp->z_filename, zp->z_linenum,
                                        rp->r_filename, rp->r_linenum);
@@ -1557,18 +1689,34 @@ addtt(starttime, addtype(startoff, startbuf, startisdst, startttisstd,
                                type = addtype(offset, buf, rp->r_stdoff != 0,
                                        rp->r_todisstd, rp->r_todisgmt);
                                addtt(ktime, type);
-                               stdoff = rp->r_stdoff;
                        }
                }
+               if (usestart) {
+                       if (*startbuf == '\0' &&
+                               zp->z_format != NULL &&
+                               strchr(zp->z_format, '%') == NULL &&
+                               strchr(zp->z_format, '/') == NULL)
+                                       (void) strcpy(startbuf, zp->z_format);
+                       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));
+               }
                /*
                ** Now we may get to set starttime for the next zone line.
                */
                if (useuntil) {
-                       starttime = tadd(zp->z_untiltime, -gmtoff);
                        startttisstd = zp->z_untilrule.r_todisstd;
                        startttisgmt = zp->z_untilrule.r_todisgmt;
+                       starttime = zp->z_untiltime;
                        if (!startttisstd)
                                starttime = tadd(starttime, -stdoff);
+                       if (!startttisgmt)
+                               starttime = tadd(starttime, -gmtoff);
                }
        }
        writezone(zpfirst->z_name);
@@ -1579,16 +1727,12 @@ addtt(starttime, type)
 const time_t   starttime;
 const int      type;
 {
-       if (timecnt != 0 && type == types[timecnt - 1])
-               return; /* easy enough! */
-       if (timecnt == 0 && type == 0 && isdsts[0] == 0)
-               return; /* handled by default rule */
        if (timecnt >= TZ_MAX_TIMES) {
-               error("too many transitions?!");
+               error(_("too many transitions?!"));
                (void) exit(EXIT_FAILURE);
        }
-       ats[timecnt] = starttime;
-       types[timecnt] = type;
+       attypes[timecnt].at = starttime;
+       attypes[timecnt].type = type;
        ++timecnt;
 }
 
@@ -1602,6 +1746,18 @@ const int                ttisgmt;
 {
        register int    i, j;
 
+       if (isdst != TRUE && isdst != FALSE) {
+               error(_("internal error - addtype called with bad isdst"));
+               (void) exit(EXIT_FAILURE);
+       }
+       if (ttisstd != TRUE && ttisstd != FALSE) {
+               error(_("internal error - addtype called with bad ttisstd"));
+               (void) exit(EXIT_FAILURE);
+       }
+       if (ttisgmt != TRUE && ttisgmt != FALSE) {
+               error(_("internal error - addtype called with bad ttisgmt"));
+               (void) exit(EXIT_FAILURE);
+       }
        /*
        ** See if there's already an entry for this zone type.
        ** If so, just return its index.
@@ -1618,7 +1774,7 @@ const int         ttisgmt;
        ** many.
        */
        if (typecnt >= TZ_MAX_TYPES) {
-               error("too many local time types");
+               error(_("too many local time types"));
                (void) exit(EXIT_FAILURE);
        }
        gmtoffs[i] = gmtoff;
@@ -1646,13 +1802,13 @@ int             count;
        register int    i, j;
 
        if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
-               error("too many leap seconds");
+               error(_("too many leap seconds"));
                (void) exit(EXIT_FAILURE);
        }
        for (i = 0; i < leapcnt; ++i)
                if (t <= trans[i]) {
                        if (t == trans[i]) {
-                               error("repeated leap second moment");
+                               error(_("repeated leap second moment"));
                                (void) exit(EXIT_FAILURE);
                        }
                        break;
@@ -1702,8 +1858,8 @@ const char * const        type;
                return TRUE;
        if (result == (1 << 8))
                return FALSE;
-       error("Wild result from command execution");
-       (void) fprintf(stderr, "%s: command was '%s', result was %d\n",
+       error(_("Wild result from command execution"));
+       (void) fprintf(stderr, _("%s: command was '%s', result was %d\n"),
                progname, buf, result);
        for ( ; ; )
                (void) exit(EXIT_FAILURE);
@@ -1797,7 +1953,7 @@ register char *   cp;
                        else while ((*dp = *cp++) != '"')
                                if (*dp != '\0')
                                        ++dp;
-                               else    error("Odd number of quotation marks");
+                               else    error(_("Odd number of quotation marks"));
                } while (*cp != '\0' && *cp != '#' &&
                        (!isascii(*cp) || !isspace((unsigned char) *cp)));
                if (isascii(*cp) && isspace((unsigned char) *cp))
@@ -1817,7 +1973,7 @@ const long        t2;
 
        t = t1 + t2;
        if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
-               error("time overflow");
+               error(_("time overflow"));
                (void) exit(EXIT_FAILURE);
        }
        return t;
@@ -1836,7 +1992,7 @@ const long        t2;
                return min_time;
        t = t1 + t2;
        if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
-               error("time overflow");
+               error(_("time overflow"));
                (void) exit(EXIT_FAILURE);
        }
        return t;
@@ -1883,7 +2039,7 @@ register const int                        wantedy;
                if (rp->r_dycode == DC_DOWLEQ)
                        --i;
                else {
-                       error("use of 2/29 in non leap-year");
+                       error(_("use of 2/29 in non leap-year"));
                        (void) exit(EXIT_FAILURE);
                }
        }
@@ -1917,7 +2073,7 @@ register const int                        wantedy;
                                --i;
                        }
                if (i < 0 || i >= len_months[isleap(y)][m]) {
-                       error("no day in month matches rule");
+                       error(_("no day in month matches rule"));
                        (void) exit(EXIT_FAILURE);
                }
        }
@@ -1940,7 +2096,7 @@ const char * const        string;
 
        i = strlen(string) + 1;
        if (charcnt + i > TZ_MAX_CHARS) {
-               error("too many, or too long, time zone abbreviations");
+               error(_("too many, or too long, time zone abbreviations"));
                (void) exit(EXIT_FAILURE);
        }
        (void) strcpy(&chars[charcnt], string);
@@ -1974,10 +2130,11 @@ char * const    argname;
                        ** It doesn't seem to exist, so we try to create it.
                        */
                        if (mkdir(name, 0755) != 0) {
+                               const char *e = strerror(errno);
+
                                (void) fprintf(stderr,
-                                       "%s: Can't create directory ",
-                                       progname);
-                               (void) perror(name);
+                                   _("%s: Can't create directory %s: %s\n"),
+                                   progname, name, e);
                                ifree(name);
                                return -1;
                        }
@@ -1997,7 +2154,7 @@ const int i;
        l = i;
        if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
                (void) fprintf(stderr,
-                       "%s: %d did not sign extend correctly\n",
+                       _("%s: %d did not sign extend correctly\n"),
                        progname, i);
                (void) exit(EXIT_FAILURE);
        }