]> git.saurik.com Git - apple/libc.git/blobdiff - stdtime/FreeBSD/localtime.c
Libc-1353.100.2.tar.gz
[apple/libc.git] / stdtime / FreeBSD / localtime.c
index 97b022c337a32ba0913faa94d60acd3e728bcead..ab37c66b696c4b52efc8fdc32fe59e257aad0c20 100644 (file)
@@ -1,20 +1,22 @@
 /*
 ** 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.
 */
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+
 #include <sys/cdefs.h>
 #ifndef lint
 #ifndef NOID
-static char    elsieid[] __unused = "@(#)localtime.c   7.78";
+static char    elsieid[] __unused = "@(#)localtime.c   8.14";
 #endif /* !defined NOID */
 #endif /* !defined lint */
-__FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11 davidxu Exp $");
+__FBSDID("$FreeBSD: head/contrib/tzcode/stdtime/localtime.c 289027 2015-10-08 11:42:15Z rodrigc $");
 
 /*
-** 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,12 +24,41 @@ __FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11
 #include "namespace.h"
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
 #include <fcntl.h>
 #include <pthread.h>
+#ifdef NOTIFY_TZ
+//#define NOTIFY_TZ_DEBUG
+//#define NOTIFY_TZ_DEBUG_FILE "/var/log/localtime.debug"
+//#define NOTIFY_TZ_LOG        "/var/log/localtime.log"
+/* force ALL_STATE if NOTIFY_TZ is set */
+#ifndef ALL_STATE
+#define ALL_STATE
+#endif /* ALL_STATE */
+#include <mach/mach_init.h>
+#include <notify.h>
+#include <alloca.h>
+#endif /* NOTIFY_TZ */
 #include "private.h"
 #include "un-namespace.h"
 
 #include "tzfile.h"
+#include "float.h"     /* for FLT_MAX and DBL_MAX */
+
+#ifndef TZ_ABBR_MAX_LEN
+/* UNIX03 requires this to be the same as sysconf(_SC_TZNAME_MAX) */
+#define TZ_ABBR_MAX_LEN        255
+#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 */
 
 #include "libc_private.h"
 
@@ -74,23 +105,23 @@ __FBSDID("$FreeBSD: src/lib/libc/stdtime/localtime.c,v 1.43 2008/04/01 06:56:11
 **     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";
+__used static const char               wildabbr[] = WILDABBR;
 
 /*
  * In June 2004 it was decided UTC was a more appropriate default time
  * zone than GMT.
  */
 
-static const char      gmt[] = "UTC";
+__used static const char       gmt[] = "UTC";
 
 /*
 ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
@@ -130,6 +161,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];
@@ -150,45 +183,117 @@ struct rule {
 #define DAY_OF_YEAR            1       /* n - day of year */
 #define MONTH_NTH_DAY_OF_WEEK  2       /* Mm.n.d - month, week, day of week */
 
+#ifdef NOTIFY_TZ
+typedef struct {
+       int token;
+       int is_set;
+       int null_bootstrap;
+} notify_tz_t;
+
+#define NOTIFY_TZ_NAME         "com.apple.system.timezone"
+#endif /* NOTIFY_TZ */
+
 /*
 ** Prototypes for static functions.
 */
+#define        localsub        _st_localsub
+#define        time1           _st_time1
+#define        tzset_basic     _st_tzset_basic
+__private_extern__
+#ifdef __LP64__
+struct tm *            localsub(const time_t * timep, long offset,
+                               struct tm * tmp);
+#else /* !__LP64__ */
+void                   localsub(const time_t * timep, long offset,
+                               struct tm * tmp);
+#endif /* __LP64__ */
+__private_extern__
+time_t                 time1(struct tm * tmp,
+#ifdef __LP64__
+                               struct tm *(*funcp) (const time_t *,
+                               long, struct tm *),
+#else /* !__LP64__ */
+                               void(*funcp) (const time_t *,
+                               long, struct tm *),
+#endif /* __LP64__ */
+                               long offset,
+                               int unix03);
+__private_extern__
+void                   tzset_basic(int);
 
+#if !BUILDING_VARIANT
 static long            detzcode(const char * codep);
-static const char *    getzname(const char * strp);
+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, char **name, size_t *len);
+static const char *    getqzname(const char * strp, const int delim)
+  ATTRIBUTE_PURE;
 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);
+#ifdef NOTIFY_TZ
+static void            gmtload(struct state * sp, char *path);
+#else /* ! NOTIFY_TZ */
 static void            gmtload(struct state * sp);
-static void            gmtsub(const time_t * timep, long offset,
+#endif /* NOTIFY_TZ */
+#ifdef __LP64__
+static struct tm *     gmtsub(const time_t * timep, long offset,
                                struct tm * tmp);
-static void            localsub(const time_t * timep, long offset,
+#else /* !__LP64__ */
+static void            gmtsub(const time_t * timep, long offset,
                                struct tm * tmp);
+#endif /* __LP64__ */
 static int             increment_overflow(int * number, int delta);
+static int             leaps_thru_end_of(int y) ATTRIBUTE_PURE;
+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);
+#ifdef NOTIFY_TZ
+static void            notify_check_tz(notify_tz_t *p);
+static void            notify_register_tz(char *file, notify_tz_t *p);
+#endif /* NOTIFY_TZ */
 static void            settzname(void);
-static time_t          time1(struct tm * tmp,
-                               void(*funcp) (const time_t *,
-                               long, struct tm *),
-                               long offset);
 static time_t          time2(struct tm *tmp,
+#ifdef __LP64__
+                               struct tm *(*funcp) (const time_t *,
+                               long, struct tm*),
+#else /* !__LP64__ */
                                void(*funcp) (const time_t *,
                                long, struct tm*),
-                               long offset, int * okayp);
+#endif /* __LP64__ */
+                               long offset, int * okayp, int unix03);
 static time_t          time2sub(struct tm *tmp,
+#ifdef __LP64__
+                               struct tm *(*funcp) (const time_t *,
+                               long, struct tm*),
+#else /* !__LP64__ */
                                void(*funcp) (const time_t *,
                                long, struct tm*),
-                               long offset, int * okayp, int do_norm_secs);
+#endif /* __LP64__ */
+                               long offset, int * okayp, int do_norm_secs,
+                               int unix03);
+#ifdef __LP64__
+static struct tm *     timesub(const time_t * timep, long offset,
+                               const struct state * sp, struct tm * tmp);
+#else /* !__LP64__ */
 static void            timesub(const time_t * timep, long offset,
                                const struct state * sp, struct tm * tmp);
+#endif /* __LP64__ */
 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);
+                                 const struct rule * rulep, long offset)
+  ATTRIBUTE_PURE;
+static int             typesequiv(const struct state * sp, int a, int b);
+#ifdef NOTIFY_TZ
+static int             tzload(const char * name, struct state * sp, char *path, int doextend);
+#else /* ! NOTIFY_TZ */
+static int             tzload(const char * name, struct state * sp, int doextend);
+#endif /* NOTIFY_TZ */
 static int             tzparse(const char * name, struct state * sp,
                                int lastditch);
 
@@ -209,14 +314,24 @@ static struct state       gmtmem;
 #endif /* !defined TZ_STRLEN_MAX */
 
 static char            lcl_TZname[TZ_STRLEN_MAX + 1];
+#ifdef NOTIFY_TZ
+#define lcl_is_set    (lcl_notify.is_set)
+#define gmt_is_set    (gmt_notify.is_set)
+#else /* ! NOTIFY_TZ */
 static int             lcl_is_set;
-static int             gmt_is_set;
-static pthread_rwlock_t        lcl_rwlock = PTHREAD_RWLOCK_INITIALIZER;
-static pthread_mutex_t gmt_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif /* NOTIFY_TZ */
+static pthread_once_t  gmt_once = PTHREAD_ONCE_INIT;
+__private_extern__ pthread_rwlock_t    lcl_rwlock = PTHREAD_RWLOCK_INITIALIZER;
+static pthread_once_t  gmtime_once = PTHREAD_ONCE_INIT;
+static pthread_key_t   gmtime_key;
+static int             gmtime_key_error;
+static pthread_once_t  localtime_once = PTHREAD_ONCE_INIT;
+static pthread_key_t   localtime_key;
+static int             localtime_key_error;
 
 char *                 tzname[2] = {
-       wildabbr,
-       wildabbr
+       (char *)wildabbr,
+       (char *)wildabbr
 };
 
 /*
@@ -224,97 +339,370 @@ 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;
 
+#define USG_COMPAT
+#define ALTZONE
 #ifdef USG_COMPAT
-time_t                 timezone = 0;
 int                    daylight = 0;
+__private_extern__ void _st_set_timezone(long);
 #endif /* defined USG_COMPAT */
 
 #ifdef ALTZONE
-time_t                 altzone = 0;
+__private_extern__ long                __darwin_altzone = 0;
+#define altzone __darwin_altzone
 #endif /* defined ALTZONE */
 
+#ifdef NOTIFY_TZ
+#ifdef NOTIFY_TZ_DEBUG
+#ifdef NOTIFY_TZ_DEBUG_FILE
+#define NOTIFY_TZ_PRINTF(fmt, args...) \
+{ \
+       FILE *_notify_tz_fp_; \
+       if((_notify_tz_fp_ = fopen(NOTIFY_TZ_DEBUG_FILE, "a")) != NULL) { \
+               fprintf(_notify_tz_fp_, "%d: " fmt, getpid(), ## args); \
+               fclose(_notify_tz_fp_); \
+       } \
+}
+#else /* ! NOTIFY_TZ_DEBUG_FILE */
+#define NOTIFY_TZ_PRINTF(args...)      fprintf(stdout, ## args)
+#endif /* NOTIFY_TZ_DEBUG_FILE */
+#endif /* NOTIFY_TZ_DEBUG */
+#ifdef NOTIFY_TZ_LOG
+#define NOTIFY_LOG(fmt, args...)       \
+{ \
+       FILE *_notify_log_fp_; \
+       if((_notify_log_fp_ = fopen(NOTIFY_TZ_LOG, "a")) != NULL) { \
+               fprintf(_notify_log_fp_, "%d: " fmt, getpid(), ## args); \
+               fclose(_notify_log_fp_); \
+       } \
+}
+#endif /* NOTIFY_TZ_LOG */
+
+static notify_tz_t     gmt_notify = {-1, 0, 0};
+static notify_tz_t     lcl_notify = {-1, 0, 0};
+static const char notify_tz_name[] = NOTIFY_TZ_NAME;
+#endif /* NOTIFY_TZ */
+
 static long
-detzcode(codep)
-const char * const     codep;
+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)
+{
+       register time_t result;
+       register 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;
-       int                     i;
-
-       tzname[0] = wildabbr;
-       tzname[1] = wildabbr;
+       int                     i, need;
+       unsigned char * types;
+#define NEED_STD       1
+#define NEED_DST       2
+#define NEED_DAYLIGHT  4
+#define NEED_ALL       (NEED_STD | NEED_DST | NEED_DAYLIGHT)
+
+       tzname[0] = (char *)wildabbr;
+       tzname[1] = (char *)wildabbr;
 #ifdef USG_COMPAT
        daylight = 0;
-       timezone = 0;
+       _st_set_timezone(0);
 #endif /* defined USG_COMPAT */
 #ifdef ALTZONE
        altzone = 0;
 #endif /* defined ALTZONE */
 #ifdef ALL_STATE
        if (sp == NULL) {
-               tzname[0] = tzname[1] = gmt;
+               tzname[0] = tzname[1] = (char *)gmt;
                return;
        }
 #endif /* defined ALL_STATE */
-       for (i = 0; i < sp->typecnt; ++i) {
-               const struct ttinfo * const     ttisp = &sp->ttis[i];
+       /*
+        * PR-3765457: The original settzname went sequentially through the ttis
+        * array, rather than correctly indexing via the types array, to get
+        * the real order of the timezone changes.  In addition, as a speed up,
+        * we start at the end of the changes, and work back, so that most of
+        * the time, we don't have to look through the entire array.
+        */
+       if (sp->timecnt == 0 && sp->typecnt == 1) {
+               /*
+                * Unfortunately, there is an edge case when typecnt == 1 and
+                * timecnt == 0, which would cause the loop to never run.  So
+                * in that case, we fudge things up so that it is as if
+                * timecnt == 1.
+                */
+               i = 0;
+               types = (unsigned char *)""; /* we use the null as index */
+       } else {
+               /* the usual case */
+               i = sp->timecnt - 1;
+               types = sp->types;
+       }
+       need = NEED_ALL;
+       for (; i >= 0 && need; --i) {
+               const struct ttinfo * const     ttisp = &sp->ttis[types[i]];
 
-               tzname[ttisp->tt_isdst] =
-                       &sp->chars[ttisp->tt_abbrind];
 #ifdef USG_COMPAT
-               if (ttisp->tt_isdst)
+               if ((need & NEED_DAYLIGHT) && ttisp->tt_isdst) {
+                       need &= ~NEED_DAYLIGHT;
                        daylight = 1;
-               if (i == 0 || !ttisp->tt_isdst)
-                       timezone = -(ttisp->tt_gmtoff);
+               }
 #endif /* defined USG_COMPAT */
+               if (ttisp->tt_isdst) {
+                       if (need & NEED_DST) {
+                               need &= ~NEED_DST;
+                               tzname[1] = &sp->chars[ttisp->tt_abbrind];
 #ifdef ALTZONE
-               if (i == 0 || ttisp->tt_isdst)
-                       altzone = -(ttisp->tt_gmtoff);
+                               altzone = -(ttisp->tt_gmtoff);
 #endif /* defined ALTZONE */
+                       }
+               } else if (need & NEED_STD) {
+                       need &= ~NEED_STD;
+                       tzname[0] = &sp->chars[ttisp->tt_abbrind];
+#ifdef USG_COMPAT
+                       _st_set_timezone(-(ttisp->tt_gmtoff));
+#endif /* defined USG_COMPAT */
+               }
+#if defined(ALTZONE) || defined(USG_COMPAT)
+               if (i == 0) {
+#endif /* defined(ALTZONE) || defined(USG_COMPAT) */
+#ifdef ALTZONE
+                       if (need & NEED_DST)
+                               altzone = -(ttisp->tt_gmtoff);
+#endif /* defined ALTZONE */
+#ifdef USG_COMPAT
+                       if (need & NEED_STD)
+                               _st_set_timezone(-(ttisp->tt_gmtoff));
+#endif /* defined USG_COMPAT */
+#if defined(ALTZONE) || defined(USG_COMPAT)
+               }
+#endif /* defined(ALTZONE) || defined(USG_COMPAT) */
        }
        /*
-       ** And to get the latest zone names into tzname. . .
+       ** Finally, scrub the abbreviations.
+       ** First, replace bogus characters.
        */
-       for (i = 0; i < sp->timecnt; ++i) {
-               const struct ttinfo * const     ttisp =
-                                                       &sp->ttis[
-                                                               sp->types[i]];
+       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) {
+               register const struct ttinfo * const    ttisp = &sp->ttis[i];
+               register char *                         cp = &sp->chars[ttisp->tt_abbrind];
 
-               tzname[ttisp->tt_isdst] =
-                       &sp->chars[ttisp->tt_abbrind];
+               if (strlen(cp) > TZ_ABBR_MAX_LEN &&
+                       strcmp(cp, GRANDPARENTED) != 0)
+                               *(cp + TZ_ABBR_MAX_LEN) = '\0';
        }
 }
 
+#ifdef NOTIFY_TZ
 static int
-tzload(name, sp)
+do_null_bootstrap_check(notify_tz_t *p)
+{
+       /*
+        * If we're running in a null bootstrap context (e.g. the bootstrap server),
+        * we will not be able to contact the notify server. In this case we want to
+        * avoid opening /etc/localtime every time the process does a asctime_r(3)
+        * or similar. But we have to do this once to get the right time zone.
+        *
+        * So first time through, we set a bit to indicate that we're in the null
+        * bootstrap context. The second time through, we force the "set" bit in the
+        * notify_tz_t structure to -1 and avoid the path where it can be set to
+        * zero (which would trigger opening and reloading the timezone file).
+        */
+       if (bootstrap_port != MACH_PORT_NULL) {
+               return -1;
+       }
+
+       if (!p->null_bootstrap) {
+               p->null_bootstrap = 1;
+               p->is_set = 0;
+               return -1;
+       }
+
+       p->is_set = -1;
+       return 0;
+}
+
+static void
+notify_check_tz(notify_tz_t *p)
+{
+       unsigned int nstat;
+       int ncheck;
+
+       if (p->token < 0)
+               return;
+       if (do_null_bootstrap_check(p) == 0) {
+               return;
+       }
+       nstat = notify_check(p->token, &ncheck);
+       if (nstat || ncheck) {
+               p->is_set = 0;
+#ifdef NOTIFY_TZ_DEBUG
+               NOTIFY_TZ_PRINTF("notify_check_tz: %s changed\n", (p == &lcl_notify) ? "lcl" : "gmt");
+#endif /* NOTIFY_TZ_DEBUG */
+       }
+#ifdef NOTIFY_TZ_DEBUG
+               NOTIFY_TZ_PRINTF("notify_check_tz: %s unchanged\n", (p == &lcl_notify) ? "lcl" : "gmt");
+#endif /* NOTIFY_TZ_DEBUG */
+}
+
+extern uint32_t notify_monitor_file(int token, char *path, int flags);
+
+static void
+notify_register_tz(char *file, notify_tz_t *p)
+{
+       char *name;
+       unsigned int nstat;
+       int ncheck;
+
+       if (do_null_bootstrap_check(p) == 0) {
+               return;
+       }
+
+       /*----------------------------------------------------------------
+        * Since we don't record the last time zone filename, just cancel
+        * (which should remove the file monitor) and setup from scratch
+        *----------------------------------------------------------------*/
+       if (p->token >= 0)
+               notify_cancel(p->token);
+       if (!file || *file == 0) {
+               /* no time zone file to monitor */
+               p->token = -1;
+               return;
+       }
+       /*----------------------------------------------------------------
+        * Just use com.apple.system.timezone if the path is /etc/localtime.
+        * Otherwise use com.apple.system.timezone.<fullpath>
+        *----------------------------------------------------------------*/
+       if (TZDEFAULT && strcmp(file, TZDEFAULT) == 0)
+               name = (char *)notify_tz_name;
+       else {
+               name = alloca(sizeof(notify_tz_name) + strlen(file) + 1);
+               if (name == NULL) {
+                       p->token = -1;
+                       return;
+               }
+               strcpy(name, notify_tz_name);
+               strcat(name, ".");
+               strcat(name, file);
+       }
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("notify_register_tz: file=%s name=%s\n", file, name);
+#endif /* NOTIFY_TZ_DEBUG */
+       nstat = notify_register_check(name, &p->token);
+       if (nstat != 0) {
+               p->token = -1;
+               p->is_set = 0;
+#ifdef NOTIFY_TZ_DEBUG
+               NOTIFY_TZ_PRINTF("***notify_register_tz: notify_register_check failed: %u\n", nstat);
+#endif /* NOTIFY_TZ_DEBUG */
+#ifdef NOTIFY_TZ_LOG
+               NOTIFY_LOG("notify_register_check(%s) failed: %u\n", name, nstat);
+#endif /* NOTIFY_TZ_LOG */
+               return;
+       }
+       /* don't need to request monitoring /etc/localtime */
+       if (name != notify_tz_name) {
+#ifdef NOTIFY_TZ_DEBUG
+               NOTIFY_TZ_PRINTF("notify_register_tz: monitor %s\n", file);
+#endif /* NOTIFY_TZ_DEBUG */
+               nstat = notify_monitor_file(p->token, file, 0);
+               if (nstat != 0) {
+                       notify_cancel(p->token);
+                       p->token = -1;
+                       p->is_set = 0;
+#ifdef NOTIFY_TZ_DEBUG
+                       NOTIFY_TZ_PRINTF("***notify_register_tz: notify_monitor_file failed: %u\n", nstat);
+#endif /* NOTIFY_TZ_DEBUG */
+#ifdef NOTIFY_TZ_LOG
+                       NOTIFY_LOG("notify_monitor_file(%s) failed: %u\n", file, nstat);
+#endif /* NOTIFY_TZ_LOG */
+                       return;
+               }
+       }
+       notify_check(p->token, &ncheck);        /* this always returns true */
+}
+#endif /* NOTIFY_TZ */
+
+static int
+differ_by_repeat(const time_t t1, const time_t t0)
+{
+       int_fast64_t _t0 = t0;
+       int_fast64_t _t1 = t1;
+
+       if (TYPE_INTEGRAL(time_t) &&
+               TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+                       return 0;
+       //turn ((int_fast64_t)(t1 - t0) == SECSPERREPEAT);
+       return _t1 - _t0 == SECSPERREPEAT;
+}
+
+static int
+#ifdef NOTIFY_TZ
+tzload(name, sp, path, doextend)
+#else /* ! NOTIFY_TZ */
+tzload(name, sp, doextend)
+#endif /* NOTIFY_TZ */
 const char *           name;
 struct state * const   sp;
+#ifdef NOTIFY_TZ
+char *                 path; /* copy full path if non-NULL */
+#endif /* NOTIFY_TZ */
+register const int     doextend;
 {
        const char *    p;
        int             i;
        int             fid;
-
+       int             stored;
+       int             nread;
+       int             res;
+       union {
+               struct tzhead   tzhead;
+               char            buf[2 * sizeof(struct tzhead) +
+                                       2 * sizeof *sp +
+                                       4 * TZ_MAX_TIMES];
+       } *u;
+
+       u = NULL;
+       res = -1;
+       sp->goback = sp->goahead = FALSE;
+
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("tzload: name=%s\n", name);
+#endif /* NOTIFY_TZ_DEBUG */
        /* XXX The following is from OpenBSD, and I'm not sure it is correct */
        if (name != NULL && issetugid() != 0)
                if ((name[0] == ':' && name[1] == '/') || 
                    name[0] == '/' || strchr(name, '.'))
                        name = NULL;
+#ifdef NOTIFY_TZ
+       if (path)
+               *path = 0; /* default to empty string on error */
+#endif /* NOTIFY_TZ */
        if (name == NULL && (name = TZDEFAULT) == NULL)
                return -1;
        {
@@ -327,16 +715,24 @@ struct state * const      sp;
                ** to hold the longest file name string that the implementation
                ** guarantees can be opened."
                */
-               char            fullname[FILENAME_MAX + 1];
+               char            *fullname;
+
+               fullname = malloc(FILENAME_MAX + 1);
+               if (fullname == NULL)
+                       goto out;
 
                if (name[0] == ':')
                        ++name;
                doaccess = name[0] == '/';
                if (!doaccess) {
-                       if ((p = TZDIR) == NULL)
+                       if ((p = TZDIR) == NULL) {
+                               free(fullname);
                                return -1;
-                       if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname)
+                       }
+                       if (strlen(p) + 1 + strlen(name) >= FILENAME_MAX) {
+                               free(fullname);
                                return -1;
+                       }
                        (void) strcpy(fullname, p);
                        (void) strcat(fullname, "/");
                        (void) strcat(fullname, name);
@@ -347,57 +743,73 @@ struct state * const      sp;
                                doaccess = TRUE;
                        name = fullname;
                }
-               if (doaccess && access(name, R_OK) != 0)
+#ifdef NOTIFY_TZ
+               if (path) {
+                       if (strlen(name) > FILENAME_MAX)
+                               return -1;
+                       strcpy(path, name);
+               }
+#endif /* NOTIFY_TZ */
+               if (doaccess && access(name, R_OK) != 0) {
+                       free(fullname);
                        return -1;
-               if ((fid = _open(name, OPEN_MODE)) == -1)
+               }
+               if ((fid = _open(name, OPEN_MODE)) == -1) {
+                       free(fullname);
                        return -1;
+               }
                if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) {
+                       free(fullname);
                        _close(fid);
                        return -1;
                }
+               free(fullname);
        }
-       {
-               struct tzhead * tzhp;
-               union {
-                       struct tzhead   tzhead;
-                       char            buf[sizeof *sp + sizeof *tzhp];
-               } u;
+       u = malloc(sizeof(*u));
+       if (u == NULL)
+               goto out;
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("tzload: reading %s\n", name);
+#endif /* NOTIFY_TZ_DEBUG */
+       nread = _read(fid, u->buf, sizeof u->buf);
+       if (_close(fid) < 0 || nread <= 0)
+               goto out;
+       for (stored = 4; stored <= 8; stored *= 2) {
                int             ttisstdcnt;
                int             ttisgmtcnt;
 
-               i = _read(fid, u.buf, sizeof u.buf);
-               if (_close(fid) != 0)
-                       return -1;
-               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;
+               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 ||
                        sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
                        (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
                        (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
-                               return -1;
-               if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
+                               goto out;
+               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;
+                               goto out;
                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++;
                        if (sp->types[i] >= sp->typecnt)
-                               return -1;
+                               goto out;
                }
                for (i = 0; i < sp->typecnt; ++i) {
                        struct ttinfo * ttisp;
@@ -407,11 +819,11 @@ struct state * const      sp;
                        p += 4;
                        ttisp->tt_isdst = (unsigned char) *p++;
                        if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
-                               return -1;
+                               goto out;
                        ttisp->tt_abbrind = (unsigned char) *p++;
                        if (ttisp->tt_abbrind < 0 ||
                                ttisp->tt_abbrind > sp->charcnt)
-                                       return -1;
+                                       goto out;
                }
                for (i = 0; i < sp->charcnt; ++i)
                        sp->chars[i] = *p++;
@@ -420,8 +832,9 @@ 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;
                }
@@ -435,7 +848,7 @@ struct state * const        sp;
                                ttisp->tt_ttisstd = *p++;
                                if (ttisp->tt_ttisstd != TRUE &&
                                        ttisp->tt_ttisstd != FALSE)
-                                               return -1;
+                                               goto out;
                        }
                }
                for (i = 0; i < sp->typecnt; ++i) {
@@ -448,11 +861,134 @@ struct state * const     sp;
                                ttisp->tt_ttisgmt = *p++;
                                if (ttisp->tt_ttisgmt != TRUE &&
                                        ttisp->tt_ttisgmt != FALSE)
-                                               return -1;
+                                               goto out;
+                       }
+               }
+               /*
+               ** 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).
+                                       */
+                                       register 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;
+                       register int    result;
+
+                       ts = malloc(sizeof(*ts));
+                       if (ts == NULL)
+                               goto out;
+                       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];
                        }
+                       free(ts);
+       }
+       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;
+       res = 0;
+out:
+       free(u);
+       return (res);
+}
+
+static int
+typesequiv(sp, a, b)
+const struct state * const     sp;
+const int                      a;
+const int                      b;
+{
+       register int    result;
+
+       if (sp == NULL ||
+               a < 0 || a >= sp->typecnt ||
+               b < 0 || b >= sp->typecnt)
+                       result = FALSE;
+       else {
+               register const struct ttinfo *  ap = &sp->ttis[a];
+               register 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] = {
@@ -466,19 +1002,48 @@ 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.
 */
 
 static const char *
-getzname(strp)
+getzname(strp, name, len)
 const char *   strp;
+char **                name;
+size_t *       len;
 {
        char    c;
+       char *  ket;
 
+       if (*strp == '<' && (ket = strchr(strp, '>')) != NULL) {
+               *name = (char *)(strp + 1);
+               *len = ket - strp - 1;
+               return ket + 1;
+       }
+       *name = (char *)strp;
        while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
                c != '+')
                        ++strp;
+       *len = strp - *name;
+       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(register const char *strp, const int delim)
+{
+       register int    c;
+
+       while ((c = *strp) != '\0' && c != delim)
+               ++strp;
        return strp;
 }
 
@@ -547,7 +1112,7 @@ 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;
@@ -586,7 +1151,7 @@ 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.
 */
@@ -705,7 +1270,7 @@ const long                         offset;
                        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.
                */
@@ -728,7 +1293,7 @@ const long                         offset;
 
        /*
        ** "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
+       ** question. To get the Epoch-relative time of the specified local
        ** time on that day, add the transition time and the current offset
        ** from UTC.
        */
@@ -758,18 +1323,15 @@ const int                        lastditch;
        int                     load_result;
 
        INITIALIZE(dstname);
-       stdname = name;
        if (lastditch) {
+               stdname = name;
                stdlen = strlen(name);  /* length of standard zone name */
                name += stdlen;
                if (stdlen >= sizeof sp->chars)
                        stdlen = (sizeof sp->chars) - 1;
                stdoffset = 0;
        } else {
-               name = getzname(name);
-               stdlen = name - stdname;
-               if (stdlen < 3)
-                       return -1;
+               name = getzname(name, (char **)&stdname, &stdlen);
                if (*name == '\0')
                        return -1;      /* was "stdoffset = 0;" */
                else {
@@ -778,15 +1340,25 @@ const int                        lastditch;
                                return -1;
                }
        }
-       load_result = tzload(TZDEFRULES, sp);
+#ifdef NOTIFY_TZ
+       load_result = tzload(TZDEFRULES, sp, NULL, FALSE);
+#else /* !NOTIFY_TZ */
+       load_result = tzload(TZDEFRULES, sp, FALSE);
+#endif /* NOTIFY_TZ */
        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, (char **)&dstname, &dstlen);
+               }
                if (*name != '\0' && *name != ',' && *name != ';') {
                        name = getoffset(name, &dstoffset);
                        if (name == NULL)
@@ -813,11 +1385,8 @@ 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;
@@ -827,7 +1396,12 @@ 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,
@@ -843,8 +1417,13 @@ 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;
@@ -956,23 +1535,58 @@ const int                        lastditch;
 }
 
 static void
+#ifdef NOTIFY_TZ
+gmtload(sp, path)
+#else /* ! NOTIFY_TZ */
 gmtload(sp)
+#endif /* NOTIFY_TZ */
 struct state * const   sp;
+#ifdef NOTIFY_TZ
+char *path;
+#endif /* NOTIFY_TZ */
 {
-       if (tzload(gmt, sp) != 0)
+#ifdef NOTIFY_TZ
+       if (tzload(gmt, sp, path, TRUE) != 0)
+#else /* ! NOTIFY_TZ */
+       if (tzload(gmt, sp, TRUE) != 0)
+#endif /* NOTIFY_TZ */
                (void) tzparse(gmt, sp, TRUE);
 }
 
 static void
 tzsetwall_basic(int rdlocked)
 {
+#ifdef NOTIFY_TZ
+       notify_check_tz(&lcl_notify);
+#else
+       if (TZDEFAULT) {
+               static struct timespec last_mtimespec = {0, 0};
+               struct stat statbuf;
+
+               if (lstat(TZDEFAULT, &statbuf) == 0) {
+                       if (statbuf.st_mtimespec.tv_sec > last_mtimespec.tv_sec ||
+                               (statbuf.st_mtimespec.tv_sec == last_mtimespec.tv_sec &&
+                               statbuf.st_mtimespec.tv_nsec > last_mtimespec.tv_nsec)) {
+                               /* Trigger resetting the local TZ */
+                               lcl_is_set = 0;
+                       }
+                       last_mtimespec = statbuf.st_mtimespec;
+               }
+       }
+#endif /* NOTIFY_TZ */
        if (!rdlocked)
                _RWLOCK_RDLOCK(&lcl_rwlock);
        if (lcl_is_set < 0) {
+#ifdef NOTIFY_TZ_DEBUG
+               NOTIFY_TZ_PRINTF("tzsetwall_basic lcl_is_set < 0\n");
+#endif
                if (!rdlocked)
                        _RWLOCK_UNLOCK(&lcl_rwlock);
                return;
        }
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("tzsetwall_basic not set\n");
+#endif
        _RWLOCK_UNLOCK(&lcl_rwlock);
 
        _RWLOCK_WRLOCK(&lcl_rwlock);
@@ -980,7 +1594,7 @@ tzsetwall_basic(int rdlocked)
 
 #ifdef ALL_STATE
        if (lclptr == NULL) {
-               lclptr = (struct state *) malloc(sizeof *lclptr);
+               lclptr = calloc(1, sizeof *lclptr);
                if (lclptr == NULL) {
                        settzname();    /* all we can do */
                        _RWLOCK_UNLOCK(&lcl_rwlock);
@@ -990,8 +1604,21 @@ tzsetwall_basic(int rdlocked)
                }
        }
 #endif /* defined ALL_STATE */
-       if (tzload((char *) NULL, lclptr) != 0)
+#ifdef NOTIFY_TZ
+       {
+               char            fullname[FILENAME_MAX + 1];
+               if (tzload((char *) NULL, lclptr, fullname, TRUE) != 0)
+                       /*
+                        * If fullname is empty (an error occurred) then
+                        * default to the UTC path
+                        */
+                       gmtload(lclptr, *fullname ? NULL : fullname);
+                       notify_register_tz(fullname, &lcl_notify);
+       }
+#else /* ! NOTIFY_TZ */
+       if (tzload((char *) NULL, lclptr, TRUE) != 0)
                gmtload(lclptr);
+#endif /* NOTIFY_TZ */
        settzname();
        _RWLOCK_UNLOCK(&lcl_rwlock);
 
@@ -1002,10 +1629,13 @@ tzsetwall_basic(int rdlocked)
 void
 tzsetwall(void)
 {
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("tzsetwall called\n");
+#endif /* NOTIFY_TZ_DEBUG */
        tzsetwall_basic(0);
 }
 
-static void
+__private_extern__ void
 tzset_basic(int rdlocked)
 {
        const char *    name;
@@ -1016,11 +1646,17 @@ tzset_basic(int rdlocked)
                return;
        }
 
+#ifdef NOTIFY_TZ
+       notify_check_tz(&lcl_notify);
+#endif /* NOTIFY_TZ */
        if (!rdlocked)
                _RWLOCK_RDLOCK(&lcl_rwlock);
        if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) {
                if (!rdlocked)
                        _RWLOCK_UNLOCK(&lcl_rwlock);
+#ifdef NOTIFY_TZ_DEBUG
+               NOTIFY_TZ_PRINTF("tzset_basic matched %s\n", lcl_TZname);
+#endif
                return;
        }
        _RWLOCK_UNLOCK(&lcl_rwlock);
@@ -1032,7 +1668,7 @@ tzset_basic(int rdlocked)
 
 #ifdef ALL_STATE
        if (lclptr == NULL) {
-               lclptr = (struct state *) malloc(sizeof *lclptr);
+               lclptr = (struct state *) calloc(1, sizeof *lclptr);
                if (lclptr == NULL) {
                        settzname();    /* all we can do */
                        _RWLOCK_UNLOCK(&lcl_rwlock);
@@ -1053,9 +1689,34 @@ tzset_basic(int rdlocked)
                lclptr->ttis[0].tt_gmtoff = 0;
                lclptr->ttis[0].tt_abbrind = 0;
                (void) strcpy(lclptr->chars, gmt);
-       } else if (tzload(name, lclptr) != 0)
+#ifdef NOTIFY_TZ
+               notify_register_tz(NULL, &lcl_notify);
+#endif /* NOTIFY_TZ */
+       } else
+#ifdef NOTIFY_TZ
+       {
+               char            fullname[FILENAME_MAX + 1];
+               /*
+                * parsedOK indicates whether tzparse() was called and
+                * succeeded.  This means that TZ is a time conversion
+                * specification, so we don't need to register for
+                * notifications.
+                */
+               int             parsedOK = FALSE;
+               if (tzload(name, lclptr, fullname, TRUE) != 0)
+                       if (name[0] == ':' || !(parsedOK = tzparse(name, lclptr, FALSE) == 0))
+                               /*
+                                * If fullname is empty (an error occurred) then
+                                * default to the UTC path
+                                */
+                               (void) gmtload(lclptr, *fullname ? NULL : fullname);
+                               notify_register_tz(parsedOK ? NULL : fullname, &lcl_notify);
+       }
+#else /* ! NOTIFY_TZ */
+       if (tzload(name, lclptr, TRUE) != 0)
                if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
                        (void) gmtload(lclptr);
+#endif /* NOTIFY_TZ */
        settzname();
        _RWLOCK_UNLOCK(&lcl_rwlock);
 
@@ -1066,37 +1727,107 @@ tzset_basic(int rdlocked)
 void
 tzset(void)
 {
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("tzset called TZ=%s\n", getenv("TZ"));
+#endif /* NOTIFY_TZ_DEBUG */
        tzset_basic(0);
 }
 
 /*
 ** 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
-localsub(timep, offset, tmp)
-const time_t * const   timep;
-const long             offset;
-struct tm * const      tmp;
+#ifdef __LP64__
+__private_extern__ struct tm *
+#else /* !__LP64__ */
+__private_extern__ void
+#endif /* __LP64__ */
+localsub(const time_t *const timep, const long offset, struct tm *const tmp)
 {
        struct state *          sp;
        const struct ttinfo *   ttisp;
        int                     i;
-       const time_t                    t = *timep;
-
+#ifdef __LP64__
+       struct tm *             result;
+#endif /* __LP64__ */
+       const time_t            t = *timep;
+
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("localsub called\n");
+#endif /* NOTIFY_TZ_DEBUG */
        sp = lclptr;
 #ifdef ALL_STATE
        if (sp == NULL) {
+#ifdef __LP64__
+               return gmtsub(timep, offset, tmp);
+#else /* !__LP64__ */
                gmtsub(timep, offset, tmp);
                return;
+#endif /* __LP64__ */
        }
 #endif /* defined ALL_STATE */
+       if ((sp->goback && t < sp->ats[0]) ||
+               (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+                       time_t                  newt = t;
+                       register time_t         seconds;
+                       register time_t         tcycles;
+                       register 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) {
+#ifdef __LP64__
+                               return NULL;
+#else /* !__LP64__ */
+                               return;
+#endif /* __LP64__ */
+                       }
+                       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])
+#ifdef __LP64__
+                                       return NULL;    /* "cannot happen" */
+                       result = localsub(&newt, offset, tmp);
+                       if (result == tmp) {
+#else /* !__LP64__ */
+                                       return;
+                       localsub(&newt, offset, tmp);
+                       {
+#endif /* __LP64__ */
+                               register 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)
+#ifdef __LP64__
+                                       return NULL;
+                       }
+                       return result;
+#else /* !__LP64__ */
+                                       return;
+                       }
+                       return;
+#endif /* __LP64__ */
+       }
        if (sp->timecnt == 0 || t < sp->ats[0]) {
                i = 0;
                while (sp->ttis[i].tt_isdst)
@@ -1105,10 +1836,17 @@ struct tm * const       tmp;
                                break;
                        }
        } else {
-               for (i = 1; i < sp->timecnt; ++i)
-                       if (t < sp->ats[i])
-                               break;
-               i = sp->types[i - 1];
+               register int    lo = 1;
+               register int    hi = sp->timecnt;
+
+               while (lo < hi) {
+                       register 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];
        /*
@@ -1117,32 +1855,41 @@ struct tm * const       tmp;
        **      t += ttisp->tt_gmtoff;
        **      timesub(&t, 0L, sp, tmp);
        */
+#ifdef __LP64__
+       result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+       if (result == NULL)
+               return NULL;
+#else /* !__LP64__ */
        timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+#endif /* __LP64__ */
        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 */
+#ifdef __LP64__
+       return result;
+#endif /* __LP64__ */
+}
+
+static void
+localtime_key_init(void)
+{
+
+       localtime_key = __LIBC_PTHREAD_KEY_LOCALTIME;
+       localtime_key_error = pthread_key_init_np(localtime_key, free);
 }
 
 struct tm *
-localtime(timep)
-const time_t * const   timep;
+localtime(const time_t *const timep)
 {
-       static pthread_mutex_t localtime_mutex = PTHREAD_MUTEX_INITIALIZER;
-       static pthread_key_t localtime_key = -1;
        struct tm *p_tm;
 
        if (__isthreaded != 0) {
-               if (localtime_key < 0) {
-                       _pthread_mutex_lock(&localtime_mutex);
-                       if (localtime_key < 0) {
-                               if (_pthread_key_create(&localtime_key, free) < 0) {
-                                       _pthread_mutex_unlock(&localtime_mutex);
-                                       return(NULL);
-                               }
-                       }
-                       _pthread_mutex_unlock(&localtime_mutex);
+               _pthread_once(&localtime_once, localtime_key_init);
+               if (localtime_key_error != 0) {
+                       errno = localtime_key_error;
+                       return(NULL);
                }
                p_tm = _pthread_getspecific(localtime_key);
                if (p_tm == NULL) {
@@ -1153,13 +1900,21 @@ const time_t * const    timep;
                }
                _RWLOCK_RDLOCK(&lcl_rwlock);
                tzset_basic(1);
+#ifdef __LP64__
+               p_tm = localsub(timep, 0L, p_tm);
+#else /* !__LP64__ */
                localsub(timep, 0L, p_tm);
+#endif /* __LP64__ */
                _RWLOCK_UNLOCK(&lcl_rwlock);
                return(p_tm);
        } else {
                tzset_basic(0);
+#ifdef __LP64__
+               return localsub(timep, 0L, &tm);
+#else /* !__LP64__ */
                localsub(timep, 0L, &tm);
                return(&tm);
+#endif /* __LP64__ */
        }
 }
 
@@ -1168,40 +1923,73 @@ const time_t * const    timep;
 */
 
 struct tm *
-localtime_r(timep, tm)
-const time_t * const   timep;
-struct tm *            tm;
+localtime_r(const time_t *const __restrict timep, struct tm * __restrict tmp)
 {
        _RWLOCK_RDLOCK(&lcl_rwlock);
        tzset_basic(1);
-       localsub(timep, 0L, tm);
+#ifdef __LP64__
+       tmp = localsub(timep, 0L, tmp);
+#else /* !__LP64__ */
+       localsub(timep, 0L, tmp);
+#endif /* __LP64__ */
        _RWLOCK_UNLOCK(&lcl_rwlock);
-       return tm;
+       return tmp;
+}
+
+static void
+gmt_init(void)
+{
+
+#ifdef ALL_STATE
+#ifdef NOTIFY_TZ
+       if (gmtptr == NULL)
+#endif /* NOTIFY_TZ */
+               gmtptr = (struct state *) calloc(1, sizeof *gmtptr);
+       if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+#ifdef NOTIFY_TZ
+       {
+               char            fullname[FILENAME_MAX + 1];
+               gmtload(gmtptr, fullname);
+               notify_register_tz(fullname, &gmt_notify);
+       }
+#else /* ! NOTIFY_TZ */
+               gmtload(gmtptr);
+#endif /* NOTIFY_TZ */
 }
 
 /*
 ** gmtsub is to gmtime as localsub is to localtime.
 */
 
+#ifdef __LP64__
+static struct tm *
+#else /* !__LP64__ */
 static void
+#endif /* __LP64__ */
 gmtsub(timep, offset, tmp)
 const time_t * const   timep;
 const long             offset;
 struct tm * const      tmp;
 {
-       if (!gmt_is_set) {
-               _MUTEX_LOCK(&gmt_mutex);
-               if (!gmt_is_set) {
-#ifdef ALL_STATE
-                       gmtptr = (struct state *) malloc(sizeof *gmtptr);
-                       if (gmtptr != NULL)
-#endif /* defined ALL_STATE */
-                               gmtload(gmtptr);
-                       gmt_is_set = TRUE;
-               }
-               _MUTEX_UNLOCK(&gmt_mutex);
-       }
+#ifdef __LP64__
+       register struct tm *    result;
+#endif /* __LP64__ */
+
+#ifdef NOTIFY_TZ_DEBUG
+       NOTIFY_TZ_PRINTF("gmtsub called\n");
+#endif /* NOTIFY_TZ_DEBUG */
+#ifdef NOTIFY_TZ
+       notify_check_tz(&gmt_notify);
+#endif /* NOTIFY_TZ */
+       pthread_once(&gmt_once, gmt_init);
+#ifdef __LP64__
+       result = timesub(timep, offset, gmtptr, tmp);
+       if (result == NULL)
+               return NULL;
+#else /* !__LP64__ */
        timesub(timep, offset, gmtptr, tmp);
+#endif /* __LP64__ */
 #ifdef TM_ZONE
        /*
        ** Could get fancy here and deliver something such as
@@ -1209,11 +1997,11 @@ struct tm * const       tmp;
        ** but this is no time for a treasure hunt.
        */
        if (offset != 0)
-               tmp->TM_ZONE = wildabbr;
+               tmp->TM_ZONE = (char*)wildabbr;
        else {
 #ifdef ALL_STATE
                if (gmtptr == NULL)
-                       tmp->TM_ZONE = gmt;
+                       tmp->TM_ZONE = (char *)gmt;
                else    tmp->TM_ZONE = gmtptr->chars;
 #endif /* defined ALL_STATE */
 #ifndef ALL_STATE
@@ -1221,26 +2009,29 @@ struct tm * const       tmp;
 #endif /* State Farm */
        }
 #endif /* defined TM_ZONE */
+#ifdef __LP64__
+       return result;
+#endif /* __LP64__ */
+}
+
+static void
+gmtime_key_init(void)
+{
+       
+       gmtime_key = __LIBC_PTHREAD_KEY_GMTIME;
+       gmtime_key_error = pthread_key_init_np(gmtime_key, free);
 }
 
 struct tm *
-gmtime(timep)
-const time_t * const   timep;
+gmtime(const time_t *const timep)
 {
-       static pthread_mutex_t gmtime_mutex = PTHREAD_MUTEX_INITIALIZER;
-       static pthread_key_t gmtime_key = -1;
        struct tm *p_tm;
 
        if (__isthreaded != 0) {
-               if (gmtime_key < 0) {
-                       _pthread_mutex_lock(&gmtime_mutex);
-                       if (gmtime_key < 0) {
-                               if (_pthread_key_create(&gmtime_key, free) < 0) {
-                                       _pthread_mutex_unlock(&gmtime_mutex);
-                                       return(NULL);
-                               }
-                       }
-                       _pthread_mutex_unlock(&gmtime_mutex);
+               _pthread_once(&gmtime_once, gmtime_key_init);
+               if (gmtime_key_error != 0) {
+                       errno = gmtime_key_error;
+                       return(NULL);
                }
                /*
                 * Changed to follow POSIX.1 threads standard, which
@@ -1253,12 +2044,20 @@ const time_t * const    timep;
                        }
                        _pthread_setspecific(gmtime_key, p_tm);
                }
+#ifdef __LP64__
+               return gmtsub(timep, 0L, p_tm);
+#else /* !__LP64__ */
                gmtsub(timep, 0L, p_tm);
                return(p_tm);
+#endif /* __LP64__ */
        }
        else {
+#ifdef __LP64__
+               return gmtsub(timep, 0L, &tm);
+#else /* !__LP64__ */
                gmtsub(timep, 0L, &tm);
                return(&tm);
+#endif /* __LP64__ */
        }
 }
 
@@ -1267,28 +2066,54 @@ const time_t * const    timep;
 */
 
 struct tm *
-gmtime_r(timep, tm)
-const time_t * const   timep;
-struct tm *            tm;
+gmtime_r(const time_t *const timep, struct tm *tmp)
 {
-       gmtsub(timep, 0L, tm);
-       return tm;
+
+#ifdef __LP64__
+       return gmtsub(timep, 0L, tmp);
+#else /* !__LP64__ */
+       gmtsub(timep, 0L, tmp);
+       return tmp;
+#endif /* __LP64__ */
 }
 
 #ifdef STD_INSPIRED
 
 struct tm *
-offtime(timep, offset)
-const time_t * const   timep;
-const long             offset;
+offtime(const time_t *const timep, const long offset)
 {
+#ifdef __LP64__
+       return gmtsub(timep, offset, &tm);
+#else /* !__LP64__ */
        gmtsub(timep, offset, &tm);
        return &tm;
+#endif /* __LP64__ */
 }
 
 #endif /* defined STD_INSPIRED */
 
+/*
+** 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.
+*/
+
+__unused static int
+leaps_thru_end_of(y)
+register const int     y;
+{
+#ifdef __LP64__
+       return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+               -(leaps_thru_end_of(-(y + 1)) + 1);
+#else /* !__LP64__ */
+       return (y / 4 - y / 100 + y / 400);
+#endif /* __LP64__ */
+}
+
+#ifdef __LP64__
+static struct tm *
+#else /* !__LP64__ */
 static void
+#endif /* __LP64__ */
 timesub(timep, offset, sp, tmp)
 const time_t * const                   timep;
 const long                             offset;
@@ -1365,7 +2190,12 @@ struct tm * const                tmp;
        if (tmp->tm_wday < 0)
                tmp->tm_wday += DAYSPERWEEK;
        y = EPOCH_YEAR;
-#define LEAPS_THRU_END_OF(y)   ((y) / 4 - (y) / 100 + (y) / 400)
+#define _LEAPS_THRU_END_OF(y)  ((y) / 4 - (y) / 100 + (y) / 400)
+#ifdef __LP64__
+#define LEAPS_THRU_END_OF(y)   ((y) >= 0 ? _LEAPS_THRU_END_OF(y) : _LEAPS_THRU_END_OF((y) + 1) - 1)
+#else /* !__LP64__ */
+#define LEAPS_THRU_END_OF(y)   _LEAPS_THRU_END_OF(y)
+#endif /* __LP64__ */
        while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
                long    newy;
 
@@ -1377,7 +2207,16 @@ struct tm * const                tmp;
                        LEAPS_THRU_END_OF(y - 1);
                y = newy;
        }
+#ifdef __LP64__
+       y -= TM_YEAR_BASE;
+       if (y < INT_MIN || y > INT_MAX) {
+               errno = EOVERFLOW;
+               return NULL;
+       }
+       tmp->tm_year = y;
+#else /* !__LP64__ */
        tmp->tm_year = y - TM_YEAR_BASE;
+#endif /* __LP64__ */
        tmp->tm_yday = (int) days;
        ip = mon_lengths[yleap];
        for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
@@ -1387,37 +2226,60 @@ struct tm * const               tmp;
 #ifdef TM_GMTOFF
        tmp->TM_GMTOFF = offset;
 #endif /* defined TM_GMTOFF */
+#ifdef __LP64__
+       return tmp;
+#endif /* __LP64__ */
 }
 
 char *
-ctime(timep)
-const time_t * const   timep;
+ctime(const time_t *const timep)
 {
 /*
 ** Section 4.12.3.2 of X3.159-1989 requires that
 **     The ctime function converts the calendar time pointed to by timer
-**     to local time in the form of a string.  It is equivalent to
+**     to local time in the form of a string. It is equivalent to
 **             asctime(localtime(timer))
 */
+#ifdef __LP64__
+       /*
+        * In 64-bit, the timep value may produce a time value with a year
+        * that exceeds 32-bits in size (won't fit in struct tm), so localtime
+        * will return NULL.
+        */
+       struct tm *tm = localtime(timep);
+
+       if (tm == NULL)
+               return NULL;
+       return asctime(tm);
+#else /* !__LP64__ */
        return asctime(localtime(timep));
+#endif /* __LP64__ */
 }
 
 char *
-ctime_r(timep, buf)
-const time_t * const   timep;
-char *                 buf;
+ctime_r(const time_t *const timep, char *buf)
 {
-       struct tm       tm;
+       struct tm       mytm;
 
-       return asctime_r(localtime_r(timep, &tm), buf);
+#ifdef __LP64__
+       /*
+        * In 64-bit, the timep value may produce a time value with a year
+        * that exceeds 32-bits in size (won't fit in struct tm), so localtime_r
+        * will return NULL.
+        */
+       if (localtime_r(timep, &mytm) == NULL)
+               return NULL;
+       return asctime_r(&mytm, buf);
+#else /* !__LP64__ */
+       return asctime_r(localtime_r(timep, &mytm), buf);
+#endif /* __LP64__ */
 }
 
 /*
 ** 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).
 */
@@ -1427,7 +2289,7 @@ char *                    buf;
 #endif /* !defined WRONG */
 
 /*
-** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
+** Simplified normalize logic courtesy Paul Eggert.
 */
 
 static int
@@ -1443,10 +2305,19 @@ int     delta;
 }
 
 static int
-normalize_overflow(tensptr, unitsptr, base)
-int * const    tensptr;
-int * const    unitsptr;
-const int      base;
+long_increment_overflow(number, delta)
+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)
 {
        int     tensdelta;
 
@@ -1457,6 +2328,18 @@ const int        base;
        return increment_overflow(tensptr, tensdelta);
 }
 
+static int
+long_normalize_overflow(long *const tensptr, int *const unitsptr, const int base)
+{
+       register int    tensdelta;
+
+       tensdelta = (*unitsptr >= 0) ?
+               (*unitsptr / base) :
+               (-1 - (-1 - *unitsptr) / base);
+       *unitsptr -= tensdelta * base;
+       return long_increment_overflow(tensptr, tensdelta);
+}
+
 static int
 tmcomp(atmp, btmp)
 const struct tm * const atmp;
@@ -1464,8 +2347,14 @@ const struct tm * const btmp;
 {
        int     result;
 
-       if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
-               (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+       /*
+        * Assume that atmp and btmp point to normalized tm strutures.
+        * So only arithmetic with tm_year could overflow in 64-bit.
+        */
+       if (atmp->tm_year != btmp->tm_year) {
+               return (atmp->tm_year > btmp->tm_year ? 1 : -1);
+       }
+       if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
                (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
                (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
                (result = (atmp->tm_min - btmp->tm_min)) == 0)
@@ -1474,21 +2363,28 @@ const struct tm * const btmp;
 }
 
 static time_t
-time2sub(tmp, funcp, offset, okayp, do_norm_secs)
-struct tm * const      tmp;
-void (* const          funcp)(const time_t*, long, struct tm*);
-const long             offset;
-int * const            okayp;
-const int              do_norm_secs;
+time2sub(struct tm *const tmp,
+#ifdef __LP64__
+        struct tm *(*const funcp)(const time_t *, long, struct tm *),
+#else /* !__LP64__ */
+        void(*funcp) (const time_t *, long, struct tm*),
+#endif /* __LP64__ */
+        const long offset,
+        int *const okayp,
+        const int do_norm_secs,
+        int unix03)
 {
        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;
@@ -1501,45 +2397,49 @@ const int               do_norm_secs;
                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;
        /* Don't go below 1900 for POLA */
        if (yourtm.tm_year < 0)
                return WRONG;
        if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
                saved_seconds = 0;
-       else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+       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.
@@ -1557,35 +2457,66 @@ const int               do_norm_secs;
                yourtm.tm_sec = 0;
        }
        /*
-       ** Divide the search space in half
-       ** (this works whether time_t is signed or unsigned).
+       ** Do a binary search (this works whatever time_t's type is).
        */
-       bits = TYPE_BIT(time_t) - 1;
-       /*
-       ** If we have more than this, we will overflow tm_year for tmcomp().
-       ** We should really return an error if we cannot represent it.
-       */
-       if (bits > 48)
-               bits = 48;
-       /*
-       ** 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.
-       */
-       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 ( ; ; ) {
+               t = lo / 2 + hi / 2;
+               if (t < lo)
+                       t = lo;
+               else if (t > hi)
+                       t = hi;
+#ifdef __LP64__
+               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);
+#else /* !__LP64__ */
                (*funcp)(&t, offset, &mytm);
                dir = tmcomp(&mytm, &yourtm);
+               // If we have searched the entire space without a match, exit
+               if (dir != 0 && t == lo && t == hi)
+                       return WRONG;
+#endif /* __LP64__ */
                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;
                }
+               sp = (funcp == localsub) ? lclptr : gmtptr;
+               if (unix03 && sp->typecnt == 1 && yourtm.tm_isdst > 0) 
+                       yourtm.tm_isdst = 0; /* alternative time does not apply */
                if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
                        break;
                /*
@@ -1594,7 +2525,6 @@ const int         do_norm_secs;
                ** It's okay to guess wrong since the guess
                ** gets checked.
                */
-               sp = (funcp == localsub) ? lclptr : gmtptr;
 #ifdef ALL_STATE
                if (sp == NULL)
                        return WRONG;
@@ -1607,7 +2537,12 @@ const int                do_norm_secs;
                                        continue;
                                newt = t + sp->ttis[j].tt_gmtoff -
                                        sp->ttis[i].tt_gmtoff;
+#ifdef __LP64__
+                               if ((*funcp)(&newt, offset, &mytm) == NULL)
+                                       continue;
+#else /* !__LP64__ */
                                (*funcp)(&newt, offset, &mytm);
+#endif /* __LP64__ */
                                if (tmcomp(&mytm, &yourtm) != 0)
                                        continue;
                                if (mytm.tm_isdst != yourtm.tm_isdst)
@@ -1626,17 +2561,26 @@ label:
        if ((newt < t) != (saved_seconds < 0))
                return WRONG;
        t = newt;
+#ifdef __LP64__
+       if ((*funcp)(&t, offset, tmp) == NULL)
+               return WRONG;
+#else /* !__LP64__ */
        (*funcp)(&t, offset, tmp);
-       *okayp = TRUE;
+#endif /* __LP64__ */
+               *okayp = TRUE;
        return t;
 }
 
 static time_t
-time2(tmp, funcp, offset, okayp)
-struct tm * const      tmp;
-void (* const          funcp)(const time_t*, long, struct tm*);
-const long             offset;
-int * const            okayp;
+time2(struct tm * const        tmp,
+#ifdef __LP64__
+      struct tm * (*const funcp)(const time_t *, long, struct tm *),
+#else /* !__LP64__ */
+      void (*const funcp)(const time_t *, long, struct tm *),
+#endif /* __LP64__ */
+      const long offset,
+      int *const okayp,
+      int unix03)
 {
        time_t  t;
 
@@ -1645,15 +2589,20 @@ int * const             okayp;
        ** (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);
+       t = time2sub(tmp, funcp, offset, okayp, FALSE, unix03);
+       return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, unix03);
 }
 
-static time_t
-time1(tmp, funcp, offset)
+__private_extern__ time_t
+time1(tmp, funcp, offset, unix03)
 struct tm * const      tmp;
-void (* const          funcp)(const time_t *, long, struct tm *);
+#ifdef __LP64__
+struct tm * (* const  funcp)(const time_t *, long, struct tm *);
+#else /* !__LP64__ */
+void (* const  funcp)(const time_t *, long, struct tm *);
+#endif /* __LP64__ */
 const long             offset;
+int                    unix03;
 {
        time_t                  t;
        const struct state *    sp;
@@ -1665,12 +2614,17 @@ const long              offset;
        int                             types[TZ_MAX_TYPES];
        int                             okay;
 
+       if (tmp == NULL) {
+               errno = EINVAL;
+               return WRONG;
+       }
+
        if (tmp->tm_isdst > 1)
                tmp->tm_isdst = 1;
-       t = time2(tmp, funcp, offset, &okay);
+       t = time2(tmp, funcp, offset, &okay, unix03);
 #ifdef PCTS
        /*
-       ** PCTS code courtesy Grant Sullivan (grant@osf.org).
+       ** PCTS code courtesy Grant Sullivan.
        */
        if (okay)
                return t;
@@ -1687,7 +2641,7 @@ const long                offset;
        ** We try to divine the type they started from and adjust to the
        ** type they need.
        */
-       sp = (funcp == localsub) ? lclptr : gmtptr;
+       sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr);
 #ifdef ALL_STATE
        if (sp == NULL)
                return WRONG;
@@ -1711,7 +2665,7 @@ const long                offset;
                        tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
                                        sp->ttis[samei].tt_gmtoff;
                        tmp->tm_isdst = !tmp->tm_isdst;
-                       t = time2(tmp, funcp, offset, &okay);
+                       t = time2(tmp, funcp, offset, &okay, unix03);
                        if (okay)
                                return t;
                        tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
@@ -1721,44 +2675,48 @@ const long              offset;
        }
        return WRONG;
 }
+#else  /* BUILDING_VARIANT */
+extern pthread_rwlock_t        lcl_rwlock;
+#endif /* BUILDING_VARIANT */
 
 time_t
-mktime(tmp)
-struct tm * const      tmp;
+mktime(struct tm *const tmp)
 {
        time_t mktime_return_value;
+       int serrno = errno;
        _RWLOCK_RDLOCK(&lcl_rwlock);
        tzset_basic(1);
-       mktime_return_value = time1(tmp, localsub, 0L);
+       mktime_return_value = time1(tmp, localsub, 0L, __DARWIN_UNIX03);
        _RWLOCK_UNLOCK(&lcl_rwlock);
+       errno = serrno;
        return(mktime_return_value);
 }
 
+#if !BUILDING_VARIANT
 #ifdef STD_INSPIRED
 
 time_t
-timelocal(tmp)
-struct tm * const      tmp;
+timelocal(struct tm *const tmp)
 {
-       tmp->tm_isdst = -1;     /* in case it wasn't initialized */
+       if (tmp != NULL)
+               tmp->tm_isdst = -1;     /* in case it wasn't initialized */
        return mktime(tmp);
 }
 
 time_t
-timegm(tmp)
-struct tm * const      tmp;
+timegm(struct tm *const tmp)
 {
-       tmp->tm_isdst = 0;
-       return time1(tmp, gmtsub, 0L);
+       if (tmp != NULL)
+               tmp->tm_isdst = 0;
+       return time1(tmp, gmtsub, 0L, __DARWIN_UNIX03);
 }
 
 time_t
-timeoff(tmp, offset)
-struct tm * const      tmp;
-const long             offset;
+timeoff(struct tm *const tmp, const long offset)
 {
-       tmp->tm_isdst = 0;
-       return time1(tmp, gmtsub, offset);
+       if (tmp != NULL)
+               tmp->tm_isdst = 0;
+       return time1(tmp, gmtsub, offset, __DARWIN_UNIX03);
 }
 
 #endif /* defined STD_INSPIRED */
@@ -1771,8 +2729,7 @@ const long                offset;
 */
 
 long
-gtime(tmp)
-struct tm * const      tmp;
+gtime(struct tm *const tmp)
 {
        const time_t    t = mktime(tmp);
 
@@ -1798,8 +2755,7 @@ struct tm * const tmp;
 */
 
 static long
-leapcorr(timep)
-time_t *       timep;
+leapcorr(time_t *timep)
 {
        struct state *          sp;
        struct lsinfo * lp;
@@ -1816,16 +2772,14 @@ time_t *        timep;
 }
 
 time_t
-time2posix(t)
-time_t t;
+time2posix(time_t t)
 {
        tzset();
        return t - leapcorr(&t);
 }
 
 time_t
-posix2time(t)
-time_t t;
+posix2time(time_t t)
 {
        time_t  x;
        time_t  y;
@@ -1833,7 +2787,7 @@ 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.
        */
@@ -1858,3 +2812,5 @@ time_t    t;
 }
 
 #endif /* defined STD_INSPIRED */
+#endif /* !BUILDING_VARIANT */
+#pragma clang diagnostic pop