Sync libc/stdtime and zdump(8)/zic(8) with tzcode2008h from elsie.
authorSascha Wildner <swildner@dragonflybsd.org>
Sun, 19 Oct 2008 20:15:58 +0000 (20:15 +0000)
committerSascha Wildner <swildner@dragonflybsd.org>
Sun, 19 Oct 2008 20:15:58 +0000 (20:15 +0000)
There shouldn't be any user visible changes. Some bugs are fixed, among
them an issue with Python's mktime() when TZ wasn't set (reported by
Aran Cox).

Another thing is that this code should behave nicely on architectures with
a 64 bits wide time_t.

18 files changed:
lib/libc/gen/tzset.3
lib/libc/stdtime/asctime.c
lib/libc/stdtime/ctime.3
lib/libc/stdtime/difftime.c
lib/libc/stdtime/localtime.c
lib/libc/stdtime/private.h
lib/libc/stdtime/strftime.3
lib/libc/stdtime/strftime.c
lib/libc/stdtime/time2posix.3
lib/libc/stdtime/tzfile.5
lib/libc/stdtime/tzfile.h
usr.sbin/zic/ialloc.c
usr.sbin/zic/private.h
usr.sbin/zic/scheck.c
usr.sbin/zic/zdump.8
usr.sbin/zic/zdump.c
usr.sbin/zic/zic.8
usr.sbin/zic/zic.c

index 0b9958a..f729588 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"    @(#)tzset.3     8.2 (Berkeley) 11/17/93
+.\" @(#)newtzset.3     8.1
 .\" $FreeBSD: src/lib/libc/gen/tzset.3,v 1.6.2.5 2001/12/14 18:33:51 ru Exp $
-.\" $DragonFly: src/lib/libc/gen/tzset.3,v 1.3 2006/04/08 08:17:06 swildner Exp $
+.\" $DragonFly: src/lib/libc/gen/tzset.3,v 1.4 2008/10/19 20:15:58 swildner Exp $
 .\"
-.Dd November 17, 1993
+.Dd October 19, 2008
 .Dt TZSET 3
 .Os
 .Sh NAME
@@ -317,6 +317,7 @@ leap seconds are loaded from
 .Xr gettimeofday 2 ,
 .Xr ctime 3 ,
 .Xr getenv 3 ,
+.Xr strftime 3 ,
 .Xr time 3 ,
 .Xr tzfile 5
 .Sh HISTORY
index b43c878..83c7170 100644 (file)
@@ -1,14 +1,17 @@
 /*
 ** This file is in the public domain, so clarified as of
-** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
-**
-** $FreeBSD: src/lib/libc/stdtime/asctime.c,v 1.7.6.1 2001/03/05 11:37:20 obrien Exp $
-** $DragonFly: src/lib/libc/stdtime/asctime.c,v 1.5 2005/12/04 23:25:40 swildner Exp $
+** 1996-06-05 by Arthur David Olson.
 */
 
 /*
- * @(#)asctime.c       7.7
- */
+** Avoid the temptation to punt entirely to strftime;
+** the output of strftime is supposed to be locale specific
+** whereas the output of asctime is supposed to be constant.
+**
+** @(#)asctime.c       8.2
+** $FreeBSD: src/lib/libc/stdtime/asctime.c,v 1.7.6.1 2001/03/05 11:37:20 obrien Exp $
+** $DragonFly: src/lib/libc/stdtime/asctime.c,v 1.6 2008/10/19 20:15:58 swildner Exp $
+*/
 /*LINTLIBRARY*/
 
 #include "namespace.h"
 #include "tzfile.h"
 
 /*
-** A la X3J11, with core dump avoidance.
+** Some systems only handle "%.2d"; others only handle "%02d";
+** "%02.2d" makes (most) everybody happy.
+** At least some versions of gcc warn about the %02.2d;
+** we conditionalize below to avoid the warning.
+*/
+/*
+** All years associated with 32-bit time_t values are exactly four digits long;
+** some years associated with 64-bit time_t values are not.
+** Vintage programs are coded for years that are always four digits long
+** and may assume that the newline always lands in the same place.
+** For years that are less than four digits, we pad the output with
+** leading zeroes to get the newline in the traditional place.
+** The -4 ensures that we get four characters of output even if
+** we call a strftime variant that produces fewer characters for some years.
+** The ISO C 1999 and POSIX 1003.1-2004 standards prohibit padding the year,
+** but many implementations pad anyway; most likely the standards are buggy.
 */
+#ifdef __GNUC__
+#define ASCTIME_FMT    "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %-4s\n"
+#else /* !defined __GNUC__ */
+#define ASCTIME_FMT    "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n"
+#endif /* !defined __GNUC__ */
+/*
+** For years that are more than four digits we put extra spaces before the year
+** so that code trying to overwrite the newline won't end up overwriting
+** a digit within a year and truncating the year (operating on the assumption
+** that no output is better than wrong output).
+*/
+#ifdef __GNUC__
+#define ASCTIME_FMT_B  "%.3s %.3s%3d %2.2d:%2.2d:%2.2d     %s\n"
+#else /* !defined __GNUC__ */
+#define ASCTIME_FMT_B  "%.3s %.3s%3d %02.2d:%02.2d:%02.2d     %s\n"
+#endif /* !defined __GNUC__ */
 
+#define STD_ASCTIME_BUF_SIZE   26
+/*
+** Big enough for something such as
+** ??? ???-2147483648 -2147483648:-2147483648:-2147483648     -2147483648\n
+** (two three-character abbreviations, five strings denoting integers,
+** seven explicit spaces, two explicit colons, a newline,
+** and a trailing ASCII nul).
+** The values above are for systems where an int is 32 bits and are provided
+** as an example; the define below calculates the maximum for the system at
+** hand.
+*/
+#define MAX_ASCTIME_BUF_SIZE   (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
+
+static char    buf_asctime[MAX_ASCTIME_BUF_SIZE];
+
+/*
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
+*/
 
-char *
-asctime(const struct tm *timeptr)
-{
-       static char             result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
-                                       3 + 2 + 1 + 1];
-       return(asctime_r(timeptr, result));
-}
 
 char *
-asctime_r(const struct tm *timeptr, char *result)
+asctime_r(const struct tm *timeptr, char *buf)
 {
        static const char       wday_name[][3] = {
                "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
@@ -39,13 +84,8 @@ asctime_r(const struct tm *timeptr, char *result)
                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        };
-       /*
-       ** Big enough for something such as
-       ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
-       ** (two three-character abbreviations, five strings denoting integers,
-       ** three explicit spaces, two explicit colons, a newline,
-       ** and a trailing ASCII nul).
-       */
+       char            year[INT_STRLEN_MAXIMUM(int) + 2];
+       char            result[MAX_ASCTIME_BUF_SIZE];
        const char *    wn;
        const char *    mn;
 
@@ -56,14 +96,33 @@ asctime_r(const struct tm *timeptr, char *result)
                mn = "???";
        else    mn = mon_name[timeptr->tm_mon];
        /*
-       ** The X3J11-suggested format is
-       **      "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
-       ** Since the .2 in 02.2d is ignored, we drop it.
+       ** Use strftime's %Y to generate the year, to avoid overflow problems
+       ** when computing timeptr->tm_year + TM_YEAR_BASE.
+       ** Assume that strftime is unaffected by other out-of-range members
+       ** (e.g., timeptr->tm_mday) when processing "%Y".
        */
-       sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+       strftime(year, sizeof year, "%Y", timeptr);
+       snprintf(result, sizeof result,
+               ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
                wn, mn,
                timeptr->tm_mday, timeptr->tm_hour,
                timeptr->tm_min, timeptr->tm_sec,
-               TM_YEAR_BASE + timeptr->tm_year);
-       return result;
+               year);
+       if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
+               (void) strcpy(buf, result);
+               return buf;
+       } else {
+               errno = EOVERFLOW;
+               return NULL;
+       }
+}
+
+/*
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
+*/
+
+char *
+asctime(const struct tm *timeptr)
+{
+       return asctime_r(timeptr, buf_asctime);
 }
index 9e1abed..13919fe 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"     From: @(#)ctime.3      8.1 (Berkeley) 6/4/93
+.\" @(#)newctime.3     8.2
 .\" $FreeBSD: src/lib/libc/stdtime/ctime.3,v 1.11.2.7 2003/05/23 23:53:40 keramida Exp $
-.\" $DragonFly: src/lib/libc/stdtime/ctime.3,v 1.3 2006/05/26 19:39:37 swildner Exp $
+.\" $DragonFly: src/lib/libc/stdtime/ctime.3,v 1.4 2008/10/19 20:15:58 swildner Exp $
 .\"
-.Dd January 2, 1999
+.Dd October 19, 2008
 .Dt CTIME 3
 .Os
 .Sh NAME
@@ -87,7 +87,7 @@ and
 all take as an argument a time value representing the time in seconds since
 the Epoch (00:00:00
 .Tn UTC ,
-January 1, 1970; see
+1970-01-01; see
 .Xr time 3 ) .
 .Pp
 The function
@@ -98,18 +98,11 @@ and returns a pointer to a
 .Dq Fa struct tm
 (described below) which contains
 the broken-out time information for the value after adjusting for the current
-time zone (and any other factors such as Daylight Saving Time).
+time zone and any time zone adjustments.
 Time zone adjustments are performed as specified by the
 .Ev TZ
 environment variable (see
 .Xr tzset 3 ) .
-The function
-.Fn localtime
-uses
-.Xr tzset 3
-to initialize time conversion information if
-.Xr tzset 3
-has not already been called by the process.
 .Pp
 After filling in the tm structure,
 .Fn localtime
@@ -134,12 +127,21 @@ The
 function
 adjusts the time value for the current time zone in the same manner as
 .Fn localtime ,
-and returns a pointer to a 26-character string of the form:
+and returns a pointer to a string of the form:
 .Bd -literal -offset indent
 Thu Nov 24 18:22:48 1986\en\e0
 .Ed
 .Pp
-All the fields have constant width.
+Years requiring fewer than four characters are padded with leading zeroes.
+For years longer than four characters, the string is of the form
+.Bd -literal -offset indent
+Thu Nov 24 18:22:48     81986\en\e0
+.Ed
+.Pp
+with five spaces before the year.
+These unusual formats are designed to make it less likely that older
+software that expects exactly 26 bytes of output will mistakenly output
+misleading values for out-of-range years.
 .Pp
 The
 .Fn ctime_r
@@ -230,7 +232,9 @@ A negative value for
 causes the
 .Fn mktime
 function to attempt to divine whether summer time is in effect for the
-specified time.
+specified time; in this case it does not use a consistent
+rule and may give a different answer when later
+presented with the same argument.
 The
 .Fa tm_isdst
 and
@@ -295,10 +299,27 @@ is the offset (in seconds) of the time represented from
 .Tn UTC ,
 with positive
 values indicating east of the Prime Meridian.
+.Sh COMPATIBILITY
+The
+.Fn asctime
+and
+.Fn ctime
+functions
+behave strangely for years before 1000 or after 9999.
+The 1989 and 1999 editions of the C Standard say
+that years from -99 through 999 are converted without
+extra spaces, but this conflicts with longstanding
+tradition and with this implementation.
+Traditional implementations of these two functions are
+restricted to years in the range 1900 through 2099.
+To avoid this portability mess, new programs should use
+.Xr strftime 3
+instead.
 .Sh SEE ALSO
 .Xr date 1 ,
 .Xr gettimeofday 2 ,
 .Xr getenv 3 ,
+.Xr strftime 3 ,
 .Xr time 3 ,
 .Xr tzset 3 ,
 .Xr tzfile 5
index bae5aef..6b77f5e 100644 (file)
@@ -1,77 +1,62 @@
 /*
 ** This file is in the public domain, so clarified as of
-** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
 **
+** @(#)difftime.c      7.7
 ** $FreeBSD: src/lib/libc/stdtime/difftime.c,v 1.4.8.1 2001/03/05 11:37:21 obrien Exp $
-** $DragonFly: src/lib/libc/stdtime/difftime.c,v 1.4 2005/12/04 23:25:40 swildner Exp $
+** $DragonFly: src/lib/libc/stdtime/difftime.c,v 1.5 2008/10/19 20:15:58 swildner Exp $
 */
-
-/*
- * @(#)difftime.c      7.7
- */
 /*LINTLIBRARY*/
 
 #include "namespace.h"
 #include "private.h"
 #include "un-namespace.h"
 
-/*
-** Algorithm courtesy Paul Eggert (eggert@twinsun.com).
-*/
-
-#ifdef HAVE_LONG_DOUBLE
-#define long_double    long double
-#endif /* defined HAVE_LONG_DOUBLE */
-#ifndef HAVE_LONG_DOUBLE
-#define long_double    double
-#endif /* !defined HAVE_LONG_DOUBLE */
-
 double
 difftime(const time_t time1, const time_t time0)
 {
-       time_t  delta;
-       time_t  hibit;
-
-       if (sizeof(time_t) < sizeof(double))
+       /*
+       ** If (sizeof (double) > sizeof (time_t)) simply convert and subtract
+       ** (assuming that the larger type has more precision).
+       ** This is the common real-world case circa 2004.
+       */
+       if (sizeof (double) > sizeof (time_t))
                return (double) time1 - (double) time0;
-       if (sizeof(time_t) < sizeof(long_double))
-               return (long_double) time1 - (long_double) time0;
-       if (time1 < time0)
-               return -difftime(time0, time1);
+       if (!TYPE_INTEGRAL(time_t)) {
+               /*
+               ** time_t is floating.
+               */
+               return time1 - time0;
+       }
+       if (!TYPE_SIGNED(time_t)) {
+               /*
+               ** time_t is integral and unsigned.
+               ** The difference of two unsigned values can't overflow
+               ** if the minuend is greater than or equal to the subtrahend.
+               */
+               if (time1 >= time0)
+                       return time1 - time0;
+               else    return -((double) (time0 - time1));
+       }
        /*
-       ** As much as possible, avoid loss of precision
-       ** by computing the difference before converting to double.
+       ** time_t is integral and signed.
+       ** Handle cases where both time1 and time0 have the same sign
+       ** (meaning that their difference cannot overflow).
        */
-       delta = time1 - time0;
-       if (delta >= 0)
-               return delta;
+       if ((time1 < 0) == (time0 < 0))
+               return time1 - time0;
        /*
-       ** Repair delta overflow.
+       ** time1 and time0 have opposite signs.
+       ** Punt if unsigned long is too narrow.
        */
-       hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1);
+       if (sizeof (unsigned long) < sizeof (time_t))
+               return (double) time1 - (double) time0;
        /*
-       ** The following expression rounds twice, which means
-       ** the result may not be the closest to the true answer.
-       ** For example, suppose time_t is 64-bit signed int,
-       ** long_double is IEEE 754 double with default rounding,
-       ** time1 = 9223372036854775807 and time0 = -1536.
-       ** Then the true difference is 9223372036854777343,
-       ** which rounds to 9223372036854777856
-       ** with a total error of 513.
-       ** But delta overflows to -9223372036854774273,
-       ** which rounds to -9223372036854774784, and correcting
-       ** this by subtracting 2 * (long_double) hibit
-       ** (i.e. by adding 2**64 = 18446744073709551616)
-       ** yields 9223372036854776832, which
-       ** rounds to 9223372036854775808
-       ** with a total error of 1535 instead.
-       ** This problem occurs only with very large differences.
-       ** It's too painful to fix this portably.
-       ** We are not alone in this problem;
-       ** some C compilers round twice when converting
-       ** large unsigned types to small floating types,
-       ** so if time_t is unsigned the "return delta" above
-       ** has the same double-rounding problem with those compilers.
+       ** Stay calm...decent optimizers will eliminate the complexity below.
        */
-       return delta - 2 * (long_double) hibit;
+       if (time1 >= 0 /* && time0 < 0 */)
+               return (unsigned long) time1 +
+                       (unsigned long) (-(time0 + 1)) + 1;
+       return -(double) ((unsigned long) time0 +
+               (unsigned long) (-(time1 + 1)) + 1);
 }
index 63fec50..38b0c2e 100644 (file)
@@ -1,18 +1,15 @@
 /*
 ** This file is in the public domain, so clarified as of
-** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
 **
+** @(#)localtime.c     8.9
 ** $FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.25.2.2 2002/08/13 16:08:07 bmilekic Exp $
-** $DragonFly: src/lib/libc/stdtime/localtime.c,v 1.6 2005/12/04 23:41:06 swildner Exp $
+** $DragonFly: src/lib/libc/stdtime/localtime.c,v 1.7 2008/10/19 20:15:58 swildner Exp $
 */
 
 /*
- * @(#)localtime.c     7.57
- */
-/*
-** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
-** POSIX-style TZ environment variable handling from Guy Harris
-** (guy@auspex.com).
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
 */
 
 /*LINTLIBRARY*/
@@ -22,6 +19,7 @@
 #include <sys/stat.h>
 
 #include <fcntl.h>
+#include <float.h>     /* for FLT_MAX and DBL_MAX */
 #include <time.h>
 #include <pthread.h>
 #include "private.h"
 #define        _MUTEX_LOCK(x)          if (__isthreaded) _pthread_mutex_lock(x)
 #define        _MUTEX_UNLOCK(x)        if (__isthreaded) _pthread_mutex_unlock(x)
 
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN        16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR       '_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
 /*
 ** SunOS 4.1.1 headers lack O_BINARY.
 */
 **     5.      They might reference tm.TM_ZONE after calling offtime.
 ** What's best to do in the above cases is open to debate;
 ** for now, we just set things up so that in any of the five cases
-** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
+** WILDABBR is used. Another possibility: initialize tzname[0] to the
 ** string "tzname[0] used before set", and similarly for the other cases.
-** And another:  initialize tzname[0] to "ERA", with an explanation in the
+** And another: initialize tzname[0] to "ERA", with an explanation in the
 ** manual page of what this "time zone abbreviation" means (doing this so
 ** that tzname[0] has the "normal" length of three characters).
 */
 #define WILDABBR       "   "
 #endif /* !defined WILDABBR */
 
-static char            wildabbr[] = "WILDABBR";
+static char            wildabbr[] = WILDABBR;
 
 static const char      gmt[] = "GMT";
 
+/*
+** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+** We default to US rules as of 1999-08-17.
+** POSIX 1003.1 section 8.1.1 says that the default DST rules are
+** implementation dependent; for historical reasons, US rules are a
+** common default.
+*/
+#ifndef TZDEFRULESTRING
+#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+#endif /* !defined TZDEFDST */
+
 struct ttinfo {                                /* time type information */
-       long            tt_gmtoff;      /* GMT offset in seconds */
+       long            tt_gmtoff;      /* UTC offset in seconds */
        int             tt_isdst;       /* used to set tm_isdst */
        int             tt_abbrind;     /* abbreviation list index */
        int             tt_ttisstd;     /* TRUE if transition is std time */
-       int             tt_ttisgmt;     /* TRUE if transition is GMT */
+       int             tt_ttisgmt;     /* TRUE if transition is UTC */
 };
 
 struct lsinfo {                                /* leap second information */
@@ -99,6 +121,8 @@ struct state {
        int             timecnt;
        int             typecnt;
        int             charcnt;
+       int             goback;
+       int             goahead;
        time_t          ats[TZ_MAX_TIMES];
        unsigned char   types[TZ_MAX_TIMES];
        struct ttinfo   ttis[TZ_MAX_TYPES];
@@ -124,33 +148,49 @@ struct rule {
 */
 
 static long            detzcode(const char * codep);
+static time_t          detzcode64(const char * codep);
+static int             differ_by_repeat(time_t t1, time_t t0);
 static const char *    getzname(const char * strp);
-static const char *    getnum(const char * strp, int * nump, int min, int max);
+static const char *    getqzname(const char * strp, const int delim);
+static const char *    getnum(const char * strp, int * nump, int min,
+                               int max);
 static const char *    getsecs(const char * strp, long * secsp);
 static const char *    getoffset(const char * strp, long * offsetp);
 static const char *    getrule(const char * strp, struct rule * rulep);
 static void            gmtload(struct state * sp);
-static void            gmtsub(const time_t * timep, long offset,
+static struct tm *     gmtsub(const time_t * timep, long offset,
                                struct tm * tmp);
-static void            localsub(const time_t * timep, long offset,
+static struct tm *     localsub(const time_t * timep, long offset,
                                struct tm * tmp);
 static int             increment_overflow(int * number, int delta);
+static int             leaps_thru_end_of(int y);
+static int             long_increment_overflow(long * number, int delta);
+static int             long_normalize_overflow(long * tensptr,
+                               int * unitsptr, int base);
 static int             normalize_overflow(int * tensptr, int * unitsptr,
                                int base);
 static void            settzname(void);
 static time_t          time1(struct tm * tmp,
-                               void(*funcp)(const time_t *, long, struct tm *),
+                               struct tm * (*funcp)(const time_t *,
+                               long, struct tm *),
                                long offset);
 static time_t          time2(struct tm *tmp,
-                               void(*funcp)(const time_t *, long, struct tm *),
+                               struct tm * (*funcp)(const time_t *,
+                               long, struct tm*),
                                long offset, int * okayp);
-static void            timesub(const time_t * timep, long offset,
+static time_t          time2sub(struct tm *tmp,
+                               struct tm * (*funcp)(const time_t *,
+                               long, struct tm*),
+                               long offset, int * okayp, int do_norm_secs);
+static struct tm *     timesub(const time_t * timep, long offset,
                                const struct state * sp, struct tm * tmp);
 static int             tmcomp(const struct tm * atmp,
                                const struct tm * btmp);
 static time_t          transtime(time_t janfirst, int year,
                                const struct rule * rulep, long offset);
-static int             tzload(const char * name, struct state * sp);
+static int             typesequiv(const struct state * sp, int a, int b);
+static int             tzload(const char * name, struct state * sp,
+                               int doextend);
 static int             tzparse(const char * name, struct state * sp,
                                int lastditch);
 
@@ -186,7 +226,7 @@ char *                      tzname[2] = {
 **     Except for the strftime function, these functions [asctime,
 **     ctime, gmtime, localtime] return values in one of two static
 **     objects: a broken-down time structure and an array of char.
-** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
+** Thanks to Paul Eggert for noting this.
 */
 
 static struct tm       tm;
@@ -206,16 +246,28 @@ detzcode(const char * const codep)
        long    result;
        int     i;
 
-       result = (codep[0] & 0x80) ? ~0L : 0L;
+       result = (codep[0] & 0x80) ? ~0L : 0;
        for (i = 0; i < 4; ++i)
                result = (result << 8) | (codep[i] & 0xff);
        return result;
 }
 
+static time_t
+detzcode64(const char * const codep)
+{
+       time_t  result;
+       int     i;
+
+       result = (codep[0] & 0x80) ?  (~(int_fast64_t) 0) : 0;
+       for (i = 0; i < 8; ++i)
+               result = result * 256 + (codep[i] & 0xff);
+       return result;
+}
+
 static void
 settzname(void)
 {
-       struct state *  sp = lclptr;
+       struct state * const    sp = lclptr;
        int                     i;
 
        tzname[0] = wildabbr;
@@ -260,14 +312,49 @@ settzname(void)
                tzname[ttisp->tt_isdst] =
                        &sp->chars[ttisp->tt_abbrind];
        }
+       /*
+       ** Finally, scrub the abbreviations.
+       ** First, replace bogus characters.
+       */
+       for (i = 0; i < sp->charcnt; ++i)
+               if (strchr(TZ_ABBR_CHAR_SET, sp->chars[i]) == NULL)
+                       sp->chars[i] = TZ_ABBR_ERR_CHAR;
+       /*
+       ** Second, truncate long abbreviations.
+       */
+       for (i = 0; i < sp->typecnt; ++i) {
+               const struct ttinfo * const     ttisp = &sp->ttis[i];
+               char *                          cp = &sp->chars[ttisp->tt_abbrind];
+
+               if (strlen(cp) > TZ_ABBR_MAX_LEN &&
+                       strcmp(cp, GRANDPARENTED) != 0)
+                               *(cp + TZ_ABBR_MAX_LEN) = '\0';
+       }
+}
+
+static int
+differ_by_repeat(const time_t t1, const time_t t0)
+{
+       if (TYPE_INTEGRAL(time_t) &&
+               TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+                       return 0;
+       return t1 - t0 == SECSPERREPEAT;
 }
 
 static int
-tzload(const char *name, struct state * const sp)
+tzload(const char *name, struct state * const sp, const int doextend)
 {
-       const char *    p;
-       int             i;
-       int             fid;
+       const char *            p;
+       int                     i;
+       int                     fid;
+       int                     stored;
+       int                     nread;
+       union {
+               struct tzhead   tzhead;
+               char            buf[2 * sizeof(struct tzhead) +
+                                       2 * sizeof *sp +
+                                       4 * TZ_MAX_TIMES];
+       } u;
 
        /* XXX The following is from OpenBSD, and I'm not sure it is correct */
        if (name != NULL && issetugid() != 0)
@@ -307,7 +394,7 @@ tzload(const char *name, struct state * const sp)
                        name = fullname;
                }
                if (doaccess && access(name, R_OK) != 0)
-                       return -1;
+                       return -1;
                if ((fid = _open(name, OPEN_MODE)) == -1)
                        return -1;
                if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) {
@@ -315,29 +402,20 @@ tzload(const char *name, struct state * const sp)
                        return -1;
                }
        }
-       {
-               struct tzhead * tzhp;
-               char            buf[sizeof *sp + sizeof *tzhp];
+       nread = read(fid, u.buf, sizeof u.buf);
+       if (close(fid) < 0 || nread <= 0)
+               return -1;
+       for (stored = 4; stored <= 8; stored *= 2) {
                int             ttisstdcnt;
                int             ttisgmtcnt;
 
-               i = _read(fid, buf, sizeof buf);
-               if (_close(fid) != 0)
-                       return -1;
-               p = buf;
-               p += (sizeof tzhp->tzh_magic) + (sizeof tzhp->tzh_reserved);
-               ttisstdcnt = (int) detzcode(p);
-               p += 4;
-               ttisgmtcnt = (int) detzcode(p);
-               p += 4;
-               sp->leapcnt = (int) detzcode(p);
-               p += 4;
-               sp->timecnt = (int) detzcode(p);
-               p += 4;
-               sp->typecnt = (int) detzcode(p);
-               p += 4;
-               sp->charcnt = (int) detzcode(p);
-               p += 4;
+               ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+               ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+               sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+               sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+               sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+               sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+               p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
                if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
                        sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
                        sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
@@ -345,17 +423,19 @@ tzload(const char *name, struct state * const sp)
                        (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
                        (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
                                return -1;
-               if (i - (p - buf) < sp->timecnt * 4 +   /* ats */
+               if (nread - (p - u.buf) <
+                       sp->timecnt * stored +          /* ats */
                        sp->timecnt +                   /* types */
-                       sp->typecnt * (4 + 2) +         /* ttinfos */
+                       sp->typecnt * 6 +               /* ttinfos */
                        sp->charcnt +                   /* chars */
-                       sp->leapcnt * (4 + 4) +         /* lsinfos */
+                       sp->leapcnt * (stored + 4) +    /* lsinfos */
                        ttisstdcnt +                    /* ttisstds */
                        ttisgmtcnt)                     /* ttisgmts */
                                return -1;
                for (i = 0; i < sp->timecnt; ++i) {
-                       sp->ats[i] = detzcode(p);
-                       p += 4;
+                       sp->ats[i] = (stored == 4) ?
+                               detzcode(p) : detzcode64(p);
+                       p += stored;
                }
                for (i = 0; i < sp->timecnt; ++i) {
                        sp->types[i] = (unsigned char) *p++;
@@ -383,8 +463,9 @@ tzload(const char *name, struct state * const sp)
                        struct lsinfo * lsisp;
 
                        lsisp = &sp->lsis[i];
-                       lsisp->ls_trans = detzcode(p);
-                       p += 4;
+                       lsisp->ls_trans = (stored == 4) ?
+                               detzcode(p) : detzcode64(p);
+                       p += stored;
                        lsisp->ls_corr = detzcode(p);
                        p += 4;
                }
@@ -414,10 +495,124 @@ tzload(const char *name, struct state * const sp)
                                                return -1;
                        }
                }
+               /*
+               ** Out-of-sort ats should mean we're running on a
+               ** signed time_t system but using a data file with
+               ** unsigned values (or vice versa).
+               */
+               for (i = 0; i < sp->timecnt - 2; ++i)
+                       if (sp->ats[i] > sp->ats[i + 1]) {
+                               ++i;
+                               if (TYPE_SIGNED(time_t)) {
+                                       /*
+                                       ** Ignore the end (easy).
+                                       */
+                                       sp->timecnt = i;
+                               } else {
+                                       /*
+                                       ** Ignore the beginning (harder).
+                                       */
+                                       int     j;
+
+                                       for (j = 0; j + i < sp->timecnt; ++j) {
+                                               sp->ats[j] = sp->ats[j + i];
+                                               sp->types[j] = sp->types[j + i];
+                                       }
+                                       sp->timecnt = j;
+                               }
+                               break;
+                       }
+               /*
+               ** If this is an old file, we're done.
+               */
+               if (u.tzhead.tzh_version[0] == '\0')
+                       break;
+               nread -= p - u.buf;
+               for (i = 0; i < nread; ++i)
+                       u.buf[i] = p[i];
+               /*
+               ** If this is a narrow integer time_t system, we're done.
+               */
+               if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+                       break;
+       }
+       if (doextend && nread > 2 &&
+               u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+               sp->typecnt + 2 <= TZ_MAX_TYPES) {
+                       struct state    ts;
+                       int             result;
+
+                       u.buf[nread - 1] = '\0';
+                       result = tzparse(&u.buf[1], &ts, FALSE);
+                       if (result == 0 && ts.typecnt == 2 &&
+                               sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+                                       for (i = 0; i < 2; ++i)
+                                               ts.ttis[i].tt_abbrind +=
+                                                       sp->charcnt;
+                                       for (i = 0; i < ts.charcnt; ++i)
+                                               sp->chars[sp->charcnt++] =
+                                                       ts.chars[i];
+                                       i = 0;
+                                       while (i < ts.timecnt &&
+                                               ts.ats[i] <=
+                                               sp->ats[sp->timecnt - 1])
+                                                       ++i;
+                                       while (i < ts.timecnt &&
+                                           sp->timecnt < TZ_MAX_TIMES) {
+                                               sp->ats[sp->timecnt] =
+                                                       ts.ats[i];
+                                               sp->types[sp->timecnt] =
+                                                       sp->typecnt +
+                                                       ts.types[i];
+                                               ++sp->timecnt;
+                                               ++i;
+                                       }
+                                       sp->ttis[sp->typecnt++] = ts.ttis[0];
+                                       sp->ttis[sp->typecnt++] = ts.ttis[1];
+                       }
+       }
+       sp->goback = sp->goahead = FALSE;
+       if (sp->timecnt > 1) {
+               for (i = 1; i < sp->timecnt; ++i)
+                       if (typesequiv(sp, sp->types[i], sp->types[0]) &&
+                               differ_by_repeat(sp->ats[i], sp->ats[0])) {
+                                       sp->goback = TRUE;
+                                       break;
+                               }
+               for (i = sp->timecnt - 2; i >= 0; --i)
+                       if (typesequiv(sp, sp->types[sp->timecnt - 1],
+                               sp->types[i]) &&
+                               differ_by_repeat(sp->ats[sp->timecnt - 1],
+                               sp->ats[i])) {
+                                       sp->goahead = TRUE;
+                                       break;
+               }
        }
        return 0;
 }
 
+static int
+typesequiv(const struct state * const sp, const int a, const int b)
+{
+       int     result;
+
+       if (sp == NULL ||
+               a < 0 || a >= sp->typecnt ||
+               b < 0 || b >= sp->typecnt)
+                       result = FALSE;
+       else {
+               const struct ttinfo *   ap = &sp->ttis[a];
+               const struct ttinfo *   bp = &sp->ttis[b];
+               result = ap->tt_gmtoff == bp->tt_gmtoff &&
+                       ap->tt_isdst == bp->tt_isdst &&
+                       ap->tt_ttisstd == bp->tt_ttisstd &&
+                       ap->tt_ttisgmt == bp->tt_ttisgmt &&
+                       strcmp(&sp->chars[ap->tt_abbrind],
+                       &sp->chars[bp->tt_abbrind]) == 0;
+       }
+       return result;
+}
+
 static const int       mon_lengths[2][MONSPERYEAR] = {
        { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
        { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
@@ -429,7 +624,7 @@ static const int    year_lengths[2] = {
 
 /*
 ** Given a pointer into a time zone string, scan until a character that is not
-** a valid character in a zone name is found.  Return a pointer to that
+** a valid character in a zone name is found. Return a pointer to that
 ** character.
 */
 
@@ -444,6 +639,25 @@ getzname(const char *strp)
        return strp;
 }
 
+/*
+** Given a pointer into an extended time zone string, scan until the ending
+** delimiter of the zone name is located. Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char *
+getqzname(const char *strp, const int delim)
+{
+       int     c;
+
+       while ((c = *strp) != '\0' && c != delim)
+               ++strp;
+       return strp;
+}
+
 /*
 ** Given a pointer into a time zone string, extract a number from that string.
 ** Check that the number is within a specified range; if it is not, return
@@ -503,7 +717,7 @@ getsecs(const char *strp, long * const secsp)
                *secsp += num * SECSPERMIN;
                if (*strp == ':') {
                        ++strp;
-                       /* `SECSPERMIN' allows for leap seconds.  */
+                       /* `SECSPERMIN' allows for leap seconds. */
                        strp = getnum(strp, &num, 0, SECSPERMIN);
                        if (strp == NULL)
                                return NULL;
@@ -540,7 +754,7 @@ getoffset(const char *strp, long * const offsetp)
 
 /*
 ** Given a pointer into a time zone string, extract a rule in the form
-** date[/time].  See POSIX section 8 for the format of "date" and "time".
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
 ** If a valid rule is not found, return NULL.
 ** Otherwise, return a pointer to the first character not part of the rule.
 */
@@ -592,8 +806,8 @@ getrule(const char *strp, struct rule * const rulep)
 }
 
 /*
-** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
-** year, a rule, and the offset from GMT at the time that rule takes effect,
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
 ** calculate the Epoch-relative time that rule takes effect.
 */
 
@@ -654,7 +868,7 @@ transtime(const time_t janfirst, const int year,
                        dow += DAYSPERWEEK;
 
                /*
-               ** "dow" is the day-of-week of the first day of the month.  Get
+               ** "dow" is the day-of-week of the first day of the month. Get
                ** the day-of-month (zero-origin) of the first "dow" day of the
                ** month.
                */
@@ -676,10 +890,10 @@ transtime(const time_t janfirst, const int year,
        }
 
        /*
-       ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
-       ** question.  To get the Epoch-relative time of the specified local
+       ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+       ** question. To get the Epoch-relative time of the specified local
        ** time on that day, add the transition time and the current offset
-       ** from GMT.
+       ** from UTC.
        */
        return value + rulep->r_time + offset;
 }
@@ -712,32 +926,47 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
                        stdlen = (sizeof sp->chars) - 1;
                stdoffset = 0;
        } else {
-               name = getzname(name);
-               stdlen = name - stdname;
-               if (stdlen < 3)
-                       return -1;
-               if (*name == '\0')
-                       return -1;      /* was "stdoffset = 0;" */
-               else {
-                       name = getoffset(name, &stdoffset);
-                       if (name == NULL)
-                               return -1;
+               if (*name == '<') {
+                       name++;
+                       stdname = name;
+                       name = getqzname(name, '>');
+                       if (*name != '>')
+                               return (-1);
+                       stdlen = name - stdname;
+                       name++;
+               } else {
+                       name = getzname(name);
+                       stdlen = name - stdname;
                }
+               if (*name == '\0')
+                       return -1;
+               name = getoffset(name, &stdoffset);
+               if (name == NULL)
+                       return -1;
        }
-       load_result = tzload(TZDEFRULES, sp);
+       load_result = tzload(TZDEFRULES, sp, FALSE);
        if (load_result != 0)
                sp->leapcnt = 0;                /* so, we're off a little */
        if (*name != '\0') {
-               dstname = name;
-               name = getzname(name);
-               dstlen = name - dstname;        /* length of DST zone name */
-               if (dstlen < 3)
-                       return -1;
+               if (*name == '<') {
+                       dstname = ++name;
+                       name = getqzname(name, '>');
+                       if (*name != '>')
+                               return -1;
+                       dstlen = name - dstname;
+                       name++;
+               } else {
+                       dstname = name;
+                       name = getzname(name);
+                       dstlen = name - dstname; /* length of DST zone name */
+               }
                if (*name != '\0' && *name != ',' && *name != ';') {
                        name = getoffset(name, &dstoffset);
                        if (name == NULL)
                                return -1;
                } else  dstoffset = stdoffset - SECSPERHOUR;
+               if (*name == '\0' && load_result != 0)
+                       name = TZDEFRULESTRING;
                if (*name == ',' || *name == ';') {
                        struct rule     start;
                        struct rule     end;
@@ -757,11 +986,8 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
                                return -1;
                        sp->typecnt = 2;        /* standard time and DST */
                        /*
-                       ** Two transitions per year, from EPOCH_YEAR to 2037.
+                       ** Two transitions per year, from EPOCH_YEAR forward.
                        */
-                       sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
-                       if (sp->timecnt > TZ_MAX_TIMES)
-                               return -1;
                        sp->ttis[0].tt_gmtoff = -dstoffset;
                        sp->ttis[0].tt_isdst = 1;
                        sp->ttis[0].tt_abbrind = stdlen + 1;
@@ -771,7 +997,12 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
                        atp = sp->ats;
                        typep = sp->types;
                        janfirst = 0;
-                       for (year = EPOCH_YEAR; year <= 2037; ++year) {
+                       sp->timecnt = 0;
+                       for (year = EPOCH_YEAR;
+                           sp->timecnt + 2 <= TZ_MAX_TIMES;
+                           ++year) {
+                               time_t  newfirst;
+
                                starttime = transtime(janfirst, year, &start,
                                        stdoffset);
                                endtime = transtime(janfirst, year, &end,
@@ -787,8 +1018,13 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
                                        *atp++ = endtime;
                                        *typep++ = 1;   /* DST ends */
                                }
-                               janfirst += year_lengths[isleap(year)] *
+                               sp->timecnt += 2;
+                               newfirst = janfirst;
+                               newfirst += year_lengths[isleap(year)] *
                                        SECSPERDAY;
+                               if (newfirst <= janfirst)
+                                       break;
+                               janfirst = newfirst;
                        }
                } else {
                        long    theirstdoffset;
@@ -800,8 +1036,6 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
 
                        if (*name != '\0')
                                return -1;
-                       if (load_result != 0)
-                               return -1;
                        /*
                        ** Initial values of theirstdoffset and theirdstoffset.
                        */
@@ -875,6 +1109,7 @@ tzparse(const char *name, struct state * const sp, 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;
@@ -887,7 +1122,7 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
        sp->charcnt = stdlen + 1;
        if (dstlen != 0)
                sp->charcnt += dstlen + 1;
-       if (sp->charcnt > sizeof sp->chars)
+       if ((size_t) sp->charcnt > sizeof sp->chars)
                return -1;
        cp = sp->chars;
        strncpy(cp, stdname, stdlen);
@@ -903,7 +1138,7 @@ tzparse(const char *name, struct state * const sp, const int lastditch)
 static void
 gmtload(struct state * const sp)
 {
-       if (tzload(gmt, sp) != 0)
+       if (tzload(gmt, sp, TRUE) != 0)
                tzparse(gmt, sp, TRUE);
 }
 
@@ -923,7 +1158,7 @@ tzsetwall_basic(void)
                }
        }
 #endif /* defined ALL_STATE */
-       if (tzload((char *) NULL, lclptr) != 0)
+       if (tzload((char *) NULL, lclptr, TRUE) != 0)
                gmtload(lclptr);
        settzname();
 }
@@ -947,9 +1182,9 @@ tzset_basic(void)
                return;
        }
 
-       if (lcl_is_set > 0  &&  strcmp(lcl_TZname, name) == 0)
+       if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0)
                return;
-       lcl_is_set = (strlen(name) < sizeof(lcl_TZname));
+       lcl_is_set = strlen(name) < sizeof lcl_TZname;
        if (lcl_is_set)
                strcpy(lcl_TZname, name);
 
@@ -968,10 +1203,12 @@ tzset_basic(void)
                */
                lclptr->leapcnt = 0;            /* so, we're off a little */
                lclptr->timecnt = 0;
+               lclptr->typecnt = 0;
+               lclptr->ttis[0].tt_isdst = 0;
                lclptr->ttis[0].tt_gmtoff = 0;
                lclptr->ttis[0].tt_abbrind = 0;
                strcpy(lclptr->chars, gmt);
-       } else if (tzload(name, lclptr) != 0)
+       } else if (tzload(name, lclptr, TRUE) != 0)
                if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
                        gmtload(lclptr);
        settzname();
@@ -988,29 +1225,67 @@ tzset(void)
 /*
 ** The easy way to behave "as if no library function calls" localtime
 ** is to not call it--so we drop its guts into "localsub", which can be
-** freely called.  (And no, the PANS doesn't require the above behavior--
+** freely called. (And no, the PANS doesn't require the above behavior--
 ** but it *is* desirable.)
 **
 ** The unused offset argument is for the benefit of mktime variants.
 */
 
 /*ARGSUSED*/
-static void
+static struct tm *
 localsub(const time_t * const timep, const long offset __unused,
         struct tm * const tmp)
 {
        struct state *          sp;
        const struct ttinfo *   ttisp;
        int                     i;
-       const time_t                    t = *timep;
+       struct tm *             result;
+       const time_t            t = *timep;
 
        sp = lclptr;
 #ifdef ALL_STATE
-       if (sp == NULL) {
-               gmtsub(timep, offset, tmp);
-               return;
-       }
+       if (sp == NULL)
+               return gmtsub(timep, offset, tmp);
 #endif /* defined ALL_STATE */
+       if ((sp->goback && t < sp->ats[0]) ||
+               (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+                       time_t          newt = t;
+                       time_t          seconds;
+                       time_t          tcycles;
+                       int_fast64_t    icycles;
+
+                       if (t < sp->ats[0])
+                               seconds = sp->ats[0] - t;
+                       else    seconds = t - sp->ats[sp->timecnt - 1];
+                       --seconds;
+                       tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+                       ++tcycles;
+                       icycles = tcycles;
+                       if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+                               return NULL;
+                       seconds = icycles;
+                       seconds *= YEARSPERREPEAT;
+                       seconds *= AVGSECSPERYEAR;
+                       if (t < sp->ats[0])
+                               newt += seconds;
+                       else    newt -= seconds;
+                       if (newt < sp->ats[0] ||
+                               newt > sp->ats[sp->timecnt - 1])
+                                       return NULL;    /* "cannot happen" */
+                       result = localsub(&newt, offset, tmp);
+                       if (result == tmp) {
+                               time_t  newy;
+
+                               newy = tmp->tm_year;
+                               if (t < sp->ats[0])
+                                       newy -= icycles * YEARSPERREPEAT;
+                               else    newy += icycles * YEARSPERREPEAT;
+                               tmp->tm_year = newy;
+                               if (tmp->tm_year != newy)
+                                       return NULL;
+                       }
+                       return result;
+       }
        if (sp->timecnt == 0 || t < sp->ats[0]) {
                i = 0;
                while (sp->ttis[i].tt_isdst)
@@ -1019,10 +1294,17 @@ localsub(const time_t * const timep, const long offset __unused,
                                break;
                        }
        } else {
-               for (i = 1; i < sp->timecnt; ++i)
-                       if (t < sp->ats[i])
-                               break;
-               i = sp->types[i - 1];
+               int     lo = 1;
+               int     hi = sp->timecnt;
+
+               while (lo < hi) {
+                       int     mid = (lo + hi) >> 1;
+
+                       if (t < sp->ats[mid])
+                               hi = mid;
+                       else    lo = mid + 1;
+               }
+               i = (int) sp->types[lo - 1];
        }
        ttisp = &sp->ttis[i];
        /*
@@ -1031,12 +1313,13 @@ localsub(const time_t * const timep, const long offset __unused,
        **      t += ttisp->tt_gmtoff;
        **      timesub(&t, 0L, sp, tmp);
        */
-       timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+       result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
        tmp->tm_isdst = ttisp->tt_isdst;
        tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
 #ifdef TM_ZONE
        tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
 #endif /* defined TM_ZONE */
+       return result;
 }
 
 struct tm *
@@ -1088,9 +1371,11 @@ localtime(const time_t * const timep)
 ** gmtsub is to gmtime as localsub is to localtime.
 */
 
-static void
+static struct tm *
 gmtsub(const time_t * const timep, const long offset, struct tm * const tmp)
 {
+       struct tm *     result;
+
        _MUTEX_LOCK(&gmt_mutex);
        if (!gmt_is_set) {
                gmt_is_set = TRUE;
@@ -1101,11 +1386,11 @@ gmtsub(const time_t * const timep, const long offset, struct tm * const tmp)
                        gmtload(gmtptr);
        }
        _MUTEX_UNLOCK(&gmt_mutex);
-       timesub(timep, offset, gmtptr, tmp);
+       result = timesub(timep, offset, gmtptr, tmp);
 #ifdef TM_ZONE
        /*
        ** Could get fancy here and deliver something such as
-       ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
+       ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
        ** but this is no time for a treasure hunt.
        */
        if (offset != 0)
@@ -1121,6 +1406,7 @@ gmtsub(const time_t * const timep, const long offset, struct tm * const tmp)
 #endif /* State Farm */
        }
 #endif /* defined TM_ZONE */
+       return result;
 }
 
 struct tm *
@@ -1136,9 +1422,9 @@ gmtime(const time_t * const timep)
                        if (_pthread_key_create(&gmtime_key, free) < 0) {
                                _pthread_mutex_unlock(&gmtime_mutex);
                                return(NULL);
+                       }
                }
-       }
-       _pthread_mutex_unlock(&gmtime_mutex);
+               _pthread_mutex_unlock(&gmtime_mutex);
                /*
                 * Changed to follow POSIX.1 threads standard, which
                 * is what BSD currently has.
@@ -1150,20 +1436,16 @@ gmtime(const time_t * const timep)
                        }
                        _pthread_setspecific(gmtime_key, p_tm);
                }
-               gmtsub(timep, 0L, p_tm);
-               return(p_tm);
-       }
-       else {
-               gmtsub(timep, 0L, &tm);
-               return(&tm);
+               return gmtsub(timep, 0L, p_tm);
+       } else {
+               return gmtsub(timep, 0L, &tm);
        }
 }
 
 struct tm *
-gmtime_r(const time_t * timep, struct tm * tm_p)
+gmtime_r(const time_t * timep, struct tm * tmp)
 {
-       gmtsub(timep, 0L, tm_p);
-       return(tm_p);
+       return gmtsub(timep, 0L, tmp);
 }
 
 #ifdef STD_INSPIRED
@@ -1171,18 +1453,30 @@ gmtime_r(const time_t * timep, struct tm * tm_p)
 struct tm *
 offtime(const time_t * const timep, const long offset)
 {
-       gmtsub(timep, offset, &tm);
-       return &tm;
+       return gmtsub(timep, offset, &tm);
 }
 
 #endif /* defined STD_INSPIRED */
 
-static void
+/*
+** Return the number of leap years through the end of the given year
+** where, to make the math easy, the answer for year zero is defined as zero.
+*/
+
+static int
+leaps_thru_end_of(const int y)
+{
+       return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+               -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+
+static struct tm *
 timesub(const time_t * const timep, const long offset,
        const struct state * const sp, struct tm * const tmp)
 {
        const struct lsinfo *   lp;
-       long                    days;
+       time_t                  tdays;
+       int                     idays;  /* unsigned would be so 2003 */
        long                    rem;
        int                     y;
        int                     yleap;
@@ -1219,60 +1513,93 @@ timesub(const time_t * const timep, const long offset,
                        break;
                }
        }
-       days = *timep / SECSPERDAY;
-       rem = *timep % SECSPERDAY;
-#ifdef mc68k
-       if (*timep == 0x80000000) {
-               /*
-               ** A 3B1 muffs the division on the most negative number.
-               */
-               days = -24855;
-               rem = -11648;
+       y = EPOCH_YEAR;
+       tdays = *timep / SECSPERDAY;
+       rem = *timep - tdays * SECSPERDAY;
+       while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+               int     newy;
+               time_t  tdelta;
+               int     idelta;
+               int     leapdays;
+
+               tdelta = tdays / DAYSPERLYEAR;
+               idelta = tdelta;
+               if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
+                       return NULL;
+               if (idelta == 0)
+                       idelta = (tdays < 0) ? -1 : 1;
+               newy = y;
+               if (increment_overflow(&newy, idelta))
+                       return NULL;
+               leapdays = leaps_thru_end_of(newy - 1) -
+                       leaps_thru_end_of(y - 1);
+               tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+               tdays -= leapdays;
+               y = newy;
        }
-#endif /* defined mc68k */
-       rem += (offset - corr);
+       {
+               long    seconds;
+
+               seconds = tdays * SECSPERDAY + 0.5;
+               tdays = seconds / SECSPERDAY;
+               rem += seconds - tdays * SECSPERDAY;
+       }
+       /*
+       ** Given the range, we can now fearlessly cast...
+       */
+       idays = tdays;
+       rem += offset - corr;
        while (rem < 0) {
                rem += SECSPERDAY;
-               --days;
+               --idays;
        }
        while (rem >= SECSPERDAY) {
                rem -= SECSPERDAY;
-               ++days;
+               ++idays;
        }
+       while (idays < 0) {
+               if (increment_overflow(&y, -1))
+                       return NULL;
+               idays += year_lengths[isleap(y)];
+       }
+       while (idays >= year_lengths[isleap(y)]) {
+               idays -= year_lengths[isleap(y)];
+               if (increment_overflow(&y, 1))
+                       return NULL;
+       }
+       tmp->tm_year = y;
+       if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+               return NULL;
+       tmp->tm_yday = idays;
+       /*
+       ** The "extra" mods below avoid overflow problems.
+       */
+       tmp->tm_wday = EPOCH_WDAY +
+               ((y - EPOCH_YEAR) % DAYSPERWEEK) *
+               (DAYSPERNYEAR % DAYSPERWEEK) +
+               leaps_thru_end_of(y - 1) -
+               leaps_thru_end_of(EPOCH_YEAR - 1) +
+               idays;
+       tmp->tm_wday %= DAYSPERWEEK;
+       if (tmp->tm_wday < 0)
+               tmp->tm_wday += DAYSPERWEEK;
        tmp->tm_hour = (int) (rem / SECSPERHOUR);
-       rem = rem % SECSPERHOUR;
+       rem %= SECSPERHOUR;
        tmp->tm_min = (int) (rem / SECSPERMIN);
        /*
        ** A positive leap second requires a special
-       ** representation.  This uses "... ??:59:60" et seq.
+       ** representation. This uses "... ??:59:60" et seq.
        */
        tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
-       tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
-       if (tmp->tm_wday < 0)
-               tmp->tm_wday += DAYSPERWEEK;
-       y = EPOCH_YEAR;
-#define LEAPS_THRU_END_OF(y)   ((y) / 4 - (y) / 100 + (y) / 400)
-       while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
-               int     newy;
-
-               newy = y + days / DAYSPERNYEAR;
-               if (days < 0)
-                       --newy;
-               days -= (newy - y) * DAYSPERNYEAR +
-                       LEAPS_THRU_END_OF(newy - 1) -
-                       LEAPS_THRU_END_OF(y - 1);
-               y = newy;
-       }
-       tmp->tm_year = y - TM_YEAR_BASE;
-       tmp->tm_yday = (int) days;
-       ip = mon_lengths[yleap];
-       for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
-               days = days - (long) ip[tmp->tm_mon];
-       tmp->tm_mday = (int) (days + 1);
+       ip = mon_lengths[isleap(y)];
+       for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+               idays -= ip[tmp->tm_mon];
+       tmp->tm_mday = (int) (idays + 1);
        tmp->tm_isdst = 0;
 #ifdef TM_GMTOFF
        tmp->TM_GMTOFF = offset;
 #endif /* defined TM_GMTOFF */
+       return tmp;
 }
 
 char *
@@ -1280,8 +1607,8 @@ ctime(const time_t * const timep)
 {
 /*
 ** Section 4.12.3.2 of X3.159-1989 requires that
-**     The ctime funciton converts the calendar time pointed to by timer
-**     to local time in the form of a string.  It is equivalent to
+**     The ctime function converts the calendar time pointed to by timer
+**     to local time in the form of a string. It is equivalent to
 **             asctime(localtime(timer))
 */
        return asctime(localtime(timep));
@@ -1290,16 +1617,15 @@ ctime(const time_t * const timep)
 char *
 ctime_r(const time_t * const timep, char *buf)
 {
-        struct tm tm1;
-       return asctime_r(localtime_r(timep, &tm1), buf);
+        struct tm      mytm;
+       return asctime_r(localtime_r(timep, &mytm), buf);
 }
 
 /*
 ** 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.
-**     [kridle@xinet.com as of 1996-01-16.]
-**     It does a binary search of the time_t space.  Since time_t's are
+**     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).
 */
@@ -1309,7 +1635,7 @@ ctime_r(const time_t * const timep, char *buf)
 #endif /* !defined WRONG */
 
 /*
-** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
+** Simplified normalize logic courtesy Paul Eggert.
 */
 
 static int
@@ -1322,6 +1648,16 @@ increment_overflow(int *number, int delta)
        return (*number < number0) != (delta < 0);
 }
 
+static int
+long_increment_overflow(long *number, int delta)
+{
+       long    number0;
+
+       number0 = *number;
+       *number += delta;
+       return (*number < number0) != (delta < 0);
+}
+
 static int
 normalize_overflow(int * const tensptr, int * const unitsptr, const int base)
 {
@@ -1334,6 +1670,19 @@ normalize_overflow(int * const tensptr, int * const unitsptr, const int base)
        return increment_overflow(tensptr, tensdelta);
 }
 
+static int
+long_normalize_overflow(long * const tensptr, int * const unitsptr,
+                       const int base)
+{
+       int     tensdelta;
+
+       tensdelta = (*unitsptr >= 0) ?
+               (*unitsptr / base) :
+               (-1 - (-1 - *unitsptr) / base);
+       *unitsptr -= tensdelta * base;
+       return long_increment_overflow(tensptr, tensdelta);
+}
+
 static int
 tmcomp(const struct tm * const atmp, const struct tm * const btmp)
 {
@@ -1349,59 +1698,73 @@ tmcomp(const struct tm * const atmp, const struct tm * const btmp)
 }
 
 static time_t
-time2(struct tm * const tmp,
-      void (* const funcp)(const time_t *, long, struct tm *),
-      const long offset, int * const okayp)
+time2sub(struct tm * const tmp,
+      struct tm * (* const funcp)(const time_t *, long, struct tm *),
+      const long offset, int * const okayp, const int do_norm_secs)
 {
        const struct state *    sp;
        int                     dir;
-       int                     bits;
-       int                     i, j ;
+       int                     i, j;
        int                     saved_seconds;
-       time_t                          newt;
-       time_t                          t;
-       struct tm                       yourtm, mytm;
+       long                    li;
+       time_t                  lo;
+       time_t                  hi;
+       long                    y;
+       time_t                  newt;
+       time_t                  t;
+       struct tm               yourtm, mytm;
 
        *okayp = FALSE;
        yourtm = *tmp;
+       if (do_norm_secs) {
+               if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+                       SECSPERMIN))
+                               return WRONG;
+       }
        if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
                return WRONG;
        if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
                return WRONG;
-       if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
+       y = yourtm.tm_year;
+       if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
                return WRONG;
        /*
-       ** Turn yourtm.tm_year into an actual year number for now.
+       ** Turn y into an actual year number for now.
        ** It is converted back to an offset from TM_YEAR_BASE later.
        */
-       if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
+       if (long_increment_overflow(&y, TM_YEAR_BASE))
                return WRONG;
        while (yourtm.tm_mday <= 0) {
-               if (increment_overflow(&yourtm.tm_year, -1))
+               if (long_increment_overflow(&y, -1))
                        return WRONG;
-               i = yourtm.tm_year + (1 < yourtm.tm_mon);
-               yourtm.tm_mday += year_lengths[isleap(i)];
+               li = y + (1 < yourtm.tm_mon);
+               yourtm.tm_mday += year_lengths[isleap(li)];
        }
        while (yourtm.tm_mday > DAYSPERLYEAR) {
-               i = yourtm.tm_year + (1 < yourtm.tm_mon);
-               yourtm.tm_mday -= year_lengths[isleap(i)];
-               if (increment_overflow(&yourtm.tm_year, 1))
+               li = y + (1 < yourtm.tm_mon);
+               yourtm.tm_mday -= year_lengths[isleap(li)];
+               if (long_increment_overflow(&y, 1))
                        return WRONG;
        }
        for ( ; ; ) {
-               i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
+               i = mon_lengths[isleap(y)][yourtm.tm_mon];
                if (yourtm.tm_mday <= i)
                        break;
                yourtm.tm_mday -= i;
                if (++yourtm.tm_mon >= MONSPERYEAR) {
                        yourtm.tm_mon = 0;
-                       if (increment_overflow(&yourtm.tm_year, 1))
+                       if (long_increment_overflow(&y, 1))
                                return WRONG;
                }
        }
-       if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
+       if (long_increment_overflow(&y, -TM_YEAR_BASE))
+               return WRONG;
+       yourtm.tm_year = y;
+       if (yourtm.tm_year != y)
                return WRONG;
-       if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+       if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+               saved_seconds = 0;
+       else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
                /*
                ** We can't set tm_sec to 0, because that might push the
                ** time below the minimum representable time.
@@ -1419,27 +1782,53 @@ time2(struct tm * const tmp,
                yourtm.tm_sec = 0;
        }
        /*
-       ** Divide the search space in half
-       ** (this works whether time_t is signed or unsigned).
-       */
-       bits = TYPE_BIT(time_t) - 1;
-       /*
-       ** If time_t is signed, then 0 is just above the median,
-       ** assuming two's complement arithmetic.
-       ** If time_t is unsigned, then (1 << bits) is just above the median.
+       ** Do a binary search (this works whatever time_t's type is).
        */
-       t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
+       if (!TYPE_SIGNED(time_t)) {
+               lo = 0;
+               hi = lo - 1;
+       } else if (!TYPE_INTEGRAL(time_t)) {
+               if (sizeof(time_t) > sizeof(float))
+                       hi = (time_t) DBL_MAX;
+               else    hi = (time_t) FLT_MAX;
+               lo = -hi;
+       } else {
+               lo = 1;
+               for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
+                       lo *= 2;
+               hi = -(lo + 1);
+       }
        for ( ; ; ) {
-               (*funcp)(&t, offset, &mytm);
-               dir = tmcomp(&mytm, &yourtm);
+               t = lo / 2 + hi / 2;
+               if (t < lo)
+                       t = lo;
+               else if (t > hi)
+                       t = hi;
+               if ((*funcp)(&t, offset, &mytm) == NULL) {
+                       /*
+                       ** Assume that t is too extreme to be represented in
+                       ** a struct tm; arrange things so that it is less
+                       ** extreme on the next pass.
+                       */
+                       dir = (t > 0) ? 1 : -1;
+               } else  dir = tmcomp(&mytm, &yourtm);
                if (dir != 0) {
-                       if (bits-- < 0)
+                       if (t == lo) {
+                               ++t;
+                               if (t <= lo)
+                                       return WRONG;
+                               ++lo;
+                       } else if (t == hi) {
+                               --t;
+                               if (t >= hi)
+                                       return WRONG;
+                               --hi;
+                       }
+                       if (lo > hi)
                                return WRONG;
-                       if (bits < 0)
-                               --t; /* may be needed if new t is minimal */
-                       else if (dir > 0)
-                               t -= ((time_t) 1) << bits;
-                       else    t += ((time_t) 1) << bits;
+                       if (dir > 0)
+                               hi = t;
+                       else    lo = t;
                        continue;
                }
                if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
@@ -1450,12 +1839,8 @@ time2(struct tm * const tmp,
                ** It's okay to guess wrong since the guess
                ** gets checked.
                */
-               /*
-               ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
-               */
                sp = (const struct state *)
-                       (((void *) funcp == (void *) localsub) ?
-                       lclptr : gmtptr);
+                       ((funcp == localsub) ? lclptr : gmtptr);
 #ifdef ALL_STATE
                if (sp == NULL)
                        return WRONG;
@@ -1468,7 +1853,8 @@ time2(struct tm * const tmp,
                                        continue;
                                newt = t + sp->ttis[j].tt_gmtoff -
                                        sp->ttis[i].tt_gmtoff;
-                               (*funcp)(&newt, offset, &mytm);
+                               if ((*funcp)(&newt, offset, &mytm) == NULL)
+                                       continue;
                                if (tmcomp(&mytm, &yourtm) != 0)
                                        continue;
                                if (mytm.tm_isdst != yourtm.tm_isdst)
@@ -1487,27 +1873,48 @@ label:
        if ((newt < t) != (saved_seconds < 0))
                return WRONG;
        t = newt;
-       (*funcp)(&t, offset, tmp);
-       *okayp = TRUE;
+       if ((*funcp)(&t, offset, tmp))
+               *okayp = TRUE;
        return t;
 }
 
+static time_t
+time2(struct tm * const tmp,
+      struct tm * (* const funcp)(const time_t *, long, struct tm *),
+      const long offset, int * const okayp)
+{
+       time_t  t;
+
+       /*
+       ** First try without normalization of seconds
+       ** (in case tm_sec contains a value associated with a leap second).
+       ** If that fails, try with normalization of seconds.
+       */
+       t = time2sub(tmp, funcp, offset, okayp, FALSE);
+       return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE);
+}
+
 static time_t
 time1(struct tm * const tmp,
-      void (* const funcp)(const time_t *, long, struct tm *),
+      struct tm * (* const funcp)(const time_t *, long, struct tm *),
       const long offset)
 {
        time_t                  t;
        const struct state *    sp;
        int                     samei, otheri;
-       int                             okay;
+       int                     sameind, otherind;
+       int                     i;
+       int                     nseen;
+       int                     seen[TZ_MAX_TYPES];
+       int                     types[TZ_MAX_TYPES];
+       int                     okay;
 
        if (tmp->tm_isdst > 1)
                tmp->tm_isdst = 1;
        t = time2(tmp, funcp, offset, &okay);
 #ifdef PCTS
        /*
-       ** PCTS code courtesy Grant Sullivan (grant@osf.org).
+       ** PCTS code courtesy Grant Sullivan.
        */
        if (okay)
                return t;
@@ -1524,19 +1931,25 @@ time1(struct tm * const tmp,
        ** We try to divine the type they started from and adjust to the
        ** type they need.
        */
-       /*
-       ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
-       */
-       sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
-               lclptr : gmtptr);
+       sp = (const struct state *) ((funcp == localsub) ?  lclptr : gmtptr);
 #ifdef ALL_STATE
        if (sp == NULL)
                return WRONG;
 #endif /* defined ALL_STATE */
-       for (samei = sp->typecnt - 1; samei >= 0; --samei) {
+       for (i = 0; i < sp->typecnt; ++i)
+               seen[i] = FALSE;
+       nseen = 0;
+       for (i = sp->timecnt - 1; i >= 0; --i)
+               if (!seen[sp->types[i]]) {
+                       seen[sp->types[i]] = TRUE;
+                       types[nseen++] = sp->types[i];
+               }
+       for (sameind = 0; sameind < nseen; ++sameind) {
+               samei = types[sameind];
                if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
                        continue;
-               for (otheri = sp->typecnt - 1; otheri >= 0; --otheri) {
+               for (otherind = 0; otherind < nseen; ++otherind) {
+                       otheri = types[otherind];
                        if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
                                continue;
                        tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
@@ -1616,7 +2029,7 @@ gtime(struct tm * const tmp)
 
 /*
 ** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
-** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which
+** shall correspond to "Wed Dec 31 23:59:59 UTC 1986", which
 ** is not the case if we are accounting for leap seconds.
 ** So, we provide the following conversion routines for use
 ** when exchanging timestamps with POSIX conforming systems.
@@ -1655,7 +2068,7 @@ posix2time(time_t t)
        tzset();
        /*
        ** For a positive leap second hit, the result
-       ** is not unique.  For a negative leap second
+       ** is not unique. For a negative leap second
        ** hit, the corresponding time doesn't exist,
        ** so we return an adjacent second.
        */
index ca191ea..bc487c3 100644 (file)
@@ -1,12 +1,15 @@
-/* $FreeBSD: src/lib/libc/stdtime/private.h,v 1.6.8.1 2000/08/23 00:19:15 jhb Exp $ */
-/* $DragonFly: src/lib/libc/stdtime/private.h,v 1.2 2003/06/17 04:26:46 dillon Exp $ */
+/*
+ * @(#)private.h       8.6
+ * $FreeBSD: src/lib/libc/stdtime/private.h,v 1.6.8.1 2000/08/23 00:19:15 jhb Exp $
+ * $DragonFly: src/lib/libc/stdtime/private.h,v 1.3 2008/10/19 20:15:58 swildner Exp $
+ */
 
 #ifndef PRIVATE_H
 
 #define PRIVATE_H
 /*
 ** This file is in the public domain, so clarified as of
-** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+** 1996-06-05 by Arthur David Olson.
 */
 
 /* Stuff moved from Makefile.inc to reduce clutter */
 ** Thank you!
 */
 
-/*
-** ID
-*/
+#define GRANDPARENTED  "Local time zone must be set--see zic manual page"
 
-/*
- * @(#)private.h       7.43
- */
 /*
 ** Defaults for preprocessor symbols.
 ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
 #define HAVE_GETTEXT           0
 #endif /* !defined HAVE_GETTEXT */
 
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R      0
+#endif /* !defined INCOMPATIBLE_CTIME_R */
+
 #ifndef HAVE_SETTIMEOFDAY
 #define HAVE_SETTIMEOFDAY      3
 #endif /* !defined HAVE_SETTIMEOFDAY */
 
-#ifndef HAVE_STRERROR
-#define HAVE_STRERROR          0
-#endif /* !defined HAVE_STRERROR */
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK           1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H                1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H                1
+#endif /* !defined HAVE_SYS_WAIT_H */
 
 #ifndef HAVE_UNISTD_H
 #define HAVE_UNISTD_H          1
 #define LOCALE_HOME            "/usr/lib/locale"
 #endif /* !defined LOCALE_HOME */
 
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
 /*
 ** Nested includes
 */
 #include "stdio.h"
 #include "errno.h"
 #include "string.h"
-#include "limits.h"    /* for CHAR_BIT */
+#include "limits.h"    /* for CHAR_BIT et al. */
 #include "time.h"
 #include "stdlib.h"
 
-#if HAVE_GETTEXT - 0
+#if HAVE_GETTEXT
 #include "libintl.h"
-#endif /* HAVE_GETTEXT - 0 */
+#endif /* HAVE_GETTEXT */
+
+#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 */
 
-#if HAVE_UNISTD_H - 0
-#include "unistd.h"    /* for F_OK and R_OK */
-#endif /* HAVE_UNISTD_H - 0 */
+#if HAVE_UNISTD_H
+#include "unistd.h"    /* for F_OK, R_OK, and other POSIX goodness */
+#endif /* HAVE_UNISTD_H */
 
-#if !(HAVE_UNISTD_H - 0)
 #ifndef F_OK
 #define F_OK   0
 #endif /* !defined F_OK */
 #ifndef R_OK
 #define R_OK   4
 #endif /* !defined R_OK */
-#endif /* !(HAVE_UNISTD_H - 0) */
 
-/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX.  */
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
 #define is_digit(c) ((unsigned)(c) - '0' <= 9)
 
 /*
-** Workarounds for compilers/systems.
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
 */
-
-#ifndef P
-#ifdef __STDC__
-#define P(x)   x
-#endif /* defined __STDC__ */
-#ifndef __STDC__
-#define P(x)   ()
-#endif /* !defined __STDC__ */
-#endif /* !defined P */
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+       (199901 <= __STDC_VERSION__ || \
+       2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long      int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long           int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
 
 /*
-** SunOS 4.1.1 headers lack FILENAME_MAX.
+** Workarounds for compilers/systems.
 */
 
-#ifndef FILENAME_MAX
+/*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+*/
 
-#ifndef MAXPATHLEN
-#ifdef unix
-#include "sys/param.h"
-#endif /* defined unix */
-#endif /* !defined MAXPATHLEN */
+#ifndef asctime_r
+extern char *  asctime_r(struct tm const *, char *);
+#endif
 
-#ifdef MAXPATHLEN
-#define FILENAME_MAX   MAXPATHLEN
-#endif /* defined MAXPATHLEN */
-#ifndef MAXPATHLEN
-#define FILENAME_MAX   1024            /* Pure guesswork */
-#endif /* !defined MAXPATHLEN */
+/*
+** Private function declarations.
+*/
 
-#endif /* !defined FILENAME_MAX */
+char *         icalloc(int nelem, int elsize);
+char *         icatalloc(char * old, const char * new);
+char *         icpyalloc(const char * string);
+char *         imalloc(int n);
+void *         irealloc(void * pointer, int size);
+void           icfree(char * pointer);
+void           ifree(char * pointer);
+const char *   scheck(const char * string, const char * format);
 
 /*
 ** Finally, some convenience items.
 #define TYPE_SIGNED(type) (((type) -1) < 0)
 #endif /* !defined TYPE_SIGNED */
 
+/*
+** Since the definition of TYPE_INTEGRAL contains floating point numbers,
+** it cannot be used in preprocessor directives.
+*/
+
+#ifndef TYPE_INTEGRAL
+#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
+#endif /* !defined TYPE_INTEGRAL */
+
 #ifndef INT_STRLEN_MAXIMUM
 /*
 ** 302 / 1000 is log10(2.0) rounded up.
 ** add one more for a minus sign if the type is signed.
 */
 #define INT_STRLEN_MAXIMUM(type) \
-    ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+       ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+       1 + TYPE_SIGNED(type))
 #endif /* !defined INT_STRLEN_MAXIMUM */
 
 /*
 */
 
 #ifndef _
-#if HAVE_GETTEXT - 0
+#if HAVE_GETTEXT
 #define _(msgid) gettext(msgid)
-#else /* !(HAVE_GETTEXT - 0) */
+#else /* !(HAVE_GETTEXT) */
 #define _(msgid) msgid
-#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !(HAVE_GETTEXT) */
 #endif /* !defined _ */
 
 #ifndef TZ_DOMAIN
 #define TZ_DOMAIN "tz"
 #endif /* !defined TZ_DOMAIN */
 
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r(struct tm const *, char *);
+char *ctime_r(time_t const *, char *);
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT         400     /* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR         31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT          ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS     34      /* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
+
 /*
-** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+** UNIX was a registered trademark of The Open Group in 2003.
 */
 
 #endif /* !defined PRIVATE_H */
index d6f031a..d3c27c7 100644 (file)
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\"     @(#)strftime.3 8.1 (Berkeley) 6/4/93
+.\" @(#)newstrftime.3  8.1
 .\" $FreeBSD: src/lib/libc/stdtime/strftime.3,v 1.18.2.10 2003/05/24 00:01:31 keramida Exp $
-.\" $DragonFly: src/lib/libc/stdtime/strftime.3,v 1.3 2007/05/17 08:19:00 swildner Exp $
+.\" $DragonFly: src/lib/libc/stdtime/strftime.3,v 1.4 2008/10/19 20:15:58 swildner Exp $
 .\"
-.Dd January 4, 2003
+.Dd October 19, 2008
 .Dt STRFTIME 3
 .Os
 .Sh NAME
@@ -123,7 +123,7 @@ to represent alternative months names
 is replaced by the day of month as a decimal number (1-31); single
 digits are preceded by a blank.
 .It Cm \&%F
-is equivalent to
++is replaced by the date in the format
 .Dq Li %Y-%m-%d .
 .It Cm \&%G
 is replaced by a year as a decimal number with century.
@@ -206,12 +206,16 @@ is replaced by the year with century as a decimal number.
 .It Cm %y
 is replaced by the year without century as a decimal number (00-99).
 .It Cm \&%Z
-is replaced by the time zone name.
+is replaced by the time zone name,
+or by the empty string if this is not determinable.
 .It Cm \&%z
-is replaced by the time zone offset from UTC; a leading plus sign stands for
-east of UTC, a minus sign for west of UTC, hours and minutes follow
-with two digits each and no delimiter between them (common form for
-RFC 822 date headers).
+is replaced by the offset from UTC in the format
+.Li ++HHMM
+or
+.Li --HHMM
+as appropriate,
+with positive values representing locations east of Greenwich,
+or by the empty string if this is not determinable.
 .It Cm %+
 is replaced by national representation of the date and time
 (the format is similar to that produced by
index adc01e5..7c8834a 100644 (file)
@@ -1,5 +1,8 @@
-/*     $NetBSD: src/lib/libc/time/strftime.c,v 1.16 2004/05/12 23:03:11 kleink Exp $   */
-/*     $DragonFly: src/lib/libc/stdtime/strftime.c,v 1.6 2005/12/04 23:25:40 swildner Exp $ */
+/*
+ * @(#)strftime.c      8.2
+ * $NetBSD: src/lib/libc/time/strftime.c,v 1.16 2004/05/12 23:03:11 kleink Exp $
+ * $DragonFly: src/lib/libc/stdtime/strftime.c,v 1.7 2008/10/19 20:15:58 swildner Exp $
+ */
 
 /*
 ** Based on the UCB version with the ID appearing below.
@@ -64,6 +67,7 @@ static char * _add(const char *, char *, const char *);
 static char *  _conv(int, const char *, char *, const char *);
 static char *  _fmt(const char *, const struct tm *, char *,
                     const char *, int *);
+static char *  _yconv(int, int, int, int, char *, const char *);
 
 #define NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
 
@@ -71,7 +75,6 @@ static char * _fmt(const char *, const struct tm *, char *,
 #define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
 #endif /* !defined YEAR_2000_NAME */
 
-
 #define IN_NONE        0
 #define IN_SOME        1
 #define IN_THIS        2
@@ -153,8 +156,8 @@ label:
                                ** something completely different.
                                ** (ado, 1993-05-24)
                                */
-                               pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
-                                       "%02d", pt, ptlim);
+                               pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
+                                       pt, ptlim);
                                continue;
                        case 'c':
                                {
@@ -209,7 +212,7 @@ label:
                                **              t->tm_hour % 12 : 12, 2, ' ');
                                ** ...and has been changed to the below to
                                ** match SunOS 4.1.1 and Arnold Robbins'
-                               ** strftime version 3.0.  That is, "%k" and
+                               ** strftime version 3.0. That is, "%k" and
                                ** "%l" have been swapped.
                                ** (ado, 1993-05-24)
                                */
@@ -229,7 +232,7 @@ label:
                                **      _conv(t->tm_hour, 2, ' ');
                                ** ...and has been changed to the below to
                                ** match SunOS 4.1.1 and Arnold Robbin's
-                               ** strftime version 3.0.  That is, "%k" and
+                               ** strftime version 3.0. That is, "%k" and
                                ** "%l" have been swapped.
                                ** (ado, 1993-05-24)
                                */
@@ -305,7 +308,7 @@ label:
                        case 'G':       /* ISO 8601 year (four digits) */
                        case 'g':       /* ISO 8601 year (two digits) */
 /*
-** From Arnold Robbins' strftime version 3.0:  "the week number of the
+** From Arnold Robbins' strftime version 3.0: "the week number of the
 ** year (the first Monday as the first day of week 1) as a decimal number
 ** (01-53)."
 ** (ado, 1993-05-24)
@@ -318,17 +321,19 @@ label:
 ** might also contain days from the previous year and the week before week
 ** 01 of a year is the last week (52 or 53) of the previous year even if
 ** it contains days from the new year. A week starts with Monday (day 1)
-** and ends with Sunday (day 7).  For example, the first week of the year
+** and ends with Sunday (day 7). For example, the first week of the year
 ** 1997 lasts from 1996-12-30 to 1997-01-05..."
 ** (ado, 1996-01-02)
 */
                                {
                                        int     year;
+                                       int     base;
                                        int     yday;
                                        int     wday;
                                        int     w;
 
-                                       year = t->tm_year + TM_YEAR_BASE;
+                                       year = t->tm_year;
+                                       base = TM_YEAR_BASE;
                                        yday = t->tm_yday;
                                        wday = t->tm_wday;
                                        for ( ; ; ) {
@@ -336,7 +341,7 @@ label:
                                                int     bot;
                                                int     top;
 
-                                               len = isleap(year) ?
+                                               len = isleap_sum(year, base) ?
                                                        DAYSPERLYEAR :
                                                        DAYSPERNYEAR;
                                                /*
@@ -355,7 +360,7 @@ label:
                                                        top += DAYSPERWEEK;
                                                top += len;
                                                if (yday >= top) {
-                                                       ++year;
+                                                       ++base;
                                                        w = 1;
                                                        break;
                                                }
@@ -364,26 +369,26 @@ label:
                                                                DAYSPERWEEK);
                                                        break;
                                                }
-                                               --year;
-                                               yday += isleap(year) ?
+                                               --base;
+                                               yday += isleap_sum(year, base) ?
                                                        DAYSPERLYEAR :
                                                        DAYSPERNYEAR;
                                        }
 #ifdef XPG4_1994_04_09
-                                       if ((w == 52
-                                            && t->tm_mon == TM_JANUARY)
-                                           || (w == 1
-                                               && t->tm_mon == TM_DECEMBER))
-                                               w = 53;
+                                       if ((w == 52 &&
+                                               t->tm_mon == TM_JANUARY) ||
+                                               (w == 1 &&
+                                               t->tm_mon == TM_DECEMBER))
+                                                       w = 53;
 #endif /* defined XPG4_1994_04_09 */
                                        if (*format == 'V')
                                                pt = _conv(w, "%02d",
                                                        pt, ptlim);
                                        else if (*format == 'g') {
                                                *warnp = IN_ALL;
-                                               pt = _conv(year % 100, "%02d",
+                                               pt = _yconv(year, base, 0, 1,
                                                        pt, ptlim);
-                                       } else  pt = _conv(year, "%04d",
+                                       } else  pt = _yconv(year, base, 1, 1,
                                                        pt, ptlim);
                                }
                                continue;
@@ -421,11 +426,11 @@ label:
                                continue;
                        case 'y':
                                *warnp = IN_ALL;
-                               pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
-                                       "%02d", pt, ptlim);
+                               pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
+                                       pt, ptlim);
                                continue;
                        case 'Y':
-                               pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
+                               pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
                                        pt, ptlim);
                                continue;
                        case 'Z':
@@ -456,12 +461,12 @@ label:
                                /*
                                ** C99 says that the UTC offset must
                                ** be computed by looking only at
-                               ** tm_isdst.  This requirement is
+                               ** tm_isdst. This requirement is
                                ** incorrect, since it means the code
                                ** must rely on magic (in this case
                                ** altzone and timezone), and the
                                ** magic might not have the correct
-                               ** offset.  Doing things correctly is
+                               ** offset. Doing things correctly is
                                ** tricky and requires disobeying C99;
                                ** see GNU C strftime for details.
                                ** For now, punt and conform to the
@@ -520,9 +525,10 @@ label:
                                        diff = -diff;
                                } else  sign = "+";
                                pt = _add(sign, pt, ptlim);
-                               diff /= 60;
-                               pt = _conv((diff/60)*100 + diff%60,
-                                       "%04d", pt, ptlim);
+                               diff /= SECSPERMIN;
+                               diff = (diff / MINSPERHOUR) * 100 +
+                                       (diff % MINSPERHOUR);
+                               pt = _conv(diff, "%04d", pt, ptlim);
                                }
                                continue;
                        case '+':
@@ -532,7 +538,7 @@ label:
                        case '%':
                        /*
                        ** X311J/88-090 (4.12.3.5): if conversion char is
-                       ** undefined, behavior is undefined.  Print out the
+                       ** undefined, behavior is undefined. Print out the
                        ** character itself as printf(3) also does.
                        */
                        default:
@@ -563,3 +569,39 @@ _add(const char *str, char *pt, const char * const ptlim)
                ++pt;
        return pt;
 }
+
+/*
+** POSIX and the C Standard are unclear or inconsistent about
+** what %C and %y do if the year is negative or exceeds 9999.
+** Use the convention that %C concatenated with %y yields the
+** same output as %Y, and that %Y contains at least 4 bytes,
+** with more only if necessary.
+*/
+
+static char *
+_yconv(const int a, const int b, const int convert_top, const int convert_yy,
+       char *pt, const char * const ptlim)
+{
+       int     lead;
+       int     trail;
+
+#define DIVISOR        100
+       trail = a % DIVISOR + b % DIVISOR;
+       lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
+       trail %= DIVISOR;
+       if (trail < 0 && lead > 0) {
+               trail += DIVISOR;
+               --lead;
+       } else if (lead < 0 && trail > 0) {
+               trail -= DIVISOR;
+               ++lead;
+       }
+       if (convert_top) {
+               if (lead == 0 && trail < 0)
+                       pt = _add("-0", pt, ptlim);
+               else    pt = _conv(lead, "%02d", pt, ptlim);
+       }
+       if (convert_yy)
+               pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
+       return pt;
+}
index 14f88c1..0a21743 100644 (file)
@@ -1,7 +1,7 @@
 .\" $FreeBSD: src/lib/libc/stdtime/time2posix.3,v 1.9.2.4 2001/12/14 18:33:59 ru Exp $
-.\" $DragonFly: src/lib/libc/stdtime/time2posix.3,v 1.2 2003/06/17 04:26:46 dillon Exp $
+.\" $DragonFly: src/lib/libc/stdtime/time2posix.3,v 1.3 2008/10/19 20:15:58 swildner Exp $
 .\"
-.Dd May 1, 1996
+.Dd October 19, 2008
 .Dt TIME2POSIX 3
 .Os
 .Sh NAME
@@ -19,7 +19,7 @@
 .Sh DESCRIPTION
 .St -p1003.1-88
 legislates that a time_t value of
-536457599 shall correspond to "Wed Dec 31 23:59:59 GMT 1986."
+536457599 shall correspond to "Wed Dec 31 23:59:59 UTC 1986."
 This effectively implies that POSIX time_t's cannot include leap
 seconds and,
 therefore,
@@ -119,3 +119,6 @@ degenerate to the identity function.
 .Xr localtime 3 ,
 .Xr mktime 3 ,
 .Xr time 3
+.\" @(#)time2posix.3   8.1
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
index e85c174..aae74d0 100644 (file)
@@ -1,6 +1,6 @@
 .\" $FreeBSD: src/lib/libc/stdtime/tzfile.5,v 1.8.2.2 2001/08/17 15:42:43 ru Exp $
-.\" $DragonFly: src/lib/libc/stdtime/tzfile.5,v 1.3 2008/05/02 02:05:04 swildner Exp $
-.Dd September 13, 1994
+.\" $DragonFly: src/lib/libc/stdtime/tzfile.5,v 1.4 2008/10/19 20:15:58 swildner Exp $
+.Dd October 19, 2008
 .Dt TZFILE 5
 .Os
 .Sh NAME
@@ -15,9 +15,17 @@ begin with the magic characters
 .Dq Li TZif
 to identify them as
 time zone information files,
-followed by sixteen bytes reserved for future use,
-followed by four four-byte values
-written in a ``standard'' byte order
+followed by a character identifying the version of the file's format
+(as of 2005, either an ASCII
+.Dv NUL
+or a
+.Sq Li 2 )
+followed by fifteen bytes containing zeroes reserved for future use,
+followed by six four-byte values of type
+.Vt long ,
+written in a
+.Dq standard
+byte order
 (the high-order byte of the value is written first).
 These values are,
 in order:
@@ -30,13 +38,18 @@ The number of standard/wall indicators stored in the file.
 .It Va tzh_leapcnt
 The number of leap seconds for which data is stored in the file.
 .It Va tzh_timecnt
-The number of ``transition times'' for which data is stored
+The number of
+.Dq transition times
+for which data is stored
 in the file.
 .It Va tzh_typecnt
-The number of ``local time types'' for which data is stored
+The number of
+.Dq local time types
+for which data is stored
 in the file (must not be zero).
 .It Va tzh_charcnt
-The number of characters of ``time zone abbreviation strings''
+The number of characters of
+.Dq time zone abbreviation strings
 stored in the file.
 .El
 .Pp
@@ -45,7 +58,9 @@ The above header is followed by
 four-byte values of type
 .Fa long ,
 sorted in ascending order.
-These values are written in ``standard'' byte order.
+These values are written in
+.Dq standard
+byte order.
 Each is used as a transition time (as returned by
 .Xr time 3 )
 at which the rules for computing local time change.
@@ -53,11 +68,15 @@ Next come
 .Va tzh_timecnt
 one-byte values of type
 .Fa "unsigned char" ;
-each one tells which of the different types of ``local time'' types
+each one tells which of the different types of
+.Dq local time
+types
 described in the file is associated with the same-indexed transition time.
 These values serve as indices into an array of
 .Fa ttinfo
-structures that appears next in the file;
+structures (with
+.Va tzh_typecnt
+entries) that appears next in the file;
 these structures are defined as follows:
 .Bd -literal -offset indent
 struct ttinfo {
@@ -129,10 +148,20 @@ if either
 .Li tzh_timecnt
 is zero or the time argument is less than the first transition time recorded
 in the file.
+.Pp
+For version-2-format time zone files,
+the above header and data is followed by a second header and data,
+identical in format except that
+eight bytes are used for each transition time or leap second time.
+After the second header and data comes a newline-enclosed,
+POSIX-TZ-environment-variable-style string for use in handling instants
+after the last transition time stored in the file
+(with nothing between the newlines if there is no POSIX representation for
+such instants).
 .Sh SEE ALSO
 .Xr ctime 3 ,
 .Xr time2posix 3 ,
 .Xr zic 8
-.\" @(#)tzfile.5       7.2
+.\" @(#)tzfile.5       8.2
 .\" This file is in the public domain, so clarified as of
-.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+.\" 1996-06-05 by Arthur David Olson.
index a1f7c33..ae5affc 100644 (file)
@@ -1,10 +1,15 @@
+/*
+ * @(#)tzfile.h                8.1
+ * $DragonFly: src/lib/libc/stdtime/tzfile.h,v 1.3 2008/10/19 20:15:58 swildner 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).
+** 1996-06-05 by Arthur David Olson.
 */
 
 /*
 ** Thank you!
 */
 
-/*
-** ID
-*/
-
-/*
- * @(#)tzfile.h        7.14
- */
 /*
 ** Information about time zone files.
 */
@@ -45,8 +43,9 @@
 #define        TZ_MAGIC        "TZif"
 
 struct tzhead {
-       char    tzh_magic[4];           /* TZ_MAGIC */
-       char    tzh_reserved[16];       /* reserved for future use */
+       char    tzh_magic[4];           /* TZ_MAGIC */
+       char    tzh_version[1];         /* '\0' or '2' as of 2005 */
+       char    tzh_reserved[15];       /* reserved--must be zero */
        char    tzh_ttisgmtcnt[4];      /* coded number of trans. time flags */
        char    tzh_ttisstdcnt[4];      /* coded number of trans. time flags */
        char    tzh_leapcnt[4];         /* coded number of leap seconds */
@@ -80,19 +79,23 @@ struct tzhead {
 **                                     assumed to be local time
 */
 
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+*/
+
 /*
 ** In the current implementation, "tzset()" refuses to deal with files that
 ** exceed any of the limits below.
 */
 
 #ifndef TZ_MAX_TIMES
-/*
-** The TZ_MAX_TIMES value below is enough to handle a bit more than a
-** year's worth of solar time (corrected daily to the nearest second) or
-** 138 years of Pacific Presidential Election time
-** (where there are three time zone transitions every fourth year).
-*/
-#define TZ_MAX_TIMES   370
+#define TZ_MAX_TIMES   1200
 #endif /* !defined TZ_MAX_TIMES */
 
 #ifndef TZ_MAX_TYPES
@@ -102,7 +105,7 @@ struct tzhead {
 #ifdef NOSOLAR
 /*
 ** Must be at least 14 for Europe/Riga as of Jan 12 1995,
-** as noted by Earl Chew <earl@hpato.aus.hp.com>.
+** as noted by Earl Chew.
 */
 #define TZ_MAX_TYPES   20      /* Maximum number of local time types */
 #endif /* !defined NOSOLAR */
@@ -153,33 +156,20 @@ struct tzhead {
 #define EPOCH_YEAR     1970
 #define EPOCH_WDAY     TM_THURSDAY
 
-/*
-** Accurate only for the past couple of centuries;
-** that will probably do.
-*/
-
 #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
 
-#ifndef USG
-
 /*
-** Use of the underscored variants may cause problems if you move your code to
-** certain System-V-based systems; for maximum portability, use the
-** underscore-free variants.  The underscored variants are provided for
-** backward compatibility only; they may disappear from future versions of
-** this file.
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+**     isleap(y) == isleap(y % 400)
+** and so
+**     isleap(a + b) == isleap((a + b) % 400)
+** or
+**     isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
 */
 
-#define SECS_PER_MIN   SECSPERMIN
-#define MINS_PER_HOUR  MINSPERHOUR
-#define HOURS_PER_DAY  HOURSPERDAY
-#define DAYS_PER_WEEK  DAYSPERWEEK
-#define DAYS_PER_NYEAR DAYSPERNYEAR
-#define DAYS_PER_LYEAR DAYSPERLYEAR
-#define SECS_PER_HOUR  SECSPERHOUR
-#define SECS_PER_DAY   SECSPERDAY
-#define MONS_PER_YEAR  MONSPERYEAR
-
-#endif /* !defined USG */
+#define isleap_sum(a, b)       isleap((a) % 400 + (b) % 400)
 
 #endif /* !defined TZFILE_H */
index c538951..c1ce785 100644 (file)
@@ -1,13 +1,12 @@
-#ifndef lint
-#ifndef NOID
-static char    elsieid[] = "@(#)ialloc.c       8.29";
-#endif /* !defined NOID */
-#endif /* !defined lint */
+/*
+** This file is in the public domain, so clarified as of
+** 2006-07-17 by Arthur David Olson.
+*/
 
 /*
- * @(#)ialloc.c        8.29
+ * @(#)ialloc.c        8.30
  * $FreeBSD: src/usr.sbin/zic/ialloc.c,v 1.5 1999/08/28 01:21:18 peter Exp $
- * $DragonFly: src/usr.sbin/zic/ialloc.c,v 1.4 2004/12/18 22:48:15 swildner Exp $
+ * $DragonFly: src/usr.sbin/zic/ialloc.c,v 1.5 2008/10/19 20:15:58 swildner Exp $
  */
 /*LINTLIBRARY*/
 
index 783795d..58b2354 100644 (file)
@@ -1,23 +1,29 @@
+/*
+ * @(#)private.h       8.6
+ * $FreeBSD: src/lib/libc/stdtime/private.h,v 1.6.8.1 2000/08/23 00:19:15 jhb Exp $
+ * $DragonFly: src/usr.sbin/zic/private.h,v 1.4 2008/10/19 20:15:58 swildner 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).
+** 1996-06-05 by Arthur David Olson.
 */
 
-/*
- * FreeBSD modifications: separate libc's privates from zic's.
- * This makes it easier when we need to update one but not the other.
- * I have removed all of the ifdef spaghetti which is not relevant to
- * zic from this file.
- *
- * $FreeBSD: src/usr.sbin/zic/private.h,v 1.3 1999/08/28 01:21:18 peter Exp $
- * $DragonFly: src/usr.sbin/zic/private.h,v 1.3 2004/02/29 16:55:28 joerg Exp $
- *
- * @(#)private.h       7.48
- */
+/* Stuff moved from Makefile.inc to reduce clutter */
+#ifndef TM_GMTOFF
+#define TM_GMTOFF      tm_gmtoff
+#define TM_ZONE                tm_zone
+#define STD_INSPIRED   1
+#define PCTS           1
+#define HAVE_LONG_DOUBLE 1
+#define HAVE_STRERROR  1
+#define        HAVE_UNISTD_H   1
+#define        LOCALE_HOME     _PATH_LOCALE
+#define TZDIR          "/usr/share/zoneinfo"
+#endif /* ndef TM_GMTOFF */
 
 /*
 ** This header is for use ONLY with the time conversion code.
 ** Thank you!
 */
 
-/*
-** ID
-*/
+#define GRANDPARENTED  "Local time zone must be set--see zic manual page"
 
 /*
 ** Defaults for preprocessor symbols.
 ** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
 */
 
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME           1
+#endif /* !defined HAVE_ADJTIME */
+
 #ifndef HAVE_GETTEXT
 #define HAVE_GETTEXT           0
 #endif /* !defined HAVE_GETTEXT */
 
-#ifndef HAVE_STRERROR
-#define HAVE_STRERROR          1
-#endif /* !defined HAVE_STRERROR */
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R      0
+#endif /* !defined INCOMPATIBLE_CTIME_R */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY      3
+#endif /* !defined HAVE_SETTIMEOFDAY */
 
 #ifndef HAVE_SYMLINK
 #define HAVE_SYMLINK           1
 #endif /* !defined HAVE_SYMLINK */
 
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H                1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H                1
+#endif /* !defined HAVE_SYS_WAIT_H */
+
 #ifndef HAVE_UNISTD_H
 #define HAVE_UNISTD_H          1
 #endif /* !defined HAVE_UNISTD_H */
 
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H           0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME            "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
 /*
 ** Nested includes
 */
 #include "stdio.h"
 #include "errno.h"
 #include "string.h"
-#include "limits.h"    /* for CHAR_BIT */
+#include "limits.h"    /* for CHAR_BIT et al. */
 #include "time.h"
 #include "stdlib.h"
 
-#if HAVE_GETTEXT - 0
+#if HAVE_GETTEXT
 #include "libintl.h"
-#endif /* HAVE_GETTEXT - 0 */
+#endif /* HAVE_GETTEXT */
+
+#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 */
 
-#if HAVE_UNISTD_H - 0
-#include "unistd.h"    /* for F_OK and R_OK */
-#endif /* HAVE_UNISTD_H - 0 */
+#if HAVE_UNISTD_H
+#include "unistd.h"    /* for F_OK, R_OK, and other POSIX goodness */
+#endif /* HAVE_UNISTD_H */
 
-#if !(HAVE_UNISTD_H - 0)
 #ifndef F_OK
 #define F_OK   0
 #endif /* !defined F_OK */
 #ifndef R_OK
 #define R_OK   4
 #endif /* !defined R_OK */
-#endif /* !(HAVE_UNISTD_H - 0) */
 
-/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX.  */
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
 #define is_digit(c) ((unsigned)(c) - '0' <= 9)
 
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
+*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+       (199901 <= __STDC_VERSION__ || \
+       2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX.  */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long      int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long           int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** Some time.h implementations don't declare asctime_r.
+** Others might define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef asctime_r
+extern char *  asctime_r(struct tm const *, char *);
+#endif
+
 /*
 ** Private function declarations.
 */
-char   *icalloc(int nelem, int elsize);
-char   *icatalloc(char *old, const char *new);
-char   *icpyalloc(const char *string);
-char   *imalloc(int n);
-void   *irealloc(void * pointer, int size);
-void    icfree(char * pointer);
-void    ifree(char * pointer);
-char   *scheck(const char *string, const char *format);
+
+char *         icalloc(int nelem, int elsize);
+char *         icatalloc(char * old, const char * new);
+char *         icpyalloc(const char * string);
+char *         imalloc(int n);
+void *         irealloc(void * pointer, int size);
+void           icfree(char * pointer);
+void           ifree(char * pointer);
+const char *   scheck(const char * string, const char * format);
 
 /*
 ** Finally, some convenience items.
@@ -116,6 +209,15 @@ char       *scheck(const char *string, const char *format);
 #define TYPE_SIGNED(type) (((type) -1) < 0)
 #endif /* !defined TYPE_SIGNED */
 
+/*
+** Since the definition of TYPE_INTEGRAL contains floating point numbers,
+** it cannot be used in preprocessor directives.
+*/
+
+#ifndef TYPE_INTEGRAL
+#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
+#endif /* !defined TYPE_INTEGRAL */
+
 #ifndef INT_STRLEN_MAXIMUM
 /*
 ** 302 / 1000 is log10(2.0) rounded up.
@@ -124,7 +226,8 @@ char        *scheck(const char *string, const char *format);
 ** add one more for a minus sign if the type is signed.
 */
 #define INT_STRLEN_MAXIMUM(type) \
-    ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+       ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+       1 + TYPE_SIGNED(type))
 #endif /* !defined INT_STRLEN_MAXIMUM */
 
 /*
@@ -158,19 +261,46 @@ char      *scheck(const char *string, const char *format);
 */
 
 #ifndef _
-#if HAVE_GETTEXT - 0
+#if HAVE_GETTEXT
 #define _(msgid) gettext(msgid)
-#else /* !(HAVE_GETTEXT - 0) */
+#else /* !(HAVE_GETTEXT) */
 #define _(msgid) msgid
-#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !(HAVE_GETTEXT) */
 #endif /* !defined _ */
 
 #ifndef TZ_DOMAIN
 #define TZ_DOMAIN "tz"
 #endif /* !defined TZ_DOMAIN */
 
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r(struct tm const *, char *);
+char *ctime_r(time_t const *, char *);
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT         400     /* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR         31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT          ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS     34      /* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
+
 /*
-** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+** UNIX was a registered trademark of The Open Group in 2003.
 */
 
 #endif /* !defined PRIVATE_H */
index 64188d9..a6c11b1 100644 (file)
@@ -1,30 +1,23 @@
-#ifndef lint
-#ifndef NOID
-static char    elsieid[] = "@(#)scheck.c       8.15";
-#endif /* !defined lint */
-#endif /* !defined NOID */
-
 /*
- * @(#)scheck.c        8.15
+ * @(#)scheck.c        8.19
  * $FreeBSD: src/usr.sbin/zic/scheck.c,v 1.4 1999/08/28 01:21:19 peter Exp $
- * $DragonFly: src/usr.sbin/zic/scheck.c,v 1.3 2004/02/29 16:55:28 joerg Exp $
+ * $DragonFly: src/usr.sbin/zic/scheck.c,v 1.4 2008/10/19 20:15:58 swildner Exp $
  */
 /*LINTLIBRARY*/
 
 #include "private.h"
 
-char *
+const char *
 scheck(const char * const string, const char * const format)
 {
        char *fbuf;
        const char *fp;
        char *tp;
        int c;
-       char *result;
+       const char *result;
        char dummy;
-       static char nada;
 
-       result = &nada;
+       result = "";
        if (string == NULL || format == NULL)
                return result;
        fbuf = imalloc((int) (2 * strlen(format) + 4));
index dd6c4c6..2fbdce0 100644 (file)
@@ -1,9 +1,9 @@
 .\"
-.\"    @(#)zdump.8     7.3
+.\" @(#)zdump.8        8.1
 .\" $FreeBSD: src/usr.sbin/zic/zdump.8,v 1.7.2.2 2003/03/11 22:31:35 trhodes Exp $
-.\" $DragonFly: src/usr.sbin/zic/zdump.8,v 1.2 2003/06/17 04:30:05 dillon Exp $
+.\" $DragonFly: src/usr.sbin/zic/zdump.8,v 1.3 2008/10/19 20:15:58 swildner Exp $
 .\"
-.Dd September 13, 1994
+.Dd October 19, 2008
 .Dt ZDUMP 8
 .Os
 .Sh NAME
@@ -12,7 +12,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl v
-.Op Fl c Ar cutoffyear
+.Op Fl c Bo Ar loyear Ns , Bc Ns Ar hiyear
 .Op Ar zonename ...
 .Sh DESCRIPTION
 The
@@ -38,9 +38,23 @@ Each line ends with
 if the given time is Daylight Saving Time or
 .Em isdst=0
 otherwise.
-.It Fl c Ar cutoffyear
-Cut off the verbose output near the start of the given year.
+.It Fl c Bo Ar loyear Ns , Bc Ns Ar hiyear
+Cut off verbose output near the start of the given year(s).
+By default,
+the program cuts off verbose output near the starts of the years -500 and 2500.
 .El
+.Sh LIMITATIONS
+The
+.Fl v
+option may not be used on systems with floating-point
+.Vt time_t
+values that are neither float nor double.
+.Pp
+Time discontinuities are found by sampling the results returned by
+.Xr localtime 3
+at twelve-hour intervals.
+This works in all real-world cases;
+one can construct artificial time zones for which this fails.
 .Sh "SEE ALSO"
 .Xr ctime 3 ,
 .Xr tzfile 5 ,
index a04ccb8..1374b7c 100644 (file)
@@ -1,13 +1,7 @@
-#ifndef lint
-#ifndef NOID
-static char    elsieid[] = "@(#)zdump.c        7.28";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
 /*
- * @(#)zdump.c 7.28
+ * @(#)zdump.c 8.6
  * $FreeBSD: src/usr.sbin/zic/zdump.c,v 1.7 1999/08/28 01:21:19 peter Exp $
- * $DragonFly: src/usr.sbin/zic/zdump.c,v 1.4 2004/12/18 22:48:15 swildner Exp $
+ * $DragonFly: src/usr.sbin/zic/zdump.c,v 1.5 2008/10/19 20:15:58 swildner Exp $
  */
 /*
 ** This code has been made independent of the rest of the time
@@ -15,7 +9,9 @@ static char    elsieid[] = "@(#)zdump.c        7.28";
 ** You can use this code to help in verifying other implementations.
 */
 
+#include <ctype.h>     /* for isalpha et al. */
 #include <err.h>
+#include <float.h>     /* for FLT_MAX and DBL_MAX */
 #include <stdio.h>     /* for stdout, stderr */
 #include <stdlib.h>    /* for exit, malloc, atoi */
 #include <string.h>    /* for strcpy */
@@ -23,6 +19,14 @@ static char  elsieid[] = "@(#)zdump.c        7.28";
 #include <time.h>      /* for struct tm */
 #include <unistd.h>
 
+#ifndef ZDUMP_LO_YEAR
+#define ZDUMP_LO_YEAR  (-500)
+#endif /* !defined ZDUMP_LO_YEAR */
+
+#ifndef ZDUMP_HI_YEAR
+#define ZDUMP_HI_YEAR  2500
+#endif /* !defined ZDUMP_HI_YEAR */
 #ifndef MAX_STRING_LENGTH
 #define MAX_STRING_LENGTH      1024
 #endif /* !defined MAX_STRING_LENGTH */
@@ -72,19 +76,24 @@ static char elsieid[] = "@(#)zdump.c        7.28";
 #endif /* !defined DAYSPERNYEAR */
 
 #ifndef isleap
-#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#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 isleap_sum
+/*
+** See tzfile.h for details on isleap_sum.
+*/
+#define isleap_sum(a, b)       isleap((a) % 400 + (b) % 400)
+#endif /* !defined isleap_sum */
+
+#define SECSPERDAY     ((long) SECSPERHOUR * HOURSPERDAY)
+#define SECSPERNYEAR   (SECSPERDAY * DAYSPERNYEAR)
+#define SECSPERLYEAR   (SECSPERNYEAR + SECSPERDAY)
 
 #ifndef GNUC_or_lint
 #ifdef lint
 #define GNUC_or_lint
-#endif /* defined lint */
-#ifndef lint
+#else /* !defined lint */
 #ifdef __GNUC__
 #define GNUC_or_lint
 #endif /* defined __GNUC__ */
@@ -94,8 +103,7 @@ static char  elsieid[] = "@(#)zdump.c        7.28";
 #ifndef INITIALIZE
 #ifdef GNUC_or_lint
 #define INITIALIZE(x)  ((x) = 0)
-#endif /* defined GNUC_or_lint */
-#ifndef GNUC_or_lint
+#else /* !defined GNUC_or_lint */
 #define INITIALIZE(x)
 #endif /* !defined GNUC_or_lint */
 #endif /* !defined INITIALIZE */
@@ -107,11 +115,7 @@ static char        elsieid[] = "@(#)zdump.c        7.28";
 */
 
 #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
@@ -121,70 +125,157 @@ static char      elsieid[] = "@(#)zdump.c        7.28";
 extern char ** environ;
 extern char *  tzname[2];
 
+static time_t   absolute_min_time;
+static time_t   absolute_max_time;
+static size_t   longest;
+static int      warned;
+
 static char    *abbr(struct tm *tmp);
+static void     abbrok(const char *abbrp, const char *zone);
 static long     delta(struct tm *newp, struct tm *oldp);
+static void     dumptime(const struct tm *tmp);
 static time_t   hunt(char *name, time_t lot, time_t hit);
-static size_t   longest;
-static char    *progname;
+static void     setabsolutes(void);
 static void     show(char *zone, time_t t, int v);
+static const char *tformat(void);
+static time_t   yeartot(long y);
 static void     usage(void);
 
+#ifndef TYPECHECK
+#define my_localtime   localtime
+#else /* !defined TYPECHECK */
+static struct tm *
+my_localtime(time_t *tp)
+{
+       struct tm *     tmp;
+
+       tmp = localtime(tp);
+       if (tp != NULL && tmp != NULL) {
+               struct tm       tm;
+               time_t          t;
+
+               tm = *tmp;
+               t = mktime(&tm);
+               if (t - *tp >= 1 || *tp - t >= 1) {
+                       fflush(stdout);
+                       fprintf(stderr, "\n%s: ", progname);
+                       fprintf(stderr, tformat(), *tp);
+                       fprintf(stderr, " ->");
+                       fprintf(stderr, " year=%d", tmp->tm_year);
+                       fprintf(stderr, " mon=%d", tmp->tm_mon);
+                       fprintf(stderr, " mday=%d", tmp->tm_mday);
+                       fprintf(stderr, " hour=%d", tmp->tm_hour);
+                       fprintf(stderr, " min=%d", tmp->tm_min);
+                       fprintf(stderr, " sec=%d", tmp->tm_sec);
+                       fprintf(stderr, " isdst=%d", tmp->tm_isdst);
+                       fprintf(stderr, " -> ");
+                       fprintf(stderr, tformat(), t);
+                       fprintf(stderr, "\n");
+               }
+       }
+       return tmp;
+}
+#endif /* !defined TYPECHECK */
+
+static void
+abbrok(const char * const abbrp, const char * const zone)
+{
+       const char *cp;
+       char *wp;
+
+       if (warned)
+               return;
+       cp = abbrp;
+       wp = NULL;
+       while (isascii((unsigned char) *cp) && isalpha((unsigned char) *cp))
+               ++cp;
+       if (cp - abbrp == 0)
+               wp = _("lacks alphabetic at start");
+       else if (cp - abbrp < 3)
+               wp = _("has fewer than 3 alphabetics");
+       else if (cp - abbrp > 6)
+               wp = _("has more than 6 alphabetics");
+       if (wp == NULL && (*cp == '+' || *cp == '-')) {
+               ++cp;
+               if (isascii((unsigned char) *cp) &&
+                       isdigit((unsigned char) *cp))
+                               if (*cp++ == '1' && *cp >= '0' && *cp <= '4')
+                                       ++cp;
+               if (*cp != '\0')
+                       wp = _("differs from POSIX standard");
+       }
+       if (wp == NULL)
+               return;
+       fflush(stdout);
+       warnx(_("warning: zone \"%s\" abbreviation \"%s\" %s\n"),
+               zone, abbrp, wp);
+       warned = TRUE;
+}
+
 int
 main(int argc, char *argv[])
 {
        int i;
        int c;
        int vflag;
-       char *cutoff;
-       int cutyear;
-       long cuttime;
+       char *cutarg;
+       long cutloyear = ZDUMP_LO_YEAR;
+       long cuthiyear = ZDUMP_HI_YEAR;
+       time_t cutlotime;
+       time_t cuthitime;
        char **fakeenv;
        time_t now;
        time_t t;
        time_t newt;
-       time_t hibit;
        struct tm tm;
        struct tm newtm;
+       struct tm *tmp;
+       struct tm *newtmp;
 
-       INITIALIZE(cuttime);
-#if HAVE_GETTEXT - 0
-       setlocale(LC_MESSAGES, "");
-#ifdef TZ_DOMAINDIR
-       bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* defined(TEXTDOMAINDIR) */
-       textdomain(TZ_DOMAIN);
-#endif /* HAVE_GETTEXT - 0 */
+       INITIALIZE(cutlotime);
+       INITIALIZE(cuthitime);
        vflag = 0;
-       cutoff = NULL;
+       cutarg = NULL;
        while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
                if (c == 'v')
                        vflag = 1;
-               else    cutoff = optarg;
+               else    cutarg = optarg;
        if ((c != EOF && c != -1) ||
                (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
                        usage();
        }
-       if (cutoff != NULL) {
-               int     y;
-
-               cutyear = atoi(cutoff);
-               cuttime = 0;
-               for (y = EPOCH_YEAR; y < cutyear; ++y)
-                       cuttime += DAYSPERNYEAR + isleap(y);
-               cuttime *= SECSPERHOUR * HOURSPERDAY;
+       if (vflag) {
+               if (cutarg != NULL) {
+                       long    lo;
+                       long    hi;
+                       char    dummy;
+
+                       if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
+                               cuthiyear = hi;
+                       } else if (sscanf(cutarg, "%ld,%ld%c",
+                               &lo, &hi, &dummy) == 2) {
+                                       cutloyear = lo;
+                                       cuthiyear = hi;
+                       } else {
+                               errx(EXIT_FAILURE,
+                                       _("%s: wild -c argument %s\n"),
+                                       cutarg);
+                       }
+               }
+               setabsolutes();
+               cutlotime = yeartot(cutloyear);
+               cuthitime = yeartot(cuthiyear);
        }
        time(&now);
        longest = 0;
        for (i = optind; i < argc; ++i)
                if (strlen(argv[i]) > longest)
                        longest = strlen(argv[i]);
-       for (hibit = 1; (hibit << 1) != 0; hibit <<= 1)
-               continue;
        {
                int from;
                int to;
 
-               for (i = 0;  environ[i] != NULL;  ++i)
+               for (i = 0; environ[i] != NULL;  ++i)
                        continue;
                fakeenv = (char **) malloc((size_t) ((i + 2) *
                        sizeof *fakeenv));
@@ -209,43 +300,47 @@ main(int argc, char *argv[])
                        show(argv[i], now, FALSE);
                        continue;
                }
-               /*
-               ** Get lowest value of t.
-               */
-               t = hibit;
-               if (t > 0)              /* time_t is unsigned */
-                       t = 0;
+               warned = FALSE;
+               t = absolute_min_time;
                show(argv[i], t, TRUE);
                t += SECSPERHOUR * HOURSPERDAY;
                show(argv[i], t, TRUE);
-               tm = *localtime(&t);
-               strncpy(buf, abbr(&tm), (sizeof buf) - 1);
+               if (t < cutlotime)
+                       t = cutlotime;
+               tmp = my_localtime(&t);
+               if (tmp != NULL) {
+                       tm = *tmp;
+                       strncpy(buf, abbr(&tm), (sizeof buf) - 1);
+               }
                for ( ; ; ) {
-                       if (cutoff != NULL && t >= cuttime)
+                       if (t >= cuthitime)
                                break;
                        newt = t + SECSPERHOUR * 12;
-                       if (cutoff != NULL && newt >= cuttime)
+                       if (newt >= cuthitime)
                                break;
                        if (newt <= t)
                                break;
-                       newtm = *localtime(&newt);
-                       if (delta(&newtm, &tm) != (newt - t) ||
+                       newtmp = localtime(&newt);
+                       if (newtmp != NULL)
+                               newtm = *newtmp;
+                       if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
+                               (delta(&newtm, &tm) != (newt - t) ||
                                newtm.tm_isdst != tm.tm_isdst ||
-                               strcmp(abbr(&newtm), buf) != 0) {
+                               strcmp(abbr(&newtm), buf) != 0)) {
                                        newt = hunt(argv[i], t, newt);
-                                       newtm = *localtime(&newt);
-                                       strncpy(buf, abbr(&newtm),
-                                               (sizeof buf) - 1);
+                                       newtmp = localtime(&newt);
+                                       if (newtmp != NULL) {
+                                               newtm = *newtmp;
+                                               strncpy(buf,
+                                                       abbr(&newtm),
+                                                       (sizeof buf) - 1);
+                                       }
                        }
                        t = newt;
                        tm = newtm;
+                       tmp = newtmp;
                }
-               /*
-               ** Get highest value of t.
-               */
-               t = ~((time_t) 0);
-               if (t < 0)              /* time_t is signed */
-                       t &= ~hibit;
+               t = absolute_max_time;
                t -= SECSPERHOUR * HOURSPERDAY;
                show(argv[i], t, TRUE);
                t += SECSPERHOUR * HOURSPERDAY;
@@ -254,10 +349,51 @@ main(int argc, char *argv[])
        if (fflush(stdout) || ferror(stdout))
                errx(EXIT_FAILURE, _("error writing standard output"));
        exit(EXIT_SUCCESS);
+       /* If exit fails to exit... */
+       return EXIT_FAILURE;
+}
+
+static void
+setabsolutes(void)
+{
+       if (0.5 == (time_t) 0.5) {
+               /*
+               ** time_t is floating.
+               */
+               if (sizeof (time_t) == sizeof (float)) {
+                       absolute_min_time = (time_t) -FLT_MAX;
+                       absolute_max_time = (time_t) FLT_MAX;
+               } else if (sizeof (time_t) == sizeof (double)) {
+                       absolute_min_time = (time_t) -DBL_MAX;
+                       absolute_max_time = (time_t) DBL_MAX;
+               } else {
+                       errx(EXIT_FAILURE,
+_("use of -v on system with floating time_t other than float or double\n"));
+               }
+       } else if (0 > (time_t) -1) {
+               /*
+               ** time_t is signed.  Assume overflow wraps around.
+               */
+               time_t t = 0;
+               time_t t1 = 1;
 
-       /* gcc -Wall pacifier */
-       for ( ; ; )
-               continue;
+               while (t < t1) {
+                       t = t1;
+                       t1 = 2 * t1 + 1;
+               }
+
+               absolute_max_time = t;
+               t = -t;
+               absolute_min_time = t - 1;
+               if (t < absolute_min_time)
+                       absolute_min_time = t;
+       } else {
+               /*
+               ** time_t is unsigned.
+               */
+               absolute_min_time = 0;
+               absolute_max_time = absolute_min_time - 1;
+       }
 }
 
 static void
@@ -267,28 +403,73 @@ usage(void)
        exit(EXIT_FAILURE);
 }
 
+static time_t
+yeartot(const long y)
+{
+       long    myy;
+       long    seconds;
+       time_t  t;
+
+       myy = EPOCH_YEAR;
+       t = 0;
+       while (myy != y) {
+               if (myy < y) {
+                       seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
+                       ++myy;
+                       if (t > absolute_max_time - seconds) {
+                               t = absolute_max_time;
+                               break;
+                       }
+                       t += seconds;
+               } else {
+                       --myy;
+                       seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
+                       if (t < absolute_min_time + seconds) {
+                               t = absolute_min_time;
+                               break;
+                       }
+                       t -= seconds;
+               }
+       }
+       return t;
+}
+
 static time_t
 hunt(char *name, time_t lot, time_t hit)
 {
        time_t          t;
+       long            diff;
        struct tm       lotm;
+       struct tm *     lotmp;
        struct tm       tm;
-       static char     loab[MAX_STRING_LENGTH];
+       struct tm *     tmp;
+       char                    loab[MAX_STRING_LENGTH];
 
-       lotm = *localtime(&lot);
-       strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
-       while ((hit - lot) >= 2) {
-               t = lot / 2 + hit / 2;
+       lotmp = my_localtime(&lot);
+       if (lotmp != NULL) {
+               lotm = *lotmp;
+               strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
+       }
+       for ( ; ; ) {
+               diff = (long) (hit - lot);
+               if (diff < 2)
+                       break;
+               t = lot;
+               t += diff / 2;
                if (t <= lot)
                        ++t;
                else if (t >= hit)
                        --t;
-               tm = *localtime(&t);
-               if (delta(&tm, &lotm) == (t - lot) &&
+               tmp = my_localtime(&t);
+               if (tmp != NULL)
+                       tm = *tmp;
+               if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
+                       (delta(&tm, &lotm) == (t - lot) &&
                        tm.tm_isdst == lotm.tm_isdst &&
-                       strcmp(abbr(&tm), loab) == 0) {
+                       strcmp(abbr(&tm), loab) == 0)) {
                                lot = t;
                                lotm = tm;
+                               lotmp = tmp;
                } else  hit = t;
        }
        show(name, lot, TRUE);
@@ -297,7 +478,7 @@ hunt(char *name, time_t lot, time_t hit)
 }
 
 /*
-** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta.
+** Thanks to Paul Eggert for logic used in delta.
 */
 
 static long
@@ -310,7 +491,7 @@ delta(struct tm *newp, struct tm *oldp)
                return -delta(oldp, newp);
        result = 0;
        for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
-               result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE);
+               result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
        result += newp->tm_yday - oldp->tm_yday;
        result *= HOURSPERDAY;
        result += newp->tm_hour - oldp->tm_hour;
@@ -327,19 +508,31 @@ show(char *zone, time_t t, int v)
        struct tm *     tmp;
 
        printf("%-*s  ", (int) longest, zone);
-       if (v)
-               printf("%.24s UTC = ", asctime(gmtime(&t)));
-       tmp = localtime(&t);
-       printf("%.24s", asctime(tmp));
-       if (*abbr(tmp) != '\0')
-               printf(" %s", abbr(tmp));
        if (v) {
-               printf(" isdst=%d", tmp->tm_isdst);
+               tmp = gmtime(&t);
+               if (tmp == NULL) {
+                       printf(tformat(), t);
+               } else {
+                       dumptime(tmp);
+                       printf(" UTC");
+               }
+               printf(" = ");
+       }
+       tmp = my_localtime(&t);
+       dumptime(tmp);
+       if (tmp != NULL) {
+               if (*abbr(tmp) != '\0')
+                       printf(" %s", abbr(tmp));
+               if (v) {
+                       printf(" isdst=%d", tmp->tm_isdst);
 #ifdef TM_GMTOFF
-               printf(" gmtoff=%ld", tmp->TM_GMTOFF);
+                       printf(" gmtoff=%ld", tmp->TM_GMTOFF);
 #endif /* defined TM_GMTOFF */
+               }
        }
        printf("\n");
+       if (tmp != NULL && *abbr(tmp) != '\0')
+               abbrok(abbr(tmp), zone);
 }
 
 static char *
@@ -353,3 +546,83 @@ abbr(struct tm *tmp)
        result = tzname[tmp->tm_isdst];
        return (result == NULL) ? &nada : result;
 }
+
+/*
+** The code below can fail on certain theoretical systems;
+** it works on all known real-world systems as of 2004-12-30.
+*/
+
+static const char *
+tformat(void)
+{
+       if (0.5 == (time_t) 0.5) {      /* floating */
+               if (sizeof (time_t) > sizeof (double))
+                       return "%Lg";
+               return "%g";
+       }
+       if (0 > (time_t) -1) {          /* signed */
+               if (sizeof (time_t) > sizeof (long))
+                       return "%lld";
+               if (sizeof (time_t) > sizeof (int))
+                       return "%ld";
+               return "%d";
+       }
+       if (sizeof (time_t) > sizeof (unsigned long))
+               return "%llu";
+       if (sizeof (time_t) > sizeof (unsigned int))
+               return "%lu";
+       return "%u";
+}
+
+static void
+dumptime(const struct tm *timeptr)
+{
+       static const char       wday_name[][3] = {
+               "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+       };
+       static const char       mon_name[][3] = {
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+       };
+       const char *    wn;
+       const char *    mn;
+       int             lead;
+       int             trail;
+
+       if (timeptr == NULL) {
+               printf("NULL");
+               return;
+       }
+       /*
+       ** The packaged versions of localtime and gmtime never put out-of-range
+       ** values in tm_wday or tm_mon, but since this code might be compiled
+       ** with other (perhaps experimental) versions, paranoia is in order.
+       */
+       if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
+               (int) (sizeof wday_name / sizeof wday_name[0]))
+                       wn = "???";
+       else            wn = wday_name[timeptr->tm_wday];
+       if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
+               (int) (sizeof mon_name / sizeof mon_name[0]))
+                       mn = "???";
+       else            mn = mon_name[timeptr->tm_mon];
+       printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
+               wn, mn,
+               timeptr->tm_mday, timeptr->tm_hour,
+               timeptr->tm_min, timeptr->tm_sec);
+#define DIVISOR        10
+       trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
+       lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
+               trail / DIVISOR;
+       trail %= DIVISOR;
+       if (trail < 0 && lead > 0) {
+               trail += DIVISOR;
+               --lead;
+       } else if (lead < 0 && trail > 0) {
+               trail -= DIVISOR;
+               ++lead;
+       }
+       if (lead == 0)
+               printf("%d", trail);
+       else    printf("%d%d", lead, ((trail < 0) ? -trail : trail));
+}
index 034ef13..24c7881 100644 (file)
@@ -1,6 +1,6 @@
 .\" $FreeBSD: src/usr.sbin/zic/zic.8,v 1.11.2.4 2003/03/11 22:31:35 trhodes Exp $
-.\" $DragonFly: src/usr.sbin/zic/zic.8,v 1.4 2007/09/02 19:30:48 swildner Exp $
-.Dd October 29, 1997
+.\" $DragonFly: src/usr.sbin/zic/zic.8,v 1.5 2008/10/19 20:15:58 swildner Exp $
+.Dd October 19, 2008
 .Dt ZIC 8
 .Os
 .Sh NAME
@@ -59,7 +59,7 @@ The
 .Nm
 utility will act as if the input contained a link line of the form
 .Bd -literal -offset indent
-.No "Link      timezone                localtime
+.No "Link      timezone                localtime"
 .Ed
 (Note that this action has no effect on
 .Dx ,
@@ -82,7 +82,7 @@ The
 .Nm
 utility will act as if the input contained a link line of the form
 .Bd -literal -offset indent
-.No "Link      timezone                posixrules
+.No "Link      timezone                posixrules"
 .Ed
 .It Fl u Ar user
 After creating each output file, change its owner to
@@ -93,6 +93,10 @@ Complain if a year that appears in a data file is outside the range
 of years representable by
 .Xr time 3
 values.
+Also complain if a time of 24:00
+(which cannot be handled by pre-1998 versions of
+.Nm )
+appears in the input.
 .It Fl s
 Limit time values stored in output files to values that are the same
 whether they're taken to be signed or unsigned.
@@ -119,7 +123,7 @@ rule lines, zone lines, and link lines.
 A rule line has the form:
 .Dl "Rule      NAME    FROM    TO      TYPE    IN      ON              AT      SAVE    LETTER/S
 For example:
-.Dl "Rule      US      1967    1973    \-      Apr     lastSun 2:00    1:00    D
+.Dl "Rule      US      1967    1973    \-      Apr     lastSun 2:00    1:00    D"
 .Pp
 The fields that make up a rule line are:
 .Bl -tag -width "LETTER/S" -offset indent
@@ -207,6 +211,8 @@ time in hours and minutes
 24-hour format time (for times after noon)
 .It 1:28:14
 time in hours, minutes, and seconds
+.It -
+equivalent to 0
 .El
 .Pp
 where hour 0 is midnight at the start of the day,
@@ -257,9 +263,9 @@ the variable part is null.
 .El
 .Pp
 A zone line has the form:
-.Dl "Zone      NAME    GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+.Dl "Zone      NAME    GMTOFF  RULES/SAVE      FORMAT  [UNTILYEAR [MONTH [DAY [TIME]]]]"
 For example:
-.Dl "Zone      Australia/Adelaide      9:30    Aus     CST     1971 Oct 31 2:00
+.Dl "Zone      Australia/Adelaide      9:30    Aus     CST     1971 Oct 31 2:00"
 The fields that make up a zone line are:
 .Bl -tag -width indent
 .It NAME
@@ -290,15 +296,15 @@ of the time zone abbreviation goes.
 Alternately,
 a slash (/)
 separates standard and daylight abbreviations.
-.It UNTIL
+.It UNTILYEAR [MONTH [DAY [TIME]]]
 The time at which the UTC offset or the rule(s) change for a location.
 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 UTC 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.
+fields of a rule; trailing fields can be omitted, and default to the
+earliest possible value for the missing fields.
 .Pp
 The next line must be a
 .Dq continuation
@@ -307,18 +313,18 @@ string
 .Dq Zone
 and the name are omitted, as the continuation line will
 place information starting at the time specified as the
-.Em UNTIL
-field in the previous line in the file used by the previous line.
-Continuation lines may contain an
-.Em UNTIL
-field, just as zone lines do, indicating that the next line is a further
+.Dq until
+information in the previous line in the file used by the previous line.
+Continuation lines may contain
+.Dq until
+information, just as zone lines do, indicating that the next line is a further
 continuation.
 .El
 .Pp
 A link line has the form
-.Dl "Link      LINK-FROM       LINK-TO
+.Dl "Link      LINK-FROM       LINK-TO"
 For example:
-.Dl "Link      Europe/Istanbul Asia/Istanbul
+.Dl "Link      Europe/Istanbul Asia/Istanbul"
 The
 .Em LINK-FROM
 field should appear as the
@@ -332,9 +338,9 @@ Except for continuation lines,
 lines may appear in any order in the input.
 .Pp
 Lines in the file that describes leap seconds have the following form:
-.Dl "Leap      YEAR    MONTH   DAY     HH:MM:SS        CORR    R/S
+.Dl "Leap      YEAR    MONTH   DAY     HH:MM:SS        CORR    R/S"
 For example:
-.Dl "Leap      1974    Dec     31      23:59:60        +       S
+.Dl "Leap      1974    Dec     31      23:59:60        +       S"
 The
 .Em YEAR ,
 .Em MONTH ,
@@ -373,12 +379,85 @@ or
 .Dq Rolling
 if the leap second time given by the other fields should be interpreted as
 local wall clock time.
-.Sh NOTE
+.Sh "EXTENDED EXAMPLE"
+Here is an extended example of
+.Nm
+input, intended to illustrate many of its features.
+.Bd -literal
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Swiss   1940    only    -       Nov     2       0:00    1:00    S
+Rule   Swiss   1940    only    -       Dec     31      0:00    0       -
+Rule   Swiss   1941    1942    -       May     Sun>=1  2:00    1:00    S
+Rule   Swiss   1941    1942    -       Oct     Sun>=1  0:00    0
+
+Rule   EU      1977    1980    -       Apr     Sun>=1  1:00u   1:00    S
+Rule   EU      1977    only    -       Sep     lastSun 1:00u   0       -
+Rule   EU      1978    only    -       Oct      1      1:00u   0       -
+Rule   EU      1979    1995    -       Sep     lastSun 1:00u   0       -
+Rule   EU      1981    max     -       Mar     lastSun 1:00u   1:00    S
+Rule   EU      1996    max     -       Oct     lastSun 1:00u   0       -
+
+# Zone NAME    GMTOFF  RULES   FORMAT  UNTIL
+Zone   Europe/Zurich   0:34:08 -       LMT     1848 Sep 12
+               0:29:44 -       BMT     1894 Jun
+               1:00    Swiss   CE%sT   1981
+               1:00    EU      CE%sT
+
+Link   Europe/Zurich   Switzerland
+.Ed
+.Pp
+In this example, the zone is named Europe/Zurich but it has an alias
+as Switzerland.
+Zurich was 34 minutes and 8 seconds west of GMT until
+1848-09-12 at 00:00, when the offset changed to 29 minutes and 44
+seconds.
+After 1894-06-01 at 00:00 Swiss daylight saving rules (defined
+with lines beginning with
+.Dq Rule Swiss )
+apply, and the GMT offset became one hour.
+From 1981 to the present, EU daylight saving rules have
+applied, and the UTC offset has remained at one hour.
+.Pp
+In 1940, daylight saving time applied from November 2 at 00:00 to
+December 31 at 00:00.
+In 1941 and 1942, daylight saving time applied
+from the first Sunday in May at 02:00 to the first Sunday in October
+at 00:00.
+The pre-1981 EU daylight-saving rules have no effect
+here, but are included for completeness.
+Since 1981, daylight
+saving has begun on the last Sunday in March at 01:00 UTC.
+Until 1995 it ended the last Sunday in September at 01:00 UTC,
+but this changed to the last Sunday in October starting in 1996.
+.Pp
+For purposes of
+display,
+.Dq LMT
+and
+.Dq BMT
+were initially used, respectively.
+Since
+Swiss rules and later EU rules were applied, the display name for the
+timezone has been CET for standard time and CEST for daylight saving
+time.
+.Sh NOTES
 For areas with more than two types of local time,
 you may need to use local standard time in the
 .Em AT
 field of the earliest transition time's rule to ensure that
 the earliest transition time recorded in the compiled file is correct.
+.Pp
+If,
+for a particular zone,
+a clock advance caused by the start of daylight saving
+coincides with and is equal to
+a clock retreat caused by a change in UTC offset,
+.Nm
+produces a single transition to daylight saving at the new UTC offset
+(without any change in wall clock time).
+To get separate transitions
+use multiple zone continuation lines
+specifying transition instants using universal time.
 .Sh FILES
 .Bl -tag -width ".Pa /usr/share/zoneinfo" -compact
 .It Pa /usr/share/zoneinfo
@@ -388,4 +467,4 @@ standard directory used for created files
 .Xr ctime 3 ,
 .Xr tzfile 5 ,
 .Xr zdump 8
-.\" @(#)zic.8  7.18
+.\" @(#)zic.8  8.4
index c4a83b1..eb1c51e 100644 (file)
@@ -1,25 +1,33 @@
-#ifndef lint
-#ifndef NOID
-static char    elsieid[] = "@(#)zic.c  7.96";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
 /*
- * @(#)zic.c   7.96
- * $FreeBSD: src/usr.sbin/zic/zic.c,v 1.11 1999/08/28 01:21:20 peter Exp $
- * $DragonFly: src/usr.sbin/zic/zic.c,v 1.6 2004/12/18 22:48:15 swildner Exp $
- */
-#include "private.h"
-#include "tzfile.h"
+** This file is in the public domain, so clarified as of
+** 2006-07-17 by Arthur David Olson.
+**
+** @(#)zic.c   8.17
+** $FreeBSD: src/usr.sbin/zic/zic.c,v 1.11 1999/08/28 01:21:20 peter Exp $
+** $DragonFly: src/usr.sbin/zic/zic.c,v 1.7 2008/10/19 20:15:58 swildner Exp $
+*/
+
 #include <err.h>
 #include <locale.h>
 #include <sys/stat.h>                  /* for umask manifest constants */
 #include <sys/types.h>
 #include <unistd.h>
+#include "private.h"
+#include "tzfile.h"
 
+#define        ZIC_VERSION     '2'
+
+typedef int_fast64_t   zic_t;
+
+#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
+#define ZIC_MAX_ABBR_LEN_WO_WARN       6
+#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
+
+#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
 /*
 ** On some ancient hosts, predicates like `isspace(C)' are defined
-** only if isascii(C) || C == EOF.  Modern hosts obey the C Standard,
+** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
 ** which says they are defined only if C == ((unsigned char) C) || C == EOF.
 ** Neither the C Standard nor Posix require that `isascii' exist.
 ** For portability, we check both ancient and modern requirements.
@@ -30,6 +38,11 @@ static char  elsieid[] = "@(#)zic.c  7.96";
 #define isascii(x) 1
 #endif
 
+#define OFFSET_STRLEN_MAXIMUM  (7 + INT_STRLEN_MAXIMUM(long))
+#define RULE_STRLEN_MAXIMUM    8       /* "Mdd.dd.d" */
+
+#define end(cp)        (strchr((cp), '\0'))
+
 struct rule {
        const char *    r_filename;
        int             r_linenum;
@@ -38,6 +51,8 @@ struct rule {
        int             r_loyear;       /* for example, 1986 */
        int             r_hiyear;       /* for example, 1986 */
        const char *    r_yrtype;
+       int             r_lowasnum;
+       int             r_hiwasnum;
 
        int             r_month;        /* 0..11 */
 
@@ -54,7 +69,7 @@ struct rule {
        const char *    r_abbrvar;      /* variable part of abbreviation */
 
        int             r_todo;         /* a rule to do (used in outzone) */
-       time_t          r_temp;         /* used in outzone */
+       zic_t           r_temp;         /* used in outzone */
 };
 
 /*
@@ -80,20 +95,21 @@ struct zone {
        int             z_nrules;
 
        struct rule     z_untilrule;
-       time_t          z_untiltime;
+       zic_t           z_untiltime;
 };
 
-static void    addtt(time_t starttime, int type);
+static void    addtt(zic_t starttime, int type);
 static int     addtype(long gmtoff, const char *abbr, int isdst,
                        int ttisstd, int ttisgmt);
-static void    leapadd(time_t t, int positive, int rolling, int count);
+static void    leapadd(zic_t t, int positive, int rolling, int count);
 static void    adjleap(void);
 static void    associate(void);
 static int     ciequal(const char *ap, const char *bp);
 static void    convert(long val, char *buf);
-static void    dolink(const char *fromfile, const char *tofile);
+static void    convert64(zic_t val, char *buf);
+static void    dolink(const char *fromfield, const char *tofield);
 static void    doabbr(char *abbr, const char *format,
-                      const char *letters, int isdst);
+                      const char *letters, int isdst, int doquotes);
 static void    eat(const char *name, int num);
 static void    eats(const char *name, int num,
                     const char *rname, int rnum);
@@ -109,6 +125,7 @@ static void inrule(char **fields, int nfields);
 static int     inzcont(char **fields, int nfields);
 static int     inzone(char **fields, int nfields);
 static int     inzsub(char **fields, int nfields, int iscont);
+static int     is32(zic_t x);
 static int     itsabbr(const char *abbr, const char *word);
 static int     itsdir(const char *name);
 static int     lowerit(int c);
@@ -118,35 +135,40 @@ static void       newabbr(const char *abbr);
 static long    oadd(long t1, long t2);
 static void    outzone(const struct zone *zp, int ntzones);
 static void    puttzcode(long code, FILE *fp);
+static void    puttzcode64(zic_t code, FILE *fp);
 static int     rcomp(const void *leftp, const void *rightp);
-static time_t  rpytime(const struct rule *rp, int wantedy);
+static zic_t   rpytime(const struct rule *rp, int wantedy);
 static void    rulesub(struct rule *rp,
                        const char *loyearp, const char *hiyearp,
                        const char *typep, const char *monthp,
                        const char *dayp, const char *timep);
+static int     stringoffset(char *result, long offset);
+static int     stringrule(char *result, const struct rule *rp,
+                          long dstoff, long gmtoff);
+static void    stringzone(char *result,
+                       const struct zone *zp, int ntzones);
 static void    setboundaries(void);
 static void    setgroup(gid_t *flag, const char *name);
 static void    setuser(uid_t *flag, const char *name);
-static time_t  tadd(time_t t1, long t2);
+static zic_t   tadd(const zic_t t1, const long t2);
 static void    usage(void);
-static void    writezone(const char *name);
+static void    writezone(const char *name, const char *string);
 static int     yearistype(int year, const char *type);
 
-#if !(HAVE_STRERROR - 0)
-static char *  strerror(int);
-#endif /* !(HAVE_STRERROR - 0) */
-
 static int             charcnt;
 static int             errors;
 static const char *    filename;
 static int             leapcnt;
+static int             leapseen;
+static int             leapminyear;
+static int             leapmaxyear;
 static int             linenum;
-static time_t          max_time;
+static int             max_abbrvar_len;
+static int             max_format_len;
+static zic_t           max_time;
 static int             max_year;
-static int             max_year_representable;
-static time_t          min_time;
+static zic_t           min_time;
 static int             min_year;
-static int             min_year_representable;
 static int             noise;
 static const char *    rfilename;
 static int             rlinenum;
@@ -333,7 +355,7 @@ static const int    len_years[2] = {
 };
 
 static struct attype {
-       time_t          at;
+       zic_t           at;
        unsigned char   type;
 }                      attypes[TZ_MAX_TIMES];
 static long            gmtoffs[TZ_MAX_TYPES];
@@ -342,7 +364,7 @@ static unsigned char        abbrinds[TZ_MAX_TYPES];
 static char            ttisstds[TZ_MAX_TYPES];
 static char            ttisgmts[TZ_MAX_TYPES];
 static char            chars[TZ_MAX_CHARS];
-static time_t          trans[TZ_MAX_LEAPS];
+static zic_t           trans[TZ_MAX_LEAPS];
 static long            corr[TZ_MAX_LEAPS];
 static char            roll[TZ_MAX_LEAPS];
 
@@ -367,18 +389,6 @@ memcheck(char * const ptr)
 ** Error handling.
 */
 
-#if !(HAVE_STRERROR - 0)
-static char *
-strerror(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(const char * const name, const int num,
      const char * const rname, const int rnum)
@@ -392,7 +402,7 @@ eats(const char * const name, const int num,
 static void
 eat(const char * const name, const int num)
 {
-       eats(name, num, (char *) NULL, -1);
+       eats(name, num, NULL, -1);
 }
 
 static void
@@ -428,8 +438,8 @@ static void
 usage(void)
 {
        fprintf(stderr, "%s\n%s\n",
-_("usage: zic [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"),
-_("           [-L leapseconds] [-y yearistype] [filename ... ]"));
+_("usage: zic [-v] [-l localtime] [-p posixrules] [-d directory]"),
+_("           [-L leapseconds] [-y yearistype] [filename ...]"));
        exit(EXIT_FAILURE);
 }
 
@@ -438,7 +448,6 @@ static const char * lcltime;
 static const char *    directory;
 static const char *    leapsec;
 static const char *    yitcommand;
-static int             sflag = FALSE;
 static int             Dflag;
 static uid_t           uflag = (uid_t)-1;
 static gid_t           gflag = (gid_t)-1;
@@ -455,13 +464,6 @@ main(int argc, char *argv[])
 #ifdef unix
        umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
 #endif /* defined unix */
-#if HAVE_GETTEXT - 0
-       setlocale(LC_MESSAGES, "");
-#ifdef TZ_DOMAINDIR
-       bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* defined TEXTDOMAINDIR */
-       textdomain(TZ_DOMAIN);
-#endif /* HAVE_GETTEXT - 0 */
        while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
                switch (c) {
                        default:
@@ -520,7 +522,7 @@ _("more than one -L option specified"));
                                noise = TRUE;
                                break;
                        case 's':
-                               sflag = TRUE;
+                               warnx(_("-s ignored\n"));
                                break;
                }
        if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
@@ -553,34 +555,45 @@ _("more than one -L option specified"));
        /*
        ** Make links.
        */
-       for (i = 0; i < nlinks; ++i)
+       for (i = 0; i < nlinks; ++i) {
+               eat(links[i].l_filename, links[i].l_linenum);
                dolink(links[i].l_from, links[i].l_to);
-       if (lcltime != NULL)
+               if (noise)
+                       for (j = 0; j < nlinks; ++j)
+                               if (strcmp(links[i].l_to,
+                                       links[j].l_from) == 0)
+                                               warning(_("link to link"));
+       }
+       if (lcltime != NULL) {
+               eat("command line", 1);
                dolink(lcltime, TZDEFAULT);
-       if (psxrules != NULL)
+       }
+       if (psxrules != NULL) {
+               eat("command line", 1);
                dolink(psxrules, TZDEFRULES);
+       }
        return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
 static void
-dolink(const char * const fromfile, const char * const tofile)
+dolink(const char * const fromfield, const char * const tofield)
 {
        char *fromname;
        char *toname;
 
-       if (fromfile[0] == '/')
-               fromname = ecpyalloc(fromfile);
+       if (fromfield[0] == '/')
+               fromname = ecpyalloc(fromfield);
        else {
                fromname = ecpyalloc(directory);
                fromname = ecatalloc(fromname, "/");
-               fromname = ecatalloc(fromname, fromfile);
+               fromname = ecatalloc(fromname, fromfield);
        }
-       if (tofile[0] == '/')
-               toname = ecpyalloc(tofile);
+       if (tofield[0] == '/')
+               toname = ecpyalloc(tofield);
        else {
                toname = ecpyalloc(directory);
                toname = ecatalloc(toname, "/");
-               toname = ecatalloc(toname, tofile);
+               toname = ecatalloc(toname, tofield);
        }
        /*
        ** We get to be careful here since
@@ -593,14 +606,29 @@ dolink(const char * const fromfile, const char * const tofile)
 
                if (mkdirs(toname) != 0)
                        exit(EXIT_FAILURE);
+
                result = link(fromname, toname);
-#if (HAVE_SYMLINK - 0) 
-               if (result != 0) {
-                       result = symlink(fromname, toname);
-                       if (result == 0)
+#if HAVE_SYMLINK
+               if (result != 0 &&
+                       access(fromname, F_OK) == 0 &&
+                       !itsdir(fromname)) {
+                               const char *s = tofield;
+                               char * symlinkcontents = NULL;
+
+                               while ((s = strchr(s+1, '/')) != NULL)
+                                       symlinkcontents =
+                                               ecatalloc(symlinkcontents,
+                                               "../");
+                               symlinkcontents =
+                                       ecatalloc(symlinkcontents,
+                                       fromname);
+                               result = symlink(symlinkcontents,
+                                       toname);
+                               if (result == 0)
 warning(_("hard link failed, symbolic link used"));
+                               ifree(symlinkcontents);
                }
-#endif
+#endif /* HAVE_SYMLINK */
                if (result != 0) {
                        err(EXIT_FAILURE, _("can't link from %s to %s"),
                            fromname, toname);
@@ -610,43 +638,17 @@ warning(_("hard link failed, symbolic link used"));
        ifree(toname);
 }
 
-#ifndef INT_MAX
-#define INT_MAX        ((int) (((unsigned)~0)>>1))
-#endif /* !defined INT_MAX */
-
-#ifndef INT_MIN
-#define INT_MIN        ((int) ~(((unsigned)~0)>>1))
-#endif /* !defined INT_MIN */
-
-/*
-** The tz file format currently allows at most 32-bit quantities.
-** This restriction should be removed before signed 32-bit values
-** wrap around in 2038, but unfortunately this will require a
-** change to the tz file format.
-*/
-
-#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    64
 
 static void
 setboundaries(void)
 {
-       if (TYPE_SIGNED(time_t)) {
-               min_time = ~ (time_t) 0;
-               min_time <<= TIME_T_BITS_IN_FILE - 1;
-               max_time = ~ (time_t) 0 - min_time;
-               if (sflag)
-                       min_time = 0;
-       } else {
-               min_time = 0;
-               max_time = 2 - sflag;
-               max_time <<= TIME_T_BITS_IN_FILE - 1;
-               --max_time;
-       }
-       min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
-       max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
-       min_year_representable = min_year;
-       max_year_representable = max_year;
+       int     i;
+
+       min_time = -1;
+       for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
+               min_time *= 2;
+       max_time = -(min_time + 1);
 }
 
 static int
@@ -740,7 +742,7 @@ associate(void)
                        */
                        eat(zp->z_filename, zp->z_linenum);
                        zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
-                                             TRUE);
+                               TRUE);
                        /*
                        ** Note, though, that if there's no rule,
                        ** a '%s' in the format is a bad thing.
@@ -843,7 +845,8 @@ _("panic: invalid l_value %d"), lp->l_value);
 static long
 gethms(const char *string, const char * const errstring, const int signable)
 {
-       int     hh, mm, ss, sign;
+       long    hh;
+       int     mm, ss, sign;
 
        if (string == NULL || *string == '\0')
                return 0;
@@ -853,25 +856,32 @@ gethms(const char *string, const char * const errstring, const int signable)
                sign = -1;
                ++string;
        } else  sign = 1;
-       if (sscanf(string, scheck(string, "%d"), &hh) == 1)
+       if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
                mm = ss = 0;
-       else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
+       else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
                ss = 0;
-       else if (sscanf(string, scheck(string, "%d:%d:%d"),
+       else if (sscanf(string, scheck(string, "%ld:%d:%d"),
                &hh, &mm, &ss) != 3) {
                        error(errstring);
                        return 0;
        }
-       if ((hh < 0 || hh >= HOURSPERDAY ||
+       if (hh < 0 ||
                mm < 0 || mm >= MINSPERHOUR ||
-               ss < 0 || ss > SECSPERMIN) &&
-               !(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
+               ss < 0 || ss > SECSPERMIN) {
                        error(errstring);
                        return 0;
        }
-       return eitol(sign) *
-               (eitol(hh * MINSPERHOUR + mm) *
-               eitol(SECSPERMIN) + eitol(ss));
+       if (LONG_MAX / SECSPERHOUR < hh) {
+               error(_("time overflow"));
+               return 0;
+       }
+       if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
+               warning(_("24:00 not handled by pre-1998 versions of zic"));
+       if (noise && (hh > HOURSPERDAY ||
+               (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
+warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
+       return oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
+                   eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)));
 }
 
 static void
@@ -894,6 +904,8 @@ inrule(char ** const fields, const int nfields)
                fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
        r.r_name = ecpyalloc(fields[RF_NAME]);
        r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+       if (max_abbrvar_len < strlen(r.r_abbrvar))
+               max_abbrvar_len = strlen(r.r_abbrvar);
        rules = (struct rule *) (void *) erealloc((char *) rules,
                (int) ((nrules + 1) * sizeof *rules));
        rules[nrules++] = r;
@@ -992,6 +1004,8 @@ inzsub(char ** const fields, const int nfields, const int iscont)
        }
        z.z_rule = ecpyalloc(fields[i_rule]);
        z.z_format = ecpyalloc(fields[i_format]);
+       if (max_format_len < strlen(z.z_format))
+               max_format_len = strlen(z.z_format);
        hasuntil = nfields > i_untilyear;
        if (hasuntil) {
                z.z_untilrule.r_filename = filename;
@@ -1012,7 +1026,9 @@ inzsub(char ** const fields, const int nfields, 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;
                }
        }
@@ -1034,7 +1050,7 @@ inleap(char ** const fields, const int nfields)
        int i, j;
        int year, month, day;
        long dayoff, tod;
-       time_t t;
+       zic_t t;
 
        if (nfields != LEAP_FIELDS) {
                error(_("wrong number of fields on Leap line"));
@@ -1043,12 +1059,17 @@ inleap(char ** const fields, const int nfields)
        dayoff = 0;
        cp = fields[LP_YEAR];
        if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
-                       /*
-                        * Leapin' Lizards!
-                        */
-                       error(_("invalid leaping year"));
-                       return;
+               /*
+               ** Leapin' Lizards!
+               */
+               error(_("invalid leaping year"));
+               return;
        }
+       if (!leapseen || leapmaxyear < year)
+               leapmaxyear = year;
+       if (!leapseen || leapminyear > year)
+               leapminyear = year;
+       leapseen = TRUE;
        j = EPOCH_YEAR;
        while (j != year) {
                if (year > j) {
@@ -1078,18 +1099,19 @@ inleap(char ** const fields, const int nfields)
                        return;
        }
        dayoff = oadd(dayoff, eitol(day - 1));
-       if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
+       if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
                error(_("time before zero"));
                return;
        }
-       t = (time_t) dayoff * SECSPERDAY;
-       /*
-       ** Cheap overflow check.
-       */
-       if (t / SECSPERDAY != dayoff) {
-               error(_("time overflow"));
+       if (dayoff < min_time / SECSPERDAY) {
+               error(_("time too small"));
+               return;
+       }
+       if (dayoff > max_time / SECSPERDAY) {
+               error(_("time too large"));
                return;
        }
+       t = (zic_t) dayoff * SECSPERDAY;
        tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
        cp = fields[LP_CORR];
        {
@@ -1113,7 +1135,9 @@ inleap(char ** const fields, const int nfields)
                        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);
@@ -1197,7 +1221,8 @@ rulesub(struct rule * const rp,
        */
        cp = loyearp;
        lp = byword(cp, begin_years);
-       if (lp != NULL) switch ((int) lp->l_value) {
+       rp->r_lowasnum = lp == NULL;
+       if (!rp->r_lowasnum) switch ((int) lp->l_value) {
                case YR_MINIMUM:
                        rp->r_loyear = INT_MIN;
                        break;
@@ -1210,14 +1235,11 @@ rulesub(struct rule * const rp,
        } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
                error(_("invalid starting year"));
                return;
-       } else if (noise) {
-               if (rp->r_loyear < min_year_representable)
-                       warning(_("starting year too low to be represented"));
-               else if (rp->r_loyear > max_year_representable)
-                       warning(_("starting year too high to be represented"));
        }
        cp = hiyearp;
-       if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
+       lp = byword(cp, end_years);
+       rp->r_hiwasnum = lp == NULL;
+       if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
                case YR_MINIMUM:
                        rp->r_hiyear = INT_MIN;
                        break;
@@ -1233,11 +1255,6 @@ rulesub(struct rule * const rp,
        } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
                error(_("invalid ending year"));
                return;
-       } else if (noise) {
-               if (rp->r_loyear < min_year_representable)
-                       warning(_("starting year too low to be represented"));
-               else if (rp->r_loyear > max_year_representable)
-                       warning(_("starting year too high to be represented"));
        }
        if (rp->r_loyear > rp->r_hiyear) {
                error(_("starting year greater than ending year"));
@@ -1252,8 +1269,6 @@ rulesub(struct rule * const rp,
                }
                rp->r_yrtype = ecpyalloc(typep);
        }
-       if (rp->r_loyear < min_year && rp->r_loyear > 0)
-               min_year = rp->r_loyear;
        /*
        ** Day work.
        ** Accept things such as:
@@ -1305,12 +1320,22 @@ static void
 convert(const long val, char * const buf)
 {
        int i;
-       long shift;
+       int shift;
 
        for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
                buf[i] = val >> shift;
 }
 
+static void
+convert64(const zic_t val, char * const buf)
+{
+       int     i;
+       int     shift;
+
+       for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
+               buf[i] = val >> shift;
+}
+
 static void
 puttzcode(const long val, FILE * const fp)
 {
@@ -1320,28 +1345,43 @@ puttzcode(const long val, FILE * const fp)
        fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
 }
 
+static void
+puttzcode64(const zic_t val, FILE * const fp)
+{
+       char    buf[8];
+
+       convert64(val, buf);
+       fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
+}
+
 static int
-atcomp(const void *avp, const void *bvp)
+atcomp(const void * avp, const void * bvp)
 {
-       const struct attype *ap = avp;
-       const struct attype *bp = bvp;
+       const zic_t     a = ((const struct attype *) avp)->at;
+       const zic_t     b = ((const struct attype *) bvp)->at;
 
-       if (ap->at < bp->at)
-               return -1;
-       else if (ap->at > bp->at)
-               return 1;
-       else    return 0;
+       return (a < b) ? -1 : (a > b);
+}
+
+static int
+is32(const zic_t x)
+{
+       return INT32_MIN <= x && x <= INT32_MAX;
 }
 
 static void
-writezone(const char * const name)
+writezone(const char * const name, const char * const string)
 {
-       FILE * fp;
-       int i, j;
-       static char *fullname;
-       static struct tzhead tzh;
-       time_t ats[TZ_MAX_TIMES];
-       unsigned char types[TZ_MAX_TIMES];
+       FILE *                  fp;
+       int                     i, j;
+       int                     leapcnt32, leapi32;
+       int                     timecnt32, timei32;
+       int                     pass;
+       static char *           fullname;
+       static const struct tzhead tzh0;
+       static struct tzhead    tzh;
+       zic_t                   ats[TZ_MAX_TIMES];
+       unsigned char           types[TZ_MAX_TIMES];
 
        /*
        ** Sort.
@@ -1364,14 +1404,13 @@ writezone(const char * const name)
                        while (fromi < timecnt && 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[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)
@@ -1386,82 +1425,194 @@ writezone(const char * const name)
                ats[i] = attypes[i].at;
                types[i] = attypes[i].type;
        }
+       /*
+       ** Correct for leap seconds.
+       */
+       for (i = 0; i < timecnt; ++i) {
+               j = leapcnt;
+               while (--j >= 0)
+                       if (ats[i] > trans[j] - corr[j]) {
+                               ats[i] = tadd(ats[i], corr[j]);
+                               break;
+                       }
+       }
+       /*
+       ** Figure out 32-bit-limited starts and counts.
+       */
+       timecnt32 = timecnt;
+       timei32 = 0;
+       leapcnt32 = leapcnt;
+       leapi32 = 0;
+       while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
+               --timecnt32;
+       while (timecnt32 > 0 && !is32(ats[timei32])) {
+               --timecnt32;
+               ++timei32;
+       }
+       while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
+               --leapcnt32;
+       while (leapcnt32 > 0 && !is32(trans[leapi32])) {
+               --leapcnt32;
+               ++leapi32;
+       }
        fullname = erealloc(fullname,
                (int) (strlen(directory) + 1 + strlen(name) + 1));
        sprintf(fullname, "%s/%s", directory, name);
-
        /*
-        * Remove old file, if any, to snap links.
-        */
+       ** Remove old file, if any, to snap links.
+       */
        if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
                err(EXIT_FAILURE, _("can't remove %s"), fullname);
-
        if ((fp = fopen(fullname, "wb")) == NULL) {
                if (mkdirs(fullname) != 0)
                        exit(EXIT_FAILURE);
                if ((fp = fopen(fullname, "wb")) == NULL)
                        err(EXIT_FAILURE, _("can't create %s"), fullname);
        }
-       convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
-       convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
-       convert(eitol(leapcnt), tzh.tzh_leapcnt);
-       convert(eitol(timecnt), tzh.tzh_timecnt);
-       convert(eitol(typecnt), tzh.tzh_typecnt);
-       convert(eitol(charcnt), tzh.tzh_charcnt);
-       strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
-#define DO(field)      fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
-       DO(tzh_magic);
-       DO(tzh_reserved);
-       DO(tzh_ttisgmtcnt);
-       DO(tzh_ttisstdcnt);
-       DO(tzh_leapcnt);
-       DO(tzh_timecnt);
-       DO(tzh_typecnt);
-       DO(tzh_charcnt);
-#undef DO
-       for (i = 0; i < timecnt; ++i) {
-               j = leapcnt;
-               while (--j >= 0)
-                       if (ats[i] >= trans[j]) {
-                               ats[i] = tadd(ats[i], corr[j]);
-                               break;
+       for (pass = 1; pass <= 2; ++pass) {
+               int     thistimei, thistimecnt;
+               int     thisleapi, thisleapcnt;
+               int     thistimelim, thisleaplim;
+               int     writetype[TZ_MAX_TIMES];
+               int     typemap[TZ_MAX_TYPES];
+               int     thistypecnt;
+               char    thischars[TZ_MAX_CHARS];
+               char    thischarcnt;
+               int     indmap[TZ_MAX_CHARS];
+
+               if (pass == 1) {
+                       thistimei = timei32;
+                       thistimecnt = timecnt32;
+                       thisleapi = leapi32;
+                       thisleapcnt = leapcnt32;
+               } else {
+                       thistimei = 0;
+                       thistimecnt = timecnt;
+                       thisleapi = 0;
+                       thisleapcnt = leapcnt;
+               }
+               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;
+               }
+               thistypecnt = 0;
+               for (i = 0; i < typecnt; ++i)
+                       typemap[i] = writetype[i] ?  thistypecnt++ : -1;
+               for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
+                       indmap[i] = -1;
+               thischarcnt = 0;
+               for (i = 0; i < typecnt; ++i) {
+                       char *  thisabbr;
+
+                       if (!writetype[i])
+                               continue;
+                       if (indmap[abbrinds[i]] >= 0)
+                               continue;
+                       thisabbr = &chars[abbrinds[i]];
+                       for (j = 0; j < thischarcnt; ++j)
+                               if (strcmp(&thischars[j], thisabbr) == 0)
+                                       break;
+                       if (j == thischarcnt) {
+                               strcpy(&thischars[(int) thischarcnt],
+                                       thisabbr);
+                               thischarcnt += strlen(thisabbr) + 1;
                        }
-               puttzcode((long) ats[i], fp);
-       }
-       if (timecnt > 0)
-               fwrite((void *) types, (size_t) sizeof types[0],
-                       (size_t) timecnt, fp);
-       for (i = 0; i < typecnt; ++i) {
-               puttzcode((long) gmtoffs[i], fp);
-               putc(isdsts[i], fp);
-               putc(abbrinds[i], fp);
-       }
-       if (charcnt != 0)
-               fwrite((void *) chars, (size_t) sizeof chars[0],
-                       (size_t) charcnt, fp);
-       for (i = 0; i < leapcnt; ++i) {
-               if (roll[i]) {
-                       if (timecnt == 0 || trans[i] < ats[0]) {
-                               j = 0;
-                               while (isdsts[j])
-                                       if (++j >= typecnt) {
-                                               j = 0;
-                                               break;
-                                       }
-                       } else {
-                               j = 1;
-                               while (j < timecnt && trans[i] >= ats[j])
-                                       ++j;
-                               j = types[j - 1];
+                       indmap[abbrinds[i]] = j;
+               }
+#define DO(field)      fwrite((void *) tzh.field, \
+                               (size_t) sizeof tzh.field, (size_t) 1, fp)
+               tzh = tzh0;
+               strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+               tzh.tzh_version[0] = ZIC_VERSION;
+               convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
+               convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
+               convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
+               convert(eitol(thistimecnt), tzh.tzh_timecnt);
+               convert(eitol(thistypecnt), tzh.tzh_typecnt);
+               convert(eitol(thischarcnt), tzh.tzh_charcnt);
+               DO(tzh_magic);
+               DO(tzh_version);
+               DO(tzh_reserved);
+               DO(tzh_ttisgmtcnt);
+               DO(tzh_ttisstdcnt);
+               DO(tzh_leapcnt);
+               DO(tzh_timecnt);
+               DO(tzh_typecnt);
+               DO(tzh_charcnt);
+#undef DO
+               for (i = thistimei; i < thistimelim; ++i)
+                       if (pass == 1)
+                               puttzcode((long) ats[i], fp);
+                       else    puttzcode64(ats[i], fp);
+               for (i = thistimei; i < thistimelim; ++i) {
+                       unsigned char   uc;
+
+                       uc = typemap[types[i]];
+                       fwrite((void *) &uc,
+                               (size_t) sizeof uc,
+                               (size_t) 1,
+                               fp);
+               }
+               for (i = 0; i < typecnt; ++i)
+                       if (writetype[i]) {
+                               puttzcode(gmtoffs[i], fp);
+                               putc(isdsts[i], fp);
+                               putc((unsigned char) indmap[abbrinds[i]], fp);
                        }
-                       puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
-               } else  puttzcode((long) trans[i], fp);
-               puttzcode((long) corr[i], fp);
-       }
-       for (i = 0; i < typecnt; ++i)
-               putc(ttisstds[i], fp);
-       for (i = 0; i < typecnt; ++i)
-               putc(ttisgmts[i], fp);
+               if (thischarcnt != 0)
+                       fwrite((void *) thischars,
+                               (size_t) sizeof thischars[0],
+                               (size_t) thischarcnt, fp);
+               for (i = thisleapi; i < thisleaplim; ++i) {
+                       register zic_t  todo;
+
+                       if (roll[i]) {
+                               if (timecnt == 0 || trans[i] < ats[0]) {
+                                       j = 0;
+                                       while (isdsts[j])
+                                               if (++j >= typecnt) {
+                                                       j = 0;
+                                                       break;
+                                               }
+                               } else {
+                                       j = 1;
+                                       while (j < timecnt &&
+                                               trans[i] >= ats[j])
+                                                       ++j;
+                                       j = types[j - 1];
+                               }
+                               todo = tadd(trans[i], -gmtoffs[j]);
+                       } else  todo = trans[i];
+                       if (pass == 1)
+                               puttzcode((long) todo, fp);
+                       else    puttzcode64(todo, fp);
+                       puttzcode(corr[i], fp);
+               }
+               for (i = 0; i < typecnt; ++i)
+                       if (writetype[i])
+                               putc(ttisstds[i], fp);
+               for (i = 0; i < typecnt; ++i)
+                       if (writetype[i])
+                               putc(ttisgmts[i], fp);
+       }
+       fprintf(fp, "\n%s\n", string);
        if (ferror(fp) || fclose(fp))
                errx(EXIT_FAILURE, _("error writing %s"), fullname);
        if (chmod(fullname, mflag) < 0)
@@ -1475,17 +1626,210 @@ writezone(const char * const name)
 
 static void
 doabbr(char * const abbr, const char * const format,
-       const char * const letters, const int isdst)
+       const char * const letters, const int isdst, const int doquotes)
 {
-       if (strchr(format, '/') == NULL) {
+       char *  cp;
+       char *  slashp;
+       int     len;
+
+       slashp = strchr(format, '/');
+       if (slashp == NULL) {
                if (letters == NULL)
                        strcpy(abbr, format);
                else    sprintf(abbr, format, letters);
-       } else if (isdst)
-               strcpy(abbr, strchr(format, '/') + 1);
-       else {
-               strcpy(abbr, format);
-               *strchr(abbr, '/') = '\0';
+       } else if (isdst) {
+               strcpy(abbr, slashp + 1);
+       } else {
+               if (slashp > format)
+                       strncpy(abbr, format,
+                               (unsigned) (slashp - format));
+               abbr[slashp - format] = '\0';
+       }
+       if (!doquotes)
+               return;
+       for (cp = abbr; *cp != '\0'; ++cp)
+               if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
+                       strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
+                               break;
+       len = strlen(abbr);
+       if (len > 0 && *cp == '\0')
+               return;
+       abbr[len + 2] = '\0';
+       abbr[len + 1] = '>';
+       for ( ; len > 0; --len)
+               abbr[len] = abbr[len - 1];
+       abbr[0] = '<';
+}
+
+static void
+updateminmax(const int x)
+{
+       if (min_year > x)
+               min_year = x;
+       if (max_year < x)
+               max_year = x;
+}
+
+static int
+stringoffset(char *result, long offset)
+{
+       int     hours;
+       int     minutes;
+       int     seconds;
+
+       result[0] = '\0';
+       if (offset < 0) {
+               strcpy(result, "-");
+               offset = -offset;
+       }
+       seconds = offset % SECSPERMIN;
+       offset /= SECSPERMIN;
+       minutes = offset % MINSPERHOUR;
+       offset /= MINSPERHOUR;
+       hours = offset;
+       if (hours >= HOURSPERDAY) {
+               result[0] = '\0';
+               return -1;
+       }
+       sprintf(end(result), "%d", hours);
+       if (minutes != 0 || seconds != 0) {
+               sprintf(end(result), ":%02d", minutes);
+               if (seconds != 0)
+                       sprintf(end(result), ":%02d", seconds);
+       }
+       return 0;
+}
+
+static int
+stringrule(char *result, const struct rule * const rp, const long dstoff,
+          const long gmtoff)
+{
+       long    tod;
+
+       result = end(result);
+       if (rp->r_dycode == DC_DOM) {
+               int     month, total;
+
+               if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
+                       return -1;
+               total = 0;
+               for (month = 0; month < rp->r_month; ++month)
+                       total += len_months[0][month];
+               sprintf(result, "J%d", total + rp->r_dayofmonth);
+       } else {
+               int     week;
+
+               if (rp->r_dycode == DC_DOWGEQ) {
+                       week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
+                       if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth)
+                               return -1;
+               } else if (rp->r_dycode == DC_DOWLEQ) {
+                       if (rp->r_dayofmonth == len_months[1][rp->r_month])
+                               week = 5;
+                       else {
+                               week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
+                               if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth)
+                                       return -1;
+                       }
+               } else  return -1;      /* "cannot happen" */
+               sprintf(result, "M%d.%d.%d",
+                       rp->r_month + 1, week, rp->r_wday);
+       }
+       tod = rp->r_tod;
+       if (rp->r_todisgmt)
+               tod += gmtoff;
+       if (rp->r_todisstd && rp->r_stdoff == 0)
+               tod += dstoff;
+       if (tod < 0) {
+               result[0] = '\0';
+               return -1;
+       }
+       if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
+               strcat(result, "/");
+               if (stringoffset(end(result), tod) != 0)
+                       return -1;
+       }
+       return 0;
+}
+
+static void
+stringzone(char *result, const struct zone * const zpfirst,
+          const int zonecount)
+{
+       const struct zone *     zp;
+       struct rule *           rp;
+       struct rule *           stdrp;
+       struct rule *           dstrp;
+       int                     i;
+       const char *            abbrvar;
+
+       result[0] = '\0';
+       zp = zpfirst + zonecount - 1;
+       stdrp = dstrp = NULL;
+       for (i = 0; i < zp->z_nrules; ++i) {
+               rp = &zp->z_rules[i];
+               if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
+                       continue;
+               if (rp->r_yrtype != NULL)
+                       continue;
+               if (rp->r_stdoff == 0) {
+                       if (stdrp == NULL)
+                               stdrp = rp;
+                       else    return;
+               } else {
+                       if (dstrp == NULL)
+                               dstrp = rp;
+                       else    return;
+               }
+       }
+       if (stdrp == NULL && dstrp == NULL) {
+               /*
+               ** There are no rules running through "max".
+               ** Let's find the latest rule.
+               */
+               for (i = 0; i < zp->z_nrules; ++i) {
+                       rp = &zp->z_rules[i];
+                       if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
+                               (rp->r_hiyear == stdrp->r_hiyear &&
+                               rp->r_month > stdrp->r_month))
+                                       stdrp = rp;
+               }
+               if (stdrp != NULL && stdrp->r_stdoff != 0)
+                       return; /* We end up in DST (a POSIX no-no). */
+               /*
+               ** Horrid special case: if year is 2037,
+               ** presume this is a zone handled on a year-by-year basis;
+               ** do not try to apply a rule to the zone.
+               */
+               if (stdrp != NULL && stdrp->r_hiyear == 2037)
+                       return;
+       }
+       if (stdrp == NULL && zp->z_nrules != 0)
+               return;
+       abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
+       doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
+       if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
+               result[0] = '\0';
+               return;
+       }
+       if (dstrp == NULL)
+               return;
+       doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
+       if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
+               if (stringoffset(end(result),
+                       -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
+                               result[0] = '\0';
+                               return;
+               }
+       strcat(result, ",");
+       if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+               result[0] = '\0';
+               return;
+       }
+       strcat(result, ",");
+       if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
+               result[0] = '\0';
+               return;
        }
 }
 
@@ -1496,7 +1840,7 @@ outzone(const struct zone * const zpfirst, const int zonecount)
        struct rule *rp;
        int i, j;
        int usestart, useuntil;
-       time_t starttime, untiltime;
+       zic_t starttime, untiltime;
        long gmtoff;
        long stdoff;
        int year;
@@ -1504,8 +1848,17 @@ outzone(const struct zone * const zpfirst, const int zonecount)
        int startttisstd;
        int startttisgmt;
        int type;
-       char startbuf[BUFSIZ];
-
+       char *startbuf;
+       char *ab;
+       char *envvar;
+       int max_abbr_len;
+       int max_envvar_len;
+
+       max_abbr_len = 2 + max_format_len + max_abbrvar_len;
+       max_envvar_len = 2 * max_abbr_len + 5 * 9;
+       startbuf = emalloc(max_abbr_len + 1);
+       ab = emalloc(max_abbr_len + 1);
+       envvar = emalloc(max_envvar_len + 1);
        INITIALIZE(untiltime);
        INITIALIZE(starttime);
        /*
@@ -1515,16 +1868,62 @@ outzone(const struct zone * const zpfirst, const int zonecount)
        typecnt = 0;
        charcnt = 0;
        /*
-       ** A guess that may well be corrected later.
-       */
-       stdoff = 0;
-       /*
-       ** Thanks to Earl Chew (earl@dnd.icp.nec.com.au)
+       ** Thanks to Earl Chew
        ** for noting the need to unconditionally initialize startttisstd.
        */
        startttisstd = FALSE;
        startttisgmt = FALSE;
+       min_year = max_year = EPOCH_YEAR;
+       if (leapseen) {
+               updateminmax(leapminyear);
+               updateminmax(leapmaxyear);
+       }
+       for (i = 0; i < zonecount; ++i) {
+               zp = &zpfirst[i];
+               if (i < zonecount - 1)
+                       updateminmax(zp->z_untilrule.r_loyear);
+               for (j = 0; j < zp->z_nrules; ++j) {
+                       rp = &zp->z_rules[j];
+                       if (rp->r_lowasnum)
+                               updateminmax(rp->r_loyear);
+                       if (rp->r_hiwasnum)
+                               updateminmax(rp->r_hiyear);
+               }
+       }
+       /*
+       ** Generate lots of data if a rule can't cover all future times.
+       */
+       stringzone(envvar, zpfirst, zonecount);
+       if (noise && envvar[0] == '\0') {
+               char *  wp;
+
+wp = ecpyalloc(_("no POSIX environment variable for zone"));
+               wp = ecatalloc(wp, " ");
+               wp = ecatalloc(wp, zpfirst->z_name);
+               warning(wp);
+               ifree(wp);
+       }
+       if (envvar[0] == '\0') {
+               if (min_year >= INT_MIN + YEARSPERREPEAT)
+                       min_year -= YEARSPERREPEAT;
+               else    min_year = INT_MIN;
+               if (max_year <= INT_MAX - YEARSPERREPEAT)
+                       max_year += YEARSPERREPEAT;
+               else    max_year = INT_MAX;
+       }
+       /*
+       ** For the benefit of older systems,
+       ** generate data from 1900 through 2037.
+       */
+       if (min_year > 1900)
+               min_year = 1900;
+       if (max_year < 2037)
+               max_year = 2037;
        for (i = 0; i < zonecount; ++i) {
+               /*
+               ** A guess that may well be corrected later.
+               */
+               stdoff = 0;
                zp = &zpfirst[i];
                usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
                useuntil = i < (zonecount - 1);
@@ -1537,15 +1936,14 @@ outzone(const struct zone * const zpfirst, const int zonecount)
                if (zp->z_nrules == 0) {
                        stdoff = zp->z_stdoff;
                        doabbr(startbuf, zp->z_format,
-                               (char *) NULL, stdoff != 0);
+                               NULL, stdoff != 0, FALSE);
                        type = addtype(oadd(zp->z_gmtoff, stdoff),
                                startbuf, stdoff != 0, startttisstd,
                                startttisgmt);
                        if (usestart) {
                                addtt(starttime, type);
                                usestart = FALSE;
-                       }
-                       else if (stdoff != 0)
+                       } else if (stdoff != 0)
                                addtt(min_time, type);
                } else for (year = min_year; year <= max_year; ++year) {
                        if (useuntil && year > zp->z_untilrule.r_hiyear)
@@ -1566,9 +1964,8 @@ outzone(const struct zone * const zpfirst, const int zonecount)
                        }
                        for ( ; ; ) {
                                int k;
-                               time_t jtime, ktime;
+                               zic_t jtime, ktime;
                                long offset;
-                               char buf[BUFSIZ];
 
                                INITIALIZE(ktime);
                                if (useuntil) {
@@ -1624,23 +2021,27 @@ outzone(const struct zone * const zpfirst, const int zonecount)
                                                        stdoff);
                                                doabbr(startbuf, zp->z_format,
                                                        rp->r_abbrvar,
-                                                       rp->r_stdoff != 0);
+                                                       rp->r_stdoff != 0,
+                                                       FALSE);
                                                continue;
                                        }
                                        if (*startbuf == '\0' &&
-                                           startoff == oadd(zp->z_gmtoff,
-                                           stdoff)) {
-                                               doabbr(startbuf, zp->z_format,
-                                                       rp->r_abbrvar,
-                                                       rp->r_stdoff != 0);
+                                               startoff == oadd(zp->z_gmtoff,
+                                               stdoff)) {
+                                                       doabbr(startbuf,
+                                                               zp->z_format,
+                                                               rp->r_abbrvar,
+                                                               rp->r_stdoff !=
+                                                               0,
+                                                               FALSE);
                                        }
                                }
                                eats(zp->z_filename, zp->z_linenum,
                                        rp->r_filename, rp->r_linenum);
-                               doabbr(buf, zp->z_format, rp->r_abbrvar,
-                                       rp->r_stdoff != 0);
+                               doabbr(ab, zp->z_format, rp->r_abbrvar,
+                                       rp->r_stdoff != 0, FALSE);
                                offset = oadd(zp->z_gmtoff, rp->r_stdoff);
-                               type = addtype(offset, buf, rp->r_stdoff != 0,
+                               type = addtype(offset, ab, rp->r_stdoff != 0,
                                        rp->r_todisstd, rp->r_todisgmt);
                                addtt(ktime, type);
                        }
@@ -1673,11 +2074,14 @@ error(_("can't determine time zone abbreviation to use just after until time"));
                                starttime = tadd(starttime, -gmtoff);
                }
        }
-       writezone(zpfirst->z_name);
+       writezone(zpfirst->z_name, envvar);
+       ifree(startbuf);
+       ifree(ab);
+       ifree(envvar);
 }
 
 static void
-addtt(const time_t starttime, int type)
+addtt(const zic_t starttime, int type)
 {
        if (starttime <= min_time ||
                (timecnt == 1 && attypes[0].at < min_time)) {
@@ -1739,6 +2143,10 @@ addtype(const long gmtoff, const char * const abbr, const int isdst,
                error(_("too many local time types"));
                exit(EXIT_FAILURE);
        }
+       if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
+               error(_("UTC offset out of range"));
+               exit(EXIT_FAILURE);
+       }
        gmtoffs[i] = gmtoff;
        isdsts[i] = isdst;
        ttisstds[i] = ttisstd;
@@ -1755,7 +2163,7 @@ addtype(const long gmtoff, const char * const abbr, const int isdst,
 }
 
 static void
-leapadd(const time_t t, const int positive, const int rolling, int count)
+leapadd(const zic_t t, const int positive, const int rolling, int count)
 {
        int i, j;
 
@@ -1810,11 +2218,13 @@ yearistype(const int year, const char * const type)
        buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
        sprintf(buf, "%s %d %s", yitcommand, year, type);
        result = system(buf);
-       if (result == 0)
-               return TRUE;
-       if (result == (1 << 8))
-               return FALSE;
-       error(_("wild result from command execution"));
+       if (WIFEXITED(result)) switch (WEXITSTATUS(result)) {
+               case 0:
+                       return TRUE;
+               case 1:
+                       return FALSE;
+       }
+       error(_("Wild result from command execution"));
        warnx(_("command was '%s', result was %d"), buf, result);
        for ( ; ; )
                exit(EXIT_FAILURE);
@@ -1890,8 +2300,9 @@ getfields(char *cp)
                emalloc((int) ((strlen(cp) + 1) * sizeof *array));
        nsubs = 0;
        for ( ; ; ) {
-               while (isascii(*cp) && isspace((unsigned char) *cp))
-                       ++cp;
+               while (isascii((unsigned char) *cp) &&
+                       isspace((unsigned char) *cp))
+                               ++cp;
                if (*cp == '\0' || *cp == '#')
                        break;
                array[nsubs++] = dp = cp;
@@ -1901,7 +2312,12 @@ getfields(char *cp)
                        else while ((*dp = *cp++) != '"')
                                if (*dp != '\0')
                                        ++dp;
-                               else    error(_("odd number of quotation marks"));
+                               else {
+                                       error(_(
+                                               "Odd number of quotation marks"
+                                               ));
+                                       exit(1);
+                               }
                } while (*cp != '\0' && *cp != '#' &&
                        (!isascii(*cp) || !isspace((unsigned char) *cp)));
                if (isascii(*cp) && isspace((unsigned char) *cp))
@@ -1925,10 +2341,10 @@ oadd(const long t1, const long t2)
        return t;
 }
 
-static time_t
-tadd(const time_t t1, const long t2)
+static zic_t
+tadd(const zic_t t1, const long t2)
 {
-       time_t t;
+       zic_t t;
 
        if (t1 == max_time && t2 > 0)
                return max_time;
@@ -1947,12 +2363,12 @@ tadd(const time_t t1, const long t2)
 ** 1970, 00:00 LOCAL time - in that year that the rule refers to.
 */
 
-static time_t
+static zic_t
 rpytime(const struct rule * const rp, const int wantedy)
 {
        int y, m, i;
        long dayoff;                    /* with a nod to Margaret O. */
-       time_t t;
+       zic_t t;
 
        if (wantedy == INT_MIN)
                return min_time;
@@ -2015,18 +2431,16 @@ rpytime(const struct rule * const rp, const int wantedy)
                                --i;
                        }
                if (i < 0 || i >= len_months[isleap(y)][m]) {
-                       error(_("no day in month matches rule"));
-                       exit(EXIT_FAILURE);
+                       if (noise)
+                               warning(_("rule goes past start/end of month--\
+will not work with pre-2004 versions of zic"));
                }
        }
-       if (dayoff < 0 && !TYPE_SIGNED(time_t))
+       if (dayoff < min_time / SECSPERDAY)
                return min_time;
-       t = (time_t) dayoff * SECSPERDAY;
-       /*
-       ** Cheap overflow check.
-       */
-       if (t / SECSPERDAY != dayoff)
-               return (dayoff > 0) ? max_time : min_time;
+       if (dayoff > max_time / SECSPERDAY)
+               return max_time;
+       t = (zic_t) dayoff * SECSPERDAY;
        return tadd(t, rp->r_tod);
 }
 
@@ -2035,6 +2449,44 @@ newabbr(const char * const string)
 {
        int i;
 
+       if (strcmp(string, GRANDPARENTED) != 0) {
+               const char *    cp;
+               char *          wp;
+
+               /*
+               ** Want one to ZIC_MAX_ABBR_LEN_WO_WARN alphabetics
+               ** optionally followed by a + or - and a number from 1 to 14.
+               */
+               cp = string;
+               wp = NULL;
+               while (isascii((unsigned char) *cp) &&
+                       isalpha((unsigned char) *cp))
+                               ++cp;
+               if (cp - string == 0)
+wp = _("time zone abbreviation lacks alphabetic at start");
+               if (noise && cp - string > 3)
+wp = _("time zone abbreviation has more than 3 alphabetics");
+               if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
+wp = _("time zone abbreviation has too many alphabetics");
+               if (wp == NULL && (*cp == '+' || *cp == '-')) {
+                       ++cp;
+                       if (isascii((unsigned char) *cp) &&
+                               isdigit((unsigned char) *cp))
+                                       if (*cp++ == '1' &&
+                                               *cp >= '0' && *cp <= '4')
+                                                       ++cp;
+               }
+               if (*cp != '\0')
+wp = _("time zone abbreviation differs from POSIX standard");
+               if (wp != NULL) {
+                       wp = ecpyalloc(wp);
+                       wp = ecatalloc(wp, " (");
+                       wp = ecatalloc(wp, string);
+                       wp = ecatalloc(wp, ")");
+                       warning(wp);
+                       ifree(wp);
+               }
+       }
        i = strlen(string) + 1;
        if (charcnt + i > TZ_MAX_CHARS) {
                error(_("too many, or too long, time zone abbreviations"));
@@ -2045,7 +2497,7 @@ newabbr(const char * const string)
 }
 
 static int
-mkdirs(char * const argname)
+mkdirs(char *argname)
 {
        char *name;
        char *cp;
@@ -2072,10 +2524,8 @@ mkdirs(char * const argname)
                        ** created by some other multiprocessor, so we get
                        ** to do extra checking.
                        */
-                       if (mkdir(name, (S_IRUSR | S_IWUSR | S_IXUSR 
-                                        | S_IRGRP | S_IXGRP | S_IROTH
-                                        | S_IXOTH)) != 0
-                               && (errno != EEXIST || !itsdir(name))) {
+                       if ((mkdir(name, MKDIR_UMASK) != 0) &&
+                               (errno != EEXIST || !itsdir(name))) {
                                warn(_("can't create directory %s"), name);
                                ifree(name);
                                return -1;
@@ -2148,5 +2598,5 @@ setuser(uid_t *flag, const char *name)
 }
 
 /*
-** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+** UNIX was a registered trademark of The Open Group in 2003.
 */