aboutsummaryrefslogtreecommitdiff
path: root/contrib/tzcode
diff options
context:
space:
mode:
authorEdwin Groothuis <edwin@FreeBSD.org>2010-02-25 21:21:34 +0000
committerEdwin Groothuis <edwin@FreeBSD.org>2010-02-25 21:21:34 +0000
commit522a35959a50f901245d92282eac9b9949aa3f62 (patch)
treedc2e5dde783dd3e1ddd1022f34614df62e99f4e6 /contrib/tzcode
parent97dcdbd4502a61499db66e4622491ebfe16a05c0 (diff)
downloadsrc-522a35959a50f901245d92282eac9b9949aa3f62.tar.gz
src-522a35959a50f901245d92282eac9b9949aa3f62.zip
Copy lib/libc/stdtime to contrib/tzcode/stdtime for the proper split
of contributed code and FreeBSD specific code.
Notes
Notes: svn path=/head/; revision=204333
Diffstat (limited to 'contrib/tzcode')
-rw-r--r--contrib/tzcode/stdtime/Makefile.inc18
-rw-r--r--contrib/tzcode/stdtime/Symbol.map35
-rw-r--r--contrib/tzcode/stdtime/asctime.c142
-rw-r--r--contrib/tzcode/stdtime/ctime.3374
-rw-r--r--contrib/tzcode/stdtime/difftime.c69
-rw-r--r--contrib/tzcode/stdtime/localtime.c2249
-rw-r--r--contrib/tzcode/stdtime/private.h326
-rw-r--r--contrib/tzcode/stdtime/strftime.3278
-rw-r--r--contrib/tzcode/stdtime/strftime.c634
-rw-r--r--contrib/tzcode/stdtime/strptime.3173
-rw-r--r--contrib/tzcode/stdtime/strptime.c566
-rw-r--r--contrib/tzcode/stdtime/time2posix.3123
-rw-r--r--contrib/tzcode/stdtime/time32.c100
-rw-r--r--contrib/tzcode/stdtime/timelocal.c117
-rw-r--r--contrib/tzcode/stdtime/timelocal.h55
-rw-r--r--contrib/tzcode/stdtime/tzfile.5152
-rw-r--r--contrib/tzcode/stdtime/tzfile.h184
17 files changed, 5595 insertions, 0 deletions
diff --git a/contrib/tzcode/stdtime/Makefile.inc b/contrib/tzcode/stdtime/Makefile.inc
new file mode 100644
index 000000000000..a58a8a2ae815
--- /dev/null
+++ b/contrib/tzcode/stdtime/Makefile.inc
@@ -0,0 +1,18 @@
+# Makefile.inc,v 1.2 1994/09/13 21:26:01 wollman Exp
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/stdtime ${.CURDIR}/../locale
+
+SRCS+= asctime.c difftime.c localtime.c strftime.c strptime.c timelocal.c \
+ time32.c
+
+SYM_MAPS+= ${.CURDIR}/stdtime/Symbol.map
+
+MAN+= ctime.3 strftime.3 strptime.3 time2posix.3
+MAN+= tzfile.5
+
+MLINKS+=ctime.3 asctime.3 ctime.3 difftime.3 ctime.3 gmtime.3 \
+ ctime.3 localtime.3 ctime.3 mktime.3 ctime.3 timegm.3 \
+ ctime.3 ctime_r.3 ctime.3 localtime_r.3 ctime.3 gmtime_r.3 \
+ ctime.3 asctime_r.3
+MLINKS+=time2posix.3 posix2time.3
diff --git a/contrib/tzcode/stdtime/Symbol.map b/contrib/tzcode/stdtime/Symbol.map
new file mode 100644
index 000000000000..3bee3735f7e6
--- /dev/null
+++ b/contrib/tzcode/stdtime/Symbol.map
@@ -0,0 +1,35 @@
+/*
+ * $FreeBSD$
+ */
+
+FBSD_1.0 {
+ _time32_to_time;
+ _time_to_time32;
+ _time64_to_time;
+ _time_to_time64;
+ _time_to_long;
+ _long_to_time;
+ _time_to_int;
+ _int_to_time;
+ strptime;
+ strftime;
+ tzname;
+ tzsetwall;
+ tzset;
+ localtime;
+ localtime_r;
+ gmtime;
+ gmtime_r;
+ offtime;
+ ctime;
+ ctime_r;
+ mktime;
+ timelocal;
+ timegm;
+ timeoff;
+ time2posix;
+ posix2time;
+ difftime;
+ asctime_r;
+ asctime;
+};
diff --git a/contrib/tzcode/stdtime/asctime.c b/contrib/tzcode/stdtime/asctime.c
new file mode 100644
index 000000000000..30606f1692a7
--- /dev/null
+++ b/contrib/tzcode/stdtime/asctime.c
@@ -0,0 +1,142 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** 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.
+*/
+
+#include <sys/cdefs.h>
+#ifndef lint
+#ifndef NOID
+static char elsieid[] __unused = "@(#)asctime.c 8.2";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+__FBSDID("$FreeBSD$");
+
+/*LINTLIBRARY*/
+
+#include "namespace.h"
+#include "private.h"
+#include "un-namespace.h"
+#include "tzfile.h"
+
+/*
+** 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_r(timeptr, buf)
+const struct tm * timeptr;
+char * buf;
+{
+ 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;
+ char year[INT_STRLEN_MAXIMUM(int) + 2];
+ char result[MAX_ASCTIME_BUF_SIZE];
+
+ if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
+ wn = "???";
+ else wn = wday_name[timeptr->tm_wday];
+ if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
+ mn = "???";
+ else mn = mon_name[timeptr->tm_mon];
+ /*
+ ** 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".
+ */
+ (void) strftime(year, sizeof year, "%Y", timeptr);
+ /*
+ ** We avoid using snprintf since it's not available on all systems.
+ */
+ (void) sprintf(result,
+ ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
+ wn, mn,
+ timeptr->tm_mday, timeptr->tm_hour,
+ timeptr->tm_min, timeptr->tm_sec,
+ year);
+ if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
+ (void) strcpy(buf, result);
+ return buf;
+ } else {
+#ifdef EOVERFLOW
+ errno = EOVERFLOW;
+#else /* !defined EOVERFLOW */
+ errno = EINVAL;
+#endif /* !defined EOVERFLOW */
+ return NULL;
+ }
+}
+
+/*
+** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
+*/
+
+char *
+asctime(timeptr)
+const struct tm * timeptr;
+{
+ return asctime_r(timeptr, buf_asctime);
+}
diff --git a/contrib/tzcode/stdtime/ctime.3 b/contrib/tzcode/stdtime/ctime.3
new file mode 100644
index 000000000000..143bc2c9fd02
--- /dev/null
+++ b/contrib/tzcode/stdtime/ctime.3
@@ -0,0 +1,374 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Arthur Olson.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" 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
+.\" $FreeBSD$
+.\"
+.Dd January 2, 1999
+.Dt CTIME 3
+.Os
+.Sh NAME
+.Nm asctime ,
+.Nm asctime_r ,
+.Nm ctime ,
+.Nm ctime_r ,
+.Nm difftime ,
+.Nm gmtime ,
+.Nm gmtime_r ,
+.Nm localtime ,
+.Nm localtime_r ,
+.Nm mktime ,
+.Nm timegm
+.Nd transform binary date and time values
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In time.h
+.Vt extern char *tzname[2] ;
+.Ft char *
+.Fn ctime "const time_t *clock"
+.Ft double
+.Fn difftime "time_t time1" "time_t time0"
+.Ft char *
+.Fn asctime "const struct tm *tm"
+.Ft struct tm *
+.Fn localtime "const time_t *clock"
+.Ft struct tm *
+.Fn gmtime "const time_t *clock"
+.Ft time_t
+.Fn mktime "struct tm *tm"
+.Ft time_t
+.Fn timegm "struct tm *tm"
+.Ft char *
+.Fn ctime_r "const time_t *clock" "char *buf"
+.Ft struct tm *
+.Fn localtime_r "const time_t *clock" "struct tm *result"
+.Ft struct tm *
+.Fn gmtime_r "const time_t *clock" "struct tm *result"
+.Ft char *
+.Fn asctime_r "const struct tm *tm" "char *buf"
+.Sh DESCRIPTION
+The functions
+.Fn ctime ,
+.Fn gmtime
+and
+.Fn localtime
+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
+.Xr time 3 ) .
+.Pp
+The function
+.Fn localtime
+converts the time value pointed at by
+.Fa clock ,
+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 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
+sets the
+.Fa tm_isdst Ns 'th
+element of
+.Fa tzname
+to a pointer to an
+.Tn ASCII
+string that is the time zone abbreviation to be
+used with
+.Fn localtime Ns 's
+return value.
+.Pp
+The function
+.Fn gmtime
+similarly converts the time value, but without any time zone adjustment,
+and returns a pointer to a tm structure (described below).
+.Pp
+The
+.Fn ctime
+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:
+.Bd -literal -offset indent
+Thu Nov 24 18:22:48 1986\en\e0
+.Ed
+.Pp
+All the fields have constant width.
+.Pp
+The
+.Fn ctime_r
+function
+provides the same functionality as
+.Fn ctime
+except the caller must provide the output buffer
+.Fa buf
+to store the result, which must be at least 26 characters long.
+The
+.Fn localtime_r
+and
+.Fn gmtime_r
+functions
+provide the same functionality as
+.Fn localtime
+and
+.Fn gmtime
+respectively, except the caller must provide the output buffer
+.Fa result .
+.Pp
+The
+.Fn asctime
+function
+converts the broken down time in the structure
+.Fa tm
+pointed at by
+.Fa *tm
+to the form
+shown in the example above.
+.Pp
+The
+.Fn asctime_r
+function
+provides the same functionality as
+.Fn asctime
+except the caller provide the output buffer
+.Fa buf
+to store the result, which must be at least 26 characters long.
+.Pp
+The functions
+.Fn mktime
+and
+.Fn timegm
+convert the broken-down time in the structure
+pointed to by tm into a time value with the same encoding as that of the
+values returned by the
+.Xr time 3
+function (that is, seconds from the Epoch,
+.Tn UTC ) .
+The
+.Fn mktime
+function
+interprets the input structure according to the current timezone setting
+(see
+.Xr tzset 3 ) .
+The
+.Fn timegm
+function
+interprets the input structure as representing Universal Coordinated Time
+.Pq Tn UTC .
+.Pp
+The original values of the
+.Fa tm_wday
+and
+.Fa tm_yday
+components of the structure are ignored, and the original values of the
+other components are not restricted to their normal ranges, and will be
+normalized if needed.
+For example,
+October 40 is changed into November 9,
+a
+.Fa tm_hour
+of \-1 means 1 hour before midnight,
+.Fa tm_mday
+of 0 means the day preceding the current month, and
+.Fa tm_mon
+of \-2 means 2 months before January of
+.Fa tm_year .
+(A positive or zero value for
+.Fa tm_isdst
+causes
+.Fn mktime
+to presume initially that summer time (for example, Daylight Saving Time)
+is or is not in effect for the specified time, respectively.
+A negative value for
+.Fa tm_isdst
+causes the
+.Fn mktime
+function to attempt to divine whether summer time is in effect for the
+specified time.
+The
+.Fa tm_isdst
+and
+.Fa tm_gmtoff
+members are forced to zero by
+.Fn timegm . )
+.Pp
+On successful completion, the values of the
+.Fa tm_wday
+and
+.Fa tm_yday
+components of the structure are set appropriately, and the other components
+are set to represent the specified calendar time, but with their values
+forced to their normal ranges; the final value of
+.Fa tm_mday
+is not set until
+.Fa tm_mon
+and
+.Fa tm_year
+are determined.
+The
+.Fn mktime
+function
+returns the specified calendar time; if the calendar time cannot be
+represented, it returns \-1;
+.Pp
+The
+.Fn difftime
+function
+returns the difference between two calendar times,
+.Pf ( Fa time1
+-
+.Fa time0 ) ,
+expressed in seconds.
+.Pp
+External declarations as well as the tm structure definition are in the
+.In time.h
+include file.
+The tm structure includes at least the following fields:
+.Bd -literal -offset indent
+int tm_sec; /\(** seconds (0 - 60) \(**/
+int tm_min; /\(** minutes (0 - 59) \(**/
+int tm_hour; /\(** hours (0 - 23) \(**/
+int tm_mday; /\(** day of month (1 - 31) \(**/
+int tm_mon; /\(** month of year (0 - 11) \(**/
+int tm_year; /\(** year \- 1900 \(**/
+int tm_wday; /\(** day of week (Sunday = 0) \(**/
+int tm_yday; /\(** day of year (0 - 365) \(**/
+int tm_isdst; /\(** is summer time in effect? \(**/
+char \(**tm_zone; /\(** abbreviation of timezone name \(**/
+long tm_gmtoff; /\(** offset from UTC in seconds \(**/
+.Ed
+.Pp
+The
+field
+.Fa tm_isdst
+is non-zero if summer time is in effect.
+.Pp
+The field
+.Fa tm_gmtoff
+is the offset (in seconds) of the time represented from
+.Tn UTC ,
+with positive
+values indicating east of the Prime Meridian.
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr gettimeofday 2 ,
+.Xr getenv 3 ,
+.Xr time 3 ,
+.Xr tzset 3 ,
+.Xr tzfile 5
+.Sh STANDARDS
+The
+.Fn asctime ,
+.Fn ctime ,
+.Fn difftime ,
+.Fn gmtime ,
+.Fn localtime ,
+and
+.Fn mktime
+functions conform to
+.St -isoC ,
+and conform to
+.St -p1003.1-96
+provided the selected local timezone does not contain a leap-second table
+(see
+.Xr zic 8 ) .
+.Pp
+The
+.Fn asctime_r ,
+.Fn ctime_r ,
+.Fn gmtime_r ,
+and
+.Fn localtime_r
+functions are expected to conform to
+.St -p1003.1-96
+(again provided the selected local timezone does not contain a leap-second
+table).
+.Pp
+The
+.Fn timegm
+function is not specified by any standard; its function cannot be
+completely emulated using the standard functions described above.
+.Sh HISTORY
+This manual page is derived from
+the time package contributed to Berkeley by
+.An Arthur Olson
+and which appeared in
+.Bx 4.3 .
+.Sh BUGS
+Except for
+.Fn difftime ,
+.Fn mktime ,
+and the
+.Fn \&_r
+variants of the other functions,
+these functions leaves their result in an internal static object and return
+a pointer to that object.
+Subsequent calls to these
+function will modify the same object.
+.Pp
+The C Standard provides no mechanism for a program to modify its current
+local timezone setting, and the
+.Tn POSIX Ns No \&-standard
+method is not reentrant.
+(However, thread-safe implementations are provided
+in the
+.Tn POSIX
+threaded environment.)
+.Pp
+The
+.Va tm_zone
+field of a returned
+.Vt tm
+structure points to a static array of characters,
+which will also be overwritten by any subsequent calls (as well as by
+subsequent calls to
+.Xr tzset 3
+and
+.Xr tzsetwall 3 ) .
+.Pp
+Use of the external variable
+.Fa tzname
+is discouraged; the
+.Fa tm_zone
+entry in the tm structure is preferred.
diff --git a/contrib/tzcode/stdtime/difftime.c b/contrib/tzcode/stdtime/difftime.c
new file mode 100644
index 000000000000..d16f9a0cdeee
--- /dev/null
+++ b/contrib/tzcode/stdtime/difftime.c
@@ -0,0 +1,69 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+#include <sys/cdefs.h>
+#ifndef lint
+#ifndef NOID
+static char elsieid[] __unused = "@(#)difftime.c 8.1";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+__FBSDID("$FreeBSD$");
+
+/*LINTLIBRARY*/
+
+#include "namespace.h"
+#include "private.h" /* for time_t, TYPE_INTEGRAL, and TYPE_SIGNED */
+#include "un-namespace.h"
+
+double
+difftime(time1, time0)
+const time_t time1;
+const time_t time0;
+{
+ /*
+ ** 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 (!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));
+ }
+ /*
+ ** time_t is integral and signed.
+ ** Handle cases where both time1 and time0 have the same sign
+ ** (meaning that their difference cannot overflow).
+ */
+ if ((time1 < 0) == (time0 < 0))
+ return time1 - time0;
+ /*
+ ** time1 and time0 have opposite signs.
+ ** Punt if unsigned long is too narrow.
+ */
+ if (sizeof (unsigned long) < sizeof (time_t))
+ return (double) time1 - (double) time0;
+ /*
+ ** Stay calm...decent optimizers will eliminate the complexity below.
+ */
+ if (time1 >= 0 /* && time0 < 0 */)
+ return (unsigned long) time1 +
+ (unsigned long) (-(time0 + 1)) + 1;
+ return -(double) ((unsigned long) time0 +
+ (unsigned long) (-(time1 + 1)) + 1);
+}
diff --git a/contrib/tzcode/stdtime/localtime.c b/contrib/tzcode/stdtime/localtime.c
new file mode 100644
index 000000000000..bee916b03f99
--- /dev/null
+++ b/contrib/tzcode/stdtime/localtime.c
@@ -0,0 +1,2249 @@
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+#include <sys/cdefs.h>
+#ifndef lint
+#ifndef NOID
+static char elsieid[] __unused = "@(#)localtime.c 8.9";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+__FBSDID("$FreeBSD$");
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/*LINTLIBRARY*/
+
+#include "namespace.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include "private.h"
+#include "un-namespace.h"
+
+#include "tzfile.h"
+#include "float.h" /* for FLT_MAX and DBL_MAX */
+
+#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 */
+
+#include "libc_private.h"
+
+#define _MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x)
+#define _MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x)
+
+#define _RWLOCK_RDLOCK(x) \
+ do { \
+ if (__isthreaded) _pthread_rwlock_rdlock(x); \
+ } while (0)
+
+#define _RWLOCK_WRLOCK(x) \
+ do { \
+ if (__isthreaded) _pthread_rwlock_wrlock(x); \
+ } while (0)
+
+#define _RWLOCK_UNLOCK(x) \
+ do { \
+ if (__isthreaded) _pthread_rwlock_unlock(x); \
+ } while (0)
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE O_RDONLY
+#endif /* !defined O_BINARY */
+
+#ifndef WILDABBR
+/*
+** Someone might make incorrect use of a time zone abbreviation:
+** 1. They might reference tzname[0] before calling tzset (explicitly
+** or implicitly).
+** 2. They might reference tzname[1] before calling tzset (explicitly
+** or implicitly).
+** 3. They might reference tzname[1] after setting to a time zone
+** in which Daylight Saving Time is never observed.
+** 4. They might reference tzname[0] after setting to a time zone
+** in which Standard Time is never observed.
+** 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
+** string "tzname[0] used before set", and similarly for the other cases.
+** 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;
+
+/*
+ * In June 2004 it was decided UTC was a more appropriate default time
+ * zone than GMT.
+ */
+
+static const char gmt[] = "UTC";
+
+/*
+** 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; /* 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 UTC */
+};
+
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif /* !defined TZNAME_MAX */
+
+struct state {
+ int leapcnt;
+ 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];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+};
+
+struct rule {
+ int r_type; /* type of rule--see below */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ long r_time; /* transition time of rule */
+};
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+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 * 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 struct tm * gmtsub(const time_t * timep, long offset,
+ struct tm * tmp);
+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,
+ struct tm * (*funcp)(const time_t *,
+ long, struct tm *),
+ long offset);
+static time_t time2(struct tm *tmp,
+ struct tm * (*funcp)(const time_t *,
+ long, struct tm*),
+ long offset, int * okayp);
+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 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);
+
+#ifdef ALL_STATE
+static struct state * lclptr;
+static struct state * gmtptr;
+#endif /* defined ALL_STATE */
+
+#ifndef ALL_STATE
+static struct state lclmem;
+static struct state gmtmem;
+#define lclptr (&lclmem)
+#define gmtptr (&gmtmem)
+#endif /* State Farm */
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+static char lcl_TZname[TZ_STRLEN_MAX + 1];
+static int lcl_is_set;
+static pthread_once_t gmt_once = PTHREAD_ONCE_INIT;
+static 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
+};
+
+/*
+** Section 4.12.3 of X3.159-1989 requires that
+** 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 for noting this.
+*/
+
+static struct tm tm;
+
+#ifdef USG_COMPAT
+time_t timezone = 0;
+int daylight = 0;
+#endif /* defined USG_COMPAT */
+
+#ifdef ALTZONE
+time_t altzone = 0;
+#endif /* defined ALTZONE */
+
+static long
+detzcode(codep)
+const char * const codep;
+{
+ long result;
+ int i;
+
+ result = (codep[0] & 0x80) ? ~0L : 0;
+ for (i = 0; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+ return result;
+}
+
+static time_t
+detzcode64(codep)
+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;
+#ifdef USG_COMPAT
+ daylight = 0;
+ timezone = 0;
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+ altzone = 0;
+#endif /* defined ALTZONE */
+#ifdef ALL_STATE
+ if (sp == NULL) {
+ tzname[0] = tzname[1] = gmt;
+ return;
+ }
+#endif /* defined ALL_STATE */
+ for (i = 0; i < sp->typecnt; ++i) {
+ const struct ttinfo * const ttisp = &sp->ttis[i];
+
+ tzname[ttisp->tt_isdst] =
+ &sp->chars[ttisp->tt_abbrind];
+#ifdef USG_COMPAT
+ if (ttisp->tt_isdst)
+ daylight = 1;
+ if (i == 0 || !ttisp->tt_isdst)
+ timezone = -(ttisp->tt_gmtoff);
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+ if (i == 0 || ttisp->tt_isdst)
+ altzone = -(ttisp->tt_gmtoff);
+#endif /* defined ALTZONE */
+ }
+ /*
+ ** And to get the latest zone names into tzname. . .
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ const struct ttinfo * const ttisp =
+ &sp->ttis[
+ sp->types[i]];
+
+ 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) {
+ register const struct ttinfo * const ttisp = &sp->ttis[i];
+ register 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(t1, t0)
+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
+tzload(name, sp, doextend)
+const char * name;
+struct state * const sp;
+register const int doextend;
+{
+ 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)
+ if ((name[0] == ':' && name[1] == '/') ||
+ name[0] == '/' || strchr(name, '.'))
+ name = NULL;
+ if (name == NULL && (name = TZDEFAULT) == NULL)
+ return -1;
+ {
+ int doaccess;
+ struct stat stab;
+ /*
+ ** Section 4.9.1 of the C standard says that
+ ** "FILENAME_MAX expands to an integral constant expression
+ ** that is the size needed for an array of char large enough
+ ** to hold the longest file name string that the implementation
+ ** guarantees can be opened."
+ */
+ char fullname[FILENAME_MAX + 1];
+
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ if ((p = TZDIR) == NULL)
+ return -1;
+ if ((strlen(p) + 1 + strlen(name) + 1) >= sizeof fullname)
+ return -1;
+ (void) strcpy(fullname, p);
+ (void) strcat(fullname, "/");
+ (void) strcat(fullname, name);
+ /*
+ ** Set doaccess if '.' (as in "../") shows up in name.
+ */
+ if (strchr(name, '.') != NULL)
+ doaccess = TRUE;
+ name = fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return -1;
+ if ((fid = _open(name, OPEN_MODE)) == -1)
+ return -1;
+ if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) {
+ _close(fid);
+ return -1;
+ }
+ }
+ 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;
+
+ 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 (nread - (p - u.buf) <
+ sp->timecnt * stored + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * 6 + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (stored + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return -1;
+ for (i = 0; i < sp->timecnt; ++i) {
+ 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;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ ttisp->tt_isdst = (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return -1;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return -1;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+ for (i = 0; i < sp->leapcnt; ++i) {
+ struct lsinfo * lsisp;
+
+ lsisp = &sp->lsis[i];
+ lsisp->ls_trans = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ lsisp->ls_corr = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = FALSE;
+ else {
+ ttisp->tt_ttisstd = *p++;
+ if (ttisp->tt_ttisstd != TRUE &&
+ ttisp->tt_ttisstd != FALSE)
+ return -1;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = FALSE;
+ else {
+ ttisp->tt_ttisgmt = *p++;
+ if (ttisp->tt_ttisgmt != TRUE &&
+ ttisp->tt_ttisgmt != FALSE)
+ 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).
+ */
+ 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;
+
+ 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(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] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** 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
+** character.
+*/
+
+static const char *
+getzname(strp)
+const char * strp;
+{
+ char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++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(register const char *strp, const int delim)
+{
+ register 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
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(strp, nump, min, max)
+const char * strp;
+int * const nump;
+const int min;
+const int max;
+{
+ char c;
+ int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *
+getsecs(strp, secsp)
+const char * strp;
+long * const secsp;
+{
+ int num;
+
+ /*
+ ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** ``02:00 on the first Sunday on or after 23 Oct''.
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (long) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* `SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *
+getoffset(strp, offsetp)
+const char * strp;
+long * const offsetp;
+{
+ int neg = 0;
+
+ if (*strp == '-') {
+ neg = 1;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*
+** 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".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(strp, rulep)
+const char * strp;
+struct rule * const rulep;
+{
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getsecs(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+** 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.
+*/
+
+static time_t
+transtime(janfirst, year, rulep, offset)
+const time_t janfirst;
+const int year;
+const struct rule * const rulep;
+const long offset;
+{
+ int leapyear;
+ time_t value;
+ int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type) {
+
+ case JULIAN_DAY:
+ /*
+ ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ ** years.
+ ** In non-leap years, or if the day number is 59 or less, just
+ ** add SECSPERDAY times the day number-1 to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+ /*
+ ** n - day of year.
+ ** Just add SECSPERDAY times the day number to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+ /*
+ ** Use Zeller's Congruence to get day-of-week of first day of
+ ** month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ ** "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.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i) {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ ** "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value += d * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "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 UTC.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(name, sp, lastditch)
+const char * name;
+struct state * const sp;
+const int lastditch;
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long stdoffset;
+ long dstoffset;
+ time_t * atp;
+ unsigned char * typep;
+ char * cp;
+ int load_result;
+
+ INITIALIZE(dstname);
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ stdoffset = 0;
+ } else {
+ 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; /* was "stdoffset = 0;" */
+ else {
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ }
+ load_result = tzload(TZDEFRULES, sp, FALSE);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ 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;
+ int year;
+ time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ 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,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ sp->timecnt += 2;
+ newfirst = janfirst;
+ newfirst += year_lengths[isleap(year)] *
+ SECSPERDAY;
+ if (newfirst <= janfirst)
+ break;
+ janfirst = newfirst;
+ }
+ } else {
+ long theirstdoffset;
+ long theirdstoffset;
+ long theiroffset;
+ int isdst;
+ int i;
+ int j;
+
+ if (*name != '\0')
+ return -1;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = FALSE;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ ** ttisstd and ttisgmt need not be handled.
+ */
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = FALSE;
+ sp->ttis[0].tt_abbrind = 0;
+ 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;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ (void) strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static void
+gmtload(sp)
+struct state * const sp;
+{
+ if (tzload(gmt, sp, TRUE) != 0)
+ (void) tzparse(gmt, sp, TRUE);
+}
+
+static void
+tzsetwall_basic(int rdlocked)
+{
+ if (!rdlocked)
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ if (lcl_is_set < 0) {
+ if (!rdlocked)
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ return;
+ }
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+
+ _RWLOCK_WRLOCK(&lcl_rwlock);
+ lcl_is_set = -1;
+
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ if (rdlocked)
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+ if (tzload((char *) NULL, lclptr, TRUE) != 0)
+ gmtload(lclptr);
+ settzname();
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+
+ if (rdlocked)
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+}
+
+void
+tzsetwall(void)
+{
+ tzsetwall_basic(0);
+}
+
+static void
+tzset_basic(int rdlocked)
+{
+ const char * name;
+
+ name = getenv("TZ");
+ if (name == NULL) {
+ tzsetwall_basic(rdlocked);
+ return;
+ }
+
+ if (!rdlocked)
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ if (lcl_is_set > 0 && strcmp(lcl_TZname, name) == 0) {
+ if (!rdlocked)
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ return;
+ }
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+
+ _RWLOCK_WRLOCK(&lcl_rwlock);
+ lcl_is_set = strlen(name) < sizeof lcl_TZname;
+ if (lcl_is_set)
+ (void) strcpy(lcl_TZname, name);
+
+#ifdef ALL_STATE
+ if (lclptr == NULL) {
+ lclptr = (struct state *) malloc(sizeof *lclptr);
+ if (lclptr == NULL) {
+ settzname(); /* all we can do */
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ if (rdlocked)
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ return;
+ }
+ }
+#endif /* defined ALL_STATE */
+ if (*name == '\0') {
+ /*
+ ** User wants it fast rather than right.
+ */
+ 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;
+ (void) strcpy(lclptr->chars, gmt);
+ } else if (tzload(name, lclptr, TRUE) != 0)
+ if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
+ (void) gmtload(lclptr);
+ settzname();
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+
+ if (rdlocked)
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+}
+
+void
+tzset(void)
+{
+ 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--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+/*ARGSUSED*/
+static struct tm *
+localsub(timep, offset, tmp)
+const time_t * const timep;
+const long offset;
+struct tm * const tmp;
+{
+ struct state * sp;
+ const struct ttinfo * ttisp;
+ int i;
+ struct tm * result;
+ const time_t t = *timep;
+
+ sp = lclptr;
+#ifdef ALL_STATE
+ 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;
+ 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)
+ 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) {
+ 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)
+ return NULL;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ } else {
+ 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];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, 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;
+}
+
+static void
+localtime_key_init(void)
+{
+
+ localtime_key_error = _pthread_key_create(&localtime_key, free);
+}
+
+struct tm *
+localtime(timep)
+const time_t * const timep;
+{
+ struct tm *p_tm;
+
+ if (__isthreaded != 0) {
+ _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) {
+ if ((p_tm = (struct tm *)malloc(sizeof(struct tm)))
+ == NULL)
+ return(NULL);
+ _pthread_setspecific(localtime_key, p_tm);
+ }
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ tzset_basic(1);
+ localsub(timep, 0L, p_tm);
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ return(p_tm);
+ } else {
+ tzset_basic(0);
+ localsub(timep, 0L, &tm);
+ return(&tm);
+ }
+}
+
+/*
+** Re-entrant version of localtime.
+*/
+
+struct tm *
+localtime_r(timep, tmp)
+const time_t * const timep;
+struct tm * tmp;
+{
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ tzset_basic(1);
+ localsub(timep, 0L, tmp);
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ return tmp;
+}
+
+static void
+gmt_init(void)
+{
+
+#ifdef ALL_STATE
+ gmtptr = (struct state *) malloc(sizeof *gmtptr);
+ if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+ gmtload(gmtptr);
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct tm *
+gmtsub(timep, offset, tmp)
+const time_t * const timep;
+const long offset;
+struct tm * const tmp;
+{
+ register struct tm * result;
+
+ _once(&gmt_once, gmt_init);
+ result = timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->TM_ZONE = wildabbr;
+ else {
+#ifdef ALL_STATE
+ if (gmtptr == NULL)
+ tmp->TM_ZONE = gmt;
+ else tmp->TM_ZONE = gmtptr->chars;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ tmp->TM_ZONE = gmtptr->chars;
+#endif /* State Farm */
+ }
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+static void
+gmtime_key_init(void)
+{
+
+ gmtime_key_error = _pthread_key_create(&gmtime_key, free);
+}
+
+struct tm *
+gmtime(timep)
+const time_t * const timep;
+{
+ struct tm *p_tm;
+
+ if (__isthreaded != 0) {
+ _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
+ * is what BSD currently has.
+ */
+ if ((p_tm = _pthread_getspecific(gmtime_key)) == NULL) {
+ if ((p_tm = (struct tm *)malloc(sizeof(struct tm)))
+ == NULL) {
+ return(NULL);
+ }
+ _pthread_setspecific(gmtime_key, p_tm);
+ }
+ gmtsub(timep, 0L, p_tm);
+ return(p_tm);
+ }
+ else {
+ gmtsub(timep, 0L, &tm);
+ return(&tm);
+ }
+}
+
+/*
+* Re-entrant version of gmtime.
+*/
+
+struct tm *
+gmtime_r(timep, tmp)
+const time_t * const timep;
+struct tm * tmp;
+{
+ return gmtsub(timep, 0L, tmp);
+}
+
+#ifdef STD_INSPIRED
+
+struct tm *
+offtime(timep, offset)
+const time_t * const timep;
+const long offset;
+{
+ return gmtsub(timep, offset, &tm);
+}
+
+#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.
+*/
+
+static int
+leaps_thru_end_of(y)
+register const int y;
+{
+ return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+ -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+
+static struct tm *
+timesub(timep, offset, sp, tmp)
+const time_t * const timep;
+const long offset;
+const struct state * const sp;
+struct tm * const tmp;
+{
+ const struct lsinfo * lp;
+ time_t tdays;
+ int idays; /* unsigned would be so 2003 */
+ long rem;
+ int y;
+ const int * ip;
+ long corr;
+ int hit;
+ int i;
+
+ corr = 0;
+ hit = 0;
+#ifdef ALL_STATE
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+ i = sp->leapcnt;
+#endif /* State Farm */
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ if (*timep == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = *timep / SECSPERDAY;
+ rem = *timep - tdays * SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+ int newy;
+ register time_t tdelta;
+ register int idelta;
+ register 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;
+ }
+ {
+ register 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;
+ --idays;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++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 %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ 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 *
+ctime(timep)
+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
+** asctime(localtime(timer))
+*/
+ return asctime(localtime(timep));
+}
+
+char *
+ctime_r(timep, buf)
+const time_t * const timep;
+char * 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.
+** 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).
+*/
+
+#ifndef WRONG
+#define WRONG (-1)
+#endif /* !defined WRONG */
+
+/*
+** Simplified normalize logic courtesy Paul Eggert.
+*/
+
+static int
+increment_overflow(number, delta)
+int * number;
+int delta;
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int
+long_increment_overflow(number, delta)
+long * number;
+int delta;
+{
+ long number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int
+normalize_overflow(tensptr, unitsptr, base)
+int * const tensptr;
+int * const unitsptr;
+const int base;
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static int
+long_normalize_overflow(tensptr, unitsptr, base)
+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;
+const struct tm * const btmp;
+{
+ int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (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)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
+}
+
+static time_t
+time2sub(tmp, funcp, offset, okayp, do_norm_secs)
+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 i, j;
+ int saved_seconds;
+ 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;
+ y = yourtm.tm_year;
+ if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn y into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (long_increment_overflow(&y, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (long_increment_overflow(&y, -1))
+ return WRONG;
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ 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(y)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ }
+ 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 (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.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Do a binary search (this works whatever time_t's type is).
+ */
+ 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;
+ 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 (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 (dir > 0)
+ hi = t;
+ else lo = t;
+ continue;
+ }
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ sp = (const struct state *)
+ ((funcp == localsub) ? lclptr : gmtptr);
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt = t + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ if ((*funcp)(&newt, offset, &mytm) == NULL)
+ continue;
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
+label:
+ newt = t + saved_seconds;
+ if ((newt < t) != (saved_seconds < 0))
+ return WRONG;
+ t = newt;
+ if ((*funcp)(&t, offset, tmp))
+ *okayp = TRUE;
+ return t;
+}
+
+static time_t
+time2(tmp, funcp, offset, okayp)
+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(tmp, funcp, offset)
+struct tm * const tmp;
+struct tm * (* const funcp)(const time_t *, long, struct tm *);
+const long offset;
+{
+ time_t t;
+ const struct state * sp;
+ int samei, otheri;
+ 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.
+ */
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+ if (okay || tmp->tm_isdst < 0)
+ return t;
+#endif /* !defined PCTS */
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ sp = (const struct state *) ((funcp == localsub) ? lclptr : gmtptr);
+#ifdef ALL_STATE
+ if (sp == NULL)
+ return WRONG;
+#endif /* defined ALL_STATE */
+ 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 (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 -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, offset, &okay);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+time_t
+mktime(tmp)
+struct tm * const tmp;
+{
+ time_t mktime_return_value;
+ _RWLOCK_RDLOCK(&lcl_rwlock);
+ tzset_basic(1);
+ mktime_return_value = time1(tmp, localsub, 0L);
+ _RWLOCK_UNLOCK(&lcl_rwlock);
+ return(mktime_return_value);
+}
+
+#ifdef STD_INSPIRED
+
+time_t
+timelocal(tmp)
+struct tm * const tmp;
+{
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime(tmp);
+}
+
+time_t
+timegm(tmp)
+struct tm * const tmp;
+{
+ tmp->tm_isdst = 0;
+ return time1(tmp, gmtsub, 0L);
+}
+
+time_t
+timeoff(tmp, offset)
+struct tm * const tmp;
+const long offset;
+{
+ tmp->tm_isdst = 0;
+ return time1(tmp, gmtsub, offset);
+}
+
+#endif /* defined STD_INSPIRED */
+
+#ifdef CMUCS
+
+/*
+** The following is supplied for compatibility with
+** previous versions of the CMUCS runtime library.
+*/
+
+long
+gtime(tmp)
+struct tm * const tmp;
+{
+ const time_t t = mktime(tmp);
+
+ if (t == WRONG)
+ return -1;
+ return t;
+}
+
+#endif /* defined CMUCS */
+
+/*
+** XXX--is the below the right way to conditionalize??
+*/
+
+#ifdef STD_INSPIRED
+
+/*
+** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
+** 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.
+*/
+
+static long
+leapcorr(timep)
+time_t * timep;
+{
+ struct state * sp;
+ struct lsinfo * lp;
+ int i;
+
+ sp = lclptr;
+ i = sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+time_t
+time2posix(t)
+time_t t;
+{
+ tzset();
+ return t - leapcorr(&t);
+}
+
+time_t
+posix2time(t)
+time_t t;
+{
+ time_t x;
+ time_t y;
+
+ tzset();
+ /*
+ ** For a positive leap second hit, the result
+ ** is not unique. For a negative leap second
+ ** hit, the corresponding time doesn't exist,
+ ** so we return an adjacent second.
+ */
+ x = t + leapcorr(&t);
+ y = x - leapcorr(&x);
+ if (y < t) {
+ do {
+ x++;
+ y = x - leapcorr(&x);
+ } while (y < t);
+ if (t != y)
+ return x - 1;
+ } else if (y > t) {
+ do {
+ --x;
+ y = x - leapcorr(&x);
+ } while (y > t);
+ if (t != y)
+ return x + 1;
+ }
+ return x;
+}
+
+#endif /* defined STD_INSPIRED */
diff --git a/contrib/tzcode/stdtime/private.h b/contrib/tzcode/stdtime/private.h
new file mode 100644
index 000000000000..dda5befd737e
--- /dev/null
+++ b/contrib/tzcode/stdtime/private.h
@@ -0,0 +1,326 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+**
+** $FreeBSD$
+*/
+
+/* 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.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+/*
+static char privatehid[] = "@(#)private.h 8.6";
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#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_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 "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h" /* for CHAR_BIT et al. */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT
+#include "libintl.h"
+#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
+#include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */
+#endif /* HAVE_UNISTD_H */
+
+#if !(HAVE_UNISTD_H)
+#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) */
+
+/* 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);
+const char * scheck(const char * string, const char * format);
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#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 */
+
+/*
+** 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.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** 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))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#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 The Open Group in 2003.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/contrib/tzcode/stdtime/strftime.3 b/contrib/tzcode/stdtime/strftime.3
new file mode 100644
index 000000000000..d5cdd7dc5638
--- /dev/null
+++ b/contrib/tzcode/stdtime/strftime.3
@@ -0,0 +1,278 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the American National Standards Committee X3, on Information
+.\" Processing Systems.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)strftime.3 8.1 (Berkeley) 6/4/93
+.\" $FreeBSD$
+.\"
+.Dd November 4, 2004
+.Dt STRFTIME 3
+.Os
+.Sh NAME
+.Nm strftime
+.Nd format date and time
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In time.h
+.Ft size_t
+.Fo strftime
+.Fa "char * restrict buf"
+.Fa "size_t maxsize"
+.Fa "const char * restrict format"
+.Fa "const struct tm * restrict timeptr"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn strftime
+function formats the information from
+.Fa timeptr
+into the buffer
+.Fa buf
+according to the string pointed to by
+.Fa format .
+.Pp
+The
+.Fa format
+string consists of zero or more conversion specifications and
+ordinary characters.
+All ordinary characters are copied directly into the buffer.
+A conversion specification consists of a percent sign
+.Dq Ql %
+and one other character.
+.Pp
+No more than
+.Fa maxsize
+characters will be placed into the array.
+If the total number of resulting characters, including the terminating
+NUL character, is not more than
+.Fa maxsize ,
+.Fn strftime
+returns the number of characters in the array, not counting the
+terminating NUL.
+Otherwise, zero is returned and the buffer contents are indeterminate.
+.Pp
+The conversion specifications are copied to the buffer after expansion
+as follows:-
+.Bl -tag -width "xxxx"
+.It Cm \&%A
+is replaced by national representation of the full weekday name.
+.It Cm %a
+is replaced by national representation of
+the abbreviated weekday name.
+.It Cm \&%B
+is replaced by national representation of the full month name.
+.It Cm %b
+is replaced by national representation of
+the abbreviated month name.
+.It Cm \&%C
+is replaced by (year / 100) as decimal number; single
+digits are preceded by a zero.
+.It Cm %c
+is replaced by national representation of time and date.
+.It Cm \&%D
+is equivalent to
+.Dq Li %m/%d/%y .
+.It Cm %d
+is replaced by the day of the month as a decimal number (01-31).
+.It Cm %E* %O*
+POSIX locale extensions.
+The sequences
+%Ec %EC %Ex %EX %Ey %EY
+%Od %Oe %OH %OI %Om %OM
+%OS %Ou %OU %OV %Ow %OW %Oy
+are supposed to provide alternate
+representations.
+.Pp
+Additionally %OB implemented
+to represent alternative months names
+(used standalone, without day mentioned).
+.It Cm %e
+is replaced by the day of the month as a decimal number (1-31); single
+digits are preceded by a blank.
+.It Cm \&%F
+is equivalent to
+.Dq Li %Y-%m-%d .
+.It Cm \&%G
+is replaced by a year as a decimal number with century.
+This year is the one that contains the greater part of
+the week (Monday as the first day of the week).
+.It Cm %g
+is replaced by the same year as in
+.Dq Li %G ,
+but as a decimal number without century (00-99).
+.It Cm \&%H
+is replaced by the hour (24-hour clock) as a decimal number (00-23).
+.It Cm %h
+the same as
+.Cm %b .
+.It Cm \&%I
+is replaced by the hour (12-hour clock) as a decimal number (01-12).
+.It Cm %j
+is replaced by the day of the year as a decimal number (001-366).
+.It Cm %k
+is replaced by the hour (24-hour clock) as a decimal number (0-23);
+single digits are preceded by a blank.
+.It Cm %l
+is replaced by the hour (12-hour clock) as a decimal number (1-12);
+single digits are preceded by a blank.
+.It Cm \&%M
+is replaced by the minute as a decimal number (00-59).
+.It Cm %m
+is replaced by the month as a decimal number (01-12).
+.It Cm %n
+is replaced by a newline.
+.It Cm %O*
+the same as
+.Cm %E* .
+.It Cm %p
+is replaced by national representation of either
+"ante meridiem" (a.m.)
+or
+"post meridiem" (p.m.)
+as appropriate.
+.It Cm \&%R
+is equivalent to
+.Dq Li %H:%M .
+.It Cm %r
+is equivalent to
+.Dq Li %I:%M:%S %p .
+.It Cm \&%S
+is replaced by the second as a decimal number (00-60).
+.It Cm %s
+is replaced by the number of seconds since the Epoch, UTC (see
+.Xr mktime 3 ) .
+.It Cm \&%T
+is equivalent to
+.Dq Li %H:%M:%S .
+.It Cm %t
+is replaced by a tab.
+.It Cm \&%U
+is replaced by the week number of the year (Sunday as the first day of
+the week) as a decimal number (00-53).
+.It Cm %u
+is replaced by the weekday (Monday as the first day of the week)
+as a decimal number (1-7).
+.It Cm \&%V
+is replaced by the week number of the year (Monday as the first day of
+the week) as a decimal number (01-53).
+If the week containing January
+1 has four or more days in the new year, then it is week 1; otherwise
+it is the last week of the previous year, and the next week is week 1.
+.It Cm %v
+is equivalent to
+.Dq Li %e-%b-%Y .
+.It Cm \&%W
+is replaced by the week number of the year (Monday as the first day of
+the week) as a decimal number (00-53).
+.It Cm %w
+is replaced by the weekday (Sunday as the first day of the week)
+as a decimal number (0-6).
+.It Cm \&%X
+is replaced by national representation of the time.
+.It Cm %x
+is replaced by national representation of the date.
+.It Cm \&%Y
+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.
+.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).
+.It Cm %+
+is replaced by national representation of the date and time
+(the format is similar to that produced by
+.Xr date 1 ) .
+.It Cm %-*
+GNU libc extension.
+Do not do any padding when performing numerical outputs.
+.It Cm %_*
+GNU libc extension.
+Explicitly specify space for padding.
+.It Cm %0*
+GNU libc extension.
+Explicitly specify zero for padding.
+.It Cm %%
+is replaced by
+.Ql % .
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr printf 1 ,
+.Xr ctime 3 ,
+.Xr printf 3 ,
+.Xr strptime 3 ,
+.Xr wcsftime 3
+.Sh STANDARDS
+The
+.Fn strftime
+function
+conforms to
+.St -isoC
+with a lot of extensions including
+.Ql %C ,
+.Ql \&%D ,
+.Ql %E* ,
+.Ql %e ,
+.Ql %G ,
+.Ql %g ,
+.Ql %h ,
+.Ql %k ,
+.Ql %l ,
+.Ql %n ,
+.Ql %O* ,
+.Ql \&%R ,
+.Ql %r ,
+.Ql %s ,
+.Ql \&%T ,
+.Ql %t ,
+.Ql %u ,
+.Ql \&%V ,
+.Ql %z ,
+.Ql %+ .
+.Pp
+The peculiar week number and year in the replacements of
+.Ql %G ,
+.Ql %g
+and
+.Ql \&%V
+are defined in ISO 8601: 1988.
+.Sh BUGS
+There is no conversion specification for the phase of the moon.
+.Pp
+The
+.Fn strftime
+function does not correctly handle multibyte characters in the
+.Fa format
+argument.
diff --git a/contrib/tzcode/stdtime/strftime.c b/contrib/tzcode/stdtime/strftime.c
new file mode 100644
index 000000000000..dac910bb724f
--- /dev/null
+++ b/contrib/tzcode/stdtime/strftime.c
@@ -0,0 +1,634 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)strftime.3 8.3";
+/*
+** Based on the UCB version with the ID appearing below.
+** This is ANSIish only when "multibyte character == plain character".
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#include "namespace.h"
+#include "private.h"
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "tzfile.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include "un-namespace.h"
+#include "timelocal.h"
+
+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 *);
+
+extern char * tzname[];
+
+#ifndef YEAR_2000_NAME
+#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
+#define IN_ALL 3
+
+#define PAD_DEFAULT 0
+#define PAD_LESS 1
+#define PAD_SPACE 2
+#define PAD_ZERO 3
+
+static const char* fmt_padding[][4] = {
+ /* DEFAULT, LESS, SPACE, ZERO */
+#define PAD_FMT_MONTHDAY 0
+#define PAD_FMT_HMS 0
+#define PAD_FMT_CENTURY 0
+#define PAD_FMT_SHORTYEAR 0
+#define PAD_FMT_MONTH 0
+#define PAD_FMT_WEEKOFYEAR 0
+#define PAD_FMT_DAYOFMONTH 0
+ { "%02d", "%d", "%2d", "%02d" },
+#define PAD_FMT_SDAYOFMONTH 1
+#define PAD_FMT_SHMS 1
+ { "%2d", "%d", "%2d", "%02d" },
+#define PAD_FMT_DAYOFYEAR 2
+ { "%03d", "%d", "%3d", "%03d" },
+#define PAD_FMT_YEAR 3
+ { "%04d", "%d", "%4d", "%04d" }
+};
+
+size_t
+strftime(char * __restrict s, size_t maxsize, const char * __restrict format,
+ const struct tm * __restrict t)
+{
+ char * p;
+ int warn;
+
+ tzset();
+ warn = IN_NONE;
+ p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
+#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
+ if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
+ (void) fprintf(stderr, "\n");
+ if (format == NULL)
+ (void) fprintf(stderr, "NULL strftime format ");
+ else (void) fprintf(stderr, "strftime format \"%s\" ",
+ format);
+ (void) fprintf(stderr, "yields only two digits of years in ");
+ if (warn == IN_SOME)
+ (void) fprintf(stderr, "some locales");
+ else if (warn == IN_THIS)
+ (void) fprintf(stderr, "the current locale");
+ else (void) fprintf(stderr, "all locales");
+ (void) fprintf(stderr, "\n");
+ }
+#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
+ if (p == s + maxsize)
+ return 0;
+ *p = '\0';
+ return p - s;
+}
+
+static char *
+_fmt(format, t, pt, ptlim, warnp)
+const char * format;
+const struct tm * const t;
+char * pt;
+const char * const ptlim;
+int * warnp;
+{
+ int Ealternative, Oalternative, PadIndex;
+ struct lc_time_T *tptr = __get_current_time_locale();
+
+ for ( ; *format; ++format) {
+ if (*format == '%') {
+ Ealternative = 0;
+ Oalternative = 0;
+ PadIndex = PAD_DEFAULT;
+label:
+ switch (*++format) {
+ case '\0':
+ --format;
+ break;
+ case 'A':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : tptr->weekday[t->tm_wday],
+ pt, ptlim);
+ continue;
+ case 'a':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : tptr->wday[t->tm_wday],
+ pt, ptlim);
+ continue;
+ case 'B':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : (Oalternative ? tptr->alt_month :
+ tptr->month)[t->tm_mon],
+ pt, ptlim);
+ continue;
+ case 'b':
+ case 'h':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : tptr->mon[t->tm_mon],
+ pt, ptlim);
+ continue;
+ case 'C':
+ /*
+ ** %C used to do a...
+ ** _fmt("%a %b %e %X %Y", t);
+ ** ...whereas now POSIX 1003.2 calls for
+ ** something completely different.
+ ** (ado, 1993-05-24)
+ */
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
+ pt, ptlim);
+ continue;
+ case 'c':
+ {
+ int warn2 = IN_SOME;
+
+ pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2);
+ if (warn2 == IN_ALL)
+ warn2 = IN_THIS;
+ if (warn2 > *warnp)
+ *warnp = warn2;
+ }
+ continue;
+ case 'D':
+ pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
+ continue;
+ case 'd':
+ pt = _conv(t->tm_mday, fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex],
+ pt, ptlim);
+ continue;
+ case 'E':
+ if (Ealternative || Oalternative)
+ break;
+ Ealternative++;
+ goto label;
+ case 'O':
+ /*
+ ** C99 locale modifiers.
+ ** The sequences
+ ** %Ec %EC %Ex %EX %Ey %EY
+ ** %Od %oe %OH %OI %Om %OM
+ ** %OS %Ou %OU %OV %Ow %OW %Oy
+ ** are supposed to provide alternate
+ ** representations.
+ **
+ ** FreeBSD extension
+ ** %OB
+ */
+ if (Ealternative || Oalternative)
+ break;
+ Oalternative++;
+ goto label;
+ case 'e':
+ pt = _conv(t->tm_mday,
+ fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], pt, ptlim);
+ continue;
+ case 'F':
+ pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
+ continue;
+ case 'H':
+ pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex],
+ pt, ptlim);
+ continue;
+ case 'I':
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim);
+ continue;
+ case 'j':
+ pt = _conv(t->tm_yday + 1,
+ fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], pt, ptlim);
+ continue;
+ case 'k':
+ /*
+ ** This used to be...
+ ** _conv(t->tm_hour % 12 ?
+ ** 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
+ ** "%l" have been swapped.
+ ** (ado, 1993-05-24)
+ */
+ pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex],
+ pt, ptlim);
+ continue;
+#ifdef KITCHEN_SINK
+ case 'K':
+ /*
+ ** After all this time, still unclaimed!
+ */
+ pt = _add("kitchen sink", pt, ptlim);
+ continue;
+#endif /* defined KITCHEN_SINK */
+ case 'l':
+ /*
+ ** This used to be...
+ ** _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
+ ** "%l" have been swapped.
+ ** (ado, 1993-05-24)
+ */
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ fmt_padding[PAD_FMT_SHMS][PadIndex], pt, ptlim);
+ continue;
+ case 'M':
+ pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex],
+ pt, ptlim);
+ continue;
+ case 'm':
+ pt = _conv(t->tm_mon + 1,
+ fmt_padding[PAD_FMT_MONTH][PadIndex], pt, ptlim);
+ continue;
+ case 'n':
+ pt = _add("\n", pt, ptlim);
+ continue;
+ case 'p':
+ pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+ tptr->pm :
+ tptr->am,
+ pt, ptlim);
+ continue;
+ case 'R':
+ pt = _fmt("%H:%M", t, pt, ptlim, warnp);
+ continue;
+ case 'r':
+ pt = _fmt(tptr->ampm_fmt, t, pt, ptlim,
+ warnp);
+ continue;
+ case 'S':
+ pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex],
+ pt, ptlim);
+ continue;
+ case 's':
+ {
+ struct tm tm;
+ char buf[INT_STRLEN_MAXIMUM(
+ time_t) + 1];
+ time_t mkt;
+
+ tm = *t;
+ mkt = mktime(&tm);
+ if (TYPE_SIGNED(time_t))
+ (void) sprintf(buf, "%ld",
+ (long) mkt);
+ else (void) sprintf(buf, "%lu",
+ (unsigned long) mkt);
+ pt = _add(buf, pt, ptlim);
+ }
+ continue;
+ case 'T':
+ pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
+ continue;
+ case 't':
+ pt = _add("\t", pt, ptlim);
+ continue;
+ case 'U':
+ pt = _conv((t->tm_yday + DAYSPERWEEK -
+ t->tm_wday) / DAYSPERWEEK,
+ fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim);
+ continue;
+ case 'u':
+ /*
+ ** From Arnold Robbins' strftime version 3.0:
+ ** "ISO 8601: Weekday as a decimal number
+ ** [1 (Monday) - 7]"
+ ** (ado, 1993-05-24)
+ */
+ pt = _conv((t->tm_wday == 0) ?
+ DAYSPERWEEK : t->tm_wday,
+ "%d", pt, ptlim);
+ continue;
+ case 'V': /* ISO 8601 week number */
+ 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
+** year (the first Monday as the first day of week 1) as a decimal number
+** (01-53)."
+** (ado, 1993-05-24)
+**
+** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
+** "Week 01 of a year is per definition the first week which has the
+** Thursday in this year, which is equivalent to the week which contains
+** the fourth day of January. In other words, the first week of a new year
+** is the week which has the majority of its days in the new year. Week 01
+** 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
+** 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;
+ base = TM_YEAR_BASE;
+ yday = t->tm_yday;
+ wday = t->tm_wday;
+ for ( ; ; ) {
+ int len;
+ int bot;
+ int top;
+
+ len = isleap_sum(year, base) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+ /*
+ ** What yday (-3 ... 3) does
+ ** the ISO year begin on?
+ */
+ bot = ((yday + 11 - wday) %
+ DAYSPERWEEK) - 3;
+ /*
+ ** What yday does the NEXT
+ ** ISO year begin on?
+ */
+ top = bot -
+ (len % DAYSPERWEEK);
+ if (top < -3)
+ top += DAYSPERWEEK;
+ top += len;
+ if (yday >= top) {
+ ++base;
+ w = 1;
+ break;
+ }
+ if (yday >= bot) {
+ w = 1 + ((yday - bot) /
+ DAYSPERWEEK);
+ break;
+ }
+ --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;
+#endif /* defined XPG4_1994_04_09 */
+ if (*format == 'V')
+ pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],
+ pt, ptlim);
+ else if (*format == 'g') {
+ *warnp = IN_ALL;
+ pt = _yconv(year, base, 0, 1,
+ pt, ptlim);
+ } else pt = _yconv(year, base, 1, 1,
+ pt, ptlim);
+ }
+ continue;
+ case 'v':
+ /*
+ ** From Arnold Robbins' strftime version 3.0:
+ ** "date as dd-bbb-YYYY"
+ ** (ado, 1993-05-24)
+ */
+ pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
+ continue;
+ case 'W':
+ pt = _conv((t->tm_yday + DAYSPERWEEK -
+ (t->tm_wday ?
+ (t->tm_wday - 1) :
+ (DAYSPERWEEK - 1))) / DAYSPERWEEK,
+ fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim);
+ continue;
+ case 'w':
+ pt = _conv(t->tm_wday, "%d", pt, ptlim);
+ continue;
+ case 'X':
+ pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp);
+ continue;
+ case 'x':
+ {
+ int warn2 = IN_SOME;
+
+ pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2);
+ if (warn2 == IN_ALL)
+ warn2 = IN_THIS;
+ if (warn2 > *warnp)
+ *warnp = warn2;
+ }
+ continue;
+ case 'y':
+ *warnp = IN_ALL;
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
+ pt, ptlim);
+ continue;
+ case 'Y':
+ pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
+ pt, ptlim);
+ continue;
+ case 'Z':
+#ifdef TM_ZONE
+ if (t->TM_ZONE != NULL)
+ pt = _add(t->TM_ZONE, pt, ptlim);
+ else
+#endif /* defined TM_ZONE */
+ if (t->tm_isdst >= 0)
+ pt = _add(tzname[t->tm_isdst != 0],
+ pt, ptlim);
+ /*
+ ** C99 says that %Z must be replaced by the
+ ** empty string if the time zone is not
+ ** determinable.
+ */
+ continue;
+ case 'z':
+ {
+ int diff;
+ char const * sign;
+
+ if (t->tm_isdst < 0)
+ continue;
+#ifdef TM_GMTOFF
+ diff = t->TM_GMTOFF;
+#else /* !defined TM_GMTOFF */
+ /*
+ ** C99 says that the UTC offset must
+ ** be computed by looking only at
+ ** 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
+ ** tricky and requires disobeying C99;
+ ** see GNU C strftime for details.
+ ** For now, punt and conform to the
+ ** standard, even though it's incorrect.
+ **
+ ** C99 says that %z must be replaced by the
+ ** empty string if the time zone is not
+ ** determinable, so output nothing if the
+ ** appropriate variables are not available.
+ */
+ if (t->tm_isdst == 0)
+#ifdef USG_COMPAT
+ diff = -timezone;
+#else /* !defined USG_COMPAT */
+ continue;
+#endif /* !defined USG_COMPAT */
+ else
+#ifdef ALTZONE
+ diff = -altzone;
+#else /* !defined ALTZONE */
+ continue;
+#endif /* !defined ALTZONE */
+#endif /* !defined TM_GMTOFF */
+ if (diff < 0) {
+ sign = "-";
+ diff = -diff;
+ } else sign = "+";
+ pt = _add(sign, pt, ptlim);
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 +
+ (diff % MINSPERHOUR);
+ pt = _conv(diff,
+ fmt_padding[PAD_FMT_YEAR][PadIndex], pt, ptlim);
+ }
+ continue;
+ case '+':
+ pt = _fmt(tptr->date_fmt, t, pt, ptlim,
+ warnp);
+ continue;
+ case '-':
+ if (PadIndex != PAD_DEFAULT)
+ break;
+ PadIndex = PAD_LESS;
+ goto label;
+ case '_':
+ if (PadIndex != PAD_DEFAULT)
+ break;
+ PadIndex = PAD_SPACE;
+ goto label;
+ case '0':
+ if (PadIndex != PAD_DEFAULT)
+ break;
+ PadIndex = PAD_ZERO;
+ goto label;
+ case '%':
+ /*
+ ** X311J/88-090 (4.12.3.5): if conversion char is
+ ** undefined, behavior is undefined. Print out the
+ ** character itself as printf(3) also does.
+ */
+ default:
+ break;
+ }
+ }
+ if (pt == ptlim)
+ break;
+ *pt++ = *format;
+ }
+ return pt;
+}
+
+static char *
+_conv(n, format, pt, ptlim)
+const int n;
+const char * const format;
+char * const pt;
+const char * const ptlim;
+{
+ char buf[INT_STRLEN_MAXIMUM(int) + 1];
+
+ (void) sprintf(buf, format, n);
+ return _add(buf, pt, ptlim);
+}
+
+static char *
+_add(str, pt, ptlim)
+const char * str;
+char * pt;
+const char * const ptlim;
+{
+ while (pt < ptlim && (*pt = *str++) != '\0')
+ ++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(a, b, convert_top, convert_yy, pt, ptlim)
+const int a;
+const int b;
+const int convert_top;
+const int convert_yy;
+char * pt;
+const char * const ptlim;
+{
+ register int lead;
+ register 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;
+}
diff --git a/contrib/tzcode/stdtime/strptime.3 b/contrib/tzcode/stdtime/strptime.3
new file mode 100644
index 000000000000..763696b0b4ca
--- /dev/null
+++ b/contrib/tzcode/stdtime/strptime.3
@@ -0,0 +1,173 @@
+.\"
+.\" Copyright (c) 1997 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" "
+.Dd January 4, 2003
+.Dt STRPTIME 3
+.Os
+.Sh NAME
+.Nm strptime
+.Nd parse date and time string
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In time.h
+.Ft char *
+.Fo strptime
+.Fa "const char * restrict buf"
+.Fa "const char * restrict format"
+.Fa "struct tm * restrict timeptr"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn strptime
+function parses the string in the buffer
+.Fa buf
+according to the string pointed to by
+.Fa format ,
+and fills in the elements of the structure pointed to by
+.Fa timeptr .
+The resulting values will be relative to the local time zone.
+Thus, it can be considered the reverse operation of
+.Xr strftime 3 .
+.Pp
+The
+.Fa format
+string consists of zero or more conversion specifications and
+ordinary characters.
+All ordinary characters are matched exactly with the buffer, where
+white space in the format string will match any amount of white space
+in the buffer.
+All conversion specifications are identical to those described in
+.Xr strftime 3 .
+.Pp
+Two-digit year values, including formats
+.Fa %y
+and
+.Fa \&%D ,
+are now interpreted as beginning at 1969 per POSIX requirements.
+Years 69-00 are interpreted in the 20th century (1969-2000), years
+01-68 in the 21st century (2001-2068).
+.Pp
+If the
+.Fa format
+string does not contain enough conversion specifications to completely
+specify the resulting
+.Vt struct tm ,
+the unspecified members of
+.Va timeptr
+are left untouched.
+For example, if
+.Fa format
+is
+.Dq Li "%H:%M:%S" ,
+only
+.Va tm_hour ,
+.Va tm_sec
+and
+.Va tm_min
+will be modified.
+If time relative to today is desired, initialize the
+.Fa timeptr
+structure with today's date before passing it to
+.Fn strptime .
+.Sh RETURN VALUES
+Upon successful completion,
+.Fn strptime
+returns the pointer to the first character in
+.Fa buf
+that has not been required to satisfy the specified conversions in
+.Fa format .
+It returns
+.Dv NULL
+if one of the conversions failed.
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr scanf 3 ,
+.Xr strftime 3
+.Sh HISTORY
+The
+.Fn strptime
+function appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Fn strptime
+function has been contributed by Powerdog Industries.
+.Pp
+This man page was written by
+.An J\(:org Wunsch .
+.Sh BUGS
+Both the
+.Fa %e
+and
+.Fa %l
+format specifiers may incorrectly scan one too many digits
+if the intended values comprise only a single digit
+and that digit is followed immediately by another digit.
+Both specifiers accept zero-padded values,
+even though they are both defined as taking unpadded values.
+.Pp
+The
+.Fa %p
+format specifier has no effect unless it is parsed
+.Em after
+hour-related specifiers.
+Specifying
+.Fa %l
+without
+.Fa %p
+will produce undefined results.
+Note that 12AM
+(ante meridiem)
+is taken as midnight
+and 12PM
+(post meridiem)
+is taken as noon.
+.Pp
+The
+.Fa \&%U
+and
+.Fa %W
+format specifiers accept any value within the range 00 to 53
+without validating against other values supplied (like month
+or day of the year, for example).
+.Pp
+The
+.Fa %Z
+format specifier only accepts time zone abbreviations of the local time zone,
+or the value "GMT".
+This limitation is because of ambiguity due to of the over loading of time
+zone abbreviations.
+One such example is
+.Fa EST
+which is both Eastern Standard Time and Eastern Australia Summer Time.
+.Pp
+The
+.Fn strptime
+function does not correctly handle multibyte characters in the
+.Fa format
+argument.
diff --git a/contrib/tzcode/stdtime/strptime.c b/contrib/tzcode/stdtime/strptime.c
new file mode 100644
index 000000000000..51d41ed6b9b1
--- /dev/null
+++ b/contrib/tzcode/stdtime/strptime.c
@@ -0,0 +1,566 @@
+/*
+ * Powerdog Industries kindly requests feedback from anyone modifying
+ * this function:
+ *
+ * Date: Thu, 05 Jun 1997 23:17:17 -0400
+ * From: Kevin Ruddy <kevin.ruddy@powerdog.com>
+ * To: James FitzGibbon <james@nexis.net>
+ * Subject: Re: Use of your strptime(3) code (fwd)
+ *
+ * The reason for the "no mod" clause was so that modifications would
+ * come back and we could integrate them and reissue so that a wider
+ * audience could use it (thereby spreading the wealth). This has
+ * made it possible to get strptime to work on many operating systems.
+ * I'm not sure why that's "plain unacceptable" to the FreeBSD team.
+ *
+ * Anyway, you can change it to "with or without modification" as
+ * you see fit. Enjoy.
+ *
+ * Kevin Ruddy
+ * Powerdog Industries, Inc.
+ */
+/*
+ * Copyright (c) 1994 Powerdog Industries. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgement:
+ * This product includes software developed by Powerdog Industries.
+ * 4. The name of Powerdog Industries may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+#ifndef NOID
+static char copyright[] __unused =
+"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
+static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
+#endif /* !defined NOID */
+#endif /* not lint */
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include "un-namespace.h"
+#include "libc_private.h"
+#include "timelocal.h"
+
+static char * _strptime(const char *, const char *, struct tm *, int *);
+
+#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
+
+static char *
+_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp)
+{
+ char c;
+ const char *ptr;
+ int i,
+ len;
+ int Ealternative, Oalternative;
+ struct lc_time_T *tptr = __get_current_time_locale();
+
+ ptr = fmt;
+ while (*ptr != 0) {
+ if (*buf == 0)
+ break;
+
+ c = *ptr++;
+
+ if (c != '%') {
+ if (isspace((unsigned char)c))
+ while (*buf != 0 && isspace((unsigned char)*buf))
+ buf++;
+ else if (c != *buf++)
+ return 0;
+ continue;
+ }
+
+ Ealternative = 0;
+ Oalternative = 0;
+label:
+ c = *ptr++;
+ switch (c) {
+ case 0:
+ case '%':
+ if (*buf++ != '%')
+ return 0;
+ break;
+
+ case '+':
+ buf = _strptime(buf, tptr->date_fmt, tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'C':
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ /* XXX This will break for 3-digit centuries. */
+ len = 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (i < 19)
+ return 0;
+
+ tm->tm_year = i * 100 - 1900;
+ break;
+
+ case 'c':
+ buf = _strptime(buf, tptr->c_fmt, tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'D':
+ buf = _strptime(buf, "%m/%d/%y", tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'E':
+ if (Ealternative || Oalternative)
+ break;
+ Ealternative++;
+ goto label;
+
+ case 'O':
+ if (Ealternative || Oalternative)
+ break;
+ Oalternative++;
+ goto label;
+
+ case 'F':
+ buf = _strptime(buf, "%Y-%m-%d", tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'R':
+ buf = _strptime(buf, "%H:%M", tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'r':
+ buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'T':
+ buf = _strptime(buf, "%H:%M:%S", tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'X':
+ buf = _strptime(buf, tptr->X_fmt, tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'x':
+ buf = _strptime(buf, tptr->x_fmt, tm, GMTp);
+ if (buf == 0)
+ return 0;
+ break;
+
+ case 'j':
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = 3;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (i < 1 || i > 366)
+ return 0;
+
+ tm->tm_yday = i - 1;
+ break;
+
+ case 'M':
+ case 'S':
+ if (*buf == 0 || isspace((unsigned char)*buf))
+ break;
+
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+
+ if (c == 'M') {
+ if (i > 59)
+ return 0;
+ tm->tm_min = i;
+ } else {
+ if (i > 60)
+ return 0;
+ tm->tm_sec = i;
+ }
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 'H':
+ case 'I':
+ case 'k':
+ case 'l':
+ /*
+ * Of these, %l is the only specifier explicitly
+ * documented as not being zero-padded. However,
+ * there is no harm in allowing zero-padding.
+ *
+ * XXX The %l specifier may gobble one too many
+ * digits if used incorrectly.
+ */
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (c == 'H' || c == 'k') {
+ if (i > 23)
+ return 0;
+ } else if (i > 12)
+ return 0;
+
+ tm->tm_hour = i;
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 'p':
+ /*
+ * XXX This is bogus if parsed before hour-related
+ * specifiers.
+ */
+ len = strlen(tptr->am);
+ if (strncasecmp(buf, tptr->am, len) == 0) {
+ if (tm->tm_hour > 12)
+ return 0;
+ if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ buf += len;
+ break;
+ }
+
+ len = strlen(tptr->pm);
+ if (strncasecmp(buf, tptr->pm, len) == 0) {
+ if (tm->tm_hour > 12)
+ return 0;
+ if (tm->tm_hour != 12)
+ tm->tm_hour += 12;
+ buf += len;
+ break;
+ }
+
+ return 0;
+
+ case 'A':
+ case 'a':
+ for (i = 0; i < asizeof(tptr->weekday); i++) {
+ len = strlen(tptr->weekday[i]);
+ if (strncasecmp(buf, tptr->weekday[i],
+ len) == 0)
+ break;
+ len = strlen(tptr->wday[i]);
+ if (strncasecmp(buf, tptr->wday[i],
+ len) == 0)
+ break;
+ }
+ if (i == asizeof(tptr->weekday))
+ return 0;
+
+ tm->tm_wday = i;
+ buf += len;
+ break;
+
+ case 'U':
+ case 'W':
+ /*
+ * XXX This is bogus, as we can not assume any valid
+ * information present in the tm structure at this
+ * point to calculate a real value, so just check the
+ * range for now.
+ */
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (i > 53)
+ return 0;
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 'w':
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ i = *buf - '0';
+ if (i > 6)
+ return 0;
+
+ tm->tm_wday = i;
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 'd':
+ case 'e':
+ /*
+ * The %e specifier is explicitly documented as not
+ * being zero-padded but there is no harm in allowing
+ * such padding.
+ *
+ * XXX The %e specifier may gobble one too many
+ * digits if used incorrectly.
+ */
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (i > 31)
+ return 0;
+
+ tm->tm_mday = i;
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 'B':
+ case 'b':
+ case 'h':
+ for (i = 0; i < asizeof(tptr->month); i++) {
+ if (Oalternative) {
+ if (c == 'B') {
+ len = strlen(tptr->alt_month[i]);
+ if (strncasecmp(buf,
+ tptr->alt_month[i],
+ len) == 0)
+ break;
+ }
+ } else {
+ len = strlen(tptr->month[i]);
+ if (strncasecmp(buf, tptr->month[i],
+ len) == 0)
+ break;
+ len = strlen(tptr->mon[i]);
+ if (strncasecmp(buf, tptr->mon[i],
+ len) == 0)
+ break;
+ }
+ }
+ if (i == asizeof(tptr->month))
+ return 0;
+
+ tm->tm_mon = i;
+ buf += len;
+ break;
+
+ case 'm':
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (i < 1 || i > 12)
+ return 0;
+
+ tm->tm_mon = i - 1;
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 's':
+ {
+ char *cp;
+ int sverrno;
+ long n;
+ time_t t;
+
+ sverrno = errno;
+ errno = 0;
+ n = strtol(buf, &cp, 10);
+ if (errno == ERANGE || (long)(t = n) != n) {
+ errno = sverrno;
+ return 0;
+ }
+ errno = sverrno;
+ buf = cp;
+ gmtime_r(&t, tm);
+ *GMTp = 1;
+ }
+ break;
+
+ case 'Y':
+ case 'y':
+ if (*buf == 0 || isspace((unsigned char)*buf))
+ break;
+
+ if (!isdigit((unsigned char)*buf))
+ return 0;
+
+ len = (c == 'Y') ? 4 : 2;
+ for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
+ i *= 10;
+ i += *buf - '0';
+ len--;
+ }
+ if (c == 'Y')
+ i -= 1900;
+ if (c == 'y' && i < 69)
+ i += 100;
+ if (i < 0)
+ return 0;
+
+ tm->tm_year = i;
+
+ if (*buf != 0 && isspace((unsigned char)*buf))
+ while (*ptr != 0 && !isspace((unsigned char)*ptr))
+ ptr++;
+ break;
+
+ case 'Z':
+ {
+ const char *cp;
+ char *zonestr;
+
+ for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
+ if (cp - buf) {
+ zonestr = alloca(cp - buf + 1);
+ strncpy(zonestr, buf, cp - buf);
+ zonestr[cp - buf] = '\0';
+ tzset();
+ if (0 == strcmp(zonestr, "GMT")) {
+ *GMTp = 1;
+ } else if (0 == strcmp(zonestr, tzname[0])) {
+ tm->tm_isdst = 0;
+ } else if (0 == strcmp(zonestr, tzname[1])) {
+ tm->tm_isdst = 1;
+ } else {
+ return 0;
+ }
+ buf += cp - buf;
+ }
+ }
+ break;
+
+ case 'z':
+ {
+ int sign = 1;
+
+ if (*buf != '+') {
+ if (*buf == '-')
+ sign = -1;
+ else
+ return 0;
+ }
+
+ buf++;
+ i = 0;
+ for (len = 4; len > 0; len--) {
+ if (isdigit((unsigned char)*buf)) {
+ i *= 10;
+ i += *buf - '0';
+ buf++;
+ } else
+ return 0;
+ }
+
+ tm->tm_hour -= sign * (i / 100);
+ tm->tm_min -= sign * (i % 100);
+ *GMTp = 1;
+ }
+ break;
+ }
+ }
+ return (char *)buf;
+}
+
+
+char *
+strptime(const char * __restrict buf, const char * __restrict fmt,
+ struct tm * __restrict tm)
+{
+ char *ret;
+ int gmt;
+
+ gmt = 0;
+ ret = _strptime(buf, fmt, tm, &gmt);
+ if (ret && gmt) {
+ time_t t = timegm(tm);
+ localtime_r(&t, tm);
+ }
+
+ return (ret);
+}
diff --git a/contrib/tzcode/stdtime/time2posix.3 b/contrib/tzcode/stdtime/time2posix.3
new file mode 100644
index 000000000000..1a1ec95699f9
--- /dev/null
+++ b/contrib/tzcode/stdtime/time2posix.3
@@ -0,0 +1,123 @@
+.\" $FreeBSD$
+.\"
+.Dd September 11, 2005
+.Dt TIME2POSIX 3
+.Os
+.Sh NAME
+.Nm time2posix ,
+.Nm posix2time
+.Nd convert seconds since the Epoch
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In time.h
+.Ft time_t
+.Fn time2posix "time_t t"
+.Ft time_t
+.Fn posix2time "time_t t"
+.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."
+This effectively implies that POSIX time_t's cannot include leap
+seconds and,
+therefore,
+that the system time must be adjusted as each leap occurs.
+.Pp
+If the time package is configured with leap-second support
+enabled,
+however,
+no such adjustment is needed and
+time_t values continue to increase over leap events
+(as a true `seconds since...' value).
+This means that these values will differ from those required by POSIX
+by the net number of leap seconds inserted since the Epoch.
+.Pp
+Typically this is not a problem as the type time_t is intended
+to be
+(mostly)
+opaque\(emtime_t values should only be obtained-from and
+passed-to functions such as
+.Xr time 3 ,
+.Xr localtime 3 ,
+.Xr mktime 3
+and
+.Xr difftime 3 .
+However,
+.St -p1003.1-88
+gives an arithmetic
+expression for directly computing a time_t value from a given date/time,
+and the same relationship is assumed by some
+(usually older)
+applications.
+Any programs creating/dissecting time_t's
+using such a relationship will typically not handle intervals
+over leap seconds correctly.
+.Pp
+The
+.Fn time2posix
+and
+.Fn posix2time
+functions are provided to address this time_t mismatch by converting
+between local time_t values and their POSIX equivalents.
+This is done by accounting for the number of time-base changes that
+would have taken place on a POSIX system as leap seconds were inserted
+or deleted.
+These converted values can then be used in lieu of correcting the older
+applications,
+or when communicating with POSIX-compliant systems.
+.Pp
+The
+.Fn time2posix
+function is single-valued.
+That is,
+every local time_t
+corresponds to a single POSIX time_t.
+The
+.Fn posix2time
+function is less well-behaved:
+for a positive leap second hit the result is not unique,
+and for a negative leap second hit the corresponding
+POSIX time_t does not exist so an adjacent value is returned.
+Both of these are good indicators of the inferiority of the
+POSIX representation.
+.Pp
+The following table summarizes the relationship between time_t
+and its conversion to,
+and back from,
+the POSIX representation over the leap second inserted at the end of June,
+1993.
+.Bl -column "93/06/30" "23:59:59" "A+0" "X=time2posix(T)"
+.It Sy "DATE TIME T X=time2posix(T) posix2time(X)"
+.It "93/06/30 23:59:59 A+0 B+0 A+0"
+.It "93/06/30 23:59:60 A+1 B+1 A+1 or A+2"
+.It "93/07/01 00:00:00 A+2 B+1 A+1 or A+2"
+.It "93/07/01 00:00:01 A+3 B+2 A+3"
+.El
+.Pp
+A leap second deletion would look like...
+.Bl -column "??/06/30" "23:59:58" "A+0" "X=time2posix(T)"
+.It Sy "DATE TIME T X=time2posix(T) posix2time(X)"
+.It "??/06/30 23:59:58 A+0 B+0 A+0"
+.It "??/07/01 00:00:00 A+1 B+2 A+1"
+.It "??/07/01 00:00:01 A+2 B+3 A+2"
+.El
+.Pp
+.D1 No "[Note: posix2time(B+1) => A+0 or A+1]"
+.Pp
+If leap-second support is not enabled,
+local time_t's and
+POSIX time_t's are equivalent,
+and both
+.Fn time2posix
+and
+.Fn posix2time
+degenerate to the identity function.
+.Sh "SEE ALSO"
+.Xr difftime 3 ,
+.Xr localtime 3 ,
+.Xr mktime 3 ,
+.Xr time 3
+.\" @(#)time2posix.3 8.2
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
diff --git a/contrib/tzcode/stdtime/time32.c b/contrib/tzcode/stdtime/time32.c
new file mode 100644
index 000000000000..e852a4f279e4
--- /dev/null
+++ b/contrib/tzcode/stdtime/time32.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 2001 FreeBSD Inc.
+ * All rights reserved.
+ *
+ * These routines are for converting time_t to fixed-bit representations
+ * for use in protocols or storage. When converting time to a larger
+ * representation of time_t these routines are expected to assume temporal
+ * locality and use the 50-year rule to properly set the msb bits. XXX
+ *
+ * Redistribution and use under the terms of the COPYRIGHT file at the
+ * base of the source tree.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <timeconv.h>
+
+/*
+ * Convert a 32 bit representation of time_t into time_t. XXX needs to
+ * implement the 50-year rule to handle post-2038 conversions.
+ */
+time_t
+_time32_to_time(__int32_t t32)
+{
+ return((time_t)t32);
+}
+
+/*
+ * Convert time_t to a 32 bit representation. If time_t is 64 bits we can
+ * simply chop it down. The resulting 32 bit representation can be
+ * converted back to a temporally local 64 bit time_t using time32_to_time.
+ */
+__int32_t
+_time_to_time32(time_t t)
+{
+ return((__int32_t)t);
+}
+
+/*
+ * Convert a 64 bit representation of time_t into time_t. If time_t is
+ * represented as 32 bits we can simply chop it and not support times
+ * past 2038.
+ */
+time_t
+_time64_to_time(__int64_t t64)
+{
+ return((time_t)t64);
+}
+
+/*
+ * Convert time_t to a 64 bit representation. If time_t is represented
+ * as 32 bits we simply sign-extend and do not support times past 2038.
+ */
+__int64_t
+_time_to_time64(time_t t)
+{
+ return((__int64_t)t);
+}
+
+/*
+ * Convert to/from 'long'. Depending on the sizeof(long) this may or
+ * may not require using the 50-year rule.
+ */
+long
+_time_to_long(time_t t)
+{
+ if (sizeof(long) == sizeof(__int64_t))
+ return(_time_to_time64(t));
+ return((long)t);
+}
+
+time_t
+_long_to_time(long tlong)
+{
+ if (sizeof(long) == sizeof(__int32_t))
+ return(_time32_to_time(tlong));
+ return((time_t)tlong);
+}
+
+/*
+ * Convert to/from 'int'. Depending on the sizeof(int) this may or
+ * may not require using the 50-year rule.
+ */
+int
+_time_to_int(time_t t)
+{
+ if (sizeof(int) == sizeof(__int64_t))
+ return(_time_to_time64(t));
+ return((int)t);
+}
+
+time_t
+_int_to_time(int tint)
+{
+ if (sizeof(int) == sizeof(__int32_t))
+ return(_time32_to_time(tint));
+ return((time_t)tint);
+}
diff --git a/contrib/tzcode/stdtime/timelocal.c b/contrib/tzcode/stdtime/timelocal.c
new file mode 100644
index 000000000000..6917e6b6ccee
--- /dev/null
+++ b/contrib/tzcode/stdtime/timelocal.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
+ * Copyright (c) 1997 FreeBSD Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+
+#include "ldpart.h"
+#include "timelocal.h"
+
+static struct lc_time_T _time_locale;
+static int _time_using_locale;
+static char *time_locale_buf;
+
+#define LCTIME_SIZE (sizeof(struct lc_time_T) / sizeof(char *))
+
+static const struct lc_time_T _C_time_locale = {
+ {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ }, {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ }, {
+ "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"
+ }, {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ },
+
+ /* X_fmt */
+ "%H:%M:%S",
+
+ /*
+ * x_fmt
+ * Since the C language standard calls for
+ * "date, using locale's date format," anything goes.
+ * Using just numbers (as here) makes Quakers happier;
+ * it's also compatible with SVR4.
+ */
+ "%m/%d/%y",
+
+ /*
+ * c_fmt
+ */
+ "%a %b %e %H:%M:%S %Y",
+
+ /* am */
+ "AM",
+
+ /* pm */
+ "PM",
+
+ /* date_fmt */
+ "%a %b %e %H:%M:%S %Z %Y",
+
+ /* alt_month
+ * Standalone months forms for %OB
+ */
+ {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ },
+
+ /* md_order
+ * Month / day order in dates
+ */
+ "md",
+
+ /* ampm_fmt
+ * To determine 12-hour clock format time (empty, if N/A)
+ */
+ "%I:%M:%S %p"
+};
+
+struct lc_time_T *
+__get_current_time_locale(void)
+{
+ return (_time_using_locale
+ ? &_time_locale
+ : (struct lc_time_T *)&_C_time_locale);
+}
+
+int
+__time_load_locale(const char *name)
+{
+ return (__part_load_locale(name, &_time_using_locale,
+ &time_locale_buf, "LC_TIME",
+ LCTIME_SIZE, LCTIME_SIZE,
+ (const char **)&_time_locale));
+}
diff --git a/contrib/tzcode/stdtime/timelocal.h b/contrib/tzcode/stdtime/timelocal.h
new file mode 100644
index 000000000000..5d26bf938d97
--- /dev/null
+++ b/contrib/tzcode/stdtime/timelocal.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1997-2002 FreeBSD Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TIMELOCAL_H_
+#define _TIMELOCAL_H_
+
+/*
+ * Private header file for the strftime and strptime localization
+ * stuff.
+ */
+struct lc_time_T {
+ const char *mon[12];
+ const char *month[12];
+ const char *wday[7];
+ const char *weekday[7];
+ const char *X_fmt;
+ const char *x_fmt;
+ const char *c_fmt;
+ const char *am;
+ const char *pm;
+ const char *date_fmt;
+ const char *alt_month[12];
+ const char *md_order;
+ const char *ampm_fmt;
+};
+
+struct lc_time_T *__get_current_time_locale(void);
+int __time_load_locale(const char *);
+
+#endif /* !_TIMELOCAL_H_ */
diff --git a/contrib/tzcode/stdtime/tzfile.5 b/contrib/tzcode/stdtime/tzfile.5
new file mode 100644
index 000000000000..1606b5ae8c45
--- /dev/null
+++ b/contrib/tzcode/stdtime/tzfile.5
@@ -0,0 +1,152 @@
+.\" $FreeBSD$
+.Dd September 13, 1994
+.Dt TZFILE 5
+.Os
+.Sh NAME
+.Nm tzfile
+.Nd timezone information
+.Sh SYNOPSIS
+.Fd #include \&"/usr/src/lib/libc/stdtime/tzfile.h\&"
+.Sh DESCRIPTION
+The time zone information files used by
+.Xr tzset 3
+begin with the magic characters
+.Dq Li TZif
+to identify them as
+time zone information files,
+followed by a character identifying the version of the file's format
+(as of 2005, either an ASCII NUL or a '2')
+followed by fifteen bytes containing zeroes reserved for future use,
+followed by four four-byte values
+written in a ``standard'' byte order
+(the high-order byte of the value is written first).
+These values are,
+in order:
+.Pp
+.Bl -tag -compact -width tzh_ttisstdcnt
+.It Va tzh_ttisgmtcnt
+The number of UTC/local indicators stored in the file.
+.It Va tzh_ttisstdcnt
+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
+in the file.
+.It Va tzh_typecnt
+The number of ``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''
+stored in the file.
+.El
+.Pp
+The above header is followed by
+.Va tzh_timecnt
+four-byte values of type
+.Fa long ,
+sorted in ascending order.
+These values are written in ``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.
+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
+described in the file is associated with the same-indexed transition time.
+These values serve as indices into an array of
+.Fa ttinfo
+structures (with
+.Fa tzh_typecnt
+entries) that appears next in the file;
+these structures are defined as follows:
+.Pp
+.Bd -literal -offset indent
+struct ttinfo {
+ long tt_gmtoff;
+ int tt_isdst;
+ unsigned int tt_abbrind;
+};
+.Ed
+.Pp
+Each structure is written as a four-byte value for
+.Va tt_gmtoff
+of type
+.Fa long ,
+in a standard byte order, followed by a one-byte value for
+.Va tt_isdst
+and a one-byte value for
+.Va tt_abbrind .
+In each structure,
+.Va tt_gmtoff
+gives the number of seconds to be added to UTC,
+.Li tt_isdst
+tells whether
+.Li tm_isdst
+should be set by
+.Xr localtime 3
+and
+.Va tt_abbrind
+serves as an index into the array of time zone abbreviation characters
+that follow the
+.Li ttinfo
+structure(s) in the file.
+.Pp
+Then there are
+.Va tzh_leapcnt
+pairs of four-byte values, written in standard byte order;
+the first value of each pair gives the time
+(as returned by
+.Xr time 3 )
+at which a leap second occurs;
+the second gives the
+.Em total
+number of leap seconds to be applied after the given time.
+The pairs of values are sorted in ascending order by time.
+.Pp
+Then there are
+.Va tzh_ttisstdcnt
+standard/wall indicators, each stored as a one-byte value;
+they tell whether the transition times associated with local time types
+were specified as standard time or wall clock time,
+and are used when a time zone file is used in handling POSIX-style
+time zone environment variables.
+.Pp
+Finally there are
+.Va tzh_ttisgmtcnt
+UTC/local indicators, each stored as a one-byte value;
+they tell whether the transition times associated with local time types
+were specified as UTC or local time,
+and are used when a time zone file is used in handling POSIX-style
+time zone environment variables.
+.Pp
+.Nm localtime
+uses the first standard-time
+.Li ttinfo
+structure in the file
+(or simply the first
+.Li ttinfo
+structure in the absence of a standard-time structure)
+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 8.3
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
diff --git a/contrib/tzcode/stdtime/tzfile.h b/contrib/tzcode/stdtime/tzfile.h
new file mode 100644
index 000000000000..85b945e6dd75
--- /dev/null
+++ b/contrib/tzcode/stdtime/tzfile.h
@@ -0,0 +1,184 @@
+#ifndef TZFILE_H
+#define TZFILE_H
+
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+**
+** $FreeBSD$
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+/*
+static char tzfilehid[] = "@(#)tzfile.h 8.1";
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "/etc/localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ 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 */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UTC offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+** time is standard time, if FALSE,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
+** time is UTC, if FALSE,
+** transition time is local time
+** if absent, transition times are
+** 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
+#define TZ_MAX_TIMES 1200
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** 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 isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined TZFILE_H */