aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2023-01-10 15:14:27 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2023-01-10 15:14:27 +0000
commitbc42155199b5b0b479311e05b07aee7f6f9c5172 (patch)
treec18d4dc2fe115961d11dc559b9925732bccfcdad
parent016e46fd869ebf9891ca4b2cf1d22b337717a8c8 (diff)
parent85639444f44f168af982f59143b53efbba37669e (diff)
downloadsrc-bc42155199b5b0b479311e05b07aee7f6f9c5172.tar.gz
src-bc42155199b5b0b479311e05b07aee7f6f9c5172.zip
Bring our tzcode up to date.
* Replay 2010[acflm] which had been merged but not recorded. * Merge 2010n. * Reorganize (unsplit) the code to match the upstream layout. * Merge 2022[cdefg]. MFC after: 1 week Sponsored by: Klara, Inc.
-rw-r--r--contrib/tzcode/CONTRIBUTING97
-rw-r--r--contrib/tzcode/LICENSE5
-rw-r--r--contrib/tzcode/Makefile1250
-rw-r--r--contrib/tzcode/NEWS5902
-rw-r--r--contrib/tzcode/README52
-rw-r--r--contrib/tzcode/SECURITY15
-rw-r--r--contrib/tzcode/asctime.c (renamed from contrib/tzcode/stdtime/asctime.c)71
-rw-r--r--contrib/tzcode/calendars173
-rw-r--r--contrib/tzcode/date.1167
-rw-r--r--contrib/tzcode/date.c216
-rw-r--r--contrib/tzcode/difftime.c62
-rw-r--r--contrib/tzcode/localtime.c2669
-rw-r--r--contrib/tzcode/newctime.3344
-rw-r--r--contrib/tzcode/newstrftime.3290
-rw-r--r--contrib/tzcode/newtzset.3350
-rw-r--r--contrib/tzcode/private.h957
-rw-r--r--contrib/tzcode/stdtime/difftime.c67
-rw-r--r--contrib/tzcode/stdtime/localtime.c2317
-rw-r--r--contrib/tzcode/stdtime/private.h318
-rw-r--r--contrib/tzcode/stdtime/tzfile.5152
-rw-r--r--contrib/tzcode/stdtime/tzfile.h184
-rw-r--r--contrib/tzcode/strftime.c657
-rw-r--r--contrib/tzcode/theory.html1479
-rw-r--r--contrib/tzcode/time2posix.3 (renamed from contrib/tzcode/stdtime/time2posix.3)74
-rw-r--r--contrib/tzcode/tz-art.html636
-rw-r--r--contrib/tzcode/tz-how-to.html719
-rw-r--r--contrib/tzcode/tz-link.html1175
-rw-r--r--contrib/tzcode/tzfile.5492
-rw-r--r--contrib/tzcode/tzfile.h125
-rw-r--r--contrib/tzcode/tzselect.8125
-rw-r--r--contrib/tzcode/tzselect.ksh586
-rw-r--r--contrib/tzcode/version1
-rw-r--r--contrib/tzcode/version.h3
-rw-r--r--contrib/tzcode/workman.sh41
-rw-r--r--contrib/tzcode/zdump.8231
-rw-r--r--contrib/tzcode/zdump.c1257
-rw-r--r--contrib/tzcode/zic.8856
-rw-r--r--contrib/tzcode/zic.c4051
-rw-r--r--contrib/tzcode/zic/README88
-rw-r--r--contrib/tzcode/zic/Theory570
-rw-r--r--contrib/tzcode/zic/ialloc.c91
-rw-r--r--contrib/tzcode/zic/private.h272
-rw-r--r--contrib/tzcode/zic/scheck.c68
-rw-r--r--contrib/tzcode/zic/zdump.863
-rw-r--r--contrib/tzcode/zic/zdump.c668
-rw-r--r--contrib/tzcode/zic/zdump/Makefile15
-rw-r--r--contrib/tzcode/zic/zic.8468
-rw-r--r--contrib/tzcode/zic/zic.c2756
-rw-r--r--contrib/tzcode/zic/zic/Makefile16
-rw-r--r--lib/libc/stdtime/Makefile.inc10
-rw-r--r--lib/libc/stdtime/ctime.3 (renamed from contrib/tzcode/stdtime/ctime.3)0
-rw-r--r--lib/libc/stdtime/strftime.c1
-rw-r--r--lib/libc/stdtime/strptime.c1
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/zdump/Makefile12
-rw-r--r--usr.sbin/zdump/Makefile.depend (renamed from usr.sbin/zic/zdump/Makefile.depend)0
-rw-r--r--usr.sbin/zic/Makefile11
-rw-r--r--usr.sbin/zic/Makefile.depend (renamed from usr.sbin/zic/zic/Makefile.depend)0
-rw-r--r--usr.sbin/zic/Makefile.inc3
-rw-r--r--usr.sbin/zic/README88
-rw-r--r--usr.sbin/zic/zdump/Makefile15
-rw-r--r--usr.sbin/zic/zic/Makefile22
62 files changed, 25079 insertions, 8326 deletions
diff --git a/contrib/tzcode/CONTRIBUTING b/contrib/tzcode/CONTRIBUTING
new file mode 100644
index 000000000000..4c0f56a50265
--- /dev/null
+++ b/contrib/tzcode/CONTRIBUTING
@@ -0,0 +1,97 @@
+# Contributing to the tz code and data
+
+Please do not create issues or pull requests on GitHub, as the
+proper procedure for proposing and distributing patches is via
+email as described below.
+
+The time zone database is by no means authoritative: governments
+change timekeeping rules erratically and sometimes with little
+warning, the data entries do not cover all of civil time before
+1970, and undoubtedly errors remain in the code and data. Feel
+free to fill gaps or fix mistakes, and please email improvements
+to <tz@iana.org> for use in the future. In your email, please give
+reliable sources that reviewers can check.
+
+## Contributing technical changes
+
+To email small changes, please run a POSIX shell command like
+'diff -u old/europe new/europe >myfix.patch', and attach
+'myfix.patch' to the email.
+
+For more-elaborate or possibly-controversial changes,
+such as renaming, adding or removing zones, please read
+"Theory and pragmatics of the tz code and data"
+<https://www.iana.org/time-zones/repository/theory.html>.
+It is also good to browse the mailing list archives
+<https://mm.icann.org/pipermail/tz/> for examples of patches that tend
+to work well. Additions to data should contain commentary citing
+reliable sources as justification. Citations should use "https:" URLs
+if available.
+
+For changes that fix sensitive security-related bugs, please see the
+distribution's 'SECURITY' file.
+
+Please submit changes against either the latest release
+<https://www.iana.org/time-zones> or the main branch of the development
+repository. The latter is preferred.
+
+## Sample Git workflow for developing contributions
+
+If you use Git the following workflow may be helpful:
+
+ * Copy the development repository.
+
+ git clone https://github.com/eggert/tz.git
+ cd tz
+
+ * Get current with the main branch.
+
+ git checkout main
+ git pull
+
+ * Switch to a new branch for the changes. Choose a different
+ branch name for each change set.
+
+ git checkout -b mybranch
+
+ * Sleuth by using 'git blame'. For example, when fixing data for
+ Africa/Sao_Tome, if the command 'git blame africa' outputs a line
+ '2951fa3b (Paul Eggert 2018-01-08 09:03:13 -0800 1068) Zone
+ Africa/Sao_Tome 0:26:56 - LMT 1884', commit 2951fa3b should
+ provide some justification for the 'Zone Africa/Sao_Tome' line.
+
+ * Edit source files. Include commentary that justifies the
+ changes by citing reliable sources.
+
+ * Debug the changes, e.g.:
+
+ make check
+ make install
+ ./zdump -v America/Los_Angeles
+
+ * For each separable change, commit it in the new branch, e.g.:
+
+ git add northamerica
+ git commit
+
+ See recent 'git log' output for the commit-message style.
+
+ * Create patch files 0001-..., 0002-..., ...
+
+ git format-patch main
+
+ * After reviewing the patch files, send the patches to <tz@iana.org>
+ for others to review.
+
+ git send-email main
+
+ For an archived example of such an email, see
+ "[PROPOSED] Fix off-by-1 error for Jamaica and T&C before 1913"
+ <https://mm.icann.org/pipermail/tz/2018-February/026122.html>.
+
+ * Start anew by getting current with the main branch again
+ (the second step above).
+
+-----
+
+This file is in the public domain.
diff --git a/contrib/tzcode/LICENSE b/contrib/tzcode/LICENSE
new file mode 100644
index 000000000000..8ba4399c622d
--- /dev/null
+++ b/contrib/tzcode/LICENSE
@@ -0,0 +1,5 @@
+Unless specified below, all files in the tz code and data (including
+this LICENSE file) are in the public domain.
+
+If the files date.c, newstrftime.3, and strftime.c are present, they
+contain material derived from BSD and use the BSD 3-clause license.
diff --git a/contrib/tzcode/Makefile b/contrib/tzcode/Makefile
new file mode 100644
index 000000000000..afb9d538a203
--- /dev/null
+++ b/contrib/tzcode/Makefile
@@ -0,0 +1,1250 @@
+# Make and install tzdb code and data.
+
+# This file is in the public domain, so clarified as of
+# 2009-05-17 by Arthur David Olson.
+
+# Package name for the code distribution.
+PACKAGE= tzcode
+
+# Version number for the distribution, overridden in the 'tarballs' rule below.
+VERSION= unknown
+
+# Email address for bug reports.
+BUGEMAIL= tz@iana.org
+
+# DATAFORM selects the data format.
+# Available formats represent essentially the same data, albeit
+# possibly with minor discrepancies that users are not likely to notice.
+# To get new features and the best data right away, use:
+# DATAFORM= vanguard
+# To wait a while before using new features, to give downstream users
+# time to upgrade zic (the default), use:
+# DATAFORM= main
+# To wait even longer for new features, use:
+# DATAFORM= rearguard
+# Rearguard users might also want "ZFLAGS = -b fat"; see below.
+DATAFORM= main
+
+# Change the line below for your timezone (after finding the one you want in
+# one of the $(TDATA) source files, or adding it to a source file).
+# Alternatively, if you discover you've got the wrong timezone, you can just
+# 'zic -l -' to remove it, or 'zic -l rightzone' to change it.
+# Use the command
+# make zonenames
+# to get a list of the values you can use for LOCALTIME.
+
+LOCALTIME= Factory
+
+# The POSIXRULES macro controls interpretation of nonstandard and obsolete
+# POSIX-like TZ settings like TZ='EET-2EEST' that lack DST transition rules.
+# Such a setting uses the rules in a template file to determine
+# "spring forward" and "fall back" days and times; the environment
+# variable itself specifies UT offsets of standard and daylight saving time.
+#
+# If POSIXRULES is '-', no template is installed; this is the default.
+#
+# Any other value for POSIXRULES is obsolete and should not be relied on, as:
+# * It does not work correctly in popular implementations such as GNU/Linux.
+# * It does not work even in tzcode, except for historical timestamps
+# that precede the last explicit transition in the POSIXRULES file.
+# Hence it typically does not work for current and future timestamps.
+# In short, software should avoid ruleless settings like TZ='EET-2EEST'
+# and so should not depend on the value of POSIXRULES.
+#
+# If, despite the above, you want a template for handling these settings,
+# you can change the line below (after finding the timezone you want in the
+# one of the $(TDATA) source files, or adding it to a source file).
+# Alternatively, if you discover you've got the wrong timezone, you can just
+# 'zic -p -' to remove it, or 'zic -p rightzone' to change it.
+# Use the command
+# make zonenames
+# to get a list of the values you can use for POSIXRULES.
+
+POSIXRULES= -
+
+# Also see TZDEFRULESTRING below, which takes effect only
+# if the time zone files cannot be accessed.
+
+
+# Installation locations.
+#
+# The defaults are suitable for Debian, except that if REDO is
+# posix_right or right_posix then files that Debian puts under
+# /usr/share/zoneinfo/posix and /usr/share/zoneinfo/right are instead
+# put under /usr/share/zoneinfo-posix and /usr/share/zoneinfo-leaps,
+# respectively. Problems with the Debian approach are discussed in
+# the commentary for the right_posix rule (below).
+
+# Destination directory, which can be used for staging.
+# 'make DESTDIR=/stage install' installs under /stage (e.g., to
+# /stage/etc/localtime instead of to /etc/localtime). Files under
+# /stage are not intended to work as-is, but can be copied by hand to
+# the root directory later. If DESTDIR is empty, 'make install' does
+# not stage, but installs directly into production locations.
+DESTDIR =
+
+# Everything is installed into subdirectories of TOPDIR, and used there.
+# TOPDIR should be empty (meaning the root directory),
+# or a directory name that does not end in "/".
+# TOPDIR should be empty or an absolute name unless you're just testing.
+TOPDIR =
+
+# The default local timezone is taken from the file TZDEFAULT.
+TZDEFAULT = $(TOPDIR)/etc/localtime
+
+# The subdirectory containing installed program and data files, and
+# likewise for installed files that can be shared among architectures.
+# These should be relative file names.
+USRDIR = usr
+USRSHAREDIR = $(USRDIR)/share
+
+# "Compiled" timezone information is placed in the "TZDIR" directory
+# (and subdirectories).
+# TZDIR_BASENAME should not contain "/" and should not be ".", ".." or empty.
+TZDIR_BASENAME= zoneinfo
+TZDIR = $(TOPDIR)/$(USRSHAREDIR)/$(TZDIR_BASENAME)
+
+# The "tzselect" and (if you do "make INSTALL") "date" commands go in:
+BINDIR = $(TOPDIR)/$(USRDIR)/bin
+
+# The "zdump" command goes in:
+ZDUMPDIR = $(BINDIR)
+
+# The "zic" command goes in:
+ZICDIR = $(TOPDIR)/$(USRDIR)/sbin
+
+# Manual pages go in subdirectories of. . .
+MANDIR = $(TOPDIR)/$(USRSHAREDIR)/man
+
+# Library functions are put in an archive in LIBDIR.
+LIBDIR = $(TOPDIR)/$(USRDIR)/lib
+
+
+# Types to try, as an alternative to time_t.
+TIME_T_ALTERNATIVES = $(TIME_T_ALTERNATIVES_HEAD) $(TIME_T_ALTERNATIVES_TAIL)
+TIME_T_ALTERNATIVES_HEAD = int_least64_t
+TIME_T_ALTERNATIVES_TAIL = int_least32_t uint_least32_t uint_least64_t
+
+# What kind of TZif data files to generate. (TZif is the binary time
+# zone data format that zic generates; see Internet RFC 8536.)
+# If you want only POSIX time, with time values interpreted as
+# seconds since the epoch (not counting leap seconds), use
+# REDO= posix_only
+# below. If you want only "right" time, with values interpreted
+# as seconds since the epoch (counting leap seconds), use
+# REDO= right_only
+# below. If you want both sets of data available, with leap seconds not
+# counted normally, use
+# REDO= posix_right
+# below. If you want both sets of data available, with leap seconds counted
+# normally, use
+# REDO= right_posix
+# below. POSIX mandates that leap seconds not be counted; for compatibility
+# with it, use "posix_only" or "posix_right". Use POSIX time on systems with
+# leap smearing; this can work better than unsmeared "right" time with
+# applications that are not leap second aware, and is closer to unsmeared
+# "right" time than unsmeared POSIX time is (e.g., 0.5 vs 1.0 s max error).
+
+REDO= posix_right
+
+# Whether to put an "Expires" line in the leapseconds file.
+# Use EXPIRES_LINE=1 to put the line in, 0 to omit it.
+# The EXPIRES_LINE value matters only if REDO's value contains "right".
+# If you change EXPIRES_LINE, remove the leapseconds file before running "make".
+# zic's support for the Expires line was introduced in tzdb 2020a,
+# and was modified in tzdb 2021b to generate version 4 TZif files.
+# EXPIRES_LINE defaults to 0 for now so that the leapseconds file
+# can be given to pre-2020a zic implementations and so that TZif files
+# built by newer zic implementations can be read by pre-2021b libraries.
+EXPIRES_LINE= 0
+
+# To install data in text form that has all the information of the TZif data,
+# (optionally incorporating leap second information), use
+# TZDATA_TEXT= tzdata.zi leapseconds
+# To install text data without leap second information (e.g., because
+# REDO='posix_only'), use
+# TZDATA_TEXT= tzdata.zi
+# To avoid installing text data, use
+# TZDATA_TEXT=
+
+TZDATA_TEXT= leapseconds tzdata.zi
+
+# For backward-compatibility links for old zone names, use
+# BACKWARD= backward
+# To omit these links, use
+# BACKWARD=
+
+BACKWARD= backward
+
+# If you want out-of-scope and often-wrong data from the file 'backzone',
+# but only for entries listed in the backward-compatibility file zone.tab, use
+# PACKRATDATA= backzone
+# PACKRATLIST= zone.tab
+# If you want all the 'backzone' data, use
+# PACKRATDATA= backzone
+# PACKRATLIST=
+# To omit this data, use
+# PACKRATDATA=
+# PACKRATLIST=
+
+PACKRATDATA=
+PACKRATLIST=
+
+# The name of a locale using the UTF-8 encoding, used during self-tests.
+# The tests are skipped if the name does not appear to work on this system.
+
+UTF8_LOCALE= en_US.utf8
+
+# Non-default libraries needed to link.
+# On some hosts, this should have -lintl unless CFLAGS has -DHAVE_GETTEXT=0.
+LDLIBS=
+
+# Add the following to the end of the "CFLAGS=" line as needed to override
+# defaults specified in the source code. "-DFOO" is equivalent to "-DFOO=1".
+# -DDEPRECATE_TWO_DIGIT_YEARS for optional runtime warnings about strftime
+# formats that generate only the last two digits of year numbers
+# -DEPOCH_LOCAL if the 'time' function returns local time not UT
+# -DEPOCH_OFFSET=N if the 'time' function returns a value N greater
+# than what POSIX specifies, assuming local time is UT.
+# For example, N is 252460800 on AmigaOS.
+# -DHAVE_DECL_ASCTIME_R=0 if <time.h> does not declare asctime_r
+# -DHAVE_DECL_ENVIRON if <unistd.h> declares 'environ'
+# -DHAVE_DECL_TIMEGM=0 if <time.h> does not declare timegm
+# -DHAVE_DIRECT_H if mkdir needs <direct.h> (MS-Windows)
+# -DHAVE_GENERIC=0 if _Generic does not work*
+# -DHAVE_GETRANDOM if getrandom works (e.g., GNU/Linux),
+# -DHAVE_GETRANDOM=0 to avoid using getrandom
+# -DHAVE_GETTEXT if gettext works (e.g., GNU/Linux, FreeBSD, Solaris),
+# where LDLIBS also needs to contain -lintl on some hosts;
+# -DHAVE_GETTEXT=0 to avoid using gettext
+# -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares
+# ctime_r and asctime_r incompatibly with the POSIX standard
+# (Solaris when _POSIX_PTHREAD_SEMANTICS is not defined).
+# -DHAVE_INTTYPES_H=0 if <inttypes.h> does not work*
+# -DHAVE_LINK=0 if your system lacks a link function
+# -DHAVE_LOCALTIME_R=0 if your system lacks a localtime_r function
+# -DHAVE_LOCALTIME_RZ=0 if you do not want zdump to use localtime_rz
+# localtime_rz can make zdump significantly faster, but is nonstandard.
+# -DHAVE_MALLOC_ERRNO=0 if malloc etc. do not set errno on failure.
+# -DHAVE_POSIX_DECLS=0 if your system's include files do not declare
+# functions like 'link' or variables like 'tzname' required by POSIX
+# -DHAVE_SETENV=0 if your system lacks the setenv function
+# -DHAVE_SNPRINTF=0 if your system lacks the snprintf function
+# -DHAVE_STDCKDINT_H=0 if neither <stdckdint.h> nor substitutes like
+# __builtin_add_overflow work*
+# -DHAVE_STDINT_H=0 if <stdint.h> does not work*
+# -DHAVE_STRFTIME_L if <time.h> declares locale_t and strftime_l
+# -DHAVE_STRDUP=0 if your system lacks the strdup function
+# -DHAVE_STRTOLL=0 if your system lacks the strtoll function
+# -DHAVE_SYMLINK=0 if your system lacks the symlink function
+# -DHAVE_SYS_STAT_H=0 if <sys/stat.h> does not work*
+# -DHAVE_TZSET=0 if your system lacks a tzset function
+# -DHAVE_UNISTD_H=0 if <unistd.h> does not work*
+# -DHAVE_UTMPX_H=0 if <utmpx.h> does not work*
+# -Dlocale_t=XXX if your system uses XXX instead of locale_t
+# -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers
+# with external linkage, e.g., applications cannot define 'localtime'.
+# -Dssize_t=long on hosts like MS-Windows that lack ssize_t
+# -DSUPPRESS_TZDIR to not prepend TZDIR to file names; this has
+# security implications and is not recommended for general use
+# -DTHREAD_SAFE to make localtime.c thread-safe, as POSIX requires;
+# not needed by the main-program tz code, which is single-threaded.
+# Append other compiler flags as needed, e.g., -pthread on GNU/Linux.
+# -Dtime_tz=\"T\" to use T as the time_t type, rather than the system time_t
+# This is intended for internal use only; it mangles external names.
+# -DTZ_DOMAIN=\"foo\" to use "foo" for gettext domain name; default is "tz"
+# -DTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
+# the default is system-supplied, typically "/usr/lib/locale"
+# -DTZDEFRULESTRING=\",date/time,date/time\" to default to the specified
+# DST transitions if the time zone files cannot be accessed
+# -DUNINIT_TRAP if reading uninitialized storage can cause problems
+# other than simply getting garbage data
+# -DUSE_LTZ=0 to build zdump with the system time zone library
+# Also set TZDOBJS=zdump.o and CHECK_TIME_T_ALTERNATIVES= below.
+# -DZIC_BLOAT_DEFAULT=\"fat\" to default zic's -b option to "fat", and
+# similarly for "slim". Fat TZif files work around incompatibilities
+# and bugs in some TZif readers, notably older ones that
+# ignore or otherwise mishandle 64-bit data in TZif files;
+# however, fat TZif files may trigger bugs in newer TZif readers.
+# Slim TZif files are more efficient, and are the default.
+# -DZIC_MAX_ABBR_LEN_WO_WARN=3
+# (or some other number) to set the maximum time zone abbreviation length
+# that zic will accept without a warning (the default is 6)
+# $(GCC_DEBUG_FLAGS) if you are using recent GCC and want lots of checking
+#
+# * Options marked "*" can be omitted if your compiler is C23 compatible.
+#
+# Select instrumentation via "make GCC_INSTRUMENT='whatever'".
+GCC_INSTRUMENT = \
+ -fsanitize=undefined -fsanitize-address-use-after-scope \
+ -fsanitize-undefined-trap-on-error -fstack-protector
+# Omit -fanalyzer from GCC_DEBUG_FLAGS, as it makes GCC too slow.
+GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
+ $(GCC_INSTRUMENT) \
+ -Wall -Wextra \
+ -Walloc-size-larger-than=100000 -Warray-bounds=2 \
+ -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wdate-time \
+ -Wdeclaration-after-statement -Wdouble-promotion \
+ -Wduplicated-branches -Wduplicated-cond \
+ -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
+ -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op \
+ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wnull-dereference \
+ -Wold-style-definition -Woverlength-strings -Wpointer-arith \
+ -Wshadow -Wshift-overflow=2 -Wstrict-overflow \
+ -Wstrict-prototypes -Wstringop-overflow=4 \
+ -Wstringop-truncation -Wsuggest-attribute=cold \
+ -Wsuggest-attribute=const -Wsuggest-attribute=format \
+ -Wsuggest-attribute=malloc \
+ -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure \
+ -Wtrampolines -Wundef -Wuninitialized -Wunused-macros -Wuse-after-free=3 \
+ -Wvariadic-macros -Wvla -Wwrite-strings \
+ -Wno-address -Wno-format-nonliteral -Wno-sign-compare \
+ -Wno-type-limits
+#
+# If your system has a "GMT offset" field in its "struct tm"s
+# (or if you decide to add such a field in your system's "time.h" file),
+# add the name to a define such as
+# -DTM_GMTOFF=tm_gmtoff
+# to the end of the "CFLAGS=" line. If not defined, the code attempts to
+# guess TM_GMTOFF from other macros; define NO_TM_GMTOFF to suppress this.
+# Similarly, if your system has a "zone abbreviation" field, define
+# -DTM_ZONE=tm_zone
+# and define NO_TM_ZONE to suppress any guessing. Although these two fields
+# not required by POSIX, a future version of POSIX is planned to require them
+# and they are widely available on GNU/Linux and BSD systems.
+#
+# The next batch of options control support for external variables
+# exported by tzcode. In practice these variables are less useful
+# than TM_GMTOFF and TM_ZONE. However, most of them are standardized.
+# #
+# # To omit or support the external variable "tzname", add one of:
+# # -DHAVE_TZNAME=0 # do not support "tzname"
+# # -DHAVE_TZNAME=1 # support "tzname", which is defined by system library
+# # -DHAVE_TZNAME=2 # support and define "tzname"
+# # to the "CFLAGS=" line. "tzname" is required by POSIX 1988 and later.
+# # If not defined, the code attempts to guess HAVE_TZNAME from other macros.
+# # Warning: unless time_tz is also defined, HAVE_TZNAME=1 can cause
+# # crashes when combined with some platforms' standard libraries,
+# # presumably due to memory allocation issues.
+# #
+# # To omit or support the external variables "timezone" and "daylight", add
+# # -DUSG_COMPAT=0 # do not support
+# # -DUSG_COMPAT=1 # support, and variables are defined by system library
+# # -DUSG_COMPAT=2 # support and define variables
+# # to the "CFLAGS=" line; "timezone" and "daylight" are inspired by
+# # Unix Systems Group code and are required by POSIX 2008 (with XSI) and later.
+# # If not defined, the code attempts to guess USG_COMPAT from other macros.
+# #
+# # To support the external variable "altzone", add
+# # -DALTZONE=0 # do not support
+# # -DALTZONE=1 # support "altzone", which is defined by system library
+# # -DALTZONE=2 # support and define "altzone"
+# # to the end of the "CFLAGS=" line; although "altzone" appeared in
+# # System V Release 3.1 it has not been standardized.
+# # If not defined, the code attempts to guess ALTZONE from other macros.
+#
+# If you want functions that were inspired by early versions of X3J11's work,
+# add
+# -DSTD_INSPIRED
+# to the end of the "CFLAGS=" line. This arranges for the following
+# functions to be added to the time conversion library.
+# "offtime" is like "gmtime" except that it accepts a second (long) argument
+# that gives an offset to add to the time_t when converting it.
+# "timelocal" is equivalent to "mktime".
+# "timeoff" is like "timegm" except that it accepts a second (long) argument
+# that gives an offset to use when converting to a time_t.
+# "posix2time" and "time2posix" are described in an included manual page.
+# X3J11's work does not describe any of these functions.
+# These functions may well disappear in future releases of the time
+# conversion package.
+#
+# If you don't want functions that were inspired by NetBSD, add
+# -DNETBSD_INSPIRED=0
+# to the end of the "CFLAGS=" line. Otherwise, the functions
+# "localtime_rz", "mktime_z", "tzalloc", and "tzfree" are added to the
+# time library, and if STD_INSPIRED is also defined the functions
+# "posix2time_z" and "time2posix_z" are added as well.
+# The functions ending in "_z" (or "_rz") are like their unsuffixed
+# (or suffixed-by-"_r") counterparts, except with an extra first
+# argument of opaque type timezone_t that specifies the timezone.
+# "tzalloc" allocates a timezone_t value, and "tzfree" frees it.
+#
+# If you want to allocate state structures in localtime, add
+# -DALL_STATE
+# to the end of the "CFLAGS=" line. Storage is obtained by calling malloc.
+#
+# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
+# out by the National Institute of Standards and Technology
+# which claims to test C and Posix conformance. If you want to pass PCTS, add
+# -DPCTS
+# to the end of the "CFLAGS=" line.
+#
+# If you want strict compliance with XPG4 as of 1994-04-09, add
+# -DXPG4_1994_04_09
+# to the end of the "CFLAGS=" line. This causes "strftime" to always return
+# 53 as a week number (rather than 52 or 53) for January days before
+# January's first Monday when a "%V" format is used and January 1
+# falls on a Friday, Saturday, or Sunday.
+
+CFLAGS=
+
+# Linker flags. Default to $(LFLAGS) for backwards compatibility
+# to release 2012h and earlier.
+
+LDFLAGS= $(LFLAGS)
+
+# For leap seconds, this Makefile uses LEAPSECONDS='-L leapseconds' in
+# submake command lines. The default is no leap seconds.
+
+LEAPSECONDS=
+
+# The zic command and its arguments.
+
+zic= ./zic
+ZIC= $(zic) $(ZFLAGS)
+
+# To shrink the size of installed TZif files,
+# append "-r @N" to omit data before N-seconds-after-the-Epoch.
+# To grow the files and work around bugs in older applications,
+# possibly at the expense of introducing bugs in newer ones,
+# append "-b fat"; see ZIC_BLOAT_DEFAULT above.
+# See the zic man page for more about -b and -r.
+ZFLAGS=
+
+# How to use zic to install TZif files.
+
+ZIC_INSTALL= $(ZIC) -d '$(DESTDIR)$(TZDIR)' $(LEAPSECONDS)
+
+# The name of a Posix-compliant 'awk' on your system.
+# mawk 1.3.3 and Solaris 10 /usr/bin/awk do not work.
+# Also, it is better (though not essential) if 'awk' supports UTF-8,
+# and unfortunately mawk and busybox awk do not support UTF-8.
+# Try AWK=gawk or AWK=nawk if your awk has the abovementioned problems.
+AWK= awk
+
+# The full path name of a Posix-compliant shell, preferably one that supports
+# the Korn shell's 'select' statement as an extension.
+# These days, Bash is the most popular.
+# It should be OK to set this to /bin/sh, on platforms where /bin/sh
+# lacks 'select' or doesn't completely conform to Posix, but /bin/bash
+# is typically nicer if it works.
+KSHELL= /bin/bash
+
+# Name of curl <https://curl.haxx.se/>, used for HTML validation.
+CURL= curl
+
+# Name of GNU Privacy Guard <https://gnupg.org/>, used to sign distributions.
+GPG= gpg
+
+# This expensive test requires USE_LTZ.
+# To suppress it, define this macro to be empty.
+CHECK_TIME_T_ALTERNATIVES = check_time_t_alternatives
+
+# SAFE_CHAR is a regular expression that matches a safe character.
+# Some parts of this distribution are limited to safe characters;
+# others can use any UTF-8 character.
+# For now, the safe characters are a safe subset of ASCII.
+# The caller must set the shell variable 'sharp' to the character '#',
+# since Makefile macros cannot contain '#'.
+# TAB_CHAR is a single tab character, in single quotes.
+TAB_CHAR= ' '
+SAFE_CHARSET1= $(TAB_CHAR)' !\"'$$sharp'$$%&'\''()*+,./0123456789:;<=>?@'
+SAFE_CHARSET2= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\^_`'
+SAFE_CHARSET3= 'abcdefghijklmnopqrstuvwxyz{|}~'
+SAFE_CHARSET= $(SAFE_CHARSET1)$(SAFE_CHARSET2)$(SAFE_CHARSET3)
+SAFE_CHAR= '[]'$(SAFE_CHARSET)'-]'
+
+# These characters are Latin-1, and so are likely to be displayable
+# even in editors with limited character sets.
+UNUSUAL_OK_LATIN_1 = «°±»½¾×
+# This IPA symbol is represented in Unicode as the composition of
+# U+0075 and U+032F, and U+032F is not considered alphabetic by some
+# grep implementations that do not grok composition.
+UNUSUAL_OK_IPA = u̯
+# Non-ASCII non-letters that OK_CHAR allows, as these characters are
+# useful in commentary.
+UNUSUAL_OK_CHARSET= $(UNUSUAL_OK_LATIN_1)$(UNUSUAL_OK_IPA)
+
+# Put this in a bracket expression to match spaces.
+s = [:space:]
+
+# OK_CHAR matches any character allowed in the distributed files.
+# This is the same as SAFE_CHAR, except that UNUSUAL_OK_CHARSET and
+# multibyte letters are also allowed so that commentary can contain a
+# few safe symbols and people's names and can quote non-English sources.
+# Other non-letters are limited to ASCII renderings for the
+# convenience of maintainers using XEmacs 21.5.34, which by default
+# mishandles Unicode characters U+0100 and greater.
+OK_CHAR= '[][:alpha:]$(UNUSUAL_OK_CHARSET)'$(SAFE_CHARSET)'-]'
+
+# SAFE_LINE matches a line of safe characters.
+# SAFE_SHARP_LINE is similar, except any OK character can follow '#';
+# this is so that comments can contain non-ASCII characters.
+# OK_LINE matches a line of OK characters.
+SAFE_LINE= '^'$(SAFE_CHAR)'*$$'
+SAFE_SHARP_LINE='^'$(SAFE_CHAR)'*('$$sharp$(OK_CHAR)'*)?$$'
+OK_LINE= '^'$(OK_CHAR)'*$$'
+
+# Flags to give 'tar' when making a distribution.
+# Try to use flags appropriate for GNU tar.
+GNUTARFLAGS= --format=pax --pax-option='delete=atime,delete=ctime' \
+ --numeric-owner --owner=0 --group=0 \
+ --mode=go+u,go-w --sort=name
+TARFLAGS= `if tar $(GNUTARFLAGS) --version >/dev/null 2>&1; \
+ then echo $(GNUTARFLAGS); \
+ else :; \
+ fi`
+
+# Flags to give 'gzip' when making a distribution.
+GZIPFLAGS= -9n
+
+# When comparing .tzs files, use GNU diff's -F'^TZ=' option if supported.
+# This makes it easier to see which Zone has been affected.
+DIFF_TZS= diff -u$$(! diff -u -F'^TZ=' - - <>/dev/null >&0 2>&1 \
+ || echo ' -F^TZ=')
+
+###############################################################################
+
+#MAKE= make
+
+cc= cc
+CC= $(cc) -DTZDIR='"$(TZDIR)"'
+
+AR= ar
+
+# ':' on typical hosts; 'ranlib' on the ancient hosts that still need ranlib.
+RANLIB= :
+
+TZCOBJS= zic.o
+TZDOBJS= zdump.o localtime.o asctime.o strftime.o
+DATEOBJS= date.o localtime.o strftime.o asctime.o
+LIBSRCS= localtime.c asctime.c difftime.c strftime.c
+LIBOBJS= localtime.o asctime.o difftime.o strftime.o
+HEADERS= tzfile.h private.h
+NONLIBSRCS= zic.c zdump.c
+NEWUCBSRCS= date.c
+SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) \
+ tzselect.ksh workman.sh
+MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
+ tzfile.5 tzselect.8 zic.8 zdump.8
+MANTXTS= newctime.3.txt newstrftime.3.txt newtzset.3.txt \
+ time2posix.3.txt \
+ tzfile.5.txt tzselect.8.txt zic.8.txt zdump.8.txt \
+ date.1.txt
+COMMON= calendars CONTRIBUTING LICENSE Makefile \
+ NEWS README SECURITY theory.html version
+WEB_PAGES= tz-art.html tz-how-to.html tz-link.html
+CHECK_WEB_PAGES=check_theory.html check_tz-art.html \
+ check_tz-how-to.html check_tz-link.html
+DOCS= $(MANS) date.1 $(MANTXTS) $(WEB_PAGES)
+PRIMARY_YDATA= africa antarctica asia australasia \
+ europe northamerica southamerica
+YDATA= $(PRIMARY_YDATA) etcetera
+NDATA= factory
+TDATA_TO_CHECK= $(YDATA) $(NDATA) backward
+TDATA= $(YDATA) $(NDATA) $(BACKWARD)
+ZONETABLES= zone1970.tab zone.tab
+TABDATA= iso3166.tab $(TZDATA_TEXT) $(ZONETABLES)
+LEAP_DEPS= leapseconds.awk leap-seconds.list
+TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \
+ $(PACKRATDATA) $(PACKRATLIST)
+DSTDATA_ZI_DEPS= ziguard.awk $(TDATA) $(PACKRATDATA) $(PACKRATLIST)
+DATA= $(TDATA_TO_CHECK) backzone iso3166.tab leap-seconds.list \
+ leapseconds $(ZONETABLES)
+AWK_SCRIPTS= checklinks.awk checktab.awk leapseconds.awk \
+ ziguard.awk zishrink.awk
+MISC= $(AWK_SCRIPTS)
+TZS_YEAR= 2050
+TZS_CUTOFF_FLAG= -c $(TZS_YEAR)
+TZS= to$(TZS_YEAR).tzs
+TZS_NEW= to$(TZS_YEAR)new.tzs
+TZS_DEPS= $(YDATA) asctime.c localtime.c \
+ private.h tzfile.h zdump.c zic.c
+TZDATA_DIST = $(COMMON) $(DATA) $(MISC)
+# EIGHT_YARDS is just a yard short of the whole ENCHILADA.
+EIGHT_YARDS = $(TZDATA_DIST) $(DOCS) $(SOURCES) tzdata.zi
+ENCHILADA = $(EIGHT_YARDS) $(TZS)
+
+# Consult these files when deciding whether to rebuild the 'version' file.
+# This list is not the same as the output of 'git ls-files', since
+# .gitignore is not distributed.
+VERSION_DEPS= \
+ calendars CONTRIBUTING LICENSE Makefile NEWS README SECURITY \
+ africa antarctica asctime.c asia australasia \
+ backward backzone \
+ checklinks.awk checktab.awk \
+ date.1 date.c difftime.c \
+ etcetera europe factory iso3166.tab \
+ leap-seconds.list leapseconds.awk localtime.c \
+ newctime.3 newstrftime.3 newtzset.3 northamerica \
+ private.h southamerica strftime.c theory.html \
+ time2posix.3 tz-art.html tz-how-to.html tz-link.html \
+ tzfile.5 tzfile.h tzselect.8 tzselect.ksh \
+ workman.sh zdump.8 zdump.c zic.8 zic.c \
+ ziguard.awk zishrink.awk \
+ zone.tab zone1970.tab
+
+# And for the benefit of csh users on systems that assume the user
+# shell should be used to handle commands in Makefiles. . .
+
+SHELL= /bin/sh
+
+all: tzselect zic zdump libtz.a $(TABDATA) \
+ vanguard.zi main.zi rearguard.zi
+
+ALL: all date $(ENCHILADA)
+
+install: all $(DATA) $(REDO) $(MANS)
+ mkdir -p '$(DESTDIR)$(BINDIR)' \
+ '$(DESTDIR)$(ZDUMPDIR)' '$(DESTDIR)$(ZICDIR)' \
+ '$(DESTDIR)$(LIBDIR)' \
+ '$(DESTDIR)$(MANDIR)/man3' '$(DESTDIR)$(MANDIR)/man5' \
+ '$(DESTDIR)$(MANDIR)/man8'
+ $(ZIC_INSTALL) -l $(LOCALTIME) \
+ `case '$(POSIXRULES)' in ?*) echo '-p';; esac \
+ ` $(POSIXRULES) \
+ -t '$(DESTDIR)$(TZDEFAULT)'
+ cp -f $(TABDATA) '$(DESTDIR)$(TZDIR)/.'
+ cp tzselect '$(DESTDIR)$(BINDIR)/.'
+ cp zdump '$(DESTDIR)$(ZDUMPDIR)/.'
+ cp zic '$(DESTDIR)$(ZICDIR)/.'
+ cp libtz.a '$(DESTDIR)$(LIBDIR)/.'
+ $(RANLIB) '$(DESTDIR)$(LIBDIR)/libtz.a'
+ cp -f newctime.3 newtzset.3 '$(DESTDIR)$(MANDIR)/man3/.'
+ cp -f tzfile.5 '$(DESTDIR)$(MANDIR)/man5/.'
+ cp -f tzselect.8 zdump.8 zic.8 '$(DESTDIR)$(MANDIR)/man8/.'
+
+INSTALL: ALL install date.1
+ mkdir -p '$(DESTDIR)$(BINDIR)' '$(DESTDIR)$(MANDIR)/man1'
+ cp date '$(DESTDIR)$(BINDIR)/.'
+ cp -f date.1 '$(DESTDIR)$(MANDIR)/man1/.'
+
+# Calculate version number from git, if available.
+# Otherwise, use $(VERSION) unless it is "unknown" and there is already
+# a 'version' file, in which case reuse the existing 'version' contents
+# and append "-dirty" if the contents do not already end in "-dirty".
+version: $(VERSION_DEPS)
+ { (type git) >/dev/null 2>&1 && \
+ V=`git describe --match '[0-9][0-9][0-9][0-9][a-z]*' \
+ --abbrev=7 --dirty` || \
+ if test '$(VERSION)' = unknown && V=`cat $@`; then \
+ case $$V in *-dirty);; *) V=$$V-dirty;; esac; \
+ else \
+ V='$(VERSION)'; \
+ fi; } && \
+ printf '%s\n' "$$V" >$@.out
+ mv $@.out $@
+
+# These files can be tailored by setting BACKWARD, PACKRATDATA, PACKRATLIST.
+vanguard.zi main.zi rearguard.zi: $(DSTDATA_ZI_DEPS)
+ $(AWK) \
+ -v DATAFORM=`expr $@ : '\(.*\).zi'` \
+ -v PACKRATDATA='$(PACKRATDATA)' \
+ -v PACKRATLIST='$(PACKRATLIST)' \
+ -f ziguard.awk \
+ $(TDATA) $(PACKRATDATA) >$@.out
+ mv $@.out $@
+# This file has a version comment that attempts to capture any tailoring
+# via BACKWARD, DATAFORM, PACKRATDATA, PACKRATLIST, and REDO.
+tzdata.zi: $(DATAFORM).zi version zishrink.awk
+ version=`sed 1q version` && \
+ LC_ALL=C $(AWK) \
+ -v dataform='$(DATAFORM)' \
+ -v deps='$(DSTDATA_ZI_DEPS) zishrink.awk' \
+ -v redo='$(REDO)' \
+ -v version="$$version" \
+ -f zishrink.awk \
+ $(DATAFORM).zi >$@.out
+ mv $@.out $@
+
+version.h: version
+ VERSION=`cat version` && printf '%s\n' \
+ 'static char const PKGVERSION[]="($(PACKAGE)) ";' \
+ "static char const TZVERSION[]=\"$$VERSION\";" \
+ 'static char const REPORT_BUGS_TO[]="$(BUGEMAIL)";' \
+ >$@.out
+ mv $@.out $@
+
+zdump: $(TZDOBJS)
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZDOBJS) $(LDLIBS)
+
+zic: $(TZCOBJS)
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(TZCOBJS) $(LDLIBS)
+
+leapseconds: $(LEAP_DEPS)
+ $(AWK) -v EXPIRES_LINE=$(EXPIRES_LINE) \
+ -f leapseconds.awk leap-seconds.list >$@.out
+ mv $@.out $@
+
+# Arguments to pass to submakes of install_data.
+# They can be overridden by later submake arguments.
+INSTALLARGS = \
+ BACKWARD='$(BACKWARD)' \
+ DESTDIR='$(DESTDIR)' \
+ LEAPSECONDS='$(LEAPSECONDS)' \
+ PACKRATDATA='$(PACKRATDATA)' \
+ PACKRATLIST='$(PACKRATLIST)' \
+ TZDEFAULT='$(TZDEFAULT)' \
+ TZDIR='$(TZDIR)' \
+ ZIC='$(ZIC)'
+
+INSTALL_DATA_DEPS = zic leapseconds tzdata.zi
+
+# 'make install_data' installs one set of TZif files.
+install_data: $(INSTALL_DATA_DEPS)
+ $(ZIC_INSTALL) tzdata.zi
+
+posix_only: $(INSTALL_DATA_DEPS)
+ $(MAKE) $(INSTALLARGS) LEAPSECONDS= install_data
+
+right_only: $(INSTALL_DATA_DEPS)
+ $(MAKE) $(INSTALLARGS) LEAPSECONDS='-L leapseconds' \
+ install_data
+
+# In earlier versions of this makefile, the other two directories were
+# subdirectories of $(TZDIR). However, this led to configuration errors.
+# For example, with posix_right under the earlier scheme,
+# TZ='right/Australia/Adelaide' got you localtime with leap seconds,
+# but gmtime without leap seconds, which led to problems with applications
+# like sendmail that subtract gmtime from localtime.
+# Therefore, the other two directories are now siblings of $(TZDIR).
+# You must replace all of $(TZDIR) to switch from not using leap seconds
+# to using them, or vice versa.
+right_posix: right_only
+ rm -fr '$(DESTDIR)$(TZDIR)-leaps'
+ ln -s '$(TZDIR_BASENAME)' '$(DESTDIR)$(TZDIR)-leaps' || \
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-leaps' right_only
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-posix' posix_only
+
+posix_right: posix_only
+ rm -fr '$(DESTDIR)$(TZDIR)-posix'
+ ln -s '$(TZDIR_BASENAME)' '$(DESTDIR)$(TZDIR)-posix' || \
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-posix' posix_only
+ $(MAKE) $(INSTALLARGS) TZDIR='$(TZDIR)-leaps' right_only
+
+zones: $(REDO)
+
+# dummy.zd is not a real file; it is mentioned here only so that the
+# top-level 'make' does not have a syntax error.
+ZDS = dummy.zd
+# Rule used only by submakes invoked by the $(TZS_NEW) rule.
+# It is separate so that GNU 'make -j' can run instances in parallel.
+$(ZDS): zdump
+ ./zdump -i $(TZS_CUTOFF_FLAG) '$(wd)/'$$(expr $@ : '\(.*\).zd') \
+ >$@
+
+TZS_NEW_DEPS = tzdata.zi zdump zic
+$(TZS_NEW): $(TZS_NEW_DEPS)
+ rm -fr tzs$(TZS_YEAR).dir
+ mkdir tzs$(TZS_YEAR).dir
+ $(zic) -d tzs$(TZS_YEAR).dir tzdata.zi
+ $(AWK) '/^L/{print "Link\t" $$2 "\t" $$3}' \
+ tzdata.zi | LC_ALL=C sort >$@.out
+ wd=`pwd` && \
+ x=`$(AWK) '/^Z/{print "tzs$(TZS_YEAR).dir/" $$2 ".zd"}' \
+ tzdata.zi \
+ | LC_ALL=C sort -t . -k 2,2` && \
+ set x $$x && \
+ shift && \
+ ZDS=$$* && \
+ $(MAKE) wd="$$wd" TZS_CUTOFF_FLAG="$(TZS_CUTOFF_FLAG)" \
+ ZDS="$$ZDS" $$ZDS && \
+ sed 's,^TZ=".*\.dir/,TZ=",' $$ZDS >>$@.out
+ rm -fr tzs$(TZS_YEAR).dir
+ mv $@.out $@
+
+# If $(TZS) exists but 'make check_tzs' fails, a maintainer should inspect the
+# failed output and fix the inconsistency, perhaps by running 'make force_tzs'.
+$(TZS):
+ touch $@
+
+force_tzs: $(TZS_NEW)
+ cp $(TZS_NEW) $(TZS)
+
+libtz.a: $(LIBOBJS)
+ rm -f $@
+ $(AR) -rc $@ $(LIBOBJS)
+ $(RANLIB) $@
+
+date: $(DATEOBJS)
+ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $(DATEOBJS) $(LDLIBS)
+
+tzselect: tzselect.ksh version
+ VERSION=`cat version` && sed \
+ -e 's|#!/bin/bash|#!$(KSHELL)|g' \
+ -e 's|AWK=[^}]*|AWK='\''$(AWK)'\''|g' \
+ -e 's|\(PKGVERSION\)=.*|\1='\''($(PACKAGE)) '\''|' \
+ -e 's|\(REPORT_BUGS_TO\)=.*|\1=$(BUGEMAIL)|' \
+ -e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \
+ -e 's|\(TZVERSION\)=.*|\1='"$$VERSION"'|' \
+ <$@.ksh >$@.out
+ chmod +x $@.out
+ mv $@.out $@
+
+check: check_back check_mild
+check_mild: check_character_set check_white_space check_links \
+ check_name_lengths check_slashed_abbrs check_sorted \
+ check_tables check_web check_ziguard check_zishrink check_tzs
+
+check_character_set: $(ENCHILADA)
+ test ! '$(UTF8_LOCALE)' || \
+ ! printf 'A\304\200B\n' | \
+ LC_ALL='$(UTF8_LOCALE)' grep -q '^A.B$$' >/dev/null 2>&1 || { \
+ LC_ALL='$(UTF8_LOCALE)' && export LC_ALL && \
+ sharp='#' && \
+ ! grep -Env $(SAFE_LINE) $(MANS) date.1 $(MANTXTS) \
+ $(MISC) $(SOURCES) $(WEB_PAGES) \
+ CONTRIBUTING LICENSE README SECURITY \
+ version tzdata.zi && \
+ ! grep -Env $(SAFE_LINE)'|^UNUSUAL_OK_'$(OK_CHAR)'*$$' \
+ Makefile && \
+ ! grep -Env $(SAFE_SHARP_LINE) $(TDATA_TO_CHECK) backzone \
+ leapseconds zone.tab && \
+ ! grep -Env $(OK_LINE) $(ENCHILADA); \
+ }
+ touch $@
+
+check_white_space: $(ENCHILADA)
+ patfmt=' \t|[\f\r\v]' && pat=`printf "$$patfmt\\n"` && \
+ ! grep -En "$$pat" \
+ $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list)
+ ! grep -n '[$s]$$' \
+ $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list)
+ touch $@
+
+PRECEDES_FILE_NAME = ^(Zone|Link[$s]+[^$s]+)[$s]+
+FILE_NAME_COMPONENT_TOO_LONG = $(PRECEDES_FILE_NAME)[^$s]*[^/$s]{15}
+
+check_name_lengths: $(TDATA_TO_CHECK) backzone
+ ! grep -En '$(FILE_NAME_COMPONENT_TOO_LONG)' \
+ $(TDATA_TO_CHECK) backzone
+ touch $@
+
+PRECEDES_STDOFF = ^(Zone[$s]+[^$s]+)?[$s]+
+STDOFF = [-+]?[0-9:.]+
+RULELESS_SAVE = (-|$(STDOFF)[sd]?)
+RULELESS_SLASHED_ABBRS = \
+ $(PRECEDES_STDOFF)$(STDOFF)[$s]+$(RULELESS_SAVE)[$s]+[^$s]*/
+
+check_slashed_abbrs: $(TDATA_TO_CHECK)
+ ! grep -En '$(RULELESS_SLASHED_ABBRS)' $(TDATA_TO_CHECK)
+ touch $@
+
+CHECK_CC_LIST = { n = split($$1,a,/,/); for (i=2; i<=n; i++) print a[1], a[i]; }
+
+check_sorted: backward backzone iso3166.tab zone.tab zone1970.tab
+ $(AWK) '/^Link/ {printf "%.5d %s\n", g, $$3} !/./ {g++}' \
+ backward | LC_ALL=C sort -cu
+ $(AWK) '/^Zone/ {print $$2}' backzone | LC_ALL=C sort -cu
+ touch $@
+
+check_back: checklinks.awk $(TDATA_TO_CHECK)
+ $(AWK) \
+ -v DATAFORM=$(DATAFORM) \
+ -v backcheck=backward \
+ -f checklinks.awk $(TDATA_TO_CHECK)
+ touch $@
+
+check_links: checklinks.awk tzdata.zi
+ $(AWK) \
+ -v DATAFORM=$(DATAFORM) \
+ -f checklinks.awk tzdata.zi
+ touch $@
+
+check_tables: checktab.awk $(YDATA) backward $(ZONETABLES)
+ for tab in $(ZONETABLES); do \
+ test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \
+ $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \
+ || exit; \
+ done
+ touch $@
+
+check_tzs: $(TZS) $(TZS_NEW)
+ if test -s $(TZS); then \
+ $(DIFF_TZS) $(TZS) $(TZS_NEW); \
+ else \
+ cp $(TZS_NEW) $(TZS); \
+ fi
+ touch $@
+
+check_web: $(CHECK_WEB_PAGES)
+check_theory.html: theory.html
+check_tz-art.html: tz-art.html
+check_tz-how-to.html: tz-how-to.html
+check_tz-link.html: tz-link.html
+check_theory.html check_tz-art.html check_tz-how-to.html check_tz-link.html:
+ $(CURL) -sS --url https://validator.w3.org/nu/ -F out=gnu \
+ -F file=@$$(expr $@ : 'check_\(.*\)') -o $@.out && \
+ test ! -s $@.out || { cat $@.out; exit 1; }
+ mv $@.out $@
+
+check_ziguard: rearguard.zi vanguard.zi ziguard.awk
+ $(AWK) -v DATAFORM=rearguard -f ziguard.awk vanguard.zi | \
+ diff -u rearguard.zi -
+ $(AWK) -v DATAFORM=vanguard -f ziguard.awk rearguard.zi | \
+ diff -u vanguard.zi -
+ touch $@
+
+# Check that zishrink.awk does not alter the data, and that ziguard.awk
+# preserves main-format data.
+check_zishrink: check_zishrink_posix check_zishrink_right
+check_zishrink_posix check_zishrink_right: \
+ zic leapseconds $(PACKRATDATA) $(PACKRATLIST) \
+ $(TDATA) $(DATAFORM).zi tzdata.zi
+ rm -fr $@.dir $@-t.dir $@-shrunk.dir
+ mkdir $@.dir $@-t.dir $@-shrunk.dir
+ case $@ in \
+ *_right) leap='-L leapseconds';; \
+ *) leap=;; \
+ esac && \
+ $(ZIC) $$leap -d $@.dir $(DATAFORM).zi && \
+ $(ZIC) $$leap -d $@-shrunk.dir tzdata.zi && \
+ case $(DATAFORM),$(PACKRATLIST) in \
+ main,) \
+ $(ZIC) $$leap -d $@-t.dir $(TDATA) && \
+ $(AWK) '/^Rule/' $(TDATA) | \
+ $(ZIC) $$leap -d $@-t.dir - $(PACKRATDATA) && \
+ diff -r $@.dir $@-t.dir;; \
+ esac
+ diff -r $@.dir $@-shrunk.dir
+ rm -fr $@.dir $@-t.dir $@-shrunk.dir
+ touch $@
+
+clean_misc:
+ rm -fr check_*.dir
+ rm -f *.o *.out $(TIME_T_ALTERNATIVES) \
+ check_* core typecheck_* \
+ date tzselect version.h zdump zic libtz.a
+clean: clean_misc
+ rm -fr *.dir tzdb-*/
+ rm -f *.zi $(TZS_NEW)
+
+maintainer-clean: clean
+ @echo 'This command is intended for maintainers to use; it'
+ @echo 'deletes files that may need special tools to rebuild.'
+ rm -f leapseconds version $(MANTXTS) $(TZS) *.asc *.tar.*
+
+names:
+ @echo $(ENCHILADA)
+
+public: check check_public $(CHECK_TIME_T_ALTERNATIVES) \
+ tarballs signatures
+
+date.1.txt: date.1
+newctime.3.txt: newctime.3
+newstrftime.3.txt: newstrftime.3
+newtzset.3.txt: newtzset.3
+time2posix.3.txt: time2posix.3
+tzfile.5.txt: tzfile.5
+tzselect.8.txt: tzselect.8
+zdump.8.txt: zdump.8
+zic.8.txt: zic.8
+
+$(MANTXTS): workman.sh
+ LC_ALL=C sh workman.sh `expr $@ : '\(.*\)\.txt$$'` >$@.out
+ mv $@.out $@
+
+# Set file timestamps deterministically if possible,
+# so that tarballs containing the timestamps are reproducible.
+#
+# '$(SET_TIMESTAMP_N) N DEST A B C ...' sets the timestamp of the
+# file DEST to the maximum of the timestamps of the files A B C ...,
+# plus N if GNU ls and touch are available.
+SET_TIMESTAMP_N = sh -c '\
+ n=$$0 dest=$$1; shift; \
+ touch -cmr `ls -t "$$@" | sed 1q` "$$dest" && \
+ if test $$n != 0 && \
+ lsout=`ls -n --time-style="+%s" "$$dest" 2>/dev/null`; then \
+ set x $$lsout && \
+ touch -cmd @`expr $$7 + $$n` "$$dest"; \
+ else :; fi'
+# If DEST depends on A B C ... in this Makefile, callers should use
+# $(SET_TIMESTAMP_DEP) DEST A B C ..., for the benefit of any
+# downstream 'make' that considers equal timestamps to be out of date.
+# POSIX allows this 'make' behavior, and HP-UX 'make' does it.
+# If all that matters is that the timestamp be reproducible
+# and plausible, use $(SET_TIMESTAMP).
+SET_TIMESTAMP = $(SET_TIMESTAMP_N) 0
+SET_TIMESTAMP_DEP = $(SET_TIMESTAMP_N) 1
+
+# Set the timestamps to those of the git repository, if available,
+# and if the files have not changed since then.
+# This uses GNU 'ls --time-style=+%s', which outputs the seconds count,
+# and GNU 'touch -d@N FILE', where N is the number of seconds since 1970.
+# If git or GNU is absent, don't bother to sync with git timestamps.
+# Also, set the timestamp of each prebuilt file like 'leapseconds'
+# to be the maximum of the files it depends on.
+set-timestamps.out: $(EIGHT_YARDS)
+ rm -f $@
+ if (type git) >/dev/null 2>&1 && \
+ files=`git ls-files $(EIGHT_YARDS)` && \
+ touch -md @1 test.out; then \
+ rm -f test.out && \
+ for file in $$files; do \
+ if git diff --quiet $$file; then \
+ time=`git log -1 --format='tformat:%ct' $$file` && \
+ touch -cmd @$$time $$file; \
+ else \
+ echo >&2 "$$file: warning: does not match repository"; \
+ fi || exit; \
+ done; \
+ fi
+ $(SET_TIMESTAMP_DEP) leapseconds $(LEAP_DEPS)
+ for file in `ls $(MANTXTS) | sed 's/\.txt$$//'`; do \
+ $(SET_TIMESTAMP_DEP) $$file.txt $$file workman.sh || \
+ exit; \
+ done
+ $(SET_TIMESTAMP_DEP) version $(VERSION_DEPS)
+ $(SET_TIMESTAMP_DEP) tzdata.zi $(TZDATA_ZI_DEPS)
+ touch $@
+set-tzs-timestamp.out: $(TZS)
+ $(SET_TIMESTAMP_DEP) $(TZS) $(TZS_DEPS)
+ touch $@
+
+# The zics below ensure that each data file can stand on its own.
+# We also do an all-files run to catch links to links.
+
+check_public: $(VERSION_DEPS)
+ rm -fr public.dir
+ mkdir public.dir
+ ln $(VERSION_DEPS) public.dir
+ cd public.dir && $(MAKE) CFLAGS='$(GCC_DEBUG_FLAGS)' ALL
+ for i in $(TDATA_TO_CHECK) public.dir/tzdata.zi \
+ public.dir/vanguard.zi public.dir/main.zi \
+ public.dir/rearguard.zi; \
+ do \
+ public.dir/zic -v -d public.dir/zoneinfo $$i 2>&1 || exit; \
+ done
+ public.dir/zic -v -d public.dir/zoneinfo-all $(TDATA_TO_CHECK)
+ :
+ : Also check 'backzone' syntax.
+ rm public.dir/main.zi
+ cd public.dir && $(MAKE) PACKRATDATA=backzone main.zi
+ public.dir/zic -d public.dir/zoneinfo main.zi
+ rm public.dir/main.zi
+ cd public.dir && \
+ $(MAKE) PACKRATDATA=backzone PACKRATLIST=zone.tab main.zi
+ public.dir/zic -d public.dir/zoneinfo main.zi
+ :
+ rm -fr public.dir
+ touch $@
+
+# Check that the code works under various alternative
+# implementations of time_t.
+check_time_t_alternatives: $(TIME_T_ALTERNATIVES)
+$(TIME_T_ALTERNATIVES_TAIL): $(TIME_T_ALTERNATIVES_HEAD)
+$(TIME_T_ALTERNATIVES): $(VERSION_DEPS)
+ rm -fr $@.dir
+ mkdir $@.dir
+ ln $(VERSION_DEPS) $@.dir
+ case $@ in \
+ int*32_t) range=-2147483648,2147483648;; \
+ u*) range=0,4294967296;; \
+ *) range=-4294967296,4294967296;; \
+ esac && \
+ wd=`pwd` && \
+ zones=`$(AWK) '/^[^#]/ { print $$3 }' <zone1970.tab` && \
+ if test $@ = $(TIME_T_ALTERNATIVES_HEAD); then \
+ range_target=; \
+ else \
+ range_target=to$$range.tzs; \
+ fi && \
+ (cd $@.dir && \
+ $(MAKE) TOPDIR="$$wd/$@.dir" \
+ CFLAGS='$(CFLAGS) -Dtime_tz='"'$@'" \
+ REDO='$(REDO)' \
+ D=$$wd/$@.dir \
+ TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
+ install $$range_target) && \
+ test $@ = $(TIME_T_ALTERNATIVES_HEAD) || { \
+ (cd $(TIME_T_ALTERNATIVES_HEAD).dir && \
+ $(MAKE) TOPDIR="$$wd/$@.dir" \
+ TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \
+ D=$$wd/$@.dir \
+ to$$range.tzs) && \
+ $(DIFF_TZS) $(TIME_T_ALTERNATIVES_HEAD).dir/to$$range.tzs \
+ $@.dir/to$$range.tzs && \
+ if diff -q Makefile Makefile 2>/dev/null; then \
+ quiet_option='-q'; \
+ else \
+ quiet_option=''; \
+ fi && \
+ diff $$quiet_option -r $(TIME_T_ALTERNATIVES_HEAD).dir/etc \
+ $@.dir/etc && \
+ diff $$quiet_option -r \
+ $(TIME_T_ALTERNATIVES_HEAD).dir/usr/share \
+ $@.dir/usr/share; \
+ }
+ touch $@
+
+TRADITIONAL_ASC = \
+ tzcode$(VERSION).tar.gz.asc \
+ tzdata$(VERSION).tar.gz.asc
+REARGUARD_ASC = \
+ tzdata$(VERSION)-rearguard.tar.gz.asc
+ALL_ASC = $(TRADITIONAL_ASC) $(REARGUARD_ASC) \
+ tzdb-$(VERSION).tar.lz.asc
+
+tarballs rearguard_tarballs tailored_tarballs traditional_tarballs \
+signatures rearguard_signatures traditional_signatures: \
+ version set-timestamps.out rearguard.zi vanguard.zi
+ VERSION=`cat version` && \
+ $(MAKE) AWK='$(AWK)' VERSION="$$VERSION" $@_version
+
+# These *_version rules are intended for use if VERSION is set by some
+# other means. Ordinarily these rules are used only by the above
+# non-_version rules, which set VERSION on the 'make' command line.
+tarballs_version: traditional_tarballs_version rearguard_tarballs_version \
+ tzdb-$(VERSION).tar.lz
+rearguard_tarballs_version: \
+ tzdata$(VERSION)-rearguard.tar.gz
+traditional_tarballs_version: \
+ tzcode$(VERSION).tar.gz tzdata$(VERSION).tar.gz
+tailored_tarballs_version: \
+ tzdata$(VERSION)-tailored.tar.gz
+signatures_version: $(ALL_ASC)
+rearguard_signatures_version: $(REARGUARD_ASC)
+traditional_signatures_version: $(TRADITIONAL_ASC)
+
+tzcode$(VERSION).tar.gz: set-timestamps.out
+ LC_ALL=C && export LC_ALL && \
+ tar $(TARFLAGS) -cf - \
+ $(COMMON) $(DOCS) $(SOURCES) | \
+ gzip $(GZIPFLAGS) >$@.out
+ mv $@.out $@
+
+tzdata$(VERSION).tar.gz: set-timestamps.out
+ LC_ALL=C && export LC_ALL && \
+ tar $(TARFLAGS) -cf - $(TZDATA_DIST) | \
+ gzip $(GZIPFLAGS) >$@.out
+ mv $@.out $@
+
+# Create empty files with a reproducible timestamp.
+CREATE_EMPTY = TZ=UTC0 touch -mt 202010122253.00
+
+# The obsolescent *rearguard* targets and related macros are present
+# for backwards compatibility with tz releases 2018e through 2022a.
+# They should go away eventually. To build rearguard tarballs you
+# can instead use 'make DATAFORM=rearguard tailored_tarballs'.
+tzdata$(VERSION)-rearguard.tar.gz: rearguard.zi set-timestamps.out
+ rm -fr $@.dir
+ mkdir $@.dir
+ ln $(TZDATA_DIST) $@.dir
+ cd $@.dir && rm -f $(TDATA) $(PACKRATDATA) version
+ for f in $(TDATA) $(PACKRATDATA); do \
+ rearf=$@.dir/$$f; \
+ $(AWK) -v DATAFORM=rearguard -f ziguard.awk $$f >$$rearf && \
+ $(SET_TIMESTAMP_DEP) $$rearf ziguard.awk $$f || exit; \
+ done
+ sed '1s/$$/-rearguard/' <version >$@.dir/version
+ : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
+ $(CREATE_EMPTY) $@.dir/pacificnew
+ touch -cmr version $@.dir/version
+ LC_ALL=C && export LC_ALL && \
+ (cd $@.dir && \
+ tar $(TARFLAGS) -cf - \
+ $(TZDATA_DIST) pacificnew | \
+ gzip $(GZIPFLAGS)) >$@.out
+ mv $@.out $@
+
+# Create a tailored tarball suitable for TZUpdater and compatible tools.
+# For example, 'make DATAFORM=vanguard tailored_tarballs' makes a tarball
+# useful for testing whether TZUpdater supports vanguard form.
+# The generated tarball is not byte-for-byte equivalent to a hand-tailored
+# traditional tarball, as data entries are put into 'etcetera' even if they
+# came from some other source file. However, the effect should be the same
+# for ordinary use, which reads all the source files.
+tzdata$(VERSION)-tailored.tar.gz: set-timestamps.out
+ rm -fr $@.dir
+ mkdir $@.dir
+ : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
+ cd $@.dir && \
+ $(CREATE_EMPTY) $(PRIMARY_YDATA) $(NDATA) backward \
+ `test $(DATAFORM) = vanguard || echo pacificnew`
+ (grep '^#' tzdata.zi && echo && cat $(DATAFORM).zi) \
+ >$@.dir/etcetera
+ touch -cmr tzdata.zi $@.dir/etcetera
+ sed -n \
+ -e '/^# *version *\(.*\)/h' \
+ -e '/^# *ddeps */H' \
+ -e '$$!d' \
+ -e 'g' \
+ -e 's/^# *version *//' \
+ -e 's/\n# *ddeps */-/' \
+ -e 's/ /-/g' \
+ -e 'p' \
+ <tzdata.zi >$@.dir/version
+ touch -cmr version $@.dir/version
+ links= && \
+ for file in $(TZDATA_DIST); do \
+ test -f $@.dir/$$file || links="$$links $$file"; \
+ done && \
+ ln $$links $@.dir
+ LC_ALL=C && export LC_ALL && \
+ (cd $@.dir && \
+ tar $(TARFLAGS) -cf - * | gzip $(GZIPFLAGS)) >$@.out
+ mv $@.out $@
+
+tzdb-$(VERSION).tar.lz: set-timestamps.out set-tzs-timestamp.out
+ rm -fr tzdb-$(VERSION)
+ mkdir tzdb-$(VERSION)
+ ln $(ENCHILADA) tzdb-$(VERSION)
+ $(SET_TIMESTAMP) tzdb-$(VERSION) tzdb-$(VERSION)/*
+ LC_ALL=C && export LC_ALL && \
+ tar $(TARFLAGS) -cf - tzdb-$(VERSION) | lzip -9 >$@.out
+ mv $@.out $@
+
+tzcode$(VERSION).tar.gz.asc: tzcode$(VERSION).tar.gz
+tzdata$(VERSION).tar.gz.asc: tzdata$(VERSION).tar.gz
+tzdata$(VERSION)-rearguard.tar.gz.asc: tzdata$(VERSION)-rearguard.tar.gz
+tzdb-$(VERSION).tar.lz.asc: tzdb-$(VERSION).tar.lz
+$(ALL_ASC):
+ $(GPG) --armor --detach-sign $?
+
+TYPECHECK_CFLAGS = $(CFLAGS) -DTYPECHECK -D__time_t_defined -D_TIME_T
+typecheck: typecheck_long_long typecheck_unsigned
+typecheck_long_long typecheck_unsigned: $(VERSION_DEPS)
+ rm -fr $@.dir
+ mkdir $@.dir
+ ln $(VERSION_DEPS) $@.dir
+ cd $@.dir && \
+ case $@ in \
+ *_long_long) i="long long";; \
+ *_unsigned ) i="unsigned" ;; \
+ esac && \
+ typecheck_cflags='' && \
+ $(MAKE) \
+ CFLAGS="$(TYPECHECK_CFLAGS) \"-Dtime_t=$$i\"" \
+ TOPDIR="`pwd`" \
+ install
+ $@.dir/zdump -i -c 1970,1971 Europe/Rome
+ touch $@
+
+zonenames: tzdata.zi
+ @$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi
+
+asctime.o: private.h tzfile.h
+date.o: private.h
+difftime.o: private.h
+localtime.o: private.h tzfile.h
+strftime.o: private.h tzfile.h
+zdump.o: version.h
+zic.o: private.h tzfile.h version.h
+
+.PHONY: ALL INSTALL all
+.PHONY: check check_mild check_time_t_alternatives
+.PHONY: check_web check_zishrink
+.PHONY: clean clean_misc dummy.zd force_tzs
+.PHONY: install install_data maintainer-clean names
+.PHONY: posix_only posix_right public
+.PHONY: rearguard_signatures rearguard_signatures_version
+.PHONY: rearguard_tarballs rearguard_tarballs_version
+.PHONY: right_only right_posix signatures signatures_version
+.PHONY: tarballs tarballs_version
+.PHONY: traditional_signatures traditional_signatures_version
+.PHONY: traditional_tarballs traditional_tarballs_version
+.PHONY: tailored_tarballs tailored_tarballs_version
+.PHONY: typecheck
+.PHONY: zonenames zones
+.PHONY: $(ZDS)
diff --git a/contrib/tzcode/NEWS b/contrib/tzcode/NEWS
new file mode 100644
index 000000000000..701e490e4834
--- /dev/null
+++ b/contrib/tzcode/NEWS
@@ -0,0 +1,5902 @@
+News for the tz database
+
+Release 2022g - 2022-11-29 08:58:31 -0800
+
+ Briefly:
+ The northern edge of Chihuahua changes to US timekeeping.
+ Much of Greenland stops changing clocks after March 2023.
+ Fix some pre-1996 timestamps in northern Canada.
+ C89 is now deprecated; please use C99 or later.
+ Portability fixes for AIX, libintl, MS-Windows, musl, z/OS
+ In C code, use more C23 features if available.
+ C23 timegm now supported by default
+ Fixes for unlikely integer overflows
+
+ Changes to future timestamps
+
+ In the Mexican state of Chihuahua, the border strip near the US
+ will change to agree with nearby US locations on 2022-11-30.
+ The strip's western part, represented by Ciudad Juárez, switches
+ from -06 all year to -07/-06 with US DST rules, like El Paso, TX.
+ The eastern part, represented by Ojinaga, will observe US DST next
+ year, like Presidio, TX. (Thanks to Heitor David Pinto.)
+ A new Zone America/Ciudad_Juarez splits from America/Ojinaga.
+
+ Much of Greenland, represented by America/Nuuk, stops observing
+ winter time after March 2023, so its daylight saving time becomes
+ standard time. (Thanks to Jonas Nyrup and Jürgen Appel.)
+
+ Changes to past timestamps
+
+ Changes for pre-1996 northern Canada (thanks to Chris Walton):
+
+ Merge America/Iqaluit and America/Pangnirtung into the former,
+ with a backward compatibility link for the latter name.
+ There is no good evidence the two locations differ since 1970.
+ This change affects pre-1996 America/Pangnirtung timestamps.
+
+ Cambridge Bay, Inuvik, Iqaluit, Rankin Inlet, Resolute and
+ Yellowknife did not observe DST in 1965, and did observe DST
+ from 1972 through 1979.
+
+ Whitehorse moved from -09 to -08 on 1966-02-27, not 1967-05-28.
+
+ Colombia's 1993 fallback was 02-06 24:00, not 04-04 00:00.
+ (Thanks to Alois Treindl.)
+
+ Singapore's 1981-12-31 change was at 16:00 UTC (23:30 local time),
+ not 24:00 local time. (Thanks to Geoff Clare via Robert Elz.)
+
+ Changes to code
+
+ Although tzcode still works with C89, bugs found in recent routine
+ maintenance indicate that bitrot has set in and that in practice
+ C89 is no longer used to build tzcode. As it is a maintenance
+ burden, support for C89 is planned to be removed soon. Instead,
+ please use compilers compatible with C99, C11, C17, or C23.
+
+ timegm, which tzcode implemented in 1989, will finally be
+ standardized 34 years later as part of C23, so timegm is now
+ supported even if STD_INSPIRED is not defined.
+
+ Fix bug in zdump's tzalloc emulation on hosts that lack tm_zone.
+ (Problem reported by Đoàn Trần Công Danh.)
+
+ Fix bug in zic on hosts where malloc(0) yields NULL on success.
+ (Problem reported by Tim McBrayer for AIX 6.1.)
+
+ Fix zic configuration to avoid linkage failures on some platforms.
+ (Problems reported by Gilmore Davidson and Igor Ivanov.)
+
+ Work around MS-Windows nmake incompatibility with POSIX.
+ (Problem reported by Manuela Friedrich.)
+
+ Port mktime and strftime to debugging platforms where accessing
+ uninitialized data has undefined behavior (strftime problem
+ reported by Robert Elz).
+
+ Check more carefully for unlikely integer overflows, preferring
+ C23 <stdckdint.h> to overflow checking by hand, as the latter has
+ had obscure bugs.
+
+ Changes to build procedure
+
+ New Makefile rule check_mild that skips checking whether Link
+ lines are in the file 'backward'. (Inspired by a suggestion from
+ Stephen Colebourne.)
+
+
+Release 2022f - 2022-10-28 18:04:57 -0700
+
+ Briefly:
+ Mexico will no longer observe DST except near the US border.
+ Chihuahua moves to year-round -06 on 2022-10-30.
+ Fiji no longer observes DST.
+ Move links to 'backward'.
+ In vanguard form, GMT is now a Zone and Etc/GMT a link.
+ zic now supports links to links, and vanguard form uses this.
+ Simplify four Ontario zones.
+ Fix a Y2438 bug when reading TZif data.
+ Enable 64-bit time_t on 32-bit glibc platforms.
+ Omit large-file support when no longer needed.
+ In C code, use some C23 features if available.
+ Remove no-longer-needed workaround for Qt bug 53071.
+
+ Changes to future timestamps
+
+ Mexico will no longer observe DST after 2022, except for areas
+ near the US border that continue to observe US DST rules.
+ On 2022-10-30 at 02:00 the Mexican state of Chihuahua moves
+ from -07 (-06 with DST) to year-round -06, thus not changing
+ its clocks that day. The new law states that Chihuahua
+ near the US border no longer observes US DST.
+ (Thanks to gera for the heads-up about Chihuahua.)
+
+ Fiji will not observe DST in 2022/3. (Thanks to Shalvin Narayan.)
+ For now, assume DST is suspended indefinitely.
+
+ Changes to data
+
+ Move links to 'backward' to ease and simplify link maintenance.
+ This affects generated data only if you use 'make BACKWARD='.
+
+ GMT is now a Zone and Etc/GMT a link instead of vice versa,
+ as GMT is needed for leap second support whereas Etc/GMT is not.
+ However, this change exposes a bug in TZUpdater 2.3.2 so it is
+ present only in vanguard form for now.
+
+ Vanguard form now uses links to links, as zic now supports this.
+
+ Changes to past timestamps
+
+ Simplify four Ontario zones, as most of the post-1970 differences
+ seem to have been imaginary. (Problem reported by Chris Walton.)
+ Move America/Nipigon, America/Rainy_River, and America/Thunder_Bay
+ to 'backzone'; backward-compatibility links still work, albeit
+ with some different timestamps before November 2005.
+
+ Changes to code
+
+ zic now supports links to links regardless of input line order.
+ For example, if Australia/Sydney is a Zone, the lines
+ Link Australia/Canberra Australia/ACT
+ Link Australia/Sydney Australia/Canberra
+ now work correctly, even though the shell commands
+ ln Australia/Canberra Australia/ACT
+ ln Australia/Sydney Australia/Canberra
+ would fail because the first command attempts to use a link
+ Australia/Canberra that does not exist until after the second
+ command is executed. Previously, zic had unspecified behavior if
+ a Link line's target was another link, and zic often misbehaved if
+ a Link line's target was a later Link line.
+
+ Fix line number in zic's diagnostic for a link to a link.
+
+ Fix a bug that caused localtime to mishandle timestamps starting
+ in the year 2438 when reading data generated by 'zic -b fat' when
+ distant-future DST transitions occur at times given in standard
+ time or in UT, not the usual case of local time. This occurs when
+ the corresponding .zi Rule lines specify DST transitions with TO
+ columns of 'max' and AT columns that end in 's' or 'u'. The
+ number 2438 comes from the 32-bit limit in the year 2038, plus the
+ 400-year Gregorian cycle. (Problem reported by Bradley White.)
+
+ On glibc 2.34 and later, which optionally supports 64-bit time_t
+ on platforms like x86 where time_t was traditionally 32 bits,
+ default time_t to 64 instead of 32 bits. This lets functions like
+ localtime support timestamps after the year 2038, and fixes
+ year-2038 problems in zic when accessing files dated after 2038.
+ To continue to limit time_t to 32 bits on these platforms, use
+ "make CFLAGS='-D_TIME_BITS=32'".
+
+ In C code, do not enable large-file support on platforms like AIX
+ and macOS that no longer need it now that tzcode does not use
+ off_t or related functions like 'stat'. Large-file support is
+ still enabled by default on GNU/Linux, as it is needed for 64-bit
+ time_t support.
+
+ In C code, prefer C23 keywords to pre-C23 macros for alignof,
+ bool, false, and true. Also, use the following C23 features if
+ available: __has_include, unreachable.
+
+ zic no longer works around Qt bug 53071, as the relevant Qt
+ releases have been out of support since 2019. This change affects
+ only fat TZif files, as thin files never had the workaround.
+
+ zdump no longer modifies the environ vector when compiled on
+ platforms lacking tm_zone or when compiled with -DUSE_LTZ=0.
+ This avoid undefined behavior on POSIX platforms.
+
+
+Release 2022e - 2022-10-11 11:13:02 -0700
+
+ Briefly:
+ Jordan and Syria switch from +02/+03 with DST to year-round +03.
+
+ Changes to future timestamps
+
+ Jordan and Syria are abandoning the DST regime and are changing to
+ permanent +03, so they will not fall back from +03 to +02 on
+ 2022-10-28. (Thanks to Steffen Thorsen and Issam Al-Zuwairi.)
+
+ Changes to past timestamps
+
+ On 1922-01-01 Tijuana adopted standard time at 00:00, not 01:00.
+
+ Changes to past time zone abbreviations and DST flags
+
+ The temporary advancement of clocks in central Mexico in summer
+ 1931 is now treated as daylight saving time, instead of as two
+ changes to standard time.
+
+
+Release 2022d - 2022-09-23 12:02:57 -0700
+
+ Briefly:
+ Palestine transitions are now Saturdays at 02:00.
+ Simplify three Ukraine zones into one.
+
+ Changes to future timestamps
+
+ Palestine now springs forward and falls back at 02:00 on the
+ first Saturday on or after March 24 and October 24, respectively.
+ This means 2022 falls back 10-29 at 02:00, not 10-28 at 01:00.
+ (Thanks to Heba Hamad.)
+
+ Changes to past timestamps
+
+ Simplify three Ukraine zones to one, since the post-1970
+ differences seem to have been imaginary. Move Europe/Uzhgorod and
+ Europe/Zaporozhye to 'backzone'; backward-compatibility links
+ still work, albeit with different timestamps before October 1991.
+
+
+Release 2022c - 2022-08-15 17:47:18 -0700
+
+ Briefly:
+ Work around awk bug in FreeBSD, macOS, etc.
+ Improve tzselect on intercontinental Zones.
+
+ Changes to code
+
+ Work around a bug in onetrueawk that broke commands like
+ 'make traditional_tarballs' on FreeBSD, macOS, etc.
+ (Problem reported by Deborah Goldsmith.)
+
+ Add code to tzselect that uses experimental structured comments in
+ zone1970.tab to clarify whether Zones like Africa/Abidjan and
+ Europe/Istanbul cross continent or ocean boundaries.
+ (Inspired by a problem reported by Peter Krefting.)
+
+ Fix bug with 'zic -d /a/b/c' when /a is unwritable but the
+ directory /a/b already exists.
+
+ Remove zoneinfo2tdf.pl, as it was unused and triggered false
+ malware alarms on some email servers.
+
+
+Release 2022b - 2022-08-10 15:38:32 -0700
+
+ Briefly:
+ Chile's DST is delayed by a week in September 2022.
+ Iran no longer observes DST after 2022.
+ Rename Europe/Kiev to Europe/Kyiv.
+ New zic -R option
+ Vanguard form now uses %z.
+ Finish moving duplicate-since-1970 zones to 'backzone'.
+ New build option PACKRATLIST
+ New tailored_tarballs target, replacing rearguard_tarballs
+
+ Changes to future timestamps
+
+ Chile's 2022 DST start is delayed from September 4 to September 11.
+ (Thanks to Juan Correa.)
+
+ Iran plans to stop observing DST permanently, after it falls back
+ on 2022-09-21. (Thanks to Ali Mirjamali.)
+
+ Changes to past timestamps
+
+ Finish moving to 'backzone' the location-based zones whose
+ timestamps since 1970 are duplicates; adjust links accordingly.
+ This change ordinarily affects only pre-1970 timestamps, and with
+ the new PACKRATLIST option it does not affect any timestamps.
+ In this round the affected zones are Antarctica/Vostok,
+ Asia/Brunei, Asia/Kuala_Lumpur, Atlantic/Reykjavik,
+ Europe/Amsterdam, Europe/Copenhagen, Europe/Luxembourg,
+ Europe/Monaco, Europe/Oslo, Europe/Stockholm, Indian/Christmas,
+ Indian/Cocos, Indian/Kerguelen, Indian/Mahe, Indian/Reunion,
+ Pacific/Chuuk, Pacific/Funafuti, Pacific/Majuro, Pacific/Pohnpei,
+ Pacific/Wake and Pacific/Wallis, and the affected links are
+ Arctic/Longyearbyen, Atlantic/Jan_Mayen, Iceland, Pacific/Ponape,
+ Pacific/Truk, and Pacific/Yap.
+
+ From fall 1994 through fall 1995, Shanks wrote that Crimea's
+ DST transitions were at 02:00 standard time, not at 00:00.
+ (Thanks to Michael Deckers.)
+
+ Iran adopted standard time in 1935, not 1946. In 1977 it observed
+ DST from 03-21 23:00 to 10-20 24:00; its 1978 transitions were on
+ 03-24 and 08-05, not 03-20 and 10-20; and its spring 1979
+ transition was on 05-27, not 03-21.
+ (Thanks to Roozbeh Pournader and Francis Santoni.)
+
+ Chile's observance of -04 from 1946-08-29 through 1947-03-31 was
+ considered DST, not standard time. Santiago and environs had moved
+ their clocks back to rejoin the rest of mainland Chile; put this
+ change at the end of 1946-08-28. (Thanks to Michael Deckers.)
+
+ Some old, small clock transitions have been removed, as people at
+ the time did not change their clocks. This affects Asia/Hong_Kong
+ in 1904, Asia/Ho_Chi_Minh in 1906, and Europe/Dublin in 1880.
+
+ Changes to zone name
+
+ Rename Europe/Kiev to Europe/Kyiv, as "Kyiv" is more common in
+ English now. Spelling of other names in Ukraine has not yet
+ demonstrably changed in common English practice so for now these
+ names retain old spellings, as in other countries (e.g.,
+ Europe/Prague not "Praha", and Europe/Sofia not "Sofiya").
+
+ Changes to code
+
+ zic has a new option '-R @N' to output explicit transitions < N.
+ (Need suggested by Almaz Mingaleev.)
+
+ 'zic -r @N' no longer outputs bad data when N < first transition.
+ (Problem introduced in 2021d and reported by Peter Krefting.)
+
+ zic now checks its input for NUL bytes and unterminated lines, and
+ now supports input line lengths up to 2048 (not 512) bytes.
+
+ gmtime and related code now use the abbreviation "UTC" not "GMT".
+ POSIX is being revised to require this.
+
+ When tzset and related functions set vestigial static variables
+ like tzname, they now prefer specified timestamps to unspecified ones.
+ (Problem reported by Almaz Mingaleev.)
+
+ zic no longer complains "can't determine time zone abbreviation to
+ use just after until time" when a transition to a new standard
+ time occurs simultaneously with the first DST fallback transition.
+
+ Changes to build procedure
+
+ Source data in vanguard form now uses the %z notation, introduced
+ in release 2015f. For example, for America/Sao_Paulo vanguard
+ form contains the zone continuation line "-3:00 Brazil %z", which
+ is simpler and more reliable than the line "-3:00 Brazil -03/-02"
+ used in main and rearguard forms. The plan is for the main form
+ to use %z eventually; in the meantime maintainers of zi parsers
+ are encouraged to test the parsers on vanguard.zi.
+
+ The Makefile has a new PACKRATLIST option to select a subset of
+ 'backzone'. For example, 'make PACKRATDATA=backzone
+ PACKRATLIST=zone.tab' now generates TZif files identical to those
+ of the global-tz project.
+
+ The Makefile has a new tailored_tarballs target for generating
+ special-purpose tarballs. It generalizes and replaces the
+ rearguard_tarballs target and related targets and macros, which
+ are now obsolescent.
+
+ 'make install' now defaults LOCALTIME to Factory not GMT,
+ which means the default abbreviation is now "-00" not "GMT".
+
+ Remove the posix_packrat target, marked obsolescent in 2016a.
+
+
+Release 2022a - 2022-03-15 23:02:01 -0700
+
+ Briefly:
+ Palestine will spring forward on 2022-03-27, not -03-26.
+ zdump -v now outputs better failure indications.
+ Bug fixes for code that reads corrupted TZif data.
+
+ Changes to future timestamps
+
+ Palestine will spring forward on 2022-03-27, not 2022-03-26.
+ (Thanks to Heba Hamad.) Predict future transitions for first
+ Sunday >= March 25. Additionally, predict fallbacks to be the first
+ Friday on or after October 23, not October's last Friday, to be more
+ consistent with recent practice. The first differing fallback
+ prediction is on 2025-10-24, not 2025-10-31.
+
+ Changes to past timestamps
+
+ From 1992 through spring 1996, Ukraine's DST transitions were at
+ 02:00 standard time, not at 01:00 UTC. (Thanks to Alois Treindl.)
+
+ Chile's Santiago Mean Time and its LMT precursor have been adjusted
+ eastward by 1 second to align with past and present law.
+
+ Changes to commentary
+
+ Add several references for Chile's 1946/1947 transitions, some of
+ which only affected portions of the country.
+
+ Changes to code
+
+ Fix bug when mktime gets confused by truncated TZif files with
+ unspecified local time. (Problem reported by Almaz Mingaleev.)
+
+ Fix bug when 32-bit time_t code reads malformed 64-bit TZif data.
+ (Problem reported by Christos Zoulas.)
+
+ When reading a version 2 or later TZif file, the TZif reader now
+ validates the version 1 header and data block only enough to skip
+ over them, as recommended by RFC 8536 section 4. Also, the TZif
+ reader no longer mistakenly attempts to parse a version 1 TZIf
+ file header as a TZ string.
+
+ zdump -v now outputs "(localtime failed)" and "(gmtime failed)"
+ when local time and UT cannot be determined for a timestamp.
+
+ Changes to build procedure
+
+ Distribution tarballs now use standard POSIX.1-1988 ustar format
+ instead of GNU format. Although the formats are almost identical
+ for these tarballs, ustar headers' magic fields contain "ustar"
+ instead of "ustar ", and their version fields contain "00" instead
+ of " ". The two formats are planned to diverge more significantly
+ for tzdb releases after 2242-03-16 12:56:31 UTC, when the ustar
+ format becomes obsolete and the tarballs switch to pax format, an
+ extension of ustar. For details about these formats, please see
+ "pax - portable archive interchange", IEEE Std 1003.1-2017,
+ <https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13>.
+
+
+Release 2021e - 2021-10-21 18:41:00 -0700
+
+ Changes to future timestamps
+
+ Palestine will fall back 10-29 (not 10-30) at 01:00.
+ (Thanks to P Chan and Heba Hemad.)
+
+
+Release 2021d - 2021-10-15 13:48:18 -0700
+
+ Briefly:
+ Fiji suspends DST for the 2021/2022 season.
+ 'zic -r' marks unspecified timestamps with "-00".
+
+ Changes to future timestamps
+
+ Fiji will suspend observance of DST for the 2021/2022 season.
+ Assume for now that it will return next year. (Thanks to Jashneel
+ Kumar and P Chan.)
+
+ Changes to code
+
+ 'zic -r' now uses "-00" time zone abbreviations for intervals
+ with UT offsets that are unspecified due to -r truncation.
+ This implements a change in draft Internet RFC 8536bis.
+
+
+Release 2021c - 2021-10-01 14:21:49 -0700
+
+ Briefly:
+ Revert most 2021b changes to 'backward'.
+ Fix 'zic -b fat' bug in pre-1970 32-bit data.
+ Fix two Link line typos.
+ Distribute SECURITY file.
+
+ This release is intended as a bugfix release, to fix compatibility
+ problems and typos reported since 2021b was released.
+
+ Changes to Link directives
+
+ Revert almost all of 2021b's changes to the 'backward' file,
+ by moving Link directives back to where they were in 2021a.
+ Although 'zic' doesn't care which source file contains a Link
+ directive, some downstream uses ran into trouble with the move.
+ (Problem reported by Stephen Colebourne for Joda-Time.)
+
+ Fix typo that linked Atlantic/Jan_Mayen to the wrong location
+ (problem reported by Chris Walton).
+
+ Fix 'backzone' typo that linked America/Virgin to the wrong
+ location (problem reported by Michael Deckers).
+
+ Changes to code
+
+ Fix a bug in 'zic -b fat' that caused old timestamps to be
+ mishandled in 32-bit-only readers (problem reported by Daniel
+ Fischer).
+
+ Changes to documentation
+
+ Distribute the SECURITY file (problem reported by Andreas Radke).
+
+
+Release 2021b - 2021-09-24 16:23:00 -0700
+
+ Briefly:
+ Jordan now starts DST on February's last Thursday.
+ Samoa no longer observes DST.
+ Merge more location-based Zones whose timestamps agree since 1970.
+ Move some backward-compatibility links to 'backward'.
+ Rename Pacific/Enderbury to Pacific/Kanton.
+ Correct many pre-1993 transitions in Malawi, Portugal, etc.
+ zic now creates each output file or link atomically.
+ zic -L no longer omits the POSIX TZ string in its output.
+ zic fixes for truncation and leap second table expiration.
+ zic now follows POSIX for TZ strings using all-year DST.
+ Fix some localtime crashes and bugs in obscure cases.
+ zdump -v now outputs more-useful boundary cases.
+ tzfile.5 better matches a draft successor to RFC 8536.
+ A new file SECURITY.
+
+ This release is prompted by recent announcements by Jordan and Samoa.
+ It incorporates many other changes that had accumulated since 2021a.
+ However, it omits most proposed changes that merged all Zones
+ agreeing since 1970, as concerns were raised about doing too many of
+ these changes at once. It does keeps some of these changes in the
+ interest of making tzdb more equitable one step at a time; see
+ "Merge more location-based Zones" below.
+
+ Changes to future timestamps
+
+ Jordan now starts DST on February's last Thursday.
+ (Thanks to Steffen Thorsen.)
+
+ Samoa no longer observes DST. (Thanks to Geoffrey D. Bennett.)
+
+ Changes to zone name
+
+ Rename Pacific/Enderbury to Pacific/Kanton. When we added
+ Enderbury in 1993, we did not know that it is uninhabited and that
+ Kanton (population two dozen) is the only inhabited location in
+ that timezone. The old name is now a backward-compatibility link.
+
+ Changes to past timestamps
+
+ Correct many pre-1993 transitions, fixing entries originally
+ derived from Shanks, Whitman, and Mundell. The fixes include:
+ - Barbados: standard time was introduced in 1911, not 1932; and
+ DST was observed in 1942-1944
+ - Cook Islands: In 1899 they switched from east to west of GMT,
+ celebrating Christmas for two days. They (and Niue) switched
+ to standard time in 1952, not 1901.
+ - Guyana: corrected LMT for Georgetown; the introduction of
+ standard time in 1911, not 1915; and corrections to 1975 and
+ 1992 transitions
+ - Kanton: uninhabited before 1937-08-31
+ - Niue: only observed -11:20 from 1952 through 1964, then went to
+ -11 instead of -11:30
+ - Portugal: DST was observed in 1950
+ - Tonga: corrected LMT; the introduction of standard time in 1945,
+ not 1901; and corrections to the transition from +12:20 to +13
+ in 1961, not 1941
+ Additional fixes to entries in the 'backzone' file include:
+ - Enderbury: inhabited only 1860/1885 and 1938-03-06/1942-02-09
+ - The Gambia: 1933 and 1942 transitions
+ - Malawi: several 1911 through 1925 transitions
+ - Sierra Leone: several 1913 through 1941 transitions, and DST
+ was NOT observed in 1957 through 1962
+ (Thanks to P Chan, Michael Deckers, Alexander Krivenyshev and
+ Alois Treindl.)
+
+ Merge more location-based Zones whose timestamps agree since 1970,
+ as pre-1970 timestamps are out of scope. This is part of a
+ process that has been ongoing since 2013. This does not affect
+ post-1970 timestamps, and timezone historians who build with 'make
+ PACKRATDATA=backzone' should see no changes to pre-1970 timestamps.
+ When merging, keep the most-populous location's data, and move
+ data for other locations to 'backzone' with a backward
+ link in 'backward'. For example, move America/Creston data to
+ 'backzone' with a link in 'backward' from America/Phoenix because
+ the two timezones' timestamps agree since 1970; this change
+ affects some pre-1968 timestamps in America/Creston because
+ Creston and Phoenix disagreed before 1968. The affected Zones
+ are Africa/Accra, America/Atikokan, America/Blanc-Sablon,
+ America/Creston, America/Curacao, America/Nassau,
+ America/Port_of_Spain, Antarctica/DumontDUrville, and
+ Antarctica/Syowa.
+
+ Changes to maintenance procedure
+
+ The new file SECURITY covers how to report security-related bugs.
+
+ Several backward-compatibility links have been moved to the
+ 'backward' file. These links, which range from Africa/Addis_Ababa
+ to Pacific/Saipan, are only for compatibility with now-obsolete
+ guidelines suggesting an entry for every ISO 3166 code.
+ The intercontinental convenience links Asia/Istanbul and
+ Europe/Nicosia have also been moved to 'backward'.
+
+ Changes to code
+
+ zic now creates each output file or link atomically,
+ possibly by creating a temporary file and then renaming it.
+ This avoids races where a TZ setting would temporarily stop
+ working while zic was installing a replacement file or link.
+
+ zic -L no longer omits the POSIX TZ string in its output.
+ Starting with 2020a, zic -L truncated its output according to the
+ "Expires" directive or "#expires" comment in the leapseconds file.
+ The resulting TZif files omitted daylight saving transitions after
+ the leap second table expired, which led to far less-accurate
+ predictions of times after the expiry. Although future timestamps
+ cannot be converted accurately in the presence of leap seconds, it
+ is more accurate to convert near-future timestamps with a few
+ seconds error than with an hour error, so zic -L no longer
+ truncates output in this way.
+
+ Instead, when zic -L is given the "Expires" directive, it now
+ outputs the expiration by appending a no-change entry to the leap
+ second table. Although this should work well with most TZif
+ readers, it does not conform to Internet RFC 8536 and some pickier
+ clients (including tzdb 2017c through 2021a) reject it, so
+ "Expires" directives are currently disabled by default. To enable
+ them, set the EXPIRES_LINE Makefile variable. If a TZif file uses
+ this new feature it is marked with a new TZif version number 4,
+ a format intended to be documented in a successor to RFC 8536.
+
+ zic -L LEAPFILE -r @LO no longer generates an invalid TZif file
+ that omits leap second information for the range LO..B when LO
+ falls between two leap seconds A and B. Instead, it generates a
+ TZif version 4 file that represents the previously-missing
+ information.
+
+ The TZif reader now allows the leap second table to begin with a
+ correction other than -1 or +1, and to contain adjacent
+ transitions with equal corrections. This supports TZif version 4.
+
+ The TZif reader now lets leap seconds occur less than 28 days
+ apart. This supports possible future TZif extensions.
+
+ Fix bug that caused 'localtime' etc. to crash when TZ was
+ set to a all-year DST string like "EST5EDT4,0/0,J365/25" that does
+ not conform to POSIX but does conform to Internet RFC 8536.
+
+ Fix another bug that caused 'localtime' etc. to crash when TZ was
+ set to a POSIX-conforming but unusual TZ string like
+ "EST5EDT4,0/0,J365/0", where almost all the year is DST.
+
+ Fix yet another bug that caused 'localtime' etc. to mishandle slim
+ TZif files containing leap seconds after the last explicit
+ transition in the table, or when handling far-future timestamps
+ in slim TZif files lacking leap seconds.
+
+ Fix localtime misbehavior involving positive leap seconds.
+ This change affects only behavior for "right" system time,
+ which contains leap seconds, and only if the UT offset is
+ not a multiple of 60 seconds when a positive leap second occurs.
+ (No such timezone exists in tzdb, luckily.) Without the fix,
+ the timestamp was ambiguous during a positive leap second.
+ With the fix, any seconds occurring after a positive leap second
+ and within the same localtime minute are counted through 60, not
+ through 59; their UT offset (tm_gmtoff) is the same as before.
+ Here is how the fix affects timestamps in a timezone with UT
+ offset +01:23:45 (5025 seconds) and with a positive leap second at
+ 1972-06-30 23:59:60 UTC (78796800):
+
+ time_t without the fix with the fix
+ 78796800 1972-07-01 01:23:45 1972-07-01 01:23:45 (leap second)
+ 78796801 1972-07-01 01:23:45 1972-07-01 01:23:46
+ ...
+ 78796815 1972-07-01 01:23:59 1972-07-01 01:23:60
+ 78796816 1972-07-01 01:24:00 1972-07-01 01:24:00
+
+ Fix an unlikely bug that caused 'localtime' etc. to misbehave if
+ civil time changes a few seconds before time_t wraps around, when
+ leap seconds are enabled.
+
+ Fix bug in zic -r; in some cases, the dummy time type after the
+ last time transition disagreed with the TZ string, contrary to
+ Internet RFC 8563 section 3.3.
+
+ Fix a bug with 'zic -r @X' when X is a negative leap second that
+ has a nonnegative correction. Without the fix, the output file
+ was truncated so that X appeared to be a positive leap second.
+ Fix a similar, even-less-likely bug when truncating at a positive
+ leap second that has a nonpositive correction.
+
+ zic -r now reports an error if given rolling leap seconds, as this
+ usage has never generally worked and is evidently unused.
+
+ zic now generates a POSIX-conforming TZ string for TZif files
+ where all-year DST is predicted for the indefinite future.
+ For example, for all-year Eastern Daylight Time, zic now generates
+ "XXX3EDT4,0/0,J365/23" where it previously generated
+ "EST5EDT,0/0,J365/25" or "". (Thanks to Michael Deckers for
+ noting the possibility of POSIX conformance.)
+
+ zic.c no longer requires sys/wait.h (thanks to spazmodius for
+ noting it wasn't needed).
+
+ When reading slim TZif files, zdump no longer mishandles leap
+ seconds on the rare platforms where time_t counts leap seconds,
+ fixing a bug introduced in 2014g.
+
+ zdump -v now outputs timestamps at boundaries of what localtime
+ and gmtime can represent, instead of the less-useful timestamps
+ one day after the minimum and one day before the maximum.
+ (Thanks to Arthur David Olson for prototype code, and to Manuela
+ Friedrich for debugging help.)
+
+ zdump's -c and -t options are now consistently inclusive for the
+ lower time bound and exclusive for the upper. Formerly they were
+ inconsistent. (Confusion noted by Martin Burnicki.)
+
+ Changes to build procedure
+
+ You can now compile with -DHAVE_MALLOC_ERRNO=0 to port to
+ non-POSIX hosts where malloc doesn't set errno.
+ (Problem reported by Jan Engelhardt.)
+
+ Changes to documentation
+
+ tzfile.5 better matches a draft successor to RFC 8536
+ <https://datatracker.ietf.org/doc/draft-murchison-rfc8536bis/01/>.
+
+
+Release 2021a - 2021-01-24 10:54:57 -0800
+
+ Changes to future timestamps
+
+ South Sudan changes from +03 to +02 on 2021-02-01 at 00:00.
+ (Thanks to Steffen Thorsen.)
+
+
+Release 2020f - 2020-12-29 00:17:46 -0800
+
+ Change to build procedure
+
+ 'make rearguard_tarballs' no longer generates a bad rearguard.zi,
+ fixing a 2020e bug. (Problem reported by Deborah Goldsmith.)
+
+
+Release 2020e - 2020-12-22 15:14:34 -0800
+
+ Briefly:
+ Volgograd switches to Moscow time on 2020-12-27 at 02:00.
+
+ Changes to future timestamps
+
+ Volgograd changes time zone from +04 to +03 on 2020-12-27 at 02:00.
+ (Thanks to Alexander Krivenyshev and Stepan Golosunov.)
+
+ Changes to past timestamps
+
+ Correct many pre-1986 transitions, fixing entries originally
+ derived from Shanks. The fixes include:
+ - Australia: several 1917 through 1971 transitions
+ - The Bahamas: several 1941 through 1945 transitions
+ - Bermuda: several 1917 through 1956 transitions
+ - Belize: several 1942 through 1968 transitions
+ - Ghana: several 1915 through 1956 transitions
+ - Israel and Palestine: several 1940 through 1985 transitions
+ - Kenya and adjacent: several 1908 through 1960 transitions
+ - Nigeria and adjacent: correcting LMT in Lagos, and several 1905
+ through 1919 transitions
+ - Seychelles: the introduction of standard time in 1907, not 1906
+ - Vanuatu: DST in 1973-1974, and a corrected 1984 transition
+ (Thanks to P Chan.)
+
+ Because of the Australia change, Australia/Currie (King Island) is
+ no longer needed, as it is identical to Australia/Hobart for all
+ timestamps since 1970 and was therefore created by mistake.
+ Australia/Currie has been moved to the 'backward' file and its
+ corrected data moved to the 'backzone' file.
+
+ Changes to past time zone abbreviations and DST flags
+
+ To better match legislation in Turks and Caicos, the 2015 shift to
+ year-round observance of -04 is now modeled as AST throughout before
+ returning to Eastern Time with US DST in 2018, rather than as
+ maintaining EDT until 2015-11-01. (Thanks to P Chan.)
+
+ Changes to documentation
+
+ The zic man page now documents zic's coalescing of transitions
+ when a zone falls back just before DST springs forward.
+
+
+Release 2020d - 2020-10-21 11:24:13 -0700
+
+ Briefly:
+ Palestine ends DST earlier than predicted, on 2020-10-24.
+
+ Changes to past and future timestamps
+
+ Palestine ends DST on 2020-10-24 at 01:00, instead of 2020-10-31
+ as previously predicted (thanks to Sharef Mustafa.) Its
+ 2019-10-26 fall-back was at 00:00, not 01:00 (thanks to Steffen
+ Thorsen.) Its 2015-10-23 transition was at 01:00 not 00:00, and
+ its spring 2020 transition was on March 28 at 00:00, not March 27
+ (thanks to Pierre Cashon.) This affects Asia/Gaza and
+ Asia/Hebron. Assume future spring and fall transitions will be on
+ the Saturday preceding the last Sunday of March and October,
+ respectively.
+
+
+Release 2020c - 2020-10-16 11:15:53 -0700
+
+ Briefly:
+ Fiji starts DST later than usual, on 2020-12-20.
+
+ Changes to future timestamps
+
+ Fiji will start DST on 2020-12-20, instead of 2020-11-08 as
+ previously predicted. DST will still end on 2021-01-17.
+ (Thanks to Raymond Kumar and Alan Mintz.) Assume for now that
+ the later-than-usual start date is a one-time departure from the
+ recent pattern.
+
+ Changes to build procedure
+
+ Rearguard tarballs now contain an empty file pacificnew.
+ Some older downstream software expects this file to exist.
+ (Problem reported by Mike Cullinan.)
+
+
+Release 2020b - 2020-10-06 18:35:04 -0700
+
+ Briefly:
+ Revised predictions for Morocco's changes starting in 2023.
+ Canada's Yukon changes to -07 on 2020-11-01, not 2020-03-08.
+ Macquarie Island has stayed in sync with Tasmania since 2011.
+ Casey, Antarctica is at +08 in winter and +11 in summer.
+ zic no longer supports -y, nor the TYPE field of Rules.
+
+ Changes to future timestamps
+
+ Morocco's spring-forward after Ramadan is now predicted to occur
+ no sooner than two days after Ramadan, instead of one day.
+ (Thanks to Milamber.) The first altered prediction is for 2023,
+ now predicted to spring-forward on April 30 instead of April 23.
+
+ Changes to past and future timestamps
+
+ Casey Station, Antarctica has been using +08 in winter and +11 in
+ summer since 2018. The most recent transition from +08 to +11 was
+ 2020-10-04 00:01. Also, Macquarie Island has been staying in
+ sync with Tasmania since 2011. (Thanks to Steffen Thorsen.)
+
+ Changes to past and future time zone abbreviations and DST flags
+
+ Canada's Yukon, represented by America/Whitehorse and
+ America/Dawson, changes its time zone rules from -08/-07 to
+ permanent -07 on 2020-11-01, not on 2020-03-08 as 2020a had it.
+ This change affects only the time zone abbreviation (MST vs PDT)
+ and daylight saving flag for the period between the two dates.
+ (Thanks to Andrew G. Smith.)
+
+ Changes to past timestamps
+
+ Correct several transitions for Hungary for 1918/1983.
+ For example, the 1983-09-25 fall-back was at 01:00, not 03:00.
+ (Thanks to Géza Nyáry.) Also, the 1890 transition to standard
+ time was on 11-01, not 10-01 (thanks to Michael Deckers).
+
+ The 1891 French transition was on March 16, not March 15. The
+ 1911-03-11 French transition was at midnight, not a minute later.
+ Monaco's transitions were on 1892-06-01 and 1911-03-29, not
+ 1891-03-15 and 1911-03-11. (Thanks to Michael Deckers.)
+
+ Changes to code
+
+ Support for zic's long-obsolete '-y YEARISTYPE' option has been
+ removed and, with it, so has support for the TYPE field in Rule
+ lines, which is now reserved for compatibility with earlier zic.
+ These features were previously deprecated in release 2015f.
+ (Thanks to Tim Parenti.)
+
+ zic now defaults to '-b slim' instead of to '-b fat'.
+
+ zic's new '-l -' and '-p -' options uninstall any existing
+ localtime and posixrules files, respectively.
+
+ The undocumented and ineffective tzsetwall function has been
+ removed.
+
+ Changes to build procedure
+
+ The Makefile now defaults POSIXRULES to '-', so the posixrules
+ feature (obsolete as of 2019b) is no longer installed by default.
+
+ Changes to documentation and commentary
+
+ The long-obsolete files pacificnew, systemv, and yearistype.sh have
+ been removed from the distribution. (Thanks to Tim Parenti.)
+
+
+Release 2020a - 2020-04-23 16:03:47 -0700
+
+ Briefly:
+ Morocco springs forward on 2020-05-31, not 2020-05-24.
+ Canada's Yukon advanced to -07 year-round on 2020-03-08.
+ America/Nuuk renamed from America/Godthab.
+ zic now supports expiration dates for leap second lists.
+
+ Changes to future timestamps
+
+ Morocco's second spring-forward transition in 2020 will be May 31,
+ not May 24 as predicted earlier. (Thanks to Semlali Naoufal.)
+ Adjust future-year predictions to use the first Sunday after the
+ day after Ramadan, not the first Sunday after Ramadan.
+
+ Canada's Yukon, represented by America/Whitehorse and
+ America/Dawson, advanced to -07 year-round, beginning with its
+ spring-forward transition on 2020-03-08, and will not fall back on
+ 2020-11-01. Although a government press release calls this
+ "permanent Pacific Daylight Saving Time", we prefer MST for
+ consistency with nearby Dawson Creek, Creston, and Fort Nelson.
+ (Thanks to Tim Parenti.)
+
+ Changes to past timestamps
+
+ Shanghai observed DST in 1919. (Thanks to Phake Nick.)
+
+ Changes to timezone identifiers
+
+ To reflect current usage in English better, America/Godthab has
+ been renamed to America/Nuuk. A backwards-compatibility link
+ remains for the old name.
+
+ Changes to code
+
+ localtime.c no longer mishandles timestamps after the last
+ transition in a TZif file with leap seconds and with daylight
+ saving time transitions projected into the indefinite future.
+ For example, with TZ='America/Los_Angeles' with leap seconds,
+ zdump formerly reported a DST transition on 2038-03-14
+ from 01:59:32.999... to 02:59:33 instead of the correct transition
+ from 01:59:59.999... to 03:00:00.
+
+ zic -L now supports an Expires line in the leapseconds file, and
+ truncates the TZif output accordingly. This propagates leap
+ second expiration information into the TZif file, and avoids the
+ abovementioned localtime.c bug as well as similar bugs present in
+ many client implementations. If no Expires line is present, zic
+ -L instead truncates the TZif output based on the #expires comment
+ present in leapseconds files distributed by tzdb 2018f and later;
+ however, this usage is obsolescent. For now, the distributed
+ leapseconds file has an Expires line that is commented out, so
+ that the file can be fed to older versions of zic which ignore the
+ commented-out line. Future tzdb distributions are planned to
+ contain a leapseconds file with an Expires line.
+
+ The configuration macros HAVE_TZNAME and USG_COMPAT should now be
+ set to 1 if the system library supports the feature, and 2 if not.
+ As before, these macros are nonzero if tzcode should support the
+ feature, zero otherwise.
+
+ The configuration macro ALTZONE now has the same values with the
+ same meaning as HAVE_TZNAME and USG_COMPAT.
+
+ The code's defense against CRLF in leap-seconds.list is now
+ portable to POSIX awk. (Problem reported by Deborah Goldsmith.)
+
+ Although the undocumented tzsetwall function is not changed in
+ this release, it is now deprecated in preparation for removal in
+ future releases. Due to POSIX requirements, tzsetwall has not
+ worked for some time. Any code that uses it should instead use
+ tzalloc(NULL) or, if portability trumps thread-safety, should
+ unset the TZ environment variable.
+
+ Changes to commentary
+
+ The Îles-de-la-Madeleine and the Listuguj reserve are noted as
+ following America/Halifax, and comments about Yukon's "south" and
+ "north" have been corrected to say "east" and "west". (Thanks to
+ Jeffery Nichols.)
+
+
+Release 2019c - 2019-09-11 08:59:48 -0700
+
+ Briefly:
+ Fiji observes DST from 2019-11-10 to 2020-01-12.
+ Norfolk Island starts observing Australian-style DST.
+
+ Changes to future timestamps
+
+ Fiji's next DST transitions will be 2019-11-10 and 2020-01-12
+ instead of 2019-11-03 and 2020-01-19. (Thanks to Raymond Kumar.)
+ Adjust future guesses accordingly.
+
+ Norfolk Island will observe Australian-style DST starting in
+ spring 2019. The first transition is on 2019-10-06. (Thanks to
+ Kyle Czech and Michael Deckers.)
+
+ Changes to past timestamps
+
+ Many corrections to time in Turkey from 1940 through 1985.
+ (Thanks to Oya Vulaş via Alois Treindl, and to Kıvanç Yazan.)
+
+ The Norfolk Island 1975-03-02 transition was at 02:00 standard
+ time, not 02:00 DST. (Thanks to Michael Deckers.)
+
+ South Korea observed DST from 1948 through 1951. Although this
+ info was supposed to appear in release 2014j, a typo inadvertently
+ suppressed the change. (Thanks to Alois Treindl.)
+
+ Detroit observed DST in 1967 and 1968 following the US DST rules,
+ except that its 1967 DST began on June 14 at 00:01. (Thanks to
+ Alois Treindl for pointing out that the old data entries were
+ probably wrong.)
+
+ Fix several errors in pre-1970 transitions in Perry County, IN.
+ (Thanks to Alois Treindl for pointing out the 1967/9 errors.)
+
+ Edmonton did not observe DST in 1967 or 1969. In 1946 Vancouver
+ ended DST on 09-29 not 10-13, and Vienna ended DST on 10-07 not
+ 10-06. In 1945 Königsberg (now Kaliningrad) switched from +01/+02
+ to +02/+03 on 04-10 not 01-01, and its +02/+03 is abbreviated
+ EET/EEST, not CET/CEST. (Thanks to Alois Treindl.) In 1946
+ Königsberg switched to +03 on 04-07 not 01-01.
+
+ In 1946 Louisville switched from CST to CDT on 04-28 at 00:01, not
+ 01-01 at 00:00. (Thanks to Alois Treindl and Michael Deckers.)
+ Also, it switched from CST to CDT on 1950-04-30, not 1947-04-27.
+
+ The 1892-05-01 transition in Brussels was at 00:17:30, not at noon.
+ (Thanks to Michael Deckers.)
+
+ Changes to past time zone abbreviations and DST flags
+
+ Hong Kong Winter Time, observed from 1941-10-01 to 1941-12-25,
+ is now flagged as DST and is abbreviated HKWT not HKT.
+
+ Changes to code
+
+ leapseconds.awk now relies only on its input data, rather than
+ also relying on its comments. (Inspired by code from Dennis
+ Ferguson and Chris Woodbury.)
+
+ The code now defends against CRLFs in leap-seconds.list.
+ (Thanks to Brian Inglis and Chris Woodbury.)
+
+ Changes to documentation and commentary
+
+ theory.html discusses leap seconds. (Thanks to Steve Summit.)
+
+ Nashville's newspapers dueled about the time of day in the 1950s.
+ (Thanks to John Seigenthaler.)
+
+ Liechtenstein observed Swiss DST in 1941/2.
+ (Thanks to Alois Treindl.)
+
+
+Release 2019b - 2019-07-01 00:09:53 -0700
+
+ Briefly:
+ Brazil no longer observes DST.
+ 'zic -b slim' outputs smaller TZif files; please try it out.
+ Palestine's 2019 spring-forward transition was on 03-29, not 03-30.
+
+ Changes to future timestamps
+
+ Brazil has canceled DST and will stay on standard time indefinitely.
+ (Thanks to Steffen Thorsen, Marcus Diniz, and Daniel Soares de
+ Oliveira.)
+
+ Predictions for Morocco now go through 2087 instead of 2037, to
+ work around a problem on newlib when using TZif files output by
+ zic 2019a or earlier. (Problem reported by David Gauchard.)
+
+ Changes to past and future timestamps
+
+ Palestine's 2019 spring transition was 03-29 at 00:00, not 03-30
+ at 01:00. (Thanks to Sharef Mustafa and Even Scharning.) Guess
+ future transitions to be March's last Friday at 00:00.
+
+ Changes to past timestamps
+
+ Hong Kong's 1941-06-15 spring-forward transition was at 03:00, not
+ 03:30. Its 1945 transition from JST to HKT was on 11-18 at 02:00,
+ not 09-15 at 00:00. In 1946 its spring-forward transition was on
+ 04-21 at 00:00, not the previous day at 03:30. From 1946 through
+ 1952 its fall-back transitions occurred at 04:30, not at 03:30.
+ In 1947 its fall-back transition was on 11-30, not 12-30.
+ (Thanks to P Chan.)
+
+ Changes to past time zone abbreviations
+
+ Italy's 1866 transition to Rome Mean Time was on December 12, not
+ September 22. This affects only the time zone abbreviation for
+ Europe/Rome between those dates. (Thanks to Stephen Trainor and
+ Luigi Rosa.)
+
+ Changes affecting metadata only
+
+ Add info about the Crimea situation in zone1970.tab and zone.tab.
+ (Problem reported by Serhii Demediuk.)
+
+ Changes to code
+
+ zic's new -b option supports a way to control data bloat and to
+ test for year-2038 bugs in software that reads TZif files.
+ 'zic -b fat' and 'zic -b slim' generate larger and smaller output;
+ for example, changing from fat to slim shrinks the Europe/London
+ file from 3648 to 1599 bytes, saving about 56%. Fat and slim
+ files represent the same set of timestamps and use the same TZif
+ format as documented in tzfile(5) and in Internet RFC 8536.
+ Fat format attempts to work around bugs or incompatibilities in
+ older software, notably software that mishandles 64-bit TZif data
+ or uses obsolete TZ strings like "EET-2EEST" that lack DST rules.
+ Slim format is more efficient and does not work around 64-bit bugs
+ or obsolete TZ strings. Currently zic defaults to fat format
+ unless you compile with -DZIC_BLOAT_DEFAULT=\"slim\"; this
+ out-of-the-box default is intended to change in future releases
+ as the buggy software often mishandles timestamps anyway.
+
+ zic no longer treats a set of rules ending in 2037 specially.
+ Previously, zic assumed that such a ruleset meant that future
+ timestamps could not be predicted, and therefore omitted a
+ POSIX-like TZ string in the TZif output. The old behavior is no
+ longer needed for current tzdata, and caused problems with newlib
+ when used with older tzdata (reported by David Gauchard).
+
+ zic no longer generates some artifact transitions. For example,
+ Europe/London no longer has a no-op transition in January 1996.
+
+ Changes to build procedure
+
+ tzdata.zi now assumes zic 2017c or later. This shrinks tzdata.zi
+ by a percent or so.
+
+ Changes to documentation and commentary
+
+ The Makefile now documents the POSIXRULES macro as being obsolete,
+ and similarly, zic's -p POSIXRULES option is now documented as
+ being obsolete. Although the POSIXRULES feature still exists and
+ works as before, in practice it is rarely used for its intended
+ purpose, and it does not work either in the default reference
+ implementation (for timestamps after 2037) or in common
+ implementations such as GNU/Linux (for contemporary timestamps).
+ Since POSIXRULES was designed primarily as a temporary transition
+ facility for System V platforms that died off decades ago, it is
+ being decommissioned rather than institutionalized.
+
+ New info on Bonin Islands and Marcus (thanks to Wakaba and Phake Nick).
+
+
+Release 2019a - 2019-03-25 22:01:33 -0700
+
+ Briefly:
+ Palestine "springs forward" on 2019-03-30 instead of 2019-03-23.
+ Metlakatla "fell back" to rejoin Alaska Time on 2019-01-20 at 02:00.
+
+ Changes to past and future timestamps
+
+ Palestine will not start DST until 2019-03-30, instead of 2019-03-23 as
+ previously predicted. Adjust our prediction by guessing that spring
+ transitions will be between 24 and 30 March, which matches recent practice
+ since 2016. (Thanks to Even Scharning and Tim Parenti.)
+
+ Metlakatla ended its observance of Pacific standard time,
+ rejoining Alaska Time, on 2019-01-20 at 02:00. (Thanks to Ryan
+ Stanley and Tim Parenti.)
+
+ Changes to past timestamps
+
+ Israel observed DST in 1980 (08-02/09-13) and 1984 (05-05/08-25).
+ (Thanks to Alois Treindl and Isaac Starkman.)
+
+ Changes to time zone abbreviations
+
+ Etc/UCT is now a backward-compatibility link to Etc/UTC, instead
+ of being a separate zone that generates the abbreviation "UCT",
+ which nowadays is typically a typo. (Problem reported by Isiah
+ Meadows.)
+
+ Changes to code
+
+ zic now has an -r option to limit the time range of output data.
+ For example, 'zic -r @1000000000' limits the output data to
+ timestamps starting 1000000000 seconds after the Epoch.
+ This helps shrink output size and can be useful for applications
+ not needing the full timestamp history, such as TZDIST truncation;
+ see Internet RFC 8536 section 5.1. (Inspired by a feature request
+ from Christopher Wong, helped along by bug reports from Wong and
+ from Tim Parenti.)
+
+ Changes to documentation
+
+ Mention Internet RFC 8536 (February 2019), which documents TZif.
+
+ tz-link.html now cites tzdata-meta
+ <https://tzdata-meta.timtimeonline.com/>.
+
+
+Release 2018i - 2018-12-30 11:05:43 -0800
+
+ Briefly:
+ São Tomé and Príncipe switches from +01 to +00 on 2019-01-01.
+
+ Changes to future timestamps
+
+ Due to a change in government, São Tomé and Príncipe switches back
+ from +01 to +00 on 2019-01-01 at 02:00. (Thanks to Vadim
+ Nasardinov and Michael Deckers.)
+
+
+Release 2018h - 2018-12-23 17:59:32 -0800
+
+ Briefly:
+ Qyzylorda, Kazakhstan moved from +06 to +05 on 2018-12-21.
+ New zone Asia/Qostanay because Qostanay, Kazakhstan didn't move.
+ Metlakatla, Alaska observes PST this winter only.
+ Guess Morocco will continue to adjust clocks around Ramadan.
+ Add predictions for Iran from 2038 through 2090.
+
+ Changes to future timestamps
+
+ Guess that Morocco will continue to fall back just before and
+ spring forward just after Ramadan, the practice since 2012.
+ (Thanks to Maamar Abdelkader.) This means Morocco will observe
+ negative DST during Ramadan in main and vanguard formats, and in
+ rearguard format it stays in the +00 timezone and observes
+ ordinary DST in all months other than Ramadan. As before, extend
+ this guesswork to the year 2037. As a consequence, Morocco is
+ scheduled to observe three DST transitions in some Gregorian years
+ (e.g., 2033) due to the mismatch between the Gregorian and Islamic
+ calendars.
+
+ The table of exact transitions for Iranian DST has been extended.
+ It formerly cut off before the year 2038 in a nod to 32-bit time_t.
+ It now cuts off before 2091 as there is doubt about how the Persian
+ calendar will treat 2091. This change predicts DST transitions in
+ 2038-9, 2042-3, and 2046-7 to occur one day later than previously
+ predicted. As before, post-cutoff transitions are approximated.
+
+ Changes to past and future timestamps
+
+ Qyzylorda (aka Kyzylorda) oblast in Kazakhstan moved from +06 to
+ +05 on 2018-12-21. This is a zone split as Qostanay (aka
+ Kostanay) did not switch, so create a zone Asia/Qostanay.
+
+ Metlakatla moved from Alaska to Pacific standard time on 2018-11-04.
+ It did not change clocks that day and remains on -08 this winter.
+ (Thanks to Ryan Stanley.) It will revert to the usual Alaska
+ rules next spring, so this change affects only timestamps
+ from 2018-11-04 through 2019-03-10.
+
+ Change to past timestamps
+
+ Kwajalein's 1993-08-20 transition from -12 to +12 was at 24:00,
+ not 00:00. I transcribed the time incorrectly from Shanks.
+ (Thanks to Phake Nick.)
+
+ Nauru's 1979 transition was on 02-10 at 02:00, not 05-01 at 00:00.
+ (Thanks to Phake Nick.)
+
+ Guam observed DST irregularly from 1959 through 1977.
+ (Thanks to Phake Nick.)
+
+ Hong Kong observed DST in 1941 starting 06-15 (not 04-01), then on
+ 10-01 changed standard time to +08:30 (not +08). Its transition
+ back to +08 after WWII was on 1945-09-15, not the previous day.
+ Its 1904-10-30 change took effect at 01:00 +08 (not 00:00 LMT).
+ (Thanks to Phake Nick, Steve Allen, and Joseph Myers.) Also,
+ its 1952 fallback was on 11-02 (not 10-25).
+
+ This release contains many changes to timestamps before 1946 due
+ to Japanese possession or occupation of Pacific/Chuuk,
+ Pacific/Guam, Pacific/Kosrae, Pacific/Kwajalein, Pacific/Majuro,
+ Pacific/Nauru, Pacific/Palau, and Pacific/Pohnpei.
+ (Thanks to Phake Nick.)
+
+ Assume that the Spanish East Indies was like the Philippines and
+ observed American time until the end of 1844. This affects
+ Pacific/Chuuk, Pacific/Kosrae, Pacific/Palau, and Pacific/Pohnpei.
+
+ Changes to past tm_isdst flags
+
+ For the recent Morocco change, the tm_isdst flag should be 1 from
+ 2018-10-27 00:00 to 2018-10-28 03:00. (Thanks to Michael Deckers.)
+ Give a URL to the official decree. (Thanks to Matt Johnson.)
+
+
+Release 2018g - 2018-10-26 22:22:45 -0700
+
+ Briefly:
+ Morocco switches to permanent +01 on 2018-10-28.
+
+ Changes to future timestamps
+
+ Morocco switches from +00/+01 to permanent +01 effective 2018-10-28,
+ so its clocks will not fall back as previously scheduled.
+ (Thanks to Mohamed Essedik Najd and Brian Inglis.)
+
+ Changes to code
+
+ When generating TZif files with leap seconds, zic no longer uses a
+ format that trips up older 32-bit clients, fixing a bug introduced
+ in 2018f. (Reported by Daniel Fischer.) Also, the zic workaround
+ for QTBUG-53071 now also works for TZif files with leap seconds.
+
+ The translator to rearguard format now rewrites the line
+ "Rule Japan 1948 1951 - Sep Sat>=8 25:00 0 S" to
+ "Rule Japan 1948 1951 - Sep Sun>=9 1:00 0 S".
+ This caters to zic before 2007 and to Oracle TZUpdater 2.2.0
+ and earlier. (Reported by Christos Zoulas.)
+
+ Changes to past time zone abbreviations
+
+ Change HDT to HWT/HPT for WWII-era abbreviations in Hawaii.
+ This reverts to 2011h, as the abbreviation change in 2011i was
+ likely inadvertent.
+
+ Changes to documentation
+
+ tzfile.5 has new sections on interoperability issues.
+
+
+Release 2018f - 2018-10-18 00:14:18 -0700
+
+ Briefly:
+ Volgograd moves from +03 to +04 on 2018-10-28.
+ Fiji ends DST 2019-01-13, not 2019-01-20.
+ Most of Chile changes DST dates, effective 2019-04-06.
+
+ Changes to future timestamps
+
+ Volgograd moves from +03 to +04 on 2018-10-28 at 02:00.
+ (Thanks to Alexander Fetisov and Stepan Golosunov.)
+
+ Fiji ends DST 2019-01-13 instead of the 2019-01-20 previously
+ predicted. (Thanks to Raymond Kumar.) Adjust future predictions
+ accordingly.
+
+ Most of Chile will end DST on the first Saturday in April at 24:00 mainland
+ time, and resume DST on the first Saturday in September at 24:00 mainland
+ time. The changes are effective from 2019-04-06, and do not affect the
+ Magallanes region modeled by America/Punta_Arenas. (Thanks to Juan Correa
+ and Tim Parenti.) Adjust future predictions accordingly.
+
+ Changes to past timestamps
+
+ The 2018-05-05 North Korea 30-minute time zone change took place
+ at 23:30 the previous day, not at 00:00 that day.
+
+ China's 1988 spring-forward transition was on April 17, not
+ April 10. Its DST transitions in 1986/91 were at 02:00, not 00:00.
+ (Thanks to P Chan.)
+
+ Fix several issues for Macau before 1992. Macau's pre-1904 LMT
+ was off by 10 s. Macau switched to +08 in 1904 not 1912, and
+ temporarily switched to +09/+10 during World War II. Macau
+ observed DST in 1942/79, not 1961/80, and there were several
+ errors for transition times and dates. (Thanks to P Chan.)
+
+ The 1948-1951 fallback transitions in Japan were at 25:00 on
+ September's second Saturday, not at 24:00. (Thanks to Phake Nick.)
+ zic turns this into 01:00 on the day after September's second
+ Saturday, which is the best that POSIX or C platforms can do.
+
+ Incorporate 1940-1949 Asia/Shanghai DST transitions from a 2014
+ paper by Li Yu, replacing more-questionable data from Shanks.
+
+ Changes to time zone abbreviations
+
+ Use "PST" and "PDT" for Philippine time. (Thanks to Paul Goyette.)
+
+ Changes to code
+
+ zic now always generates TZif files where time type 0 is used for
+ timestamps before the first transition. This simplifies the
+ reading of TZif files and should not affect behavior of existing
+ TZif readers because the same set of time types is used; only
+ their internal indexes may have changed. This affects only the
+ legacy zones EST5EDT, CST6CDT, MST7MDT, PST8PDT, CET, MET, and
+ EET, which previously used nonzero types for these timestamps.
+
+ Because of the type 0 change, zic no longer outputs a dummy
+ transition at time -2**59 (before the Big Bang), as clients should
+ no longer need this to handle historical timestamps correctly.
+ This reverts a change introduced in 2013d and shrinks most TZif
+ files by a few bytes.
+
+ zic now supports negative time-of-day in Rule and Leap lines, e.g.,
+ "Rule X min max - Apr lastSun -6:00 1:00 -" means the transition
+ occurs at 18:00 on the Saturday before the last Sunday in April.
+ This behavior was documented in 2018a but the code did not
+ entirely match the documentation.
+
+ localtime.c no longer requires at least one time type in TZif
+ files that lack transitions or have a POSIX-style TZ string. This
+ future-proofs the code against possible future extensions to the
+ format that would allow TZif files with POSIX-style TZ strings and
+ without transitions or time types.
+
+ A read-access subscript error in localtime.c has been fixed.
+ It could occur only in TZif files with timecnt == 0, something that
+ does not happen in practice now but could happen in future versions.
+
+ localtime.c no longer ignores TZif POSIX-style TZ strings that
+ specify only standard time. Instead, these TZ strings now
+ override the default time type for timestamps after the last
+ transition (or for all timestamps if there are no transitions),
+ just as DST strings specifying DST have always done.
+
+ leapseconds.awk now outputs "#updated" and "#expires" comments,
+ and supports leap seconds at the ends of months other than June
+ and December. (Inspired by suggestions from Chris Woodbury.)
+
+ Changes to documentation
+
+ New restrictions: A Rule name must start with a character that
+ is neither an ASCII digit nor "-" nor "+", and an unquoted name
+ should not use characters in the set "!$%&'()*,/:;<=>?@[\]^`{|}~".
+ The latter restriction makes room for future extensions (a
+ possibility noted by Tom Lane).
+
+ tzfile.5 now documents what time types apply before the first and
+ after the last transition, if any.
+
+ Documentation now uses the spelling "timezone" for a TZ setting
+ that determines timestamp history, and "time zone" for a
+ geographic region currently sharing the same standard time.
+
+ The name "TZif" is now used for the tz binary data format.
+
+ tz-link.htm now mentions the A0 TimeZone Migration utilities.
+ (Thanks to Aldrin Martoq for the link.)
+
+ Changes to build procedure
+
+ New 'make' target 'rearguard_tarballs' to build the rearguard
+ tarball only. This is a convenience on platforms that lack lzip
+ if you want to build the rearguard tarball. (Problem reported by
+ Deborah Goldsmith.)
+
+ tzdata.zi is now more stable from release to release. (Problem
+ noted by Tom Lane.) It is also a bit shorter.
+
+ tzdata.zi now can contain comment lines documenting configuration
+ information, such as which data format was selected, which input
+ files were used, and how leap seconds are treated. (Problems
+ noted by Lester Caine and Brian Inglis.) If the Makefile defaults
+ are used these comment lines are absent, for backward
+ compatibility. A redistributor intending to alter its copy of the
+ files should also append "-LABEL" to the 'version' file's first
+ line, where "LABEL" identifies the redistributor's change.
+
+
+Release 2018e - 2018-05-01 23:42:51 -0700
+
+ Briefly:
+
+ North Korea switches back to +09 on 2018-05-05.
+ The main format uses negative DST again, for Ireland etc.
+ 'make tarballs' now also builds a rearguard tarball.
+ New 's' and 'd' suffixes in SAVE columns of Rule and Zone lines.
+
+ Changes to past and future timestamps
+
+ North Korea switches back from +0830 to +09 on 2018-05-05.
+ (Thanks to Kang Seonghoon, Arthur David Olson, Seo Sanghyeon,
+ and Tim Parenti.)
+
+ Bring back the negative-DST changes of 2018a, except be more
+ compatible with data parsers that do not support negative DST.
+ Also, this now affects historical timestamps in Namibia and the
+ former Czechoslovakia, not just Ireland. The main format now uses
+ negative DST to model timestamps in Europe/Dublin (from 1971 on),
+ Europe/Prague (1946/7), and Africa/Windhoek (1994/2017). This
+ does not affect UT offsets, only time zone abbreviations and the
+ tm_isdst flag. Also, this does not affect rearguard or vanguard
+ formats; effectively the main format now uses vanguard instead of
+ rearguard format. Data parsers that do not support negative DST
+ can still use data from the rearguard tarball described below.
+
+ Changes to build procedure
+
+ The command 'make tarballs' now also builds the tarball
+ tzdataVERSION-rearguard.tar.gz, which is like tzdataVERSION.tar.gz
+ except that it uses rearguard format intended for trailing-edge
+ data parsers.
+
+ Changes to data format and to code
+
+ The SAVE column of Rule and Zone lines can now have an 's' or 'd'
+ suffix, which specifies whether the adjusted time is standard time
+ or daylight saving time. If no suffix is given, daylight saving
+ time is used if and only if the SAVE column is nonzero; this is
+ the longstanding behavior. Although this new feature is not used
+ in tzdata, it could be used to specify the legal time in Namibia
+ 1994-2017, as opposed to the popular time (see below).
+
+ Changes to past timestamps
+
+ From 1994 through 2017 Namibia observed DST in winter, not summer.
+ That is, it used negative DST, as Ireland still does. This change
+ does not affect UTC offsets; it affects only the tm_isdst flag and
+ the abbreviation used during summer, which is now CAT, not WAST.
+ Although (as noted by Michael Deckers) summer and winter time were
+ both simply called "standard time" in Namibian law, in common
+ practice winter time was considered to be DST (as noted by Stephen
+ Colebourne). The full effect of this change is only in vanguard
+ and main format; in rearguard format, the tm_isdst flag is still
+ zero in winter and nonzero in summer.
+
+ In 1946/7 Czechoslovakia also observed negative DST in winter.
+ The full effect of this change is only in vanguard and main
+ formats; in rearguard format, it is modeled as plain GMT without
+ daylight saving. Also, the dates of some 1944/5 DST transitions
+ in Czechoslovakia have been changed.
+
+
+Release 2018d - 2018-03-22 07:05:46 -0700
+
+ Briefly:
+
+ Palestine starts DST a week earlier in 2018.
+ Add support for vanguard and rearguard data consumers.
+ Add subsecond precision to source data format, though not to data.
+
+ Changes to future timestamps
+
+ In 2018, Palestine starts DST on March 24, not March 31.
+ Adjust future predictions accordingly. (Thanks to Sharef Mustafa.)
+
+ Changes to past and future timestamps
+
+ Casey Station in Antarctica changed from +11 to +08 on 2018-03-11
+ at 04:00. (Thanks to Steffen Thorsen.)
+
+ Changes to past timestamps
+
+ Historical transitions for Uruguay, represented by
+ America/Montevideo, have been updated per official legal documents,
+ replacing previous data mainly originating from the inventions of
+ Shanks & Pottenger. This has resulted in adjustments ranging from
+ 30 to 90 minutes in either direction over at least two dozen
+ distinct periods ranging from one day to several years in length.
+ A mere handful of pre-1991 transitions are unaffected; data since
+ then has come from more reliable contemporaneous reporting. These
+ changes affect various timestamps in 1920-1923, 1936, 1939,
+ 1942-1943, 1959, 1966-1970, 1972, 1974-1980, and 1988-1990.
+ Additionally, Uruguay's pre-standard-time UT offset has been
+ adjusted westward by 7 seconds, from UT-03:44:44 to UT-03:44:51, to
+ match the location of the Observatory of the National Meteorological
+ Institute in Montevideo.
+ (Thanks to Jeremie Bonjour, Tim Parenti, and Michael Deckers.)
+
+ East Kiribati skipped New Year's Eve 1994, not New Year's Day 1995.
+ (Thanks to Kerry Shetline.)
+
+ Fix the 1912-01-01 transition for Portugal and its colonies.
+ This transition was at 00:00 according to the new UT offset, not
+ according to the old one. Also assume that Cape Verde switched on
+ the same date as the rest, not in 1907. This affects
+ Africa/Bissau, Africa/Sao_Tome, Asia/Macau, Atlantic/Azores,
+ Atlantic/Cape_Verde, Atlantic/Madeira, and Europe/Lisbon.
+ (Thanks to Michael Deckers.)
+
+ Fix an off-by-1 error for pre-1913 timestamps in Jamaica and in
+ Turks & Caicos.
+
+ Changes to past time zone abbreviations
+
+ MMT took effect in Uruguay from 1908-06-10, not 1898-06-28. There
+ is no clock change associated with the transition.
+
+ Changes to build procedure
+
+ The new DATAFORM macro in the Makefile lets the installer choose
+ among three source data formats. The idea is to lessen downstream
+ disruption when data formats are improved.
+
+ * DATAFORM=vanguard installs from the latest, bleeding-edge
+ format. DATAFORM=main (the default) installs from the format
+ used in the 'africa' etc. files. DATAFORM=rearguard installs
+ from a trailing-edge format. Eventually, elements of today's
+ vanguard format should move to the main format, and similarly
+ the main format's features should eventually move to the
+ rearguard format.
+
+ * In the current version, the main and rearguard formats are
+ identical and match that of 2018c, so this change does not
+ affect default behavior. The vanguard format currently contains
+ one feature not in the main format: negative SAVE values. This
+ improves support for Ireland, which uses Irish Standard Time
+ (IST, UTC+01) in summer and GMT (UTC) in winter. tzcode has
+ supported negative SAVE values for decades, and this feature
+ should move to the main format soon. However, it will not move
+ to the rearguard format for quite some time because some
+ downstream parsers do not support it.
+
+ * The build procedure constructs three files vanguard.zi, main.zi,
+ and rearguard.zi, one for each format. Although the files
+ represent essentially the same data, they may have minor
+ discrepancies that users are not likely to notice. The files
+ are intended for downstream data consumers and are not
+ installed. Zoneinfo parsers that do not support negative SAVE values
+ should start using rearguard.zi, so that they will be unaffected
+ when the negative-DST feature moves from vanguard to main.
+ Bleeding-edge Zoneinfo parsers that support the new features
+ already can use vanguard.zi; in this respect, current tzcode is
+ bleeding-edge.
+
+ The Makefile should now be safe for parallelized builds, and 'make
+ -j to2050new.tzs' is now much faster on a multiprocessor host
+ with GNU Make.
+
+ When built with -DSUPPRESS_TZDIR, the tzcode library no longer
+ prepends TZDIR/ to file names that do not begin with '/'. This is
+ not recommended for general use, due to its security implications.
+ (From a suggestion by Manuela Friedrich.)
+
+ Changes to code
+
+ zic now accepts subsecond precision in expressions like
+ 00:19:32.13, which is approximately the legal time of the
+ Netherlands from 1835 to 1937. However, because it is
+ questionable whether the few recorded uses of non-integer offsets
+ had subsecond precision in practice, there are no plans for tzdata
+ to use this feature. (Thanks to Steve Allen for pointing out
+ the limitations of historical data in this area.)
+
+ The code is a bit more portable to MS-Windows. Installers can
+ compile with -DRESERVE_STD_EXT_IDS on MS-Windows platforms that
+ reserve identifiers like 'localtime'. (Thanks to Manuela
+ Friedrich.)
+
+ Changes to documentation and commentary
+
+ theory.html now outlines tzdb's extensions to POSIX's model for
+ civil time, and has a section "POSIX features no longer needed"
+ that lists POSIX API components that are now vestigial.
+ (From suggestions by Steve Summit.) It also better distinguishes
+ time zones from tz regions. (From a suggestion by Guy Harris.)
+
+ Commentary is now more consistent about using the phrase "daylight
+ saving time", to match the C name tm_isdst. Daylight saving time
+ need not occur in summer, and need not have a positive offset from
+ standard time.
+
+ Commentary about historical transitions in Uruguay has been expanded
+ with links to many relevant legal documents.
+ (Thanks to Tim Parenti.)
+
+ Commentary now uses some non-ASCII characters with Unicode value
+ less than U+0100, as they can be useful and should work even with
+ older editors such as XEmacs.
+
+
+Release 2018c - 2018-01-22 23:00:44 -0800
+
+ Briefly:
+ Revert Irish changes that relied on negative SAVE values.
+
+ Changes to tm_isdst
+
+ Revert the 2018a change to Europe/Dublin. As before, this change
+ does not affect UT offsets or abbreviations; it affects only
+ whether timestamps are considered to be standard time or
+ daylight-saving time, as expressed in the tm_isdst flag of C's
+ struct tm type. This reversion is intended to be a temporary
+ workaround for problems discovered with downstream uses of
+ releases 2018a and 2018b, which implemented Irish time by using
+ negative SAVE values in the Eire rules of the 'europe' file.
+ Although negative SAVE values have been part of tzcode for many
+ years and are supported by many platforms, they were not
+ documented before 2018a and ICU and OpenJDK do not currently
+ support them. A mechanism to export data to platforms lacking
+ support for negative DST is planned to be developed before the
+ change is reapplied. (Problems reported by Deborah Goldsmith and
+ Stephen Colebourne.)
+
+ Changes to past timestamps
+
+ Japanese DST transitions (1948-1951) were Sundays at 00:00, not
+ Saturdays or Sundays at 02:00. (Thanks to Takayuki Nikai.)
+
+ Changes to build procedure
+
+ The build procedure now works around mawk 1.3.3's lack of support
+ for character class expressions. (Problem reported by Ohyama.)
+
+
+Release 2018b - 2018-01-17 23:24:48 -0800
+
+ Briefly:
+ Fix a packaging problem in tz2018a, which was missing 'pacificnew'.
+
+ Changes to build procedure
+
+ The distribution now contains the file 'pacificnew' again.
+ This file was inadvertently omitted in the 2018a distribution.
+ (Problem reported by Matias Fonzo.)
+
+
+Release 2018a - 2018-01-12 22:29:21 -0800
+
+ Briefly:
+ São Tomé and Príncipe switched from +00 to +01.
+ Brazil's DST will now start on November's first Sunday.
+ Ireland's standard time is now in the summer, not the winter.
+ Use Debian-style installation locations, instead of 4.3BSD-style.
+ New zic option -t.
+
+ Changes to past and future timestamps
+
+ São Tomé and Príncipe switched from +00 to +01 on 2018-01-01 at
+ 01:00. (Thanks to Steffen Thorsen and Michael Deckers.)
+
+ Changes to future timestamps
+
+ Starting in 2018 southern Brazil will begin DST on November's
+ first Sunday instead of October's third Sunday. (Thanks to
+ Steffen Thorsen.)
+
+ Changes to past timestamps
+
+ A discrepancy of 4 s in timestamps before 1931 in South Sudan has
+ been corrected. The 'backzone' and 'zone.tab' files did not agree
+ with the 'africa' and 'zone1970.tab' files. (Problem reported by
+ Michael Deckers.)
+
+ The abbreviation invented for Bolivia Summer Time (1931-2) is now
+ BST instead of BOST, to be more consistent with the convention
+ used for Latvian Summer Time (1918-9) and for British Summer Time.
+
+ Changes to tm_isdst
+
+ Change Europe/Dublin so that it observes Irish Standard Time (UT
+ +01) in summer and GMT (as negative daylight-saving) in winter,
+ instead of observing standard time (GMT) in winter and Irish
+ Summer Time (UT +01) in summer. This change does not affect UT
+ offsets or abbreviations; it affects only whether timestamps are
+ considered to be standard time or daylight-saving time, as
+ expressed in the tm_isdst flag of C's struct tm type.
+ (Discrepancy noted by Derick Rethans.)
+
+ Changes to build procedure
+
+ The default installation locations have been changed to mostly
+ match Debian circa 2017, instead of being designed as an add-on to
+ 4.3BSD circa 1986. This affects the Makefile macros TOPDIR,
+ TZDIR, MANDIR, and LIBDIR. New Makefile macros TZDEFAULT, USRDIR,
+ USRSHAREDIR, BINDIR, ZDUMPDIR, and ZICDIR let installers tailor
+ locations more precisely. (This responds to suggestions from
+ Brian Inglis and from Steve Summit.)
+
+ The default installation procedure no longer creates the
+ backward-compatibility link US/Pacific-New, which causes
+ confusion during user setup (e.g., see Debian bug 815200).
+ Use 'make BACKWARD="backward pacificnew"' to create the link
+ anyway, for now. Eventually we plan to remove the link entirely.
+
+ tzdata.zi now contains a version-number comment.
+ (Suggested by Tom Lane.)
+
+ The Makefile now quotes values like BACKWARD more carefully when
+ passing them to the shell. (Problem reported by Zefram.)
+
+ Builders no longer need to specify -DHAVE_SNPRINTF on platforms
+ that have snprintf and use pre-C99 compilers. (Problem reported
+ by Jon Skeet.)
+
+ Changes to code
+
+ zic has a new option -t FILE that specifies the location of the
+ file that determines local time when TZ is unset. The default for
+ this location can be configured via the new TZDEFAULT makefile
+ macro, which defaults to /etc/localtime.
+
+ Diagnostics and commentary now distinguish UT from UTC more
+ carefully; see theory.html for more information about UT vs UTC.
+
+ zic has been ported to GCC 8's -Wstringop-truncation option.
+ (Problem reported by Martin Sebor.)
+
+ Changes to documentation and commentary
+
+ The zic man page now documents the longstanding behavior that
+ times and years can be out of the usual range, with negative times
+ counting backwards from midnight and with year 0 preceding year 1.
+ (Problem reported by Michael Deckers.)
+
+ The theory.html file now mentions the POSIX limit of six chars
+ per abbreviation, and lists alphabetic abbreviations used.
+
+ The files tz-art.htm and tz-link.htm have been renamed to
+ tz-art.html and tz-link.html, respectively, for consistency with
+ other file names and to simplify web server configuration.
+
+
+Release 2017c - 2017-10-20 14:49:34 -0700
+
+ Briefly:
+ Northern Cyprus switches from +03 to +02/+03 on 2017-10-29.
+ Fiji ends DST 2018-01-14, not 2018-01-21.
+ Namibia switches from +01/+02 to +02 on 2018-04-01.
+ Sudan switches from +03 to +02 on 2017-11-01.
+ Tonga likely switches from +13/+14 to +13 on 2017-11-05.
+ Turks & Caicos switches from -04 to -05/-04 on 2018-11-04.
+ A new file tzdata.zi now holds a small text copy of all data.
+ The zic input format has been regularized slightly.
+
+ Changes to future timestamps
+
+ Northern Cyprus has decided to resume EU rules starting
+ 2017-10-29, thus reinstituting winter time.
+
+ Fiji ends DST 2018-01-14 instead of the 2018-01-21 previously
+ predicted. (Thanks to Dominic Fok.) Adjust future predictions
+ accordingly.
+
+ Namibia will switch from +01 with DST to +02 all year on
+ 2017-09-03 at 02:00. This affects UT offsets starting 2018-04-01
+ at 02:00. (Thanks to Steffen Thorsen.)
+
+ Sudan will switch from +03 to +02 on 2017-11-01. (Thanks to Ahmed
+ Atyya and Yahia Abdalla.) South Sudan is not switching, so
+ Africa/Juba is no longer a link to Africa/Khartoum.
+
+ Tonga has likely ended its experiment with DST, and will not
+ adjust its clocks on 2017-11-05. Although Tonga has not announced
+ whether it will continue to observe DST, the IATA is assuming that
+ it will not. (Thanks to David Wade.)
+
+ Turks & Caicos will switch from -04 all year to -05 with US DST on
+ 2018-03-11 at 03:00. This affects UT offsets starting 2018-11-04
+ at 02:00. (Thanks to Steffen Thorsen.)
+
+ Changes to past timestamps
+
+ Namibia switched from +02 to +01 on 1994-03-21, not 1994-04-03.
+ (Thanks to Arthur David Olson.)
+
+ Detroit did not observe DST in 1967.
+
+ Use railway time for Asia/Kolkata before 1941, by switching to
+ Madras local time (UT +052110) in 1870, then to IST (UT +0530) in
+ 1906. Also, treat 1941-2's +0630 as DST, like 1942-5.
+
+ Europe/Dublin's 1946 and 1947 fallback transitions occurred at
+ 02:00 standard time, not 02:00 DST. (Thanks to Michael Deckers.)
+
+ Pacific/Apia and Pacific/Pago_Pago switched from Antipodean to
+ American time in 1892, not 1879. (Thanks to Michael Deckers.)
+
+ Adjust the 1867 transition in Alaska to better reflect the
+ historical record, by changing it to occur on 1867-10-18 at 15:30
+ Sitka time rather than at the start of 1867-10-17 local time.
+ Although strictly speaking this is accurate only for Sitka,
+ the rest of Alaska's blanks need to be filled in somehow.
+
+ Fix off-by-one errors in UT offsets for Adak and Nome before 1867.
+ (Thanks to Michael Deckers.)
+
+ Add 7 s to the UT offset in Asia/Yangon before 1920.
+
+ Changes to zone names
+
+ Remove Canada/East-Saskatchewan from the 'backward' file, as it
+ exceeded the 14-character limit and was an unused misnomer anyway.
+
+ Changes to build procedure
+
+ To support applications that prefer to read time zone data in text
+ form, two zic input files tzdata.zi and leapseconds are now
+ installed by default. The commands 'zic tzdata.zi' and 'zic -L
+ leapseconds tzdata.zi' can reproduce the tzdata binary files
+ without and with leap seconds, respectively. To prevent these two
+ new files from being installed, use 'make TZDATA_TEXT=', and to
+ suppress leap seconds from the tzdata text installation, use 'make
+ TZDATA_TEXT=tzdata.zi'.
+
+ 'make BACKWARD=' now suppresses backward-compatibility names
+ like 'US/Pacific' that are defined in the 'backward' and
+ 'pacificnew' files.
+
+ 'make check' now works on systems that lack a UTF-8 locale,
+ or that lack the nsgmls program. Set UTF8_LOCALE to configure
+ the name of a UTF-8 locale, if you have one.
+
+ Y2K runtime checks are no longer enabled by default. Add
+ -DDEPRECATE_TWO_DIGIT_YEARS to CFLAGS to enable them, instead of
+ adding -DNO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
+ to disable them. (New name suggested by Brian Inglis.)
+
+ The build procedure for zdump now works on AIX 7.1.
+ (Problem reported by Kees Dekker.)
+
+ Changes to code
+
+ zic and the reference runtime now reject multiple leap seconds
+ within 28 days of each other, or leap seconds before the Epoch.
+ As a result, support for double leap seconds, which was
+ obsolescent and undocumented, has been removed. Double leap
+ seconds were an error in the C89 standard; they have never existed
+ in civil timekeeping. (Thanks to Robert Elz and Bradley White for
+ noticing glitches in the code that uncovered this problem.)
+
+ zic now warns about use of the obsolescent and undocumented -y
+ option, and about use of the obsolescent TYPE field of Rule lines.
+
+ zic now allows unambiguous abbreviations like "Sa" and "Su" for
+ weekdays; formerly it rejected them due to a bug. Conversely, zic
+ no longer considers non-prefixes to be abbreviations; for example,
+ it no longer accepts "lF" as an abbreviation for "lastFriday".
+ Also, zic warns about the undocumented usage with a "last-"
+ prefix, e.g., "last-Fri".
+
+ Similarly, zic now accepts the unambiguous abbreviation "L" for
+ "Link" in ordinary context and for "Leap" in leap-second context.
+ Conversely, zic no longer accepts non-prefixes such as "La" as
+ abbreviations for words like "Leap".
+
+ zic no longer accepts leap second lines in ordinary input, or
+ ordinary lines in leap second input. Formerly, zic sometimes
+ warned about this undocumented usage and handled it incorrectly.
+
+ The new macro HAVE_TZNAME governs whether the tzname external
+ variable is exported, instead of USG_COMPAT. USG_COMPAT now
+ governs only the external variables "timezone" and "daylight".
+ This change is needed because the three variables are not in the
+ same category: although POSIX requires tzname, it specifies the
+ other two variables as optional. Also, USG_COMPAT is now 1 or 0:
+ if not defined, the code attempts to guess it from other macros.
+
+ localtime.c and difftime.c no longer require stdio.h, and .c files
+ other than zic.c no longer require sys/wait.h.
+
+ zdump.c no longer assumes snprintf. (Reported by Jonathan Leffler.)
+
+ Calculation of time_t extrema works around a bug in GCC 4.8.4
+ (Reported by Stan Shebs and Joseph Myers.)
+
+ zic.c no longer mistranslates formats of line numbers in non-English
+ locales. (Problem reported by Benno Schulenberg.)
+
+ Several minor changes have been made to the code to make it a
+ bit easier to port to MS-Windows and Solaris. (Thanks to Kees
+ Dekker for reporting the problems.)
+
+ Changes to documentation and commentary
+
+ The two new files 'theory.html' and 'calendars' contain the
+ contents of the removed file 'Theory'. The goal is to document
+ tzdb theory more accessibly.
+
+ The zic man page now documents abbreviation rules.
+
+ tz-link.htm now covers how to apply tzdata changes to clients.
+ (Thanks to Jorge Fábregas for the AIX link.) It also mentions MySQL.
+
+ The leap-seconds.list URL has been updated to something that is
+ more reliable for tzdb. (Thanks to Tim Parenti and Brian Inglis.)
+
+Release 2017b - 2017-03-17 07:30:38 -0700
+
+ Briefly: Haiti has resumed DST.
+
+ Changes to past and future timestamps
+
+ Haiti resumed observance of DST in 2017. (Thanks to Steffen Thorsen.)
+
+ Changes to past timestamps
+
+ Liberia changed from -004430 to +00 on 1972-01-07, not 1972-05-01.
+
+ Use "MMT" to abbreviate Liberia's time zone before 1972, as "-004430"
+ is one byte over the POSIX limit. (Problem reported by Derick Rethans.)
+
+ Changes to code
+
+ The reference localtime implementation now falls back on the
+ current US daylight-saving transition rules rather than the
+ 1987-2006 rules. This fallback occurs only when (1) the TZ
+ environment variable has a value like "AST4ADT" that asks
+ for daylight saving time but does not specify the rules, (2) there
+ is no file by that name, and (3) the TZDEFRULES file cannot be
+ loaded. (Thanks to Tom Lane.)
+
+
+Release 2017a - 2017-02-28 00:05:36 -0800
+
+ Briefly: Southern Chile moves from -04/-03 to -03, and Mongolia
+ discontinues DST.
+
+ Changes to future timestamps
+
+ Mongolia no longer observes DST. (Thanks to Ganbold Tsagaankhuu.)
+
+ Chile's Region of Magallanes moves from -04/-03 to -03 year-round.
+ Its clocks diverge from America/Santiago starting 2017-05-13 at
+ 23:00, hiving off a new zone America/Punta_Arenas. Although the
+ Chilean government says this change expires in May 2019, for now
+ assume it's permanent. (Thanks to Juan Correa and Deborah
+ Goldsmith.) This also affects Antarctica/Palmer.
+
+ Changes to past timestamps
+
+ Fix many entries for historical timestamps for Europe/Madrid
+ before 1979, to agree with tables compiled by Pere Planesas of the
+ National Astronomical Observatory of Spain. As a side effect,
+ this changes some timestamps for Africa/Ceuta before 1929, which
+ are probably guesswork anyway. (Thanks to Steve Allen and
+ Pierpaolo Bernardi for the heads-ups, and to Michael Deckers for
+ correcting the 1901 transition.)
+
+ Ecuador observed DST from 1992-11-28 to 1993-02-05.
+ (Thanks to Alois Treindl.)
+
+ Asia/Atyrau and Asia/Oral were at +03 (not +04) before 1930-06-21.
+ (Thanks to Stepan Golosunov.)
+
+ Changes to past and future time zone abbreviations
+
+ Switch to numeric time zone abbreviations for South America, as
+ part of the ongoing project of removing invented abbreviations.
+ This avoids the need to invent an abbreviation for the new Chilean
+ new zone. Similarly, switch from invented to numeric time zone
+ abbreviations for Afghanistan, American Samoa, the Azores,
+ Bangladesh, Bhutan, the British Indian Ocean Territory, Brunei,
+ Cape Verde, Chatham Is, Christmas I, Cocos (Keeling) Is, Cook Is,
+ Dubai, East Timor, Eucla, Fiji, French Polynesia, Greenland,
+ Indochina, Iran, Iraq, Kiribati, Lord Howe, Macquarie, Malaysia,
+ the Maldives, Marshall Is, Mauritius, Micronesia, Mongolia,
+ Myanmar, Nauru, Nepal, New Caledonia, Niue, Norfolk I, Palau,
+ Papua New Guinea, the Philippines, Pitcairn, Qatar, Réunion, St
+ Pierre & Miquelon, Samoa, Saudi Arabia, Seychelles, Singapore,
+ Solomon Is, Tokelau, Tuvalu, Wake, Vanuatu, Wallis & Futuna, and
+ Xinjiang; for 20-minute daylight saving time in Ghana before 1943;
+ for half-hour daylight saving time in Belize before 1944 and in
+ the Dominican Republic before 1975; and for Canary Islands before
+ 1946, for Guinea-Bissau before 1975, for Iceland before 1969, for
+ Indian Summer Time before 1942, for Indonesia before around 1964,
+ for Kenya before 1960, for Liberia before 1973, for Madeira before
+ 1967, for Namibia before 1943, for the Netherlands in 1937-9, for
+ Pakistan before 1971, for Western Sahara before 1977, and for
+ Zaporozhye in 1880-1924.
+
+ For Alaska time from 1900 through 1967, instead of "CAT" use the
+ abbreviation "AST", the abbreviation commonly used at the time
+ (Atlantic Standard Time had not been standardized yet). Use "AWT"
+ and "APT" instead of the invented abbreviations "CAWT" and "CAPT".
+
+ Use "CST" and "CDT" instead of invented abbreviations for Macau
+ before 1999 and Taiwan before 1938, and use "JST" instead of the
+ invented abbreviation "JCST" for Japan and Korea before 1938.
+
+ Change to database entry category
+
+ Move the Pacific/Johnston link from 'australasia' to 'backward',
+ since Johnston is now uninhabited.
+
+ Changes to code
+
+ zic no longer mishandles some transitions in January 2038 when it
+ attempts to work around Qt bug 53071. This fixes a bug affecting
+ Pacific/Tongatapu that was introduced in zic 2016e. localtime.c
+ now contains a workaround, useful when loading a file generated by
+ a buggy zic. (Problem and localtime.c fix reported by Bradley
+ White.)
+
+ zdump -i now outputs non-hour numeric time zone abbreviations
+ without a colon, e.g., "+0530" rather than "+05:30". This agrees
+ with zic %z and with common practice, and simplifies auditing of
+ zdump output.
+
+ zdump is now buildable again with -DUSE_LTZ=0.
+ (Problem reported by Joseph Myers.)
+
+ zdump.c now always includes private.h, to avoid code duplication
+ with private.h. (Problem reported by Kees Dekker.)
+
+ localtime.c no longer mishandles early or late timestamps
+ when TZ is set to a POSIX-style string that specifies DST.
+ (Problem reported by Kees Dekker.)
+
+ date and strftime now cause %z to generate "-0000" instead of
+ "+0000" when the UT offset is zero and the time zone abbreviation
+ begins with "-".
+
+ Changes to documentation and commentary
+
+ The 'Theory' file now better documents choice of historical time
+ zone abbreviations. (Problems reported by Michael Deckers.)
+
+ tz-link.htm now covers leap smearing, which is popular in clouds.
+
+
+Release 2016j - 2016-11-22 23:17:13 -0800
+
+ Briefly: Saratov, Russia moves from +03 to +04 on 2016-12-04.
+
+ Changes to future timestamps
+
+ Saratov, Russia switches from +03 to +04 on 2016-12-04 at 02:00.
+ This hives off a new zone Europe/Saratov from Europe/Volgograd.
+ (Thanks to Yuri Konotopov and Stepan Golosunov.)
+
+ Changes to past timestamps
+
+ The new zone Asia/Atyrau for Atyraū Region, Kazakhstan, is like
+ Asia/Aqtau except it switched from +05/+06 to +04/+05 in spring
+ 1999, not fall 1994. (Thanks to Stepan Golosunov.)
+
+ Changes to past time zone abbreviations
+
+ Asia/Gaza and Asia/Hebron now use "EEST", not "EET", to denote
+ summer time before 1948. The old use of "EET" was a typo.
+
+ Changes to code
+
+ zic no longer mishandles file systems that lack hard links, fixing
+ bugs introduced in 2016g. (Problems reported by Tom Lane.)
+ Also, when the destination already contains symbolic links, zic
+ should now work better on systems where the 'link' system call
+ does not follow symbolic links.
+
+ Changes to documentation and commentary
+
+ tz-link.htm now documents the relationship between release version
+ numbers and development-repository commit tags. (Suggested by
+ Paul Koning.)
+
+ The 'Theory' file now documents UT.
+
+ iso3166.tab now accents "Curaçao", and commentary now mentions
+ the names "Cabo Verde" and "Czechia". (Thanks to Jiří Boháč.)
+
+
+Release 2016i - 2016-11-01 23:19:52 -0700
+
+ Briefly: Cyprus split into two time zones on 2016-10-30, and Tonga
+ reintroduces DST on 2016-11-06.
+
+ Changes to future timestamps
+
+ Pacific/Tongatapu begins DST on 2016-11-06 at 02:00, ending on
+ 2017-01-15 at 03:00. Assume future observances in Tonga will be
+ from the first Sunday in November through the third Sunday in
+ January, like Fiji. (Thanks to Pulu ʻAnau.) Switch to numeric
+ time zone abbreviations for this zone.
+
+ Changes to past and future timestamps
+
+ Northern Cyprus is now +03 year round, causing a split in Cyprus
+ time zones starting 2016-10-30 at 04:00. This creates a zone
+ Asia/Famagusta. (Thanks to Even Scharning and Matt Johnson.)
+
+ Antarctica/Casey switched from +08 to +11 on 2016-10-22.
+ (Thanks to Steffen Thorsen.)
+
+ Changes to past timestamps
+
+ Several corrections were made for pre-1975 timestamps in Italy.
+ These affect Europe/Malta, Europe/Rome, Europe/San_Marino, and
+ Europe/Vatican.
+
+ First, the 1893-11-01 00:00 transition in Italy used the new UT
+ offset (+01), not the old (+00:49:56). (Thanks to Michael
+ Deckers.)
+
+ Second, rules for daylight saving in Italy were changed to agree
+ with Italy's National Institute of Metrological Research (INRiM)
+ except for 1944, as follows (thanks to Pierpaolo Bernardi, Brian
+ Inglis, and Michael Deckers):
+
+ The 1916-06-03 transition was at 24:00, not 00:00.
+
+ The 1916-10-01, 1919-10-05, and 1920-09-19 transitions were at
+ 00:00, not 01:00.
+
+ The 1917-09-30 and 1918-10-06 transitions were at 24:00, not
+ 01:00.
+
+ The 1944-09-17 transition was at 03:00, not 01:00. This
+ particular change is taken from Italian law as INRiM's table,
+ (which says 02:00) appears to have a typo here. Also, keep the
+ 1944-04-03 transition for Europe/Rome, as Rome was controlled by
+ Germany then.
+
+ The 1967-1970 and 1972-1974 fallback transitions were at 01:00,
+ not 00:00.
+
+ Changes to code
+
+ The code should now be buildable on AmigaOS merely by setting the
+ appropriate Makefile variables. (From a patch by Carsten Larsen.)
+
+
+Release 2016h - 2016-10-19 23:17:57 -0700
+
+ Changes to future timestamps
+
+ Asia/Gaza and Asia/Hebron end DST on 2016-10-29 at 01:00, not
+ 2016-10-21 at 00:00. (Thanks to Sharef Mustafa.) Predict that
+ future fall transitions will be on the last Saturday of October
+ at 01:00, which is consistent with predicted spring transitions
+ on the last Saturday of March. (Thanks to Tim Parenti.)
+
+ Changes to past timestamps
+
+ In Turkey, transitions in 1986-1990 were at 01:00 standard time
+ not at 02:00, and the spring 1994 transition was on March 20, not
+ March 27. (Thanks to Kıvanç Yazan.)
+
+ Changes to past and future time zone abbreviations
+
+ Asia/Colombo now uses numeric time zone abbreviations like "+0530"
+ instead of alphabetic ones like "IST" and "LKT". Various
+ English-language sources use "IST", "LKT" and "SLST", with no
+ working consensus. (Usage of "SLST" mentioned by Sadika
+ Sumanapala.)
+
+ Changes to code
+
+ zic no longer mishandles relativizing file names when creating
+ symbolic links like /etc/localtime, when these symbolic links
+ are outside the usual directory hierarchy. This fixes a bug
+ introduced in 2016g. (Problem reported by Andreas Stieger.)
+
+ Changes to build procedure
+
+ New rules 'traditional_tarballs' and 'traditional_signatures' for
+ building just the traditional-format distribution. (Requested by
+ Deborah Goldsmith.)
+
+ The file 'version' is now put into the tzdata tarball too.
+ (Requested by Howard Hinnant.)
+
+ Changes to documentation and commentary
+
+ The 'Theory' file now has a section on interface stability.
+ (Requested by Paul Koning.) It also mentions features like
+ tm_zone and localtime_rz that have long been supported by the
+ reference code.
+
+ tz-link.htm has improved coverage of time zone boundaries suitable
+ for geolocation. (Thanks to heads-ups from Evan Siroky and Matt
+ Johnson.)
+
+ The US commentary now mentions Allen and the "day of two noons".
+
+ The Fiji commentary mentions the government's 2016-10-03 press
+ release. (Thanks to Raymond Kumar.)
+
+
+Release 2016g - 2016-09-13 08:56:38 -0700
+
+ Changes to future timestamps
+
+ Turkey switched from EET/EEST (+02/+03) to permanent +03,
+ effective 2016-09-07. (Thanks to Burak AYDIN.) Use "+03" rather
+ than an invented abbreviation for the new time.
+
+ New leap second 2016-12-31 23:59:60 UTC as per IERS Bulletin C 52.
+ (Thanks to Tim Parenti.)
+
+ Changes to past timestamps
+
+ For America/Los_Angeles, spring-forward transition times have been
+ corrected from 02:00 to 02:01 in 1948, and from 02:00 to 01:00 in
+ 1950-1966.
+
+ For zones using Soviet time on 1919-07-01, transitions to UT-based
+ time were at 00:00 UT, not at 02:00 local time. The affected
+ zones are Europe/Kirov, Europe/Moscow, Europe/Samara, and
+ Europe/Ulyanovsk. (Thanks to Alexander Belopolsky.)
+
+ Changes to past and future time zone abbreviations
+
+ The Factory zone now uses the time zone abbreviation -00 instead
+ of a long English-language string, as -00 is now the normal way to
+ represent an undefined time zone.
+
+ Several zones in Antarctica and the former Soviet Union, along
+ with zones intended for ships at sea that cannot use POSIX TZ
+ strings, now use numeric time zone abbreviations instead of
+ invented or obsolete alphanumeric abbreviations. The affected
+ zones are Antarctica/Casey, Antarctica/Davis,
+ Antarctica/DumontDUrville, Antarctica/Mawson, Antarctica/Rothera,
+ Antarctica/Syowa, Antarctica/Troll, Antarctica/Vostok,
+ Asia/Anadyr, Asia/Ashgabat, Asia/Baku, Asia/Bishkek, Asia/Chita,
+ Asia/Dushanbe, Asia/Irkutsk, Asia/Kamchatka, Asia/Khandyga,
+ Asia/Krasnoyarsk, Asia/Magadan, Asia/Omsk, Asia/Sakhalin,
+ Asia/Samarkand, Asia/Srednekolymsk, Asia/Tashkent, Asia/Tbilisi,
+ Asia/Ust-Nera, Asia/Vladivostok, Asia/Yakutsk, Asia/Yekaterinburg,
+ Asia/Yerevan, Etc/GMT-14, Etc/GMT-13, Etc/GMT-12, Etc/GMT-11,
+ Etc/GMT-10, Etc/GMT-9, Etc/GMT-8, Etc/GMT-7, Etc/GMT-6, Etc/GMT-5,
+ Etc/GMT-4, Etc/GMT-3, Etc/GMT-2, Etc/GMT-1, Etc/GMT+1, Etc/GMT+2,
+ Etc/GMT+3, Etc/GMT+4, Etc/GMT+5, Etc/GMT+6, Etc/GMT+7, Etc/GMT+8,
+ Etc/GMT+9, Etc/GMT+10, Etc/GMT+11, Etc/GMT+12, Europe/Kaliningrad,
+ Europe/Minsk, Europe/Samara, Europe/Volgograd, and
+ Indian/Kerguelen. For Europe/Moscow the invented abbreviation MSM
+ was replaced by +05, whereas MSK and MSD were kept as they are not
+ our invention and are widely used.
+
+ Changes to zone names
+
+ Rename Asia/Rangoon to Asia/Yangon, with a backward compatibility link.
+ (Thanks to David Massoud.)
+
+ Changes to code
+
+ zic no longer generates binary files containing POSIX TZ-like
+ strings that disagree with the local time type after the last
+ explicit transition in the data. This fixes a bug with
+ Africa/Casablanca and Africa/El_Aaiun in some year-2037 timestamps
+ on the reference platform. (Thanks to Alexander Belopolsky for
+ reporting the bug and suggesting a way forward.)
+
+ If the installed localtime and/or posixrules files are symbolic
+ links, zic now keeps them symbolic links when updating them, for
+ compatibility with platforms like OpenSUSE where other programs
+ configure these files as symlinks.
+
+ zic now avoids hard linking to symbolic links, avoids some
+ unnecessary mkdir and stat system calls, and uses shorter file
+ names internally.
+
+ zdump has a new -i option to generate transitions in a
+ more-compact but still human-readable format. This option is
+ experimental, and the output format may change in future versions.
+ (Thanks to Jon Skeet for suggesting that an option was needed,
+ and thanks to Tim Parenti and Chris Rovick for further comments.)
+
+ Changes to build procedure
+
+ An experimental distribution format is available, in addition
+ to the traditional format which will continue to be distributed.
+ The new format is a tarball tzdb-VERSION.tar.lz with signature
+ file tzdb-VERSION.tar.lz.asc. It unpacks to a top-level directory
+ tzdb-VERSION containing the code and data of the traditional
+ two-tarball format, along with extra data that may be useful.
+ (Thanks to Antonio Diaz Diaz, Oscar van Vlijmen, and many others
+ for comments about the experimental format.)
+
+ The release version number is now more accurate in the usual case
+ where releases are built from a Git repository. For example, if
+ 23 commits and some working-file changes have been made since
+ release 2016g, the version number is now something like
+ '2016g-23-g50556e3-dirty' instead of the misleading '2016g'.
+ Tagged releases use the same version number format as before,
+ e.g., '2016g'. To support the more-accurate version number, its
+ specification has moved from a line in the Makefile to a new
+ source file 'version'.
+
+ The experimental distribution contains a file to2050.tzs that
+ contains what should be the output of 'zdump -i -c 2050' on
+ primary zones. If this file is available, 'make check' now checks
+ that zdump generates this output.
+
+ 'make check_web' now works on Fedora-like distributions.
+
+ Changes to documentation and commentary
+
+ tzfile.5 now documents the new restriction on POSIX TZ-like
+ strings that is now implemented by zic.
+
+ Comments now cite URLs for some 1917-1921 Russian DST decrees.
+ (Thanks to Alexander Belopolsky.)
+
+ tz-link.htm mentions JuliaTime (thanks to Curtis Vogt) and Time4J
+ (thanks to Meno Hochschild) and ThreeTen-Extra, and its
+ description of Java 8 has been brought up to date (thanks to
+ Stephen Colebourne). Its description of local time on Mars has
+ been updated to match current practice, and URLs have been updated
+ and some obsolete ones removed.
+
+
+Release 2016f - 2016-07-05 16:26:51 +0200
+
+ Changes affecting future timestamps
+
+ The Egyptian government changed its mind on short notice, and
+ Africa/Cairo will not introduce DST starting 2016-07-07 after all.
+ (Thanks to Mina Samuel.)
+
+ Asia/Novosibirsk switches from +06 to +07 on 2016-07-24 at 02:00.
+ (Thanks to Stepan Golosunov.)
+
+ Changes to past and future timestamps
+
+ Asia/Novokuznetsk and Asia/Novosibirsk now use numeric time zone
+ abbreviations instead of invented ones.
+
+ Changes affecting past timestamps
+
+ Europe/Minsk's 1992-03-29 spring-forward transition was at 02:00 not 00:00.
+ (Thanks to Stepan Golosunov.)
+
+
+Release 2016e - 2016-06-14 08:46:16 -0700
+
+ Changes affecting future timestamps
+
+ Africa/Cairo observes DST in 2016 from July 7 to the end of October.
+ Guess October 27 and 24:00 transitions. (Thanks to Steffen Thorsen.)
+ For future years, guess April's last Thursday to October's last
+ Thursday except for Ramadan.
+
+ Changes affecting past timestamps
+
+ Locations while uninhabited now use '-00', not 'zzz', as a
+ placeholder time zone abbreviation. This is inspired by Internet
+ RFC 3339 and is more consistent with numeric time zone
+ abbreviations already used elsewhere. The change affects several
+ arctic and antarctic locations, e.g., America/Cambridge_Bay before
+ 1920 and Antarctica/Troll before 2005.
+
+ Asia/Baku's 1992-09-27 transition from +04 (DST) to +04 (non-DST) was
+ at 03:00, not 23:00 the previous day. (Thanks to Michael Deckers.)
+
+ Changes to code
+
+ zic now outputs a dummy transition at time 2**31 - 1 in zones
+ whose POSIX-style TZ strings contain a '<'. This mostly works
+ around Qt bug 53071 <https://bugreports.qt.io/browse/QTBUG-53071>.
+ (Thanks to Zhanibek Adilbekov for reporting the Qt bug.)
+
+ Changes affecting documentation and commentary
+
+ tz-link.htm says why governments should give plenty of notice for
+ time zone or DST changes, and refers to Matt Johnson's blog post.
+
+ tz-link.htm mentions Tzdata for Elixir. (Thanks to Matt Johnson.)
+
+
+Release 2016d - 2016-04-17 22:50:29 -0700
+
+ Changes affecting future timestamps
+
+ America/Caracas switches from -0430 to -04 on 2016-05-01 at 02:30.
+ (Thanks to Alexander Krivenyshev for the heads-up.)
+
+ Asia/Magadan switches from +10 to +11 on 2016-04-24 at 02:00.
+ (Thanks to Alexander Krivenyshev and Matt Johnson.)
+
+ New zone Asia/Tomsk, split off from Asia/Novosibirsk. It covers
+ Tomsk Oblast, Russia, which switches from +06 to +07 on 2016-05-29
+ at 02:00. (Thanks to Stepan Golosunov.)
+
+ Changes affecting past timestamps
+
+ New zone Europe/Kirov, split off from Europe/Volgograd. It covers
+ Kirov Oblast, Russia, which switched from +04/+05 to +03/+04 on
+ 1989-03-26 at 02:00, roughly a year after Europe/Volgograd made
+ the same change. (Thanks to Stepan Golosunov.)
+
+ Russia and nearby locations had daylight-saving transitions on
+ 1992-03-29 at 02:00 and 1992-09-27 at 03:00, instead of on
+ 1992-03-28 at 23:00 and 1992-09-26 at 23:00. (Thanks to Stepan
+ Golosunov.)
+
+ Many corrections to historical time in Kazakhstan from 1991
+ through 2005. (Thanks to Stepan Golosunov.) Replace Kazakhstan's
+ invented time zone abbreviations with numeric abbreviations.
+
+ Changes to commentary
+
+ Mention Internet RFCs 7808 (TZDIST) and 7809 (CalDAV time zone references).
+
+
+Release 2016c - 2016-03-23 00:51:27 -0700
+
+ Changes affecting future timestamps
+
+ Azerbaijan no longer observes DST. (Thanks to Steffen Thorsen.)
+
+ Chile reverts from permanent to seasonal DST. (Thanks to Juan
+ Correa for the heads-up, and to Tim Parenti for corrections.)
+ Guess that future transitions are August's and May's second
+ Saturdays at 24:00 mainland time. Also, call the period from
+ 2014-09-07 through 2016-05-14 daylight saving time instead of
+ standard time, as that seems more appropriate now.
+
+ Changes affecting past timestamps
+
+ Europe/Kaliningrad and Europe/Vilnius changed from +03/+04 to
+ +02/+03 on 1989-03-26, not 1991-03-31. Europe/Volgograd changed
+ from +04/+05 to +03/+04 on 1988-03-27, not 1989-03-26.
+ (Thanks to Stepan Golosunov.)
+
+ Changes to commentary
+
+ Several updates and URLs for historical and proposed Russian changes.
+ (Thanks to Stepan Golosunov, Matt Johnson, and Alexander Krivenyshev.)
+
+
+Release 2016b - 2016-03-12 17:30:14 -0800
+
+ Compatibility note
+
+ Starting with release 2016b, some data entries cause zic implementations
+ derived from tz releases 2005j through 2015e to issue warnings like
+ "time zone abbreviation differs from POSIX standard (+03)".
+ These warnings should not otherwise affect zic's output and can safely be
+ ignored on today's platforms, as the warnings refer to a restriction in
+ POSIX.1-1988 that was removed in POSIX.1-2001. One way to suppress the
+ warnings is to upgrade to zic derived from tz releases 2015f and later.
+
+ Changes affecting future timestamps
+
+ New zones Europe/Astrakhan and Europe/Ulyanovsk for Astrakhan and
+ Ulyanovsk Oblasts, Russia, both of which will switch from +03 to +04 on
+ 2016-03-27 at 02:00 local time. They need distinct zones since their
+ post-1970 histories disagree. New zone Asia/Barnaul for Altai Krai and
+ Altai Republic, Russia, which will switch from +06 to +07 on the same date
+ and local time. The Astrakhan change is already official; the others have
+ passed the first reading in the State Duma and are extremely likely.
+ Also, Asia/Sakhalin moves from +10 to +11 on 2016-03-27 at 02:00.
+ (Thanks to Alexander Krivenyshev for the heads-up, and to Matt Johnson
+ and Stepan Golosunov for followup.)
+
+ As a trial of a new system that needs less information to be made up,
+ the new zones use numeric time zone abbreviations like "+04"
+ instead of invented abbreviations like "ASTT".
+
+ Haiti will not observe DST in 2016. (Thanks to Jean Antoine via
+ Steffen Thorsen.)
+
+ Palestine's spring-forward transition on 2016-03-26 is at 01:00, not 00:00.
+ (Thanks to Hannah Kreitem.) Guess future transitions will be March's last
+ Saturday at 01:00, not March's last Friday at 24:00.
+
+ Changes affecting past timestamps
+
+ Europe/Chisinau observed DST during 1990, and switched from +04 to
+ +03 at 1990-05-06 02:00, instead of switching from +03 to +02.
+ (Thanks to Stepan Golosunov.)
+
+ 1991 abbreviations in Europe/Samara should be SAMT/SAMST, not
+ KUYT/KUYST. (Thanks to Stepan Golosunov.)
+
+ Changes to code
+
+ tzselect's diagnostics and checking, and checktab.awk's checking,
+ have been improved. (Thanks to J William Piggott.)
+
+ tzcode now builds under MinGW. (Thanks to Ian Abbott and Esben Haabendal.)
+
+ tzselect now tests Julian-date TZ settings more accurately.
+ (Thanks to J William Piggott.)
+
+ Changes to commentary
+
+ Comments in zone tables have been improved. (Thanks to J William Piggott.)
+
+ tzselect again limits its menu comments so that menus fit on a
+ 24×80 alphanumeric display.
+
+ A new web page tz-how-to.html. (Thanks to Bill Seymour.)
+
+ In the Theory file, the description of possible time zone abbreviations in
+ tzdata has been cleaned up, as the old description was unclear and
+ inconsistent. (Thanks to Alain Mouette for reporting the problem.)
+
+
+Release 2016a - 2016-01-26 23:28:02 -0800
+
+ Changes affecting future timestamps
+
+ America/Cayman will not observe daylight saving this year after all.
+ Revert our guess that it would. (Thanks to Matt Johnson.)
+
+ Asia/Chita switches from +0800 to +0900 on 2016-03-27 at 02:00.
+ (Thanks to Alexander Krivenyshev.)
+
+ Asia/Tehran now has DST predictions for the year 2038 and later,
+ to be March 21 00:00 to September 21 00:00. This is likely better
+ than predicting no DST, albeit off by a day every now and then.
+
+ Changes affecting past and future timestamps
+
+ America/Metlakatla switched from PST all year to AKST/AKDT on
+ 2015-11-01 at 02:00. (Thanks to Steffen Thorsen.)
+
+ America/Santa_Isabel has been removed, and replaced with a
+ backward compatibility link to America/Tijuana. Its contents were
+ apparently based on a misreading of Mexican legislation.
+
+ Changes affecting past timestamps
+
+ Asia/Karachi's two transition times in 2002 were off by a minute.
+ (Thanks to Matt Johnson.)
+
+ Changes affecting build procedure
+
+ An installer can now combine leap seconds with use of the backzone file,
+ e.g., with 'make PACKRATDATA=backzone REDO=posix_right zones'.
+ The old 'make posix_packrat' rule is now marked as obsolescent.
+ (Thanks to Ian Abbott for an initial implementation.)
+
+ Changes affecting documentation and commentary
+
+ A new file LICENSE makes it easier to see that the code and data
+ are mostly public-domain. (Thanks to James Knight.) The three
+ non-public-domain files now use the current (3-clause) BSD license
+ instead of older versions of that license.
+
+ tz-link.htm mentions the BDE library (thanks to Andrew Paprocki),
+ CCTZ (thanks to Tim Parenti), TimeJones.com, and has a new section
+ on editing tz source files (with a mention of Sublime zoneinfo,
+ thanks to Gilmore Davidson).
+
+ The Theory and asia files now mention the 2015 book "The Global
+ Transformation of Time, 1870-1950", and cite a couple of reviews.
+
+ The America/Chicago entry now documents the informal use of US
+ central time in Fort Pierre, South Dakota. (Thanks to Rick
+ McDermid, Matt Johnson, and Steve Jones.)
+
+
+Release 2015g - 2015-10-01 00:39:51 -0700
+
+ Changes affecting future timestamps
+
+ Turkey's 2015 fall-back transition is scheduled for Nov. 8, not Oct. 25.
+ (Thanks to Fatih.)
+
+ Norfolk moves from +1130 to +1100 on 2015-10-04 at 02:00 local time.
+ (Thanks to Alexander Krivenyshev.)
+
+ Fiji's 2016 fall-back transition is scheduled for January 17, not 24.
+ (Thanks to Ken Rylander.)
+
+ Fort Nelson, British Columbia will not fall back on 2015-11-01. It has
+ effectively been on MST (-0700) since it advanced its clocks on 2015-03-08.
+ New zone America/Fort_Nelson. (Thanks to Matt Johnson.)
+
+ Changes affecting past timestamps
+
+ Norfolk observed DST from 1974-10-27 02:00 to 1975-03-02 02:00.
+
+ Changes affecting code
+
+ localtime no longer mishandles America/Anchorage after 2037.
+ (Thanks to Bradley White for reporting the bug.)
+
+ On hosts with signed 32-bit time_t, localtime no longer mishandles
+ Pacific/Fiji after 2038-01-16 14:00 UTC.
+
+ The localtime module allows the variables 'timezone', 'daylight',
+ and 'altzone' to be in common storage shared with other modules,
+ and declares them in case the system <time.h> does not.
+ (Problems reported by Kees Dekker.)
+
+ On platforms with tm_zone, strftime.c now assumes it is not NULL.
+ This simplifies the code and is consistent with zdump.c.
+ (Problem reported by Christos Zoulas.)
+
+ Changes affecting documentation
+
+ The tzfile man page now documents that transition times denote the
+ starts (not the ends) of the corresponding time periods.
+ (Ambiguity reported by Bill Seymour.)
+
+
+Release 2015f - 2015-08-10 18:06:56 -0700
+
+ Changes affecting future timestamps
+
+ North Korea switches to +0830 on 2015-08-15. (Thanks to Steffen Thorsen.)
+ The abbreviation remains "KST". (Thanks to Robert Elz.)
+
+ Uruguay no longer observes DST. (Thanks to Steffen Thorsen
+ and Pablo Camargo.)
+
+ Changes affecting past and future timestamps
+
+ Moldova starts and ends DST at 00:00 UTC, not at 01:00 UTC.
+ (Thanks to Roman Tudos.)
+
+ Changes affecting data format and code
+
+ zic's '-y YEARISTYPE' option is no longer documented. The TYPE
+ field of a Rule line should now be '-'; the old values 'even',
+ 'odd', 'uspres', 'nonpres', 'nonuspres' were already undocumented.
+ Although the implementation has not changed, these features do not
+ work in the default installation, they are not used in the data,
+ and they are now considered obsolescent.
+
+ zic now checks that two rules don't take effect at the same time.
+ (Thanks to Jon Skeet and Arthur David Olson.) Constraints on
+ simultaneity are now documented.
+
+ The two characters '%z' in a zone format now stand for the UT
+ offset, e.g., '-07' for seven hours behind UT and '+0530' for
+ five hours and thirty minutes ahead. This better supports time
+ zone abbreviations conforming to POSIX.1-2001 and later.
+
+ Changes affecting installed data files
+
+ Comments for America/Halifax and America/Glace_Bay have been improved.
+ (Thanks to Brian Inglis.)
+
+ Data entries have been simplified for Atlantic/Canary, Europe/Simferopol,
+ Europe/Sofia, and Europe/Tallinn. This yields slightly smaller
+ installed data files for Europe/Simferopol and Europe/Tallinn.
+ It does not affect timestamps. (Thanks to Howard Hinnant.)
+
+ Changes affecting code
+
+ zdump and zic no longer warn about valid time zone abbreviations
+ like '-05'.
+
+ Some Visual Studio 2013 warnings have been suppressed.
+ (Thanks to Kees Dekker.)
+
+ 'date' no longer sets the time of day and its -a, -d, -n and -t
+ options have been removed. Long obsolescent, the implementation
+ of these features had porting problems. Builders no longer need
+ to configure HAVE_ADJTIME, HAVE_SETTIMEOFDAY, or HAVE_UTMPX_H.
+ (Thanks to Kees Dekker for pointing out the problem.)
+
+ Changes affecting documentation
+
+ The Theory file mentions naming issues earlier, as these seem to be
+ poorly publicized (thanks to Gilmore Davidson for reporting the problem).
+
+ tz-link.htm mentions Time Zone Database Parser (thanks to Howard Hinnant).
+
+ Mention that Herbert Samuel introduced the term "Summer Time".
+
+
+Release 2015e - 2015-06-13 10:56:02 -0700
+
+ Changes affecting future timestamps
+
+ Morocco will suspend DST from 2015-06-14 03:00 through 2015-07-19 02:00,
+ not 06-13 and 07-18 as we had guessed. (Thanks to Milamber.)
+
+ Assume Cayman Islands will observe DST starting next year, using US rules.
+ Although it isn't guaranteed, it is the most likely.
+
+ Changes affecting data format
+
+ The file 'iso3166.tab' now uses UTF-8, so that its entries can better
+ spell the names of Åland Islands, Côte d'Ivoire, and Réunion.
+
+ Changes affecting code
+
+ When displaying data, tzselect converts it to the current locale's
+ encoding if the iconv command works. (Problem reported by random832.)
+
+ tzselect no longer mishandles Dominica, fixing a bug introduced
+ in Release 2014f. (Problem reported by Owen Leibman.)
+
+ zic -l no longer fails when compiled with -DTZDEFAULT=\"/etc/localtime\".
+ This fixes a bug introduced in Release 2014f.
+ (Problem reported by Leonardo Chiquitto.)
+
+
+Release 2015d - 2015-04-24 08:09:46 -0700
+
+ Changes affecting future timestamps
+
+ Egypt will not observe DST in 2015 and will consider canceling it
+ permanently. For now, assume no DST indefinitely.
+ (Thanks to Ahmed Nazmy and Tim Parenti.)
+
+ Changes affecting past timestamps
+
+ America/Whitehorse switched from UT -09 to -08 on 1967-05-28, not
+ 1966-07-01. Also, Yukon's time zone history is documented better.
+ (Thanks to Brian Inglis and Dennis Ferguson.)
+
+ Change affecting past and future time zone abbreviations
+
+ The abbreviations for Hawaii-Aleutian standard and daylight times
+ have been changed from HAST/HADT to HST/HDT, as per US Government
+ Printing Office style. This affects only America/Adak since 1983,
+ as America/Honolulu was already using the new style.
+
+ Changes affecting code
+
+ zic has some minor performance improvements.
+
+
+Release 2015c - 2015-04-11 08:55:55 -0700
+
+ Changes affecting future timestamps
+
+ Egypt's spring-forward transition is at 24:00 on April's last Thursday,
+ not 00:00 on April's last Friday. 2015's transition will therefore be on
+ Thursday, April 30 at 24:00, not Friday, April 24 at 00:00. Similar fixes
+ apply to 2026, 2037, 2043, etc. (Thanks to Steffen Thorsen.)
+
+ Changes affecting past timestamps
+
+ The following changes affect some pre-1991 Chile-related timestamps
+ in America/Santiago, Antarctica/Palmer, and Pacific/Easter.
+
+ The 1910 transition was January 10, not January 1.
+
+ The 1918 transition was September 10, not September 1.
+
+ The UT -04 time observed from 1932 to 1942 is now considered to
+ be standard time, not year-round DST.
+
+ Santiago observed DST (UT -03) from 1946-07-15 through
+ 1946-08-31, then reverted to standard time, then switched to -05
+ on 1947-04-01.
+
+ Assume transitions before 1968 were at 00:00, since we have no data
+ saying otherwise.
+
+ The spring 1988 transition was 1988-10-09, not 1988-10-02.
+ The fall 1990 transition was 1990-03-11, not 1990-03-18.
+
+ Assume no UT offset change for Pacific/Easter on 1890-01-01,
+ and omit all transitions on Pacific/Easter from 1942 through 1946
+ since we have no data suggesting that they existed.
+
+ One more zone has been turned into a link, as it differed
+ from an existing zone only for older timestamps. As usual,
+ this change affects UT offsets in pre-1970 timestamps only.
+ The zone's old contents have been moved to the 'backzone' file.
+ The affected zone is America/Montreal.
+
+ Changes affecting commentary
+
+ Mention the TZUpdater tool.
+
+ Mention "The Time Now". (Thanks to Brandon Ramsey.)
+
+
+Release 2015b - 2015-03-19 23:28:11 -0700
+
+ Changes affecting future timestamps
+
+ Mongolia will start observing DST again this year, from the last
+ Saturday in March at 02:00 to the last Saturday in September at 00:00.
+ (Thanks to Ganbold Tsagaankhuu.)
+
+ Palestine will start DST on March 28, not March 27. Also,
+ correct the fall 2014 transition from September 26 to October 24.
+ Adjust future predictions accordingly. (Thanks to Steffen Thorsen.)
+
+ Changes affecting past timestamps
+
+ The 1982 zone shift in Pacific/Easter has been corrected, fixing a 2015a
+ regression. (Thanks to Stuart Bishop for reporting the problem.)
+
+ Some more zones have been turned into links, when they differed
+ from existing zones only for older timestamps. As usual,
+ these changes affect UT offsets in pre-1970 timestamps only.
+ Their old contents have been moved to the 'backzone' file.
+ The affected zones are: America/Antigua, America/Cayman,
+ Pacific/Midway, and Pacific/Saipan.
+
+ Changes affecting time zone abbreviations
+
+ Correct the 1992-2010 DST abbreviation in Volgograd from "MSK" to "MSD".
+ (Thanks to Hank W.)
+
+ Changes affecting code
+
+ Fix integer overflow bug in reference 'mktime' implementation.
+ (Problem reported by Jörg Richter.)
+
+ Allow -Dtime_tz=time_t compilations, and allow -Dtime_tz=... libraries
+ to be used in the same executable as standard-library time_t functions.
+ (Problems reported by Bradley White.)
+
+ Changes affecting commentary
+
+ Cite the recent Mexican decree changing Quintana Roo's time zone.
+ (Thanks to Carlos Raúl Perasso.)
+
+ Likewise for the recent Chilean decree. (Thanks to Eduardo Romero Urra.)
+
+ Update info about Mars time.
+
+
+Release 2015a - 2015-01-29 22:35:20 -0800
+
+ Changes affecting future timestamps
+
+ The Mexican state of Quintana Roo, represented by America/Cancun,
+ will shift from Central Time with DST to Eastern Time without DST
+ on 2015-02-01 at 02:00. (Thanks to Steffen Thorsen and Gwillim Law.)
+
+ Chile will not change clocks in April or thereafter; its new standard time
+ will be its old daylight saving time. This affects America/Santiago,
+ Pacific/Easter, and Antarctica/Palmer. (Thanks to Juan Correa.)
+
+ New leap second 2015-06-30 23:59:60 UTC as per IERS Bulletin C 49.
+ (Thanks to Tim Parenti.)
+
+ Changes affecting past timestamps
+
+ Iceland observed DST in 1919 and 1921, and its 1939 fallback
+ transition was Oct. 29, not Nov. 29. Remove incorrect data from
+ Shanks about time in Iceland between 1837 and 1908.
+
+ Some more zones have been turned into links, when they differed
+ from existing zones only for older timestamps. As usual,
+ these changes affect UT offsets in pre-1970 timestamps only.
+ Their old contents have been moved to the 'backzone' file.
+ The affected zones are: Asia/Aden, Asia/Bahrain, Asia/Kuwait,
+ and Asia/Muscat.
+
+ Changes affecting code
+
+ tzalloc now scrubs time zone abbreviations compatibly with the way
+ that tzset always has, by replacing invalid bytes with '_' and by
+ shortening too-long abbreviations.
+
+ tzselect ports to POSIX awk implementations, no longer mishandles
+ POSIX TZ settings when GNU awk is used, and reports POSIX TZ
+ settings to the user. (Thanks to Stefan Kuhn.)
+
+ Changes affecting build procedure
+
+ 'make check' now checks for links to links in the data.
+ One such link (for Africa/Asmera) has been fixed.
+ (Thanks to Stephen Colebourne for pointing out the problem.)
+
+ Changes affecting commentary
+
+ The leapseconds file commentary now mentions the expiration date.
+ (Problem reported by Martin Burnicki.)
+
+ Update Mexican Library of Congress URL.
+
+
+Release 2014j - 2014-11-10 17:37:11 -0800
+
+ Changes affecting current and future timestamps
+
+ Turks & Caicos' switch from US eastern time to UT -04 year-round
+ did not occur on 2014-11-02 at 02:00. It's currently scheduled
+ for 2015-11-01 at 02:00. (Thanks to Chris Walton.)
+
+ Changes affecting past timestamps
+
+ Many pre-1989 timestamps have been corrected for Asia/Seoul and
+ Asia/Pyongyang, based on sources for the Korean-language Wikipedia
+ entry for time in Korea. (Thanks to Sanghyuk Jung.) Also, no
+ longer guess that Pyongyang mimicked Seoul time after World War II,
+ as this is politically implausible.
+
+ Some more zones have been turned into links, when they differed
+ from existing zones only for older timestamps. As usual,
+ these changes affect UT offsets in pre-1970 timestamps only.
+ Their old contents have been moved to the 'backzone' file.
+ The affected zones are: Africa/Addis_Ababa, Africa/Asmara,
+ Africa/Dar_es_Salaam, Africa/Djibouti, Africa/Kampala,
+ Africa/Mogadishu, Indian/Antananarivo, Indian/Comoro, and
+ Indian/Mayotte.
+
+ Changes affecting commentary
+
+ The commentary is less enthusiastic about Shanks as a source,
+ and is more careful to distinguish UT from UTC.
+
+
+Release 2014i - 2014-10-21 22:04:57 -0700
+
+ Changes affecting future timestamps
+
+ Pacific/Fiji will observe DST from 2014-11-02 02:00 to 2015-01-18 03:00.
+ (Thanks to Ken Rylander for the heads-up.) Guess that future
+ years will use a similar pattern.
+
+ A new Zone Pacific/Bougainville, for the part of Papua New Guinea
+ that plans to switch from UT +10 to +11 on 2014-12-28 at 02:00.
+ (Thanks to Kiley Walbom for the heads-up.)
+
+ Changes affecting time zone abbreviations
+
+ Since Belarus is not changing its clocks even though Moscow is,
+ the time zone abbreviation in Europe/Minsk is changing from FET
+ to its more-traditional value MSK on 2014-10-26 at 01:00.
+ (Thanks to Alexander Bokovoy for the heads-up about Belarus.)
+
+ The new abbreviation IDT stands for the pre-1976 use of UT +08 in
+ Indochina, to distinguish it better from ICT (+07).
+
+ Changes affecting past timestamps
+
+ Many timestamps have been corrected for Asia/Ho_Chi_Minh before 1976
+ (thanks to Trần Ngọc Quân for an indirect pointer to Trần Tiến Bình's
+ authoritative book). Asia/Ho_Chi_Minh has been added to
+ zone1970.tab, to give tzselect users in Vietnam two choices,
+ since north and south Vietnam disagreed after our 1970 cutoff.
+
+ Asia/Phnom_Penh and Asia/Vientiane have been turned into links, as
+ they differed from existing zones only for older timestamps. As
+ usual, these changes affect pre-1970 timestamps only. Their old
+ contents have been moved to the 'backzone' file.
+
+ Changes affecting code
+
+ The time-related library functions now set errno on failure, and
+ some crashes in the new tzalloc-related library functions have
+ been fixed. (Thanks to Christos Zoulas for reporting most of
+ these problems and for suggesting fixes.)
+
+ If USG_COMPAT is defined and the requested timestamp is standard time,
+ the tz library's localtime and mktime functions now set the extern
+ variable timezone to a value appropriate for that timestamp; and
+ similarly for ALTZONE, daylight saving time, and the altzone variable.
+ This change is a companion to the tzname change in 2014h, and is
+ designed to make timezone and altzone more compatible with tzname.
+
+ The tz library's functions now set errno to EOVERFLOW if they fail
+ because the result cannot be represented. ctime and ctime_r now
+ return NULL and set errno when a timestamp is out of range, rather
+ than having undefined behavior.
+
+ Some bugs associated with the new 2014g functions have been fixed.
+ This includes a bug that largely incapacitated the new functions
+ time2posix_z and posix2time_z. (Thanks to Christos Zoulas.)
+ It also includes some uses of uninitialized variables after tzalloc.
+ The new code uses the standard type 'ssize_t', which the Makefile
+ now gives porting advice about.
+
+ Changes affecting commentary
+
+ Updated URLs for NRC Canada (thanks to Matt Johnson and Brian Inglis).
+
+
+Release 2014h - 2014-09-25 18:59:03 -0700
+
+ Changes affecting past timestamps
+
+ America/Jamaica's 1974 spring-forward transition was Jan. 6, not Apr. 28.
+
+ Shanks says Asia/Novokuznetsk switched from LMT (not "NMT") on 1924-05-01,
+ not 1920-01-06. The old entry was based on a misinterpretation of Shanks.
+
+ Some more zones have been turned into links, when they differed
+ from existing zones only for older timestamps. As usual,
+ these changes affect UT offsets in pre-1970 timestamps only.
+ Their old contents have been moved to the 'backzone' file.
+ The affected zones are: Africa/Blantyre, Africa/Bujumbura,
+ Africa/Gaborone, Africa/Harare, Africa/Kigali, Africa/Lubumbashi,
+ Africa/Lusaka, Africa/Maseru, and Africa/Mbabane.
+
+ Changes affecting code
+
+ zdump -V and -v now output gmtoff= values on all platforms,
+ not merely on platforms defining TM_GMTOFF.
+
+ The tz library's localtime and mktime functions now set tzname to a value
+ appropriate for the requested timestamp, and zdump now uses this
+ on platforms not defining TM_ZONE, fixing a 2014g regression.
+ (Thanks to Tim Parenti for reporting the problem.)
+
+ The tz library no longer sets tzname if localtime or mktime fails.
+
+ zdump -c no longer mishandles transitions near year boundaries.
+ (Thanks to Tim Parenti for reporting the problem.)
+
+ An access to uninitialized data has been fixed.
+ (Thanks to Jörg Richter for reporting the problem.)
+
+ When THREAD_SAFE is defined, the code ports to the C11 memory model.
+ A memory leak has been fixed if ALL_STATE and THREAD_SAFE are defined
+ and two threads race to initialize data used by gmtime-like functions.
+ (Thanks to Andy Heninger for reporting the problems.)
+
+ Changes affecting build procedure
+
+ 'make check' now checks better for properly-sorted data.
+
+ Changes affecting documentation and commentary
+
+ zdump's gmtoff=N output is now documented, and its isdst=D output
+ is now documented to possibly output D values other than 0 or 1.
+
+ zdump -c's treatment of years is now documented to use the
+ Gregorian calendar and Universal Time without leap seconds,
+ and its behavior at cutoff boundaries is now documented better.
+ (Thanks to Arthur David Olson and Tim Parenti for reporting the problems.)
+
+ Programs are now documented to use the proleptic Gregorian calendar.
+ (Thanks to Alan Barrett for the suggestion.)
+
+ Fractional-second GMT offsets have been documented for civil time
+ in 19th-century Chennai, Jakarta, and New York.
+
+
+Release 2014g - 2014-08-28 12:31:23 -0700
+
+ Changes affecting future timestamps
+
+ Turks & Caicos is switching from US eastern time to UT -04
+ year-round, modeled as a switch on 2014-11-02 at 02:00.
+ [As noted in 2014j, this switch was later delayed.]
+
+ Changes affecting past timestamps
+
+ Time in Russia or the USSR before 1926 or so has been corrected by
+ a few seconds in the following zones: Asia/Irkutsk,
+ Asia/Krasnoyarsk, Asia/Omsk, Asia/Samarkand, Asia/Tbilisi,
+ Asia/Vladivostok, Asia/Yakutsk, Europe/Riga, Europe/Samara. For
+ Asia/Yekaterinburg the correction is a few minutes. (Thanks to
+ Vladimir Karpinsky.)
+
+ The Portuguese decree of 1911-05-26 took effect on 1912-01-01.
+ This affects 1911 timestamps in Africa/Bissau, Africa/Luanda,
+ Atlantic/Azores, and Atlantic/Madeira. Also, Lisbon's pre-1912
+ GMT offset was -0:36:45 (rounded from -0:36:44.68), not -0:36:32.
+ (Thanks to Stephen Colebourne for pointing to the decree.)
+
+ Asia/Dhaka ended DST on 2009-12-31 at 24:00, not 23:59.
+
+ A new file 'backzone' contains data which may appeal to
+ connoisseurs of old timestamps, although it is out of scope for
+ the tz database, is often poorly sourced, and contains some data
+ that is known to be incorrect. The new file is not recommended
+ for ordinary use and its entries are not installed by default.
+ (Thanks to Lester Caine for the high-quality Jersey, Guernsey, and
+ Isle of Man entries.)
+
+ Some more zones have been turned into links, when they differed
+ from existing zones only for older timestamps. As usual,
+ these changes affect UT offsets in pre-1970 timestamps only.
+ Their old contents have been moved to the 'backzone' file.
+ The affected zones are: Africa/Bangui, Africa/Brazzaville,
+ Africa/Douala, Africa/Kinshasa, Africa/Libreville, Africa/Luanda,
+ Africa/Malabo, Africa/Niamey, and Africa/Porto-Novo.
+
+ Changes affecting code
+
+ Unless NETBSD_INSPIRED is defined to 0, the tz library now
+ supplies functions for creating and using objects that represent
+ timezones. The new functions are tzalloc, tzfree, localtime_rz,
+ mktime_z, and (if STD_INSPIRED is also defined) posix2time_z and
+ time2posix_z. They are intended for performance: for example,
+ localtime_rz (unlike localtime_r) is trivially thread-safe without
+ locking. (Thanks to Christos Zoulas for proposing NetBSD-inspired
+ functions, and to Alan Barrett and Jonathan Lennox for helping to
+ debug the change.)
+
+ zdump now builds with the tz library unless USE_LTZ is defined to 0,
+ This lets zdump use tz features even if the system library lacks them.
+ To build zdump with the system library, use 'make CFLAGS=-DUSE_LTZ=0
+ TZDOBJS=zdump.o CHECK_TIME_T_ALTERNATIVES='.
+
+ zdump now uses localtime_rz if available, as it's significantly faster,
+ and it can help zdump better diagnose invalid timezone names.
+ Define HAVE_LOCALTIME_RZ to 0 to suppress this. HAVE_LOCALTIME_RZ
+ defaults to 1 if NETBSD_INSPIRED && USE_LTZ. When localtime_rz is
+ not available, zdump now uses localtime_r and tzset if available,
+ as this is a bit cleaner and faster than plain localtime. Compile
+ with -DHAVE_LOCALTIME_R=0 and/or -DHAVE_TZSET=0 if your system
+ lacks these two functions.
+
+ If THREAD_SAFE is defined to 1, the tz library is now thread-safe.
+ Although not needed for tz's own applications, which are single-threaded,
+ this supports POSIX better if the tz library is used in multithreaded apps.
+
+ Some crashes have been fixed when zdump or the tz library is given
+ invalid or outlandish input.
+
+ The tz library no longer mishandles leap seconds on platforms with
+ unsigned time_t in timezones that lack ordinary transitions after 1970.
+
+ The tz code now attempts to infer TM_GMTOFF and TM_ZONE if not
+ already defined, to make it easier to configure on common platforms.
+ Define NO_TM_GMTOFF and NO_TM_ZONE to suppress this.
+
+ Unless the new macro UNINIT_TRAP is defined to 1, the tz code now
+ assumes that reading uninitialized memory yields garbage values
+ but does not cause other problems such as traps.
+
+ If TM_GMTOFF is defined and UNINIT_TRAP is 0, mktime is now
+ more likely to guess right for ambiguous timestamps near
+ transitions where tm_isdst does not change.
+
+ If HAVE_STRFTIME_L is defined to 1, the tz library now defines
+ strftime_l for compatibility with recent versions of POSIX.
+ Only the C locale is supported, though. HAVE_STRFTIME_L defaults
+ to 1 on recent POSIX versions, and to 0 otherwise.
+
+ tzselect -c now uses a hybrid distance measure that works better
+ in Africa. (Thanks to Alan Barrett for noting the problem.)
+
+ The C source code now ports to NetBSD when GCC_DEBUG_FLAGS is used,
+ or when time_tz is defined.
+
+ When HAVE_UTMPX_H is set the 'date' command now builds on systems
+ whose <utmpx.h> file does not define WTMPX_FILE, and when setting
+ the date it updates the wtmpx file if _PATH_WTMPX is defined.
+ This affects GNU/Linux and similar systems.
+
+ For easier maintenance later, some C code has been simplified,
+ some lint has been removed, and the code has been tweaked so that
+ plain 'make' is more likely to work.
+
+ The C type 'bool' is now used for boolean values, instead of 'int'.
+
+ The long-obsolete LOCALE_HOME code has been removed.
+
+ The long-obsolete 'gtime' function has been removed.
+
+ Changes affecting build procedure
+
+ 'zdump' no longer links in ialloc.o, as it's not needed.
+
+ 'make check_time_t_alternatives' no longer assumes GNU diff.
+
+ Changes affecting distribution tarballs
+
+ The files checktab.awk and zoneinfo2tdf.pl are now distributed in
+ the tzdata tarball instead of the tzcode tarball, since they help
+ maintain the data. The NEWS and Theory files are now also
+ distributed in the tzdata tarball, as they're relevant for data.
+ (Thanks to Alan Barrett for pointing this out.) Also, the
+ leapseconds.awk file is no longer distributed in the tzcode
+ tarball, since it belongs in the tzdata tarball (where 2014f
+ inadvertently also distributed it).
+
+ Changes affecting documentation and commentary
+
+ A new file CONTRIBUTING is distributed. (Thanks to Tim Parenti for
+ suggesting a CONTRIBUTING file, and to Tony Finch and Walter Harms
+ for debugging it.)
+
+ The man pages have been updated to use function prototypes,
+ to document thread-safe variants like localtime_r, and to document
+ the NetBSD-inspired functions tzalloc, tzfree, localtime_rz, and
+ mktime_z.
+
+ The fields in Link lines have been renamed to be more descriptive
+ and more like the parameters of 'ln'. LINK-FROM has become TARGET,
+ and LINK-TO has become LINK-NAME.
+
+ tz-link.htm mentions the IETF's tzdist working group; Windows
+ Runtime etc. (thanks to Matt Johnson); and HP-UX's tztab.
+
+ Some broken URLs have been fixed in the commentary. (Thanks to
+ Lester Caine.)
+
+ Commentary about Philippines DST has been updated, and commentary
+ on pre-1970 time in India has been added.
+
+
+Release 2014f - 2014-08-05 17:42:36 -0700
+
+ Changes affecting future timestamps
+
+ Russia will subtract an hour from most of its time zones on 2014-10-26
+ at 02:00 local time. (Thanks to Alexander Krivenyshev.)
+ There are a few exceptions: Magadan Oblast (Asia/Magadan) and Zabaykalsky
+ Krai are subtracting two hours; conversely, Chukotka Autonomous Okrug
+ (Asia/Anadyr), Kamchatka Krai (Asia/Kamchatka), Kemerovo Oblast
+ (Asia/Novokuznetsk), and the Samara Oblast and the Udmurt Republic
+ (Europe/Samara) are not changing their clocks. The changed zones are
+ Europe/Kaliningrad, Europe/Moscow, Europe/Simferopol, Europe/Volgograd,
+ Asia/Yekaterinburg, Asia/Omsk, Asia/Novosibirsk, Asia/Krasnoyarsk,
+ Asia/Irkutsk, Asia/Yakutsk, Asia/Vladivostok, Asia/Khandyga,
+ Asia/Sakhalin, and Asia/Ust-Nera; Asia/Magadan will have two hours
+ subtracted; and Asia/Novokuznetsk's time zone abbreviation is affected,
+ but not its UTC offset. Two zones are added: Asia/Chita (split
+ from Asia/Yakutsk, and also with two hours subtracted) and
+ Asia/Srednekolymsk (split from Asia/Magadan, but with only one hour
+ subtracted). (Thanks to Tim Parenti for much of the above.)
+
+ Changes affecting time zone abbreviations
+
+ Australian eastern time zone abbreviations are now AEST/AEDT not EST,
+ and similarly for the other Australian zones. That is, for eastern
+ standard and daylight saving time the abbreviations are AEST and AEDT
+ instead of the former EST for both; similarly, ACST/ACDT, ACWST/ACWDT,
+ and AWST/AWDT are now used instead of the former CST, CWST, and WST.
+ This change does not affect UT offsets, only time zone abbreviations.
+ (Thanks to Rich Tibbett and many others.)
+
+ Asia/Novokuznetsk shifts from NOVT to KRAT (remaining on UT +07)
+ effective 2014-10-26 at 02:00 local time.
+
+ The time zone abbreviation for Xinjiang Time (observed in Ürümqi)
+ has been changed from URUT to XJT. (Thanks to Luther Ma.)
+
+ Prefer MSK/MSD for Moscow time in Russia, even in other cities.
+ Similarly, prefer EET/EEST for eastern European time in Russia.
+
+ Change time zone abbreviations in (western) Samoa to use "ST" and
+ "DT" suffixes, as this is more likely to match common practice.
+ Prefix "W" to (western) Samoa time when its standard-time offset
+ disagrees with that of American Samoa.
+
+ America/Metlakatla now uses PST, not MeST, to abbreviate its time zone.
+
+ Time zone abbreviations have been updated for Japan's two time
+ zones used 1896-1937. JWST now stands for Western Standard
+ Time, and JCST for Central Standard Time (formerly this was CJT).
+ These abbreviations are now used for time in Korea, Taiwan,
+ and Sakhalin while controlled by Japan.
+
+ Changes affecting past timestamps
+
+ China's five zones have been simplified to two, since the post-1970
+ differences in the other three seem to have been imaginary. The
+ zones Asia/Harbin, Asia/Chongqing, and Asia/Kashgar have been
+ removed; backwards-compatibility links still work, albeit with
+ different behaviors for timestamps before May 1980. Asia/Urumqi's
+ 1980 transition to UT +08 has been removed, so that it is now at
+ +06 and not +08. (Thanks to Luther Ma and to Alois Treindl;
+ Treindl sent helpful translations of two papers by Guo Qingsheng.)
+
+ Some zones have been turned into links, when they differed from existing
+ zones only for older UT offsets where data entries were likely invented.
+ These changes affect UT offsets in pre-1970 timestamps only. This is
+ similar to the change in release 2013e, except this time for western
+ Africa. The affected zones are: Africa/Bamako, Africa/Banjul,
+ Africa/Conakry, Africa/Dakar, Africa/Freetown, Africa/Lome,
+ Africa/Nouakchott, Africa/Ouagadougou, Africa/Sao_Tome, and
+ Atlantic/St_Helena. This also affects the backwards-compatibility
+ link Africa/Timbuktu. (Thanks to Alan Barrett, Stephen Colebourne,
+ Tim Parenti, and David Patte for reporting problems in earlier
+ versions of this change.)
+
+ Asia/Shanghai's pre-standard-time UT offset has been changed from
+ 8:05:57 to 8:05:43, the location of Xujiahui Observatory. Its
+ transition to standard time has been changed from 1928 to 1901.
+
+ Asia/Taipei switched to JWST on 1896-01-01, then to JST on 1937-10-01,
+ then to CST on 1945-09-21 at 01:00, and did not observe DST in 1945.
+ In 1946 it observed DST from 05-15 through 09-30; in 1947
+ from 04-15 through 10-31; and in 1979 from 07-01 through 09-30.
+ (Thanks to Yu-Cheng Chuang.)
+
+ Asia/Riyadh's transition to standard time is now 1947-03-14, not 1950.
+
+ Europe/Helsinki's 1942 fall-back transition was 10-04 at 01:00, not
+ 10-03 at 00:00. (Thanks to Konstantin Hyppönen.)
+
+ Pacific/Pago_Pago has been changed from UT -11:30 to -11 for the
+ period from 1911 to 1950.
+
+ Pacific/Chatham has been changed to New Zealand standard time plus
+ 45 minutes for the period before 1957, reflecting a 1956 remark in
+ the New Zealand parliament.
+
+ Europe/Budapest has several pre-1946 corrections: in 1918 the transition
+ out of DST was on 09-16, not 09-29; in 1919 it was on 11-24, not 09-15; in
+ 1945 it was on 11-01, not 11-03; in 1941 the transition to DST was 04-08
+ not 04-06 at 02:00; and there was no DST in 1920.
+
+ Africa/Accra is now assumed to have observed DST from 1920 through 1935.
+
+ Time in Russia before 1927 or so has been corrected by a few seconds in
+ the following zones: Europe/Moscow, Asia/Irkutsk, Asia/Tbilisi,
+ Asia/Tashkent, Asia/Vladivostok, Asia/Yekaterinburg, Europe/Helsinki, and
+ Europe/Riga. Also, Moscow's location has been changed to its Kilometer 0
+ point. (Thanks to Vladimir Karpinsky for the Moscow changes.)
+
+ Changes affecting data format
+
+ A new file 'zone1970.tab' supersedes 'zone.tab' in the installed data.
+ The new file's extended format allows multiple country codes per zone.
+ The older file is still installed but is deprecated; its format is
+ not changing and it will still be distributed for a while, but new
+ applications should use the new file.
+
+ The new file format simplifies maintenance of obscure locations.
+ To test this, it adds coverage for the Crozet Islands and the
+ Scattered Islands. (Thanks to Tobias Conradi and Antoine Leca.)
+
+ The file 'iso3166.tab' is planned to switch from ASCII to UTF-8.
+ It is still ASCII now, but commentary about the switch has been added.
+ The new file 'zone1970.tab' already uses UTF-8.
+
+ Changes affecting code
+
+ 'localtime', 'mktime', etc. now use much less stack space if ALL_STATE
+ is defined. (Thanks to Elliott Hughes for reporting the problem.)
+
+ 'zic' no longer mishandles input when ignoring case in locales that
+ are not compatible with English, e.g., unibyte Turkish locales when
+ compiled with HAVE_GETTEXT.
+
+ Error diagnostics of 'zic' and 'yearistype' have been reworded so that
+ they no longer use ASCII '-' as if it were a dash.
+
+ 'zic' now rejects output file names that contain '.' or '..' components.
+ (Thanks to Tim Parenti for reporting the problem.)
+
+ 'zic -v' now warns about output file names that do not follow
+ POSIX rules, or that contain a digit or '.'. (Thanks to Arthur
+ David Olson for starting the ball rolling on this.)
+
+ Some lint has been removed when using GCC_DEBUG_FLAGS with GCC 4.9.0.
+
+ Changes affecting build procedure
+
+ 'zic' no longer links in localtime.o and asctime.o, as they're not needed.
+ (Thanks to John Cochran.)
+
+ Changes affecting documentation and commentary
+
+ The 'Theory' file documents legacy names, the longstanding
+ exceptions to the POSIX-inspired file name rules.
+
+ The 'zic' documentation clarifies the role of time types when
+ interpreting dates. (Thanks to Arthur David Olson.)
+
+ Documentation and commentary now prefer UTF-8 to US-ASCII,
+ allowing the use of proper accents in foreign words and names.
+ Code and data have not changed because of this. (Thanks to
+ Garrett Wollman, Ian Abbott, and Guy Harris for helping to debug
+ this.)
+
+ Non-HTML documentation and commentary now use plain-text URLs instead of
+ HTML insertions, and are more consistent about bracketing URLs when they
+ are not already surrounded by white space. (Thanks to suggestions by
+ Steffen Nurpmeso.)
+
+ There is new commentary about Xujiahui Observatory, the five time-zone
+ project in China from 1918 to 1949, timekeeping in Japanese-occupied
+ Shanghai, and Tibet Time in the 1950s. The sharp-eyed can spot the
+ warlord Jin Shuren in the data.
+
+ Commentary about the coverage of each Russian zone has been standardized.
+ (Thanks to Tim Parenti.)
+
+ There is new commentary about contemporary timekeeping in Ethiopia.
+
+ Obsolete comments about a 2007 proposal for DST in Kuwait has been removed.
+
+ There is new commentary about time in Poland in 1919.
+
+ Proper credit has been given to DST inventor George Vernon Hudson.
+
+ Commentary about time in Metlakatla, AK and Resolute, NU has been
+ improved, with a new source for the former.
+
+ In zone.tab, Pacific/Easter no longer mentions Salas y Gómez, as it
+ is uninhabited.
+
+ Commentary about permanent Antarctic bases has been updated.
+
+ Several typos have been corrected. (Thanks to Tim Parenti for
+ contributing some of these fixes.)
+
+ tz-link.htm now mentions the JavaScript libraries Moment Timezone,
+ TimezoneJS.Date, Walltime-js, and Timezone. (Thanks to a heads-up
+ from Matt Johnson.) Also, it mentions the Go 'latlong' package.
+ (Thanks to a heads-up from Dirkjan Ochtman.)
+
+ The files usno1988, usno1989, usno1989a, usno1995, usno1997, and usno1998
+ have been removed. These obsolescent US Naval Observatory entries were no
+ longer helpful for maintenance. (Thanks to Tim Parenti for the suggestion.)
+
+
+Release 2014e - 2014-06-12 21:53:52 -0700
+
+ Changes affecting near-future timestamps
+
+ Egypt's 2014 Ramadan-based transitions are June 26 and July 31 at 24:00.
+ (Thanks to Imed Chihi.) Guess that from 2015 on Egypt will temporarily
+ switch to standard time at 24:00 the last Thursday before Ramadan, and
+ back to DST at 00:00 the first Friday after Ramadan.
+
+ Similarly, Morocco's are June 28 at 03:00 and August 2 at 02:00. (Thanks
+ to Milamber Space Network.) Guess that from 2015 on Morocco will
+ temporarily switch to standard time at 03:00 the last Saturday before
+ Ramadan, and back to DST at 02:00 the first Saturday after Ramadan.
+
+ Changes affecting past timestamps
+
+ The abbreviation "MSM" (Moscow Midsummer Time) is now used instead of
+ "MSD" for Moscow's double daylight time in summer 1921. Also, a typo
+ "VLASST" has been repaired to be "VLAST" for Vladivostok summer time
+ in 1991. (Thanks to Hank W. for reporting the problems.)
+
+ Changes affecting commentary
+
+ tz-link.htm now cites RFC 7265 for jCal, mentions PTP and the
+ draft CalDAV extension, updates URLs for TSP, TZInfo, IATA, and
+ removes stale pointers to World Time Explorer and WORLDTIME.
+
+
+Release 2014d - 2014-05-27 21:34:40 -0700
+
+ Changes affecting code
+
+ zic no longer generates files containing timestamps before the Big Bang.
+ This works around GNOME glib bug 878
+ <https://gitlab.gnome.org/GNOME/glib/issues/878>
+ (Thanks to Leonardo Chiquitto for reporting the bug, and to
+ Arthur David Olson and James Cloos for suggesting improvements to the fix.)
+
+ Changes affecting documentation
+
+ tz-link.htm now mentions GNOME.
+
+
+Release 2014c - 2014-05-13 07:44:13 -0700
+
+ Changes affecting near-future timestamps
+
+ Egypt observes DST starting 2014-05-15 at 24:00.
+ (Thanks to Ahmad El-Dardiry and Gunther Vermier.)
+ Details have not been announced, except that DST will not be observed
+ during Ramadan. Guess that DST will stop during the same Ramadan dates as
+ Morocco, and that Egypt's future spring and fall transitions will be the
+ same as 2010 when it last observed DST, namely April's last Friday at
+ 00:00 to September's last Thursday at 23:00 standard time. Also, guess
+ that Ramadan transitions will be at 00:00 standard time.
+
+ Changes affecting code
+
+ zic now generates transitions for minimum time values, eliminating guesswork
+ when handling low-valued timestamps. (Thanks to Arthur David Olson.)
+
+ Port to Cygwin sans glibc. (Thanks to Arthur David Olson.)
+
+ Changes affecting commentary and documentation
+
+ Remove now-confusing comment about Jordan. (Thanks to Oleksii Nochovnyi.)
+
+
+Release 2014b - 2014-03-24 21:28:50 -0700
+
+ Changes affecting near-future timestamps
+
+ Crimea switches to Moscow time on 2014-03-30 at 02:00 local time.
+ (Thanks to Alexander Krivenyshev.) Move its zone.tab entry from UA to RU.
+
+ New entry for Troll station, Antarctica. (Thanks to Paul-Inge Flakstad and
+ Bengt-Inge Larsson.) This is currently an approximation; a better version
+ will require the zic and localtime fixes mentioned below, and the plan is
+ to wait for a while until at least the zic fixes propagate.
+
+ Changes affecting code
+
+ 'zic' and 'localtime' no longer reject locations needing four transitions
+ per year for the foreseeable future. (Thanks to Andrew Main (Zefram).)
+ Also, 'zic' avoids some unlikely failures due to integer overflow.
+
+ Changes affecting build procedure
+
+ 'make check' now detects Rule lines defined but never used.
+ The NZAQ rules, an instance of this problem, have been removed.
+
+ Changes affecting commentary and documentation
+
+ Fix Tuesday/Thursday typo in description of time in Israel.
+ (Thanks to Bert Katz via Pavel Kharitonov and Mike Frysinger.)
+
+ Microsoft Windows 8.1 doesn't support tz database names. (Thanks
+ to Donald MacQueen.) Instead, the Microsoft Windows Store app
+ library supports them.
+
+ Add comments about Johnston Island time in the 1960s.
+ (Thanks to Lyle McElhaney.)
+
+ Morocco's 2014 DST start will be as predicted.
+ (Thanks to Sebastien Willemijns.)
+
+
+Release 2014a - 2014-03-07 23:30:29 -0800
+
+ Changes affecting near-future timestamps
+
+ Turkey begins DST on 2014-03-31, not 03-30. (Thanks to Faruk Pasin for
+ the heads-up, and to Tim Parenti for simplifying the update.)
+
+ Changes affecting past timestamps
+
+ Fiji ended DST on 2014-01-19 at 02:00, not the previously-scheduled 03:00.
+ (Thanks to Steffen Thorsen.)
+
+ Ukraine switched from Moscow to Eastern European time on 1990-07-01
+ (not 1992-01-01), and observed DST during the entire next winter.
+ (Thanks to Vladimir in Moscow via Alois Treindl.)
+
+ In 1988 Israel observed DST from 04-10 to 09-04, not 04-09 to 09-03.
+ (Thanks to Avigdor Finkelstein.)
+
+ Changes affecting code
+
+ A uninitialized-storage bug in 'localtime' has been fixed.
+ (Thanks to Logan Chien.)
+
+ Changes affecting the build procedure
+
+ The settings for 'make check_web' now default to Ubuntu 13.10.
+
+ Changes affecting commentary and documentation
+
+ The boundary of the US Pacific time zone is given more accurately.
+ (Thanks to Alan Mintz.)
+
+ Chile's 2014 DST will be as predicted. (Thanks to José Miguel Garrido.)
+
+ Paraguay's 2014 DST will be as predicted. (Thanks to Carlos Raúl Perasso.)
+
+ Better descriptions of countries with same time zone history as
+ Trinidad and Tobago since 1970. (Thanks to Alan Barrett for suggestion.)
+
+ Several changes affect tz-link.htm, the main web page.
+
+ Mention Time.is (thanks to Even Scharning) and WX-now (thanks to
+ David Braverman).
+
+ Mention xCal (Internet RFC 6321) and jCal.
+
+ Microsoft has some support for tz database names.
+
+ CLDR data formats include both XML and JSON.
+
+ Mention Maggiolo's map of solar vs standard time.
+ (Thanks to Arthur David Olson.)
+
+ Mention TZ4Net. (Thanks to Matt Johnson.)
+
+ Mention the timezone-olson Haskell package.
+
+ Mention zeitverschiebung.net. (Thanks to Martin Jäger.)
+
+ Remove moribund links to daylight-savings-time.info and to
+ Simple Timer + Clocks.
+
+ Update two links. (Thanks to Oscar van Vlijmen.)
+
+ Fix some formatting glitches, e.g., remove random newlines from
+ abbr elements' title attributes.
+
+
+Release 2013i - 2013-12-17 07:25:23 -0800
+
+ Changes affecting near-future timestamps:
+
+ Jordan switches back to standard time at 00:00 on December 20, 2013.
+ The 2006-2011 transition schedule is planned to resume in 2014.
+ (Thanks to Steffen Thorsen.)
+
+ Changes affecting past timestamps:
+
+ In 2004, Cuba began DST on March 28, not April 4.
+ (Thanks to Steffen Thorsen.)
+
+ Changes affecting code
+
+ The compile-time flag NOSOLAR has been removed, as nowadays the
+ benefit of slightly shrinking runtime table size is outweighed by the
+ cost of disallowing potential future updates that exceed old limits.
+
+ Changes affecting documentation and commentary
+
+ The files solar87, solar88, and solar89 are no longer distributed.
+ They were a negative experiment - that is, a demonstration that
+ tz data can represent solar time only with some difficulty and error.
+ Their presence in the distribution caused confusion, as Riyadh
+ civil time was generally not solar time in those years.
+
+ tz-link.htm now mentions Noda Time. (Thanks to Matt Johnson.)
+
+
+Release 2013h - 2013-10-25 15:32:32 -0700
+
+ Changes affecting current and future timestamps:
+
+ Libya has switched its UT offset back to +02 without DST, instead
+ of +01 with DST. (Thanks to Even Scharning.)
+
+ Western Sahara (Africa/El_Aaiun) uses Morocco's DST rules.
+ (Thanks to Gwillim Law.)
+
+ Changes affecting future timestamps:
+
+ Acre and (we guess) western Amazonas will switch from UT -04 to -05
+ on 2013-11-10. This affects America/Rio_Branco and America/Eirunepe.
+ (Thanks to Steffen Thorsen.)
+
+ Add entries for DST transitions in Morocco in the year 2038.
+ This avoids some year-2038 glitches introduced in 2013g.
+ (Thanks to Yoshito Umaoka for reporting the problem.)
+
+ Changes affecting API
+
+ The 'tzselect' command no longer requires the 'select' command,
+ and should now work with /bin/sh on more platforms. It also works
+ around a bug in BusyBox awk before version 1.21.0. (Thanks to
+ Patrick 'P. J.' McDermott and Alan Barrett.)
+
+ Changes affecting code
+
+ Fix localtime overflow bugs with 32-bit unsigned time_t.
+
+ zdump no longer assumes sscanf returns maximal values on overflow.
+
+ Changes affecting the build procedure
+
+ The builder can specify which programs to use, if any, instead of
+ 'ar' and 'ranlib', and libtz.a is now built locally before being
+ installed. (Thanks to Michael Forney.)
+
+ A dependency typo in the 'zdump' rule has been fixed.
+ (Thanks to Andrew Paprocki.)
+
+ The Makefile has been simplified by assuming that 'mkdir -p' and 'cp -f'
+ work as specified by POSIX.2-1992 or later; this is portable nowadays.
+
+ 'make clean' no longer removes 'leapseconds', since it's
+ host-independent and is part of the distribution.
+
+ The unused makefile macros TZCSRCS, TZDSRCS, DATESRCS have been removed.
+
+ Changes affecting documentation and commentary
+
+ tz-link.htm now mentions TC TIMEZONE's draft time zone service protocol
+ (thanks to Mike Douglass) and TimezoneJS.Date (thanks to Jim Fehrle).
+
+ Update URLs in tz-link page. Add URLs for Microsoft Windows, since
+ 8.1 introduces tz support. Remove URLs for Tru64 and UnixWare (no
+ longer maintained) and for old advisories. SOFA now does C.
+
+Release 2013g - 2013-09-30 21:08:26 -0700
+
+ Changes affecting current and near-future timestamps
+
+ Morocco now observes DST from the last Sunday in March to the last
+ Sunday in October, not April to September respectively. (Thanks
+ to Steffen Thorsen.)
+
+ Changes affecting 'zic'
+
+ 'zic' now runs on platforms that lack both hard links and symlinks.
+ (Thanks to Theo Veenker for reporting the problem, for MinGW.)
+ Also, fix some bugs on platforms that lack hard links but have symlinks.
+
+ 'zic -v' again warns that Asia/Tehran has no POSIX environment variable
+ to predict the far future, fixing a bug introduced in 2013e.
+
+ Changes affecting the build procedure
+
+ The 'leapseconds' file is again put into the tzdata tarball.
+ Also, 'leapseconds.awk', so tzdata is self-contained. (Thanks to
+ Matt Burgess and Ian Abbott.) The timestamps of these and other
+ dependent files in tarballs are adjusted more consistently.
+
+ Changes affecting documentation and commentary
+
+ The README file is now part of the data tarball as well as the code.
+ It now states that files are public domain unless otherwise specified.
+ (Thanks to Andrew Main (Zefram) for asking for clarifications.)
+ Its details about the 1989 release moved to a place of honor near
+ the end of NEWS.
+
+
+Release 2013f - 2013-09-24 23:37:36 -0700
+
+ Changes affecting near-future timestamps
+
+ Tocantins will very likely not observe DST starting this spring.
+ (Thanks to Steffen Thorsen.)
+
+ Jordan will likely stay at UT +03 indefinitely, and will not fall
+ back this fall.
+
+ Palestine will fall back at 00:00, not 01:00. (Thanks to Steffen Thorsen.)
+
+ Changes affecting API
+
+ The types of the global variables 'timezone' and 'altzone' (if present)
+ have been changed back to 'long'. This is required for 'timezone'
+ by POSIX, and for 'altzone' by common practice, e.g., Solaris 11.
+ These variables were originally 'long' in the tz code, but were
+ mistakenly changed to 'time_t' in 1987; nobody reported the
+ incompatibility until now. The difference matters on x32, where
+ 'long' is 32 bits and 'time_t' is 64. (Thanks to Elliott Hughes.)
+
+ Changes affecting the build procedure
+
+ Avoid long strings in leapseconds.awk to work around a mawk bug.
+ (Thanks to Cyril Baurand.)
+
+ Changes affecting documentation and commentary
+
+ New file 'NEWS' that contains release notes like this one.
+
+ Paraguay's law does not specify DST transition time; 00:00 is customary.
+ (Thanks to Waldemar Villamayor-Venialbo.)
+
+ Minor capitalization fixes.
+
+ Changes affecting version-control only
+
+ The experimental GitHub repository now contains annotated and
+ signed tags for recent releases, e.g., '2013e' for Release 2013e.
+ Releases are tagged starting with 2012e; earlier releases were
+ done differently, and tags would either not have a simple name or
+ not exactly match what was released.
+
+ 'make set-timestamps' is now simpler and a bit more portable.
+
+
+Release 2013e - 2013-09-19 23:50:04 -0700
+
+ Changes affecting near-future timestamps
+
+ This year Fiji will start DST on October 27, not October 20.
+ (Thanks to David Wheeler for the heads-up.) For now, guess that
+ Fiji will continue to spring forward the Sunday before the fourth
+ Monday in October.
+
+ Changes affecting current and future time zone abbreviations
+
+ Use WIB/WITA/WIT rather than WIT/CIT/EIT for alphabetic Indonesian
+ time zone abbreviations since 1932. (Thanks to George Ziegler,
+ Priyadi Iman Nurcahyo, Zakaria, Jason Grimes, Martin Pitt, and
+ Benny Lin.) This affects Asia/Dili, Asia/Jakarta, Asia/Jayapura,
+ Asia/Makassar, and Asia/Pontianak.
+
+ Use ART (UT -03, standard time), rather than WARST (also -03, but
+ daylight saving time) for San Luis, Argentina since 2009.
+
+ Changes affecting Godthåb timestamps after 2037 if version mismatch
+
+ Allow POSIX-like TZ strings where the transition time's hour can
+ range from -167 through 167, instead of the POSIX-required 0
+ through 24. E.g., TZ='FJT-12FJST,M10.3.1/146,M1.3.4/75' for the
+ new Fiji rules. This is a more-compact way to represent
+ far-future timestamps for America/Godthab, America/Santiago,
+ Antarctica/Palmer, Asia/Gaza, Asia/Hebron, Asia/Jerusalem,
+ Pacific/Easter, and Pacific/Fiji. Other zones are unaffected by
+ this change. (Derived from a suggestion by Arthur David Olson.)
+
+ Allow POSIX-like TZ strings where daylight saving time is in
+ effect all year. E.g., TZ='WART4WARST,J1/0,J365/25' for Western
+ Argentina Summer Time all year. This supports a more-compact way
+ to represent the 2013d data for America/Argentina/San_Luis.
+ Because of the change for San Luis noted above this change does not
+ affect the current data. (Thanks to Andrew Main (Zefram) for
+ suggestions that improved this change.)
+
+ Where these two TZ changes take effect, there is a minor extension
+ to the tz file format in that it allows new values for the
+ embedded TZ-format string, and the tz file format version number
+ has therefore been increased from 2 to 3 as a precaution.
+ Version-2-based client code should continue to work as before for
+ all timestamps before 2038. Existing version-2-based client code
+ (tzcode, GNU/Linux, Solaris) has been tested on version-3-format
+ files, and typically works in practice even for timestamps after
+ 2037; the only known exception is America/Godthab.
+
+ Changes affecting timestamps before 1970
+
+ Pacific/Johnston is now a link to Pacific/Honolulu. This corrects
+ some errors before 1947.
+
+ Some zones have been turned into links, when they differ from existing
+ zones only in older data entries that were likely invented or that
+ differ only in LMT or transitions from LMT. These changes affect
+ only timestamps before 1943. The affected zones are:
+ Africa/Juba, America/Anguilla, America/Aruba, America/Dominica,
+ America/Grenada, America/Guadeloupe, America/Marigot,
+ America/Montserrat, America/St_Barthelemy, America/St_Kitts,
+ America/St_Lucia, America/St_Thomas, America/St_Vincent,
+ America/Tortola, and Europe/Vaduz. (Thanks to Alois Treindl for
+ confirming that the old Europe/Vaduz zone was wrong and the new
+ link is better for WWII-era times.)
+
+ Change Kingston Mean Time from -5:07:12 to -5:07:11. This affects
+ America/Cayman, America/Jamaica and America/Grand_Turk timestamps
+ from 1890 to 1912.
+
+ Change the UT offset of Bern Mean Time from 0:29:44 to 0:29:46.
+ This affects Europe/Zurich timestamps from 1853 to 1894. (Thanks
+ to Alois Treindl.)
+
+ Change the date of the circa-1850 Zurich transition from 1849-09-12
+ to 1853-07-16, overriding Shanks with data from Messerli about
+ postal and telegraph time in Switzerland.
+
+ Changes affecting time zone abbreviations before 1970
+
+ For Asia/Jakarta, use BMT (not JMT) for mean time from 1923 to 1932,
+ as Jakarta was called Batavia back then.
+
+ Changes affecting API
+
+ The 'zic' command now outputs a dummy transition when far-future
+ data can't be summarized using a TZ string, and uses a 402-year
+ window rather than a 400-year window. For the current data, this
+ affects only the Asia/Tehran file. It does not affect any of the
+ timestamps that this file represents, so zdump outputs the same
+ information as before. (Thanks to Andrew Main (Zefram).)
+
+ The 'date' command has a new '-r' option, which lets you specify
+ the integer time to display, a la FreeBSD.
+
+ The 'tzselect' command has two new options '-c' and '-n', which lets you
+ select a zone based on latitude and longitude.
+
+ The 'zic' command's '-v' option now warns about constructs that
+ require the new version-3 binary file format. (Thanks to Arthur
+ David Olson for the suggestion.)
+
+ Support for floating-point time_t has been removed.
+ It was always dicey, and POSIX no longer requires it.
+ (Thanks to Eric Blake for suggesting to the POSIX committee to
+ remove it, and thanks to Alan Barrett, Clive D.W. Feather, Andy
+ Heninger, Arthur David Olson, and Alois Treindl, for reporting
+ bugs and elucidating some of the corners of the old floating-point
+ implementation.)
+
+ The signatures of 'offtime', 'timeoff', and 'gtime' have been
+ changed back to the old practice of using 'long' to represent UT
+ offsets. This had been inadvertently and mistakenly changed to
+ 'int_fast32_t'. (Thanks to Christos Zoulas.)
+
+ The code avoids undefined behavior on integer overflow in some
+ more places, including gmtime, localtime, mktime and zdump.
+
+ Changes affecting the zdump utility
+
+ zdump now outputs "UT" when referring to Universal Time, not "UTC".
+ "UTC" does not make sense for timestamps that predate the introduction
+ of UTC, whereas "UT", a more-generic term, does. (Thanks to Steve Allen
+ for clarifying UT vs UTC.)
+
+ Data changes affecting behavior of tzselect and similar programs
+
+ Country code BQ is now called the more-common name "Caribbean Netherlands"
+ rather than the more-official "Bonaire, St Eustatius & Saba".
+
+ Remove from zone.tab the names America/Montreal, America/Shiprock,
+ and Antarctica/South_Pole, as they are equivalent to existing
+ same-country-code zones for post-1970 timestamps. The data entries for
+ these names are unchanged, so the names continue to work as before.
+
+ Changes affecting code internals
+
+ zic -c now runs way faster on 64-bit hosts when given large numbers.
+
+ zic now uses vfprintf to avoid allocating and freeing some memory.
+
+ tzselect now computes the list of continents from the data,
+ rather than have it hard-coded.
+
+ Minor changes pacify GCC 4.7.3 and GCC 4.8.1.
+
+ Changes affecting the build procedure
+
+ The 'leapseconds' file is now generated automatically from a
+ new file 'leap-seconds.list', which is a copy of
+ <ftp://ftp.nist.gov/pub/time/leap-seconds.list>
+ A new source file 'leapseconds.awk' implements this.
+ The goal is simplification of the future maintenance of 'leapseconds'.
+
+ When building the 'posix' or 'right' subdirectories, if the
+ subdirectory would be a copy of the default subdirectory, it is
+ now made a symbolic link if that is supported. This saves about
+ 2 MB of file system space.
+
+ The links America/Shiprock and Antarctica/South_Pole have been
+ moved to the 'backward' file. This affects only nondefault builds
+ that omit 'backward'.
+
+ Changes affecting version-control only
+
+ .gitignore now ignores 'date'.
+
+ Changes affecting documentation and commentary
+
+ Changes to the 'tzfile' man page
+
+ It now mentions that the binary file format may be extended in
+ future versions by appending data.
+
+ It now refers to the 'zdump' and 'zic' man pages.
+
+ Changes to the 'zic' man page
+
+ It lists conditions that elicit a warning with '-v'.
+
+ It says that the behavior is unspecified when duplicate names
+ are given, or if the source of one link is the target of another.
+
+ Its examples are updated to match the latest data.
+
+ The definition of white space has been clarified slightly.
+ (Thanks to Michael Deckers.)
+
+ Changes to the 'Theory' file
+
+ There is a new section about the accuracy of the tz database,
+ describing the many ways that errors can creep in, and
+ explaining why so many of the pre-1970 timestamps are wrong or
+ misleading (thanks to Steve Allen, Lester Caine, and Garrett
+ Wollman for discussions that contributed to this).
+
+ The 'Theory' file describes LMT better (this follows a
+ suggestion by Guy Harris).
+
+ It refers to the 2013 edition of POSIX rather than the 2004 edition.
+
+ It's mentioned that excluding 'backward' should not affect the
+ other data, and it suggests at least one zone.tab name per
+ inhabited country (thanks to Stephen Colebourne).
+
+ Some longstanding restrictions on names are documented, e.g.,
+ 'America/New_York' precludes 'America/New_York/Bronx'.
+
+ It gives more reasons for the 1970 cutoff.
+
+ It now mentions which time_t variants are supported, such as
+ signed integer time_t. (Thanks to Paul Goyette for reporting
+ typos in an experimental version of this change.)
+
+ (Thanks to Philip Newton for correcting typos in these changes.)
+
+ Documentation and commentary is more careful to distinguish UT in
+ general from UTC in particular. (Thanks to Steve Allen.)
+
+ Add a better source for the Zurich 1894 transition.
+ (Thanks to Pierre-Yves Berger.)
+
+ Update shapefile citations in tz-link.htm. (Thanks to Guy Harris.)
+
+
+Release 2013d - 2013-07-05 07:38:01 -0700
+
+ Changes affecting future timestamps:
+
+ Morocco's midsummer transitions this year are July 7 and August 10,
+ not July 9 and August 8. (Thanks to Andrew Paprocki.)
+
+ Israel now falls back on the last Sunday of October.
+ (Thanks to Ephraim Silverberg.)
+
+ Changes affecting past timestamps:
+
+ Specify Jerusalem's location more precisely; this changes the pre-1880
+ times by 2 s.
+
+ Changing affecting metadata only:
+
+ Fix typos in the entries for country codes BQ and SX.
+
+ Changes affecting code:
+
+ Rework the code to fix a bug with handling Australia/Macquarie on
+ 32-bit hosts (thanks to Arthur David Olson).
+
+ Port to platforms like NetBSD, where time_t can be wider than long.
+
+ Add support for testing time_t types other than the system's.
+ Run 'make check_time_t_alternatives' to try this out.
+ Currently, the tests fail for unsigned time_t;
+ this should get fixed at some point.
+
+ Changes affecting documentation and commentary:
+
+ Deemphasize the significance of national borders.
+
+ Update the zdump man page.
+
+ Remove obsolete NOID comment (thanks to Denis Excoffier).
+
+ Update several URLs and comments in the web pages.
+
+ Spelling fixes (thanks to Kevin Lyda and Jonathan Leffler).
+
+ Update URL for CLDR Zone->Tzid table (thanks to Yoshito Umaoka).
+
+
+Release 2013c - 2013-04-19 16:17:40 -0700
+
+ Changes affecting current and future timestamps:
+
+ Palestine observed DST starting March 29, 2013. (Thanks to
+ Steffen Thorsen.) From 2013 on, Gaza and Hebron both observe DST,
+ with the predicted rules being the last Thursday in March at 24:00
+ to the first Friday on or after September 21 at 01:00.
+
+ Assume that the recent change to Paraguay's DST rules is permanent,
+ by moving the end of DST to the 4th Sunday in March every year.
+ (Thanks to Carlos Raúl Perasso.)
+
+ Changes affecting past timestamps:
+
+ Fix some historical data for Palestine to agree with that of
+ timeanddate.com, as follows:
+
+ The spring 2008 change in Gaza and Hebron was on 00:00 Mar 28, not
+ 00:00 Apr 1.
+
+ The fall 2009 change in Gaza and Hebron on Sep 4 was at 01:00, not
+ 02:00.
+
+ The spring 2010 change in Hebron was 00:00 Mar 26, not 00:01 Mar 27.
+
+ The spring 2011 change in Gaza was 00:01 Apr 1, not 12:01 Apr 2.
+
+ The spring 2011 change in Hebron on Apr 1 was at 00:01, not 12:01.
+
+ The fall 2011 change in Hebron on Sep 30 was at 00:00, not 03:00.
+
+ Fix times of habitation for Macquarie to agree with the Tasmania
+ Parks & Wildlife Service history, which indicates that permanent
+ habitation was 1899-1919 and 1948 on.
+
+ Changing affecting metadata only:
+
+ Macquarie Island is politically part of Australia, not Antarctica.
+ (Thanks to Tobias Conradi.)
+
+ Sort Macquarie more-consistently with other parts of Australia.
+ (Thanks to Tim Parenti.)
+
+
+Release 2013b - 2013-03-10 22:33:40 -0700
+
+ Changes affecting current and future timestamps:
+
+ Haiti uses US daylight-saving rules this year, and presumably future years.
+ This changes timestamps starting today. (Thanks to Steffen Thorsen.)
+
+ Paraguay will end DST on March 24 this year.
+ (Thanks to Steffen Thorsen.) For now, assume it's just this year.
+
+ Morocco does not observe DST during Ramadan;
+ try to predict Ramadan in Morocco as best we can.
+ (Thanks to Erik Homoet for the heads-up.)
+
+ Changes affecting commentary:
+
+ Update URLs in tz-link page. Add URLs for webOS, BB10, iOS.
+ Update URL for Solaris. Mention Internet RFC 6557.
+ Update Internet RFCs 2445->5545, 2822->5322.
+ Switch from FTP to HTTP for Internet RFCs.
+
+
+Release 2013a - 2013-02-27 09:20:35 -0800
+
+ Change affecting binary data format:
+
+ The zone offset at the end of version-2-format zone files is now
+ allowed to be 24:00, as per POSIX.1-2008. (Thanks to Arthur David Olson.)
+
+ Changes affecting current and future timestamps:
+
+ Chile's 2013 rules, and we guess rules for 2014 and later, will be
+ the same as 2012, namely Apr Sun>=23 03:00 UTC to Sep Sun>=2 04:00 UTC.
+ (Thanks to Steffen Thorsen and Robert Elz.)
+
+ New Zones Asia/Khandyga, Asia/Ust-Nera, Europe/Busingen.
+ (Thanks to Tobias Conradi and Arthur David Olson.)
+
+ Many changes affect historical timestamps before 1940.
+ These were deduced from: Milne J. Civil time. Geogr J. 1899
+ Feb;13(2):173-94 <https://www.jstor.org/stable/1774359>.
+
+ Changes affecting the code:
+
+ Fix zic bug that mishandled Egypt's 2010 changes (this also affected
+ the data). (Thanks to Arthur David Olson.)
+
+ Fix localtime bug when time_t is unsigned and data files were generated
+ by a signed time_t system. (Thanks to Doug Bailey for reporting and
+ to Arthur David Olson for fixing.)
+
+ Allow the email address for bug reports to be set by the packager.
+ The default is tz@iana.org, as before. (Thanks to Joseph S. Myers.)
+
+ Update HTML checking to be compatible with Ubuntu 12.10.
+
+ Check that files are a safe subset of ASCII. At some point we may
+ relax this requirement to a safe subset of UTF-8. Without the
+ check, some non-UTF-8 encodings were leaking into the distribution.
+
+ Commentary changes:
+
+ Restore a comment about copyright notices that was inadvertently deleted.
+ (Thanks to Arthur David Olson.)
+
+ Improve the commentary about which districts observe what times
+ in Russia. (Thanks to Oscar van Vlijmen and Arthur David Olson.)
+
+ Add web page links to tz.js.
+
+ Add "Run by the Monkeys" to tz-art. (Thanks to Arthur David Olson.)
+
+
+Release 2012j - 2012-11-12 18:34:49 -0800
+
+ Libya moved to CET this weekend, but with DST planned next year.
+ (Thanks to Even Scharning, Steffen Thorsen, and Tim Parenti.)
+
+ Signatures now have the extension .asc, not .sign, as that's more
+ standard. (Thanks to Phil Pennock.)
+
+ The output of 'zdump --version', and of 'zic --version', now
+ uses a format that is more typical for --version.
+ (Thanks to Joseph S. Myers.)
+
+ The output of 'tzselect --help', 'zdump --help', and 'zic --help'
+ now uses tz@iana.org rather than the old elsie address.
+
+ zic -v now complains about abbreviations that are less than 3
+ or more than 6 characters, as per Posix. Formerly, it checked
+ for abbreviations that were more than 3.
+
+ 'make public' no longer puts its temporary directory under /tmp,
+ and uses the just-built zic rather than the system zic.
+
+ Various fixes to documentation and commentary.
+
+
+Release 2012i - 2012-11-03 12:57:09 -0700
+
+ Cuba switches from DST tomorrow at 01:00. (Thanks to Steffen Thorsen.)
+
+ Linker flags can now be specified via LDFLAGS.
+ AWK now defaults to 'awk', not 'nawk'.
+ The shell in tzselect now defaults to /bin/bash, but this can
+ be overridden by specifying KSHELL.
+ The main web page now mentions the unofficial GitHub repository.
+ (Thanks to Mike Frysinger.)
+
+ Tarball signatures can now be built by running 'make signatures'.
+ There are also new makefile rules 'tarballs', 'check_public', and
+ separate makefile rules for each tarball and signature file.
+ A few makefile rules are now more portable to strict POSIX.
+
+ The main web page now lists the canonical IANA URL.
+
+
+Release 2012h - 2012-10-26 22:49:10 -0700
+
+ Bahia no longer has DST. (Thanks to Kelley Cook.)
+
+ Tocantins has DST. (Thanks to Rodrigo Severo.)
+
+ Israel has new DST rules next year. (Thanks to Ephraim Silverberg.)
+
+ Jordan stays on DST this winter. (Thanks to Steffen Thorsen.)
+
+ Web page updates.
+
+ More C modernization, except that at Arthur David Olson's suggestion
+ the instances of 'register' were kept.
+
+
+Release 2012g - 2012-10-17 20:59:45 -0700
+
+ Samoa fall 2012 and later. (Thanks to Nicholas Pereira and Robert Elz.)
+
+ Palestine fall 2012. (Thanks to Steffen Thorsen.)
+
+ Assume C89.
+
+ To attack the version-number problem, this release ships the file
+ 'Makefile' (which contains the release number) in both the tzcode and
+ the tzdata tarballs. The two Makefiles are identical, and should be
+ identical in any matching pair of tarballs, so it shouldn't matter
+ which order you extract the tarballs. Perhaps we can come up with a
+ better version-number scheme at some point; this scheme does have the
+ virtue of not adding more files.
+
+
+Release 2012f - 2012-09-12 23:17:03 -0700
+
+ * australasia (Pacific/Fiji): Fiji DST is October 21 through January
+ 20 this year. (Thanks to Steffen Thorsen.)
+
+
+Release 2012e - 2012-08-02 20:44:55 -0700
+
+ * australasia (Pacific/Fakaofo): Tokelau is UT +13, not +14.
+ (Thanks to Steffen Thorsen.)
+
+ * Use a single version number for both code and data.
+
+ * .gitignore: New file.
+
+ * Remove trailing white space.
+
+
+Release code2012c-data2012d - 2012-07-19 16:35:33 -0700
+
+ Changes for Morocco's timestamps, which take effect in a couple of
+ hours, along with infrastructure changes to accommodate how the tz
+ code and data are released on IANA.
+
+
+Release data2012c - 2012-03-27 12:17:25 -0400
+
+ africa
+ Summer time changes for Morocco (to start late April 2012)
+
+ asia
+ Changes for 2012 for Gaza & the West Bank (Hebron) and Syria
+
+ northamerica
+ Haiti following US/Canada rules for 2012 (and we're assuming,
+ for now anyway, for the future).
+
+
+Release 2012b - 2012-03-02 12:29:15 +0700
+
+ There is just one change to tzcode2012b (compared with 2012a):
+ the Makefile that was accidentally included with 2012a has been
+ replaced with the version that should have been there, which is
+ identical with the previous version (from tzcode2011i).
+
+ There are just two changes in tzdata2012b compared with 2012a.
+
+ Most significantly, summer time in Cuba has been delayed 3 weeks
+ (now starts April 1 rather than March 11). Since Mar 11 (the old start
+ date, as listed in 2012a) is just a little over a week away, this
+ change is urgent.
+
+ Less importantly, an excess tab in one of the changes in zone.tab
+ in 2012a has been removed.
+
+
+Release 2012a - 2012-03-01 18:28:10 +0700
+
+ The changes in tzcode2012a (compared to the previous version, 2011i)
+ are entirely to the README and tz-art.htm and tz-link.htm files, if
+ none of those concern you, you can ignore the code update. The changes
+ reflect the changed addresses for the mailing list and the code and
+ data distribution points & methods (and a link to DateTime::TimeZone::Tzfile
+ has been added to tz-link.htm).
+
+ In tzdata2012a (compared to the previous release, which was 2011n)
+ the major changes are:
+ Chile 2011/2012 and 2012/2013 summer time date adjustments.
+ Falkland Islands onto permanent summer time (we're assuming for the
+ foreseeable future, though 2012 is all we're fairly certain of.)
+ Armenia has abolished Summer Time.
+ Tokelau jumped the International Date Line back last December
+ (just the same as their near neighbour, Samoa).
+ America/Creston is a new zone for a small area of British Columbia
+ There will be a leapsecond 2012-06-30 23:59:60 UTC.
+
+ Other minor changes are:
+ Corrections to 1918 Canadian summer time end dates.
+ Updated URL for UK time zone history (in comments)
+ A few typos in Le Corre's list of free French place names (comments)
+
+
+Release data2011n - 2011-10-30 14:57:54 +0700
+
+ There are three changes of note - most urgently, Cuba (America/Havana)
+ has extended summer time by two weeks, now to end on Nov 13, rather than
+ the (already past) Oct 30. Second, the Pridnestrovian Moldavian Republic
+ (Europe/Tiraspol) decided not to split from the rest of Moldova after
+ all, and consequently that zone has been removed (again) and reinstated
+ in the "backward" file as a link to Europe/Chisinau. And third, the
+ end date for Fiji's summer time this summer was moved forward from the
+ earlier planned Feb 26, to Jan 22.
+
+ Apart from that, Moldova (MD) returns to a single entry in zone.tab
+ (and the incorrect syntax that was in the 2011m version of that file
+ is so fixed - it would have been fixed in a different way had this
+ change not happened - that's the "missing" sccs version id).
+
+
+Release data2011m - 2011-10-24 21:42:16 +0700
+
+ In particular, the typos in comments in the data (2011-11-17 should have
+ been 2011-10-17 as Alan Barrett noted, and spelling of Tiraspol that
+ Tim Parenti noted) have been fixed, and the change for Ukraine has been
+ made in all 4 Ukrainian zones, rather than just Kiev (again, thanks to
+ Tim Parenti, and also Denys Gavrysh)
+
+ In addition, I added Europe/Tiraspol to zone.tab.
+
+ This time, all the files have new version numbers... (including the files
+ otherwise unchanged in 2011m that were changed in 2011l but didn't get new
+ version numbers there...)
+
+
+Release data2011l - 2011-10-10 11:15:43 +0700
+
+ There are just 2 changes that cause different generated tzdata files from
+ zic, to Asia/Hebron and Pacific/Fiji - the possible change for Bahia, Brazil
+ is included, but commented out. Compared with the diff I sent out last week,
+ this version also includes attributions for the sources for the changes
+ (in much the same format as ado used, but the html tags have not been
+ checked, verified, or used in any way at all, so if there are errors there,
+ please let me know.)
+
+
+Release data2011k - 2011-09-20 17:54:03 -0400
+
+ [not summarized]
+
+
+Release data2011j - 2011-09-12 09:22:49 -0400
+
+ (contemporary changes for Samoa; past changes for Kenya, Uganda, and
+ Tanzania); there are also two spelling corrections to comments in
+ the australasia file (with thanks to Christos Zoulas).
+
+
+Release 2011i - 2011-08-29 05:56:32 -0400
+
+ [not summarized]
+
+
+Release data2011h - 2011-06-15 18:41:48 -0400
+
+ Russia and Curaçao changes
+
+
+Release 2011g - 2011-04-25 09:07:22 -0400
+
+ update the rules for Egypt to reflect its abandonment of DST this year
+
+
+Release 2011f - 2011-04-06 17:14:53 -0400
+
+ [not summarized]
+
+
+Release 2011e - 2011-03-31 16:04:38 -0400
+
+ Morocco, Chile, and tz-link changes
+
+
+Release 2011d - 2011-03-14 09:18:01 -0400
+
+ changes that impact present-day timestamps in Cuba, Samoa, and Turkey
+
+
+Release 2011c - 2011-03-07 09:30:09 -0500
+
+ These do affect current timestamps in Chile and Annette Island, Canada.
+
+
+Release 2011b - 2011-02-07 08:44:50 -0500
+
+ [not summarized]
+
+
+Release 2011a - 2011-01-24 10:30:16 -0500
+
+ [not summarized]
+
+
+Release data2010o - 2010-11-01 09:18:23 -0400
+
+ change to the end of DST in Fiji in 2011
+
+
+Release 2010n - 2010-10-25 08:19:17 -0400
+
+ [not summarized]
+
+
+Release 2010m - 2010-09-27 09:24:48 -0400
+
+ Hong Kong, Vostok, and zic.c changes
+
+
+Release 2010l - 2010-08-16 06:57:25 -0400
+
+ [not summarized]
+
+
+Release 2010k - 2010-07-26 10:42:27 -0400
+
+ [not summarized]
+
+
+Release 2010j - 2010-05-10 09:07:48 -0400
+
+ changes for Bahía de Banderas and for version naming
+
+
+Release data2010i - 2010-04-16 18:50:45 -0400
+
+ the end of DST in Morocco on 2010-08-08
+
+
+Release data2010h - 2010-04-05 09:58:56 -0400
+
+ [not summarized]
+
+
+Release data2010g - 2010-03-24 11:14:53 -0400
+
+ [not summarized]
+
+
+Release 2010f - 2010-03-22 09:45:46 -0400
+
+ [not summarized]
+
+
+Release data2010e - 2010-03-08 14:24:27 -0500
+
+ corrects the Dhaka bug found by Danvin Ruangchan
+
+
+Release data2010d - 2010-03-06 07:26:01 -0500
+
+ [not summarized]
+
+
+Release 2010c - 2010-03-01 09:20:58 -0500
+
+ changes including KRE's suggestion for earlier initialization of
+ "goahead" and "goback" structure elements
+
+
+Release code2010a - 2010-02-16 10:40:04 -0500
+
+ [not summarized]
+
+
+Release data2010b - 2010-01-20 12:37:01 -0500
+
+ Mexico changes
+
+
+Release data2010a - 2010-01-18 08:30:04 -0500
+
+ changes to Dhaka
+
+
+Release data2009u - 2009-12-26 08:32:28 -0500
+
+ changes to DST in Bangladesh
+
+
+Release 2009t - 2009-12-21 13:24:27 -0500
+
+ [not summarized]
+
+
+Release data2009s - 2009-11-14 10:26:32 -0500
+
+ (cosmetic) Antarctica change and the DST-in-Fiji-in-2009-and-2010 change
+
+
+Release 2009r - 2009-11-09 10:10:31 -0500
+
+ "antarctica" and "tz-link.htm" changes
+
+
+Release 2009q - 2009-11-02 09:12:40 -0500
+
+ with two corrections as reported by Eric Muller and Philip Newton
+
+
+Release data2009p - 2009-10-23 15:05:27 -0400
+
+ Argentina (including San Luis) changes (with the correction from
+ Mariano Absatz)
+
+
+Release data2009o - 2009-10-14 16:49:38 -0400
+
+ Samoa (commentary only), Pakistan, and Bangladesh changes
+
+
+Release data2009n - 2009-09-22 15:13:38 -0400
+
+ added commentary for Argentina and a change to the end of DST in
+ 2009 in Pakistan
+
+
+Release data2009m - 2009-09-03 10:23:43 -0400
+
+ Samoa and Palestine changes
+
+
+Release data2009l - 2009-08-14 09:13:07 -0400
+
+ Samoa (comments only) and Egypt
+
+
+Release 2009k - 2009-07-20 09:46:08 -0400
+
+ [not summarized]
+
+
+Release data2009j - 2009-06-15 06:43:59 -0400
+
+ Bangladesh change (with a short turnaround since the DST change is
+ impending)
+
+
+Release 2009i - 2009-06-08 09:21:22 -0400
+
+ updating for DST in Bangladesh this year
+
+
+Release 2009h - 2009-05-26 09:19:14 -0400
+
+ [not summarized]
+
+
+Release data2009g - 2009-04-20 16:34:07 -0400
+
+ Cairo
+
+
+Release data2009f - 2009-04-10 11:00:52 -0400
+
+ correct DST in Pakistan
+
+
+Release 2009e - 2009-04-06 09:08:11 -0400
+
+ [not summarized]
+
+
+Release 2009d - 2009-03-23 09:38:12 -0400
+
+ Morocco, Tunisia, Argentina, and American Astronomical Society changes
+
+
+Release data2009c - 2009-03-16 09:47:51 -0400
+
+ change to the start of Cuban DST
+
+
+Release 2009b - 2009-02-09 11:15:22 -0500
+
+ [not summarized]
+
+
+Release 2009a - 2009-01-21 10:09:39 -0500
+
+ [not summarized]
+
+
+Release data2008i - 2008-10-21 12:10:25 -0400
+
+ southamerica and zone.tab files, with Argentina DST rule changes and
+ United States zone reordering and recommenting
+
+
+Release 2008h - 2008-10-13 07:33:56 -0400
+
+ [not summarized]
+
+
+Release 2008g - 2008-10-06 09:03:18 -0400
+
+ Fix a broken HTML anchor and update Brazil's DST transitions;
+ there's also a slight reordering of information in tz-art.htm.
+
+
+Release data2008f - 2008-09-09 22:33:26 -0400
+
+ [not summarized]
+
+
+Release 2008e - 2008-07-28 14:11:17 -0400
+
+ changes by Arthur David Olson and Jesper Nørgaard Welen
+
+
+Release data2008d - 2008-07-07 09:51:38 -0400
+
+ changes by Arthur David Olson, Paul Eggert, and Rodrigo Severo
+
+
+Release data2008c - 2008-05-19 17:48:03 -0400
+
+ Pakistan, Morocco, and Mongolia
+
+
+Release data2008b - 2008-03-24 08:30:59 -0400
+
+ including renaming Asia/Calcutta to Asia/Kolkata, with a backward
+ link provided
+
+
+Release 2008a - 2008-03-08 05:42:16 -0500
+
+ [not summarized]
+
+
+Release 2007k - 2007-12-31 10:25:22 -0500
+
+ most importantly, changes to the "southamerica" file based on
+ Argentina's readoption of daylight saving time
+
+
+Release 2007j - 2007-12-03 09:51:01 -0500
+
+ 1. eliminate the "P" (parameter) macro;
+
+ 2. the "noncontroversial" changes circulated on the time zone
+ mailing list (less the changes to "logwtmp.c");
+
+ 3. eliminate "too many transition" errors when "min" is used in time
+ zone rules;
+
+ 4. changes by Paul Eggert (including updated information for Venezuela).
+
+
+Release data2007i - 2007-10-30 10:28:11 -0400
+
+ changes for Cuba and Syria
+
+
+Release 2007h - 2007-10-01 10:05:51 -0400
+
+ changes by Paul Eggert, as well as an updated link to the ICU
+ project in tz-link.htm
+
+
+Release 2007g - 2007-08-20 10:47:59 -0400
+
+ changes by Paul Eggert
+
+ The "leapseconds" file has been updated to incorporate the most
+ recent International Earth Rotation and Reference Systems Service
+ (IERS) bulletin.
+
+ There's an addition to tz-art.htm regarding the television show "Medium".
+
+
+Release 2007f - 2007-05-07 10:46:46 -0400
+
+ changes by Paul Eggert (including Haiti, Turks and Caicos, and New
+ Zealand)
+
+ changes to zic.c to allow hour values greater than 24 (along with
+ Paul's improved time value overflow checking)
+
+
+Release 2007e - 2007-04-02 10:11:52 -0400
+
+ Syria and Honduras changes by Paul Eggert
+
+ zic.c variable renaming changes by Arthur David Olson
+
+
+Release 2007d - 2007-03-20 08:48:30 -0400
+
+ changes by Paul Eggert
+
+ the elimination of white space at the ends of lines
+
+
+Release 2007c - 2007-02-26 09:09:37 -0500
+
+ changes by Paul Eggert
+
+
+Release 2007b - 2007-02-12 09:34:20 -0500
+
+ Paul Eggert's proposed change to the quotation handling logic in zic.c.
+
+ changes to the commentary in "leapseconds" reflecting the IERS
+ announcement that there is to be no positive leap second at the end
+ of June 2007.
+
+
+Release 2007a - 2007-01-08 12:28:29 -0500
+
+ changes by Paul Eggert
+
+ Derick Rethans's Asmara change
+
+ Oscar van Vlijmen's Easter Island local mean time change
+
+ symbolic link changes
+
+
+Release 2006p - 2006-11-27 08:54:27 -0500
+
+ changes by Paul Eggert
+
+
+Release 2006o - 2006-11-06 09:18:07 -0500
+
+ changes by Paul Eggert
+
+
+Release 2006n - 2006-10-10 11:32:06 -0400
+
+ changes by Paul Eggert
+
+
+Release 2006m - 2006-10-02 15:32:35 -0400
+
+ changes for Uruguay, Palestine, and Egypt by Paul Eggert
+
+ (minimalist) changes to zic.8 to clarify "until" information
+
+
+Release data2006l - 2006-09-18 12:58:11 -0400
+
+ Paul's best-effort work on this coming weekend's Egypt time change
+
+
+Release 2006k - 2006-08-28 12:19:09 -0400
+
+ changes by Paul Eggert
+
+
+Release 2006j - 2006-08-21 09:56:32 -0400
+
+ changes by Paul Eggert
+
+
+Release code2006i - 2006-08-07 12:30:55 -0400
+
+ localtime.c fixes
+
+ Ken Pizzini's conversion script
+
+
+Release code2006h - 2006-07-24 09:19:37 -0400
+
+ adds public domain notices to four files
+
+ includes a fix for transition times being off by a second
+
+ adds a new recording to the "arts" file (information courtesy Colin Bowern)
+
+
+Release 2006g - 2006-05-08 17:18:09 -0400
+
+ northamerica changes by Paul Eggert
+
+
+Release 2006f - 2006-05-01 11:46:00 -0400
+
+ a missing version number problem is fixed (with thanks to Bradley
+ White for catching the problem)
+
+
+Release 2006d - 2006-04-17 14:33:43 -0400
+
+ changes by Paul Eggert
+
+ added new items to tz-arts.htm that were found by Paul
+
+
+Release 2006c - 2006-04-03 10:09:32 -0400
+
+ two sets of data changes by Paul Eggert
+
+ a fencepost error fix in zic.c
+
+ changes to zic.c and the "europe" file to minimize differences
+ between output produced by the old 32-bit zic and the new 64-bit
+ version
+
+
+Release 2006b - 2006-02-20 10:08:18 -0500
+ [tz32code2006b + tz64code2006b + tzdata2006b]
+
+ 64-bit code
+
+ All SCCS IDs were bumped to "8.1" for this release.
+
+
+Release 2006a - 2006-01-30 08:59:31 -0500
+
+ changes by Paul Eggert (in particular, Indiana time zone moves)
+
+ an addition to the zic manual page to describe how special-case
+ transitions are handled
+
+
+Release 2005r - 2005-12-27 09:27:13 -0500
+
+ Canadian changes by Paul Eggert
+
+ They also add "<pre>" directives to time zone data files and reflect
+ changes to warning message logic in "zdump.c" (but with calls to
+ "gettext" kept unbundled at the suggestion of Ken Pizzini).
+
+
+Release 2005q - 2005-12-13 09:17:09 -0500
+
+ Nothing earth-shaking here:
+ 1. Electronic mail addresses have been removed.
+ 2. Casts of the return value of exit have been removed.
+ 3. Casts of the argument of is.* macros have been added.
+ 4. Indentation in one section of zic.c has been fixed.
+ 5. References to dead URLs in the data files have been dealt with.
+
+
+Release 2005p - 2005-12-05 10:30:53 -0500
+
+ "systemv", "tz-link.htm", and "zdump.c" changes
+ (less the casts of arguments to the is* macros)
+
+
+Release 2005o - 2005-11-28 10:55:26 -0500
+
+ Georgia, Cuba, Nicaragua, and Jordan changes by Paul Eggert
+
+ zdump.c lint fixes by Arthur David Olson
+
+
+Release 2005n - 2005-10-03 09:44:09 -0400
+
+ changes by Paul Eggert (both the Uruguay changes and the Kyrgyzstan
+ et al. changes)
+
+
+Release 2005m - 2005-08-29 12:15:40 -0400
+
+ changes by Paul Eggert (with a small tweak to the tz-art change)
+
+ a declaration of an unused variable has been removed from zdump.c
+
+
+Release 2005l - 2005-08-22 12:06:39 -0400
+
+ changes by Paul Eggert
+
+ overflow/underflow checks by Arthur David Olson, minus changes to
+ the "Theory" file about the pending addition of 64-bit data (I grow
+ less confident of the changes being accepted with each passing day,
+ and the changes no longer increase the data files nine-fold--there's
+ less than a doubling in size by my local Sun's reckoning)
+
+
+Release 2005k - 2005-07-14 14:14:24 -0400
+
+ The "leapseconds" file has been edited to reflect the recently
+ announced leap second at the end of 2005.
+
+ I've also deleted electronic mail addresses from the files as an
+ anti-spam measure.
+
+
+Release 2005j - 2005-06-13 14:34:13 -0400
+
+ These reflect changes to limit the length of time zone abbreviations
+ and the characters used in those abbreviations.
+
+ There are also changes to handle POSIX-style "quoted" timezone
+ environment variables.
+
+ The changes were circulated on the time zone mailing list; the only
+ change since then was the removal of a couple of minimum-length of
+ abbreviation checks.
+
+
+Release data2005i - 2005-04-21 15:04:16 -0400
+
+ changes (most importantly to Nicaragua and Haiti) by Paul Eggert
+
+
+Release 2005h - 2005-04-04 11:24:47 -0400
+
+ changes by Paul Eggert
+
+ minor changes to Makefile and zdump.c to produce more useful output
+ when doing a "make typecheck"
+
+
+Release 2005g - 2005-03-14 10:11:21 -0500
+
+ changes by Paul Eggert (a change to current DST rules in Uruguay and
+ an update to a link to time zone software)
+
+
+Release 2005f - 2005-03-01 08:45:32 -0500
+
+ data and documentation changes by Paul Eggert
+
+
+Release 2005e - 2005-02-10 15:59:44 -0500
+
+ [not summarized]
+
+
+Release code2005d - 2005-01-31 09:21:47 -0500
+
+ make zic complain about links to links if the -v flag is used
+
+ have "make public" do more code checking
+
+ add an include to "localtime.c" for the benefit of gcc systems
+
+
+Release 2005c - 2005-01-17 18:36:29 -0500
+
+ get better results when mktime runs on a system where time_t is double
+
+ changes to the data files (most importantly to Paraguay)
+
+
+Release 2005b - 2005-01-10 09:19:54 -0500
+
+ Get localtime and gmtime working on systems with exotic time_t types.
+
+ Update the leap second commentary in the "leapseconds" file.
+
+
+Release 2005a - 2005-01-01 13:13:44 -0500
+
+ [not summarized]
+
+
+Release code2004i - 2004-12-14 13:42:58 -0500
+
+ Deal with systems where time_t is unsigned.
+
+
+Release code2004h - 2004-12-07 11:40:18 -0500
+
+ 64-bit-time_t changes
+
+
+Release 2004g - 2004-11-02 09:06:01 -0500
+
+ update to Cuba (taking effect this weekend)
+
+ other changes by Paul Eggert
+
+ correction of the spelling of Oslo
+
+ changed versions of difftime.c and private.h
+
+
+Release code2004f - 2004-10-21 10:25:22 -0400
+
+ Cope with wide-ranging tm_year values.
+
+
+Release 2004e - 2004-10-11 14:47:21 -0400
+
+ Brazil/Argentina/Israel changes by Paul Eggert
+
+ changes to tz-link.htm by Paul
+
+ one small fix to Makefile
+
+
+Release 2004d - 2004-09-22 08:27:29 -0400
+
+ Avoid overflow problems when TM_YEAR_BASE is added to an integer.
+
+
+Release 2004c - 2004-08-11 12:06:26 -0400
+
+ asctime-related changes
+
+ (variants of) some of the documentation changes suggested by Paul Eggert
+
+
+Release 2004b - 2004-07-19 14:33:35 -0400
+
+ data changes by Paul Eggert - most importantly, updates for Argentina
+
+
+Release 2004a - 2004-05-27 12:00:47 -0400
+
+ changes by Paul Eggert
+
+ Handle DST transitions that occur at the end of a month in some
+ years but at the start of the following month in other years.
+
+ Add a copy of the correspondence that's the basis for claims about
+ DST in the Navajo Nation.
+
+
+Release 2003e - 2003-12-15 09:36:47 -0500
+
+ changes by Arthur David Olson (primarily code changes)
+
+ changes by Paul Eggert (primarily data changes)
+
+ minor changes to "Makefile" and "northamerica" (in the latter case,
+ optimization of the "Toronto" rules)
+
+
+Release 2003d - 2003-10-06 09:34:44 -0400
+
+ changes by Paul Eggert
+
+
+Release 2003c - 2003-09-16 10:47:05 -0400
+
+ Fix bad returns in zic.c's inleap function.
+ Thanks to Bradley White for catching the problem!
+
+
+Release 2003b - 2003-09-16 07:13:44 -0400
+
+ Add a "--version" option (and documentation) to the zic and zdump commands.
+
+ changes to overflow/underflow checking in zic
+
+ a localtime typo fix.
+
+ Update the leapseconds and tz-art.htm files.
+
+
+Release 2003a - 2003-03-24 09:30:54 -0500
+
+ changes by Paul Eggert
+
+ a few additions and modifications to the tz-art.htm file
+
+
+Release 2002d - 2002-10-15 13:12:42 -0400
+
+ changes by Paul Eggert, less the "Britain (UK)" change in iso3166.tab
+
+ There's also a new time zone quote in "tz-art.htm".
+
+
+Release 2002c - 2002-04-04 11:55:20 -0500
+
+ changes by Paul Eggert
+
+ Change zic.c to avoid creating symlinks to files that don't exist.
+
+
+Release 2002b - 2002-01-28 12:56:03 -0500
+
+ [These change notes are for Release 2002a, which was corrupted.
+ 2002b was a corrected version of 2002a.]
+
+ changes by Paul Eggert
+
+ Update the "leapseconds" file to note that there'll be no leap
+ second at the end of June, 2002.
+
+ Change "zic.c" to deal with a problem in handling the "Asia/Bishkek" zone.
+
+ Change to "difftime.c" to avoid sizeof problems.
+
+
+Release 2001d - 2001-10-09 13:31:32 -0400
+
+ changes by Paul Eggert
+
+
+Release 2001c - 2001-06-05 13:59:55 -0400
+
+ changes by Paul Eggert and Andrew Brown
+
+
+Release 2001b - 2001-04-05 16:44:38 -0400
+
+ changes by Paul Eggert (modulo jnorgard's typo fix)
+
+ tz-art.htm has been HTMLified.
+
+
+Release 2001a - 2001-03-13 12:57:44 -0500
+
+ changes by Paul Eggert
+
+ An addition to the "leapseconds" file: comments with the text of the
+ latest IERS leap second notice.
+
+ Trailing white space has been removed from data file lines, and
+ repeated spaces in "Rule Jordan" lines in the "asia" file have been
+ converted to tabs.
+
+
+Release 2000h - 2000-12-14 15:33:38 -0500
+
+ changes by Paul Eggert
+
+ one typo fix in the "art" file
+
+ With providence, this is the last update of the millennium.
+
+
+Release 2000g - 2000-10-10 11:35:22 -0400
+
+ changes by Paul Eggert
+
+ correction of John Mackin's name submitted by Robert Elz
+
+ Garry Shandling's Daylight Saving Time joke (!?!) from the recent
+ Emmy Awards broadcast.
+
+
+Release 2000f - 2000-08-10 09:31:58 -0400
+
+ changes by Paul Eggert
+
+ Added information in "tz-art.htm" on a Seinfeld reference to DST.
+
+ Error checking and messages in the "yearistype" script have been
+ improved.
+
+
+Release 2000e - 2000-07-31 09:27:54 -0400
+
+ data changes by Paul Eggert
+
+ a change to the default value of the defined constant HAVE_STRERROR
+
+ the addition of a Dave Barry quote on DST to the tz-arts file
+
+
+Release 2000d - 2000-04-20 15:43:04 -0400
+
+ changes to the documentation and code of strftime for C99 conformance
+
+ a bug fix for date.c
+
+ These are based on (though modified from) changes by Paul Eggert.
+
+
+Release 2000c - 2000-03-04 10:31:43 -0500
+
+ changes by Paul Eggert
+
+
+Release 2000b - 2000-02-21 12:16:29 -0500
+
+ changes by Paul Eggert and Joseph Myers
+
+ modest tweaks to the tz-art.htm and tz-link.htm files
+
+
+Release 2000a - 2000-01-18 09:21:26 -0500
+
+ changes by Paul Eggert
+
+ The two hypertext documents have also been renamed.
+
+
+Release code1999i-data1999j - 1999-11-15 18:43:22 -0500
+
+ Paul Eggert's changes
+
+ additions to the "zic" manual page and the "Arts.htm" file
+
+
+Release code1999h-data1999i - 1999-11-08 14:55:21 -0500
+
+ [not summarized]
+
+
+Release data1999h - 1999-10-07 03:50:29 -0400
+
+ changes by Paul Eggert to "europe" (most importantly, fixing
+ Lithuania and Estonia)
+
+
+Release 1999g - 1999-09-28 11:06:18 -0400
+
+ data changes by Paul Eggert (most importantly, the change for
+ Lebanon that buys correctness for this coming Sunday)
+
+ The "code" file contains changes to "Makefile" and "checktab.awk" to
+ allow better checking of time zone files before they are published.
+
+
+Release 1999f - 1999-09-23 09:48:14 -0400
+
+ changes by Arthur David Olson and Paul Eggert
+
+
+Release 1999e - 1999-08-17 15:20:54 -0400
+
+ changes circulated by Paul Eggert, although the change to handling
+ of DST-specifying timezone names has been commented out for now
+ (search for "XXX" in "localtime.c" for details). These files also
+ do not make any changes to the start of DST in Brazil.
+
+ In addition to Paul's changes, there are updates to "Arts.htm" and
+ cleanups of URLs.
+
+
+Release 1999d - 1999-03-30 11:31:07 -0500
+
+ changes by Paul Eggert
+
+ The Makefile's "make public" rule has also been changed to do a test
+ compile of each individual time zone data file (which should help
+ avoid problems such as the one we had with Nicosia).
+
+
+Release 1999c - 1999-03-25 09:47:47 -0500
+
+ changes by Paul Eggert, most importantly the change for Chile.
+
+
+Release 1999b - 1999-02-01 17:51:44 -0500
+
+ changes by Paul Eggert
+
+ code changes (suggested by Mani Varadarajan, mani at be.com) for
+ correct handling of symbolic links when building using a relative directory
+
+ code changes to generate correct messages for failed links
+
+ updates to the URLs in Arts.htm
+
+
+Release 1999a - 1999-01-19 16:20:29 -0500
+
+ error message internationalizations and corrections in zic.c and
+ zdump.c (as suggested by Vladimir Michl, vladimir.michl at upol.cz,
+ to whom thanks!)
+
+
+Release code1998h-data1998i - 1998-10-01 09:56:10 -0400
+
+ changes for Brazil, Chile, and Germany
+
+ support for use of "24:00" in the input files for the time zone compiler
+
+
+Release code1998g-data1998h - 1998-09-24 10:50:28 -0400
+
+ changes by Paul Eggert
+
+ correction to a define in the "private.h" file
+
+
+Release data1998g - 1998-08-11 03:28:35 -0000
+ [tzdata1998g.tar.gz is missing!]
+
+ Lithuanian change provided by mgedmin at pub.osf.it
+
+ Move creation of the GMT link with Etc/GMT to "etcetera" (from
+ "backward") to ensure that the GMT file is created even where folks
+ don't want the "backward" links (as suggested by Paul Eggert).
+
+
+Release data1998f - 1998-07-20 13:50:00 -0000
+ [tzdata1998f.tar.gz is missing!]
+
+ Update the "leapseconds" file to include the newly-announced
+ insertion at the end of 1998.
+
+
+Release code1998f - 1998-06-01 10:18:31 -0400
+
+ addition to localtime.c by Guy Harris
+
+
+Release 1998e - 1998-05-28 09:56:26 -0400
+
+ The Makefile is changed to produce zoneinfo-posix rather than
+ zoneinfo/posix, and to produce zoneinfo-leaps rather than
+ zoneinfo/right.
+
+ data changes by Paul Eggert
+
+ changes from Guy Harris to provide asctime_r and ctime_r
+
+ A usno1998 file (substantially identical to usno1997) has been added.
+
+
+Release 1998d - 1998-05-14 11:58:34 -0400
+
+ changes to comments (in particular, elimination of references to CIA maps).
+ "Arts.htm", "WWW.htm", "asia", and "australasia" are the only places
+ where changes occur.
+
+
+Release 1998c - 1998-02-28 12:32:26 -0500
+
+ changes by Paul Eggert (save the "French correction," on which I'll
+ wait for the dust to settle)
+
+ symlink changes
+
+ changes and additions to Arts.htm
+
+
+Release 1998b - 1998-01-17 14:31:51 -0500
+
+ URL cleanups and additions
+
+
+Release 1998a - 1998-01-13 12:37:35 -0500
+
+ changes by Paul Eggert
+
+
+Release code1997i-data1997k - 1997-12-29 09:53:41 -0500
+
+ changes by Paul Eggert, with minor modifications from Arthur David
+ Olson to make the files more browser friendly
+
+
+Release code1997h-data1997j - 1997-12-18 17:47:35 -0500
+
+ minor changes to put "TZif" at the start of each timezone information file
+
+ a rule has also been added to the Makefile so you can
+ make zones
+ to just recompile the zone information files (rather than doing a
+ full "make install" with its other effects).
+
+
+Release data1997i - 1997-10-07 08:45:38 -0400
+
+ changes to Africa by Paul Eggert
+
+
+Release code1997g-data1997h - 1997-09-04 16:56:54 -0400
+
+ corrections for Uruguay (and other locations)
+
+ Arthur David Olson's simple-minded fix allowing mktime to both
+ correctly handle leap seconds and correctly handle tm_sec values
+ upon which arithmetic has been performed.
+
+
+Release code1997f-data1997g - 1997-07-19 13:15:02 -0400
+
+ Paul Eggert's updates
+
+ a small change to a function prototype;
+
+ "Music" has been renamed "Arts.htm", HTMLified, and augmented to
+ include information on Around the World in Eighty Days.
+
+
+Release code1997e-data1997f - 1997-05-03 18:52:34 -0400
+
+ fixes to zic's error handling
+
+ changes inspired by the item circulated on Slovenia
+
+ The description of Web resources has been HTMLified for browsing
+ convenience.
+
+ A new piece of tz-related music has been added to the "Music" file.
+
+
+Release code1997d-data1997e - 1997-03-29 12:48:52 -0500
+
+ Paul Eggert's latest suggestions
+
+
+Release code1997c-data1997d - 1997-03-07 20:37:54 -0500
+
+ changes to "zic.c" to correct performance of the "-s" option
+
+ a new file "usno1997"
+
+
+Release data1997c - 1997-03-04 09:58:18 -0500
+
+ changes in Israel
+
+
+Release 1997b - 1997-02-27 18:34:19 -0500
+
+ The data file incorporates the 1997 leap second.
+
+ The code file incorporates Arthur David Olson's take on the
+ zic/multiprocessor/directory-creation situation.
+
+
+Release 1997a - 1997-01-21 09:11:10 -0500
+
+ Paul Eggert's Antarctica (and other changes)
+
+ Arthur David Olson finessed the "getopt" issue by checking against
+ both -1 and EOF (regardless of POSIX, SunOS 4.1.1's manual says -1
+ is returned while SunOS 5.5's manual says EOF is returned).
+
+
+Release code1996o-data1996n - 1996-12-27 21:42:05 -0500
+
+ Paul Eggert's latest changes
+
+
+Release code1996n - 1996-12-16 09:42:02 -0500
+
+ link snapping fix from Bruce Evans (via Garrett Wollman)
+
+
+Release data1996m - 1996-11-24 02:37:34 -0000
+ [tzdata1996m.tar.gz is missing!]
+
+ Paul Eggert's batch of changes
+
+
+Release code1996m-data1996l - 1996-11-05 14:00:12 -0500
+
+ No functional changes here; the files have simply been changed to
+ make more use of ISO style dates in comments. The names of the above
+ files now include the year in full.
+
+
+Release code96l - 1996-09-08 17:12:20 -0400
+
+ tzcode96k was missing a couple of pieces.
+
+
+Release 96k - 1996-09-08 16:06:22 -0400
+
+ the latest round of changes from Paul Eggert
+
+ the recent Year 2000 material
+
+
+Release code96j - 1996-07-30 13:18:53 -0400
+
+ Set sp->typecnt as suggested by Timothy Patrick Murphy.
+
+
+Release code96i - 1996-07-27 20:11:35 -0400
+
+ Paul's suggested patch for strftime %V week numbers
+
+
+Release data96i - 1996-07-01 18:13:04 -0400
+
+ "northamerica" and "europe" changes by Paul Eggert
+
+
+Release code96h - 1996-06-05 08:02:21 -0400
+
+ fix for handling transitions specified in Universal Time
+
+ Some "public domain" notices have also been added.
+
+
+Release code96g - 1996-05-16 14:00:26 -0400
+
+ fix for the simultaneous-DST-and-zone-change challenge
+
+
+Release data96h - 1996-05-09 17:40:51 -0400
+
+ changes by Paul Eggert
+
+
+Release code96f-data96g - 1996-05-03 03:09:59 -0000
+ [tzcode96f.tar.gz + tzdata96g.tar.gz are both missing!]
+
+ The changes get us some of the way to fixing the problems noted in Paul
+ Eggert's letter yesterday (in addition to a few others). The approach
+ has been to make zic a bit smarter about figuring out what time zone
+ abbreviations apply just after the time specified in the "UNTIL" part
+ of a zone line. Putting the smarts in zic means avoiding having
+ transition times show up in both "Zone" lines and "Rule" lines, which
+ in turn avoids multiple transition time entries in time zone files.
+ (This also makes the zic input files such as "europe" a bit shorter and
+ should ease maintenance.)
+
+
+Release data96f - 1996-04-19 19:20:03 -0000
+ [tzdata96f.tar.gz is missing!]
+
+ The only changes are to the "northamerica" file; the time zone
+ abbreviation for Denver is corrected to MST (and MDT), and the
+ comments for Mexico have been updated.
+
+
+Release data96e - 1996-03-19 17:37:26 -0500
+
+ Proposals by Paul Eggert, in particular the Portugal change that
+ comes into play at the end of this month.
+
+
+Release data96d - 1996-03-18 20:49:39 -0500
+
+ [not summarized]
+
+
+Release code96e - 1996-02-29 15:43:27 -0000
+ [tzcode96e.tar.gz is missing!]
+
+ internationalization changes and the fix to the documentation for strftime
+
+
+Release code96d-data96c - 1996-02-12 11:05:27 -0500
+
+ The "code" file simply updates Bob Kridle's electronic address.
+
+ The "data" file updates rules for Mexico.
+
+
+Release data96b - 1996-01-27 15:44:42 -0500
+
+ Kiribati change
+
+
+Release code96c - 1996-01-16 16:58:15 -0500
+
+ leap-year streamlining and binary-search changes
+
+ fix to newctime.3
+
+
+Release code96b - 1996-01-10 20:42:39 -0500
+
+ fixes and enhancements from Paul Eggert, including code that
+ emulates the behavior of recent versions of the SunOS "date"
+ command.
+
+
+Release 96a - 1996-01-06 09:08:24 -0500
+
+ Israel updates
+
+ fixes to strftime.c for correct ISO 8601 week number generation,
+ plus support for two new formats ('G' and 'g') to give ISO 8601 year
+ numbers (which are not necessarily the same as calendar year numbers)
+
+
+Release code95i-data95m - 1995-12-21 12:46:47 -0500
+
+ The latest revisions from Paul Eggert are included, the usno1995
+ file has been updated, and a new file ("WWW") covering useful URLs
+ has been added.
+
+
+Release code95h-data95l - 1995-12-19 18:10:12 -0500
+
+ A simplification of a macro definition, a change to data for Sudan,
+ and (for last minute shoppers) notes in the "Music" file on the CD
+ "Old Man Time".
+
+
+Release code95g-data95k - 1995-10-30 10:32:47 -0500
+
+ (slightly reformatted) 8-bit-clean proposed patch
+
+ minor patch: US/Eastern -> America/New_York
+
+ snapshot of the USNO's latest data ("usno1995")
+
+ some other minor cleanups
+
+
+Release code95f-data95j - 1995-10-28 21:01:34 -0000
+ [tzcode95f.tar.gz + tzdata95j.tar.gz are both missing!]
+
+ European cleanups
+
+ support for 64-bit time_t's
+
+ optimization in localtime.c
+
+
+Release code95e - 1995-10-13 13:23:57 -0400
+
+ the mktime change to scan from future to past when trying to find time zone
+ offsets
+
+
+Release data95i - 1995-09-26 10:43:26 -0400
+
+ For Canada/Central, guess that the Sun customer's "one week too
+ early" was just a approximation, and the true error is one month
+ too early. This is consistent with the rest of Canada.
+
+
+Release data95h - 1995-09-21 11:26:48 -0400
+
+ latest changes from Paul Eggert
+
+
+Release code95d - 1995-09-14 11:14:45 -0400
+
+ the addition of a "Music" file, which documents four recorded
+ versions of the tune "Save That Time".
+
+
+Release data95g - 1995-09-01 17:21:36 -0400
+
+ "yearistype" correction
+
+
+Release data95f - 1995-08-28 20:46:56 -0400
+
+ Paul Eggert's change to the australasia file
+
+
+Release data95e - 1995-07-08 18:02:34 -0400
+
+ The only change is a leap second at the end of this year.
+ Thanks to Bradley White for forwarding news on the leap second.
+
+
+Release data95d - 1995-07-03 13:26:22 -0400
+
+ Paul Eggert's changes
+
+
+Release data95c - 1995-07-02 19:19:28 -0400
+
+ changes to "asia", "backward", "europe", and "southamerica"
+ (read: northamericacentrics need not apply)
+
+
+Release code95c - 1995-03-13 14:00:46 -0500
+
+ one-line fix for sign extension problems in detzcode
+
+
+Release 95b - 1995-03-04 11:22:38 -0500
+
+ Minor changes in both:
+
+ The "code" file contains a workaround for the lack of "unistd.h" in
+ Microsoft C++ version 7.
+
+ The "data" file contains a fixed "Link" for America/Shiprock.
+
+
+Release 94h - 1994-12-10 12:51:14 -0500
+
+ The files:
+
+ * incorporate the changes to "zdump" and "date" to make changes to
+ the "TZ" environment variable permanent;
+
+ * incorporate the table changes by Paul Eggert;
+
+ * include (and document) support for universal time specifications in
+ data files - but do not (yet) include use of this feature in the
+ data files.
+
+ Think of this as "TZ Classic" - the software has been set up not to break if
+ universal time shows up in its input, and data entries have been
+ left as is so as not to break existing implementations.
+
+
+Release data94f - 1994-08-20 12:56:09 -0400
+
+ (with thanks!) the latest data updates from Paul Eggert
+
+
+Release data94e - 1994-06-04 13:13:53 -0400
+
+ [not summarized]
+
+
+Release code94g - 1994-05-05 12:14:07 -0400
+
+ fix missing "optind.c" and a reference to it in the Makefile
+
+
+Release code94f - 1994-05-05 13:00:33 -0000
+ [tzcode94f.tar.gz is missing!]
+
+ changes to avoid overflow in difftime, as well as changes to cope
+ with the 52/53 challenge in strftime
+
+
+Release code94e - 1994-03-30 23:32:59 -0500
+
+ change for the benefit of PCTS
+
+
+Release 94d - 1994-02-24 15:42:25 -0500
+
+ Avoid clashes with POSIX semantics for zones such as GMT+4.
+
+ Some other very minor housekeeping is also present.
+
+
+Release code94c - 1994-02-10 08:52:40 -0500
+
+ Fix bug where mkdirs was broken unless you compile with
+ -fwritable-strings (which is generally losing to do).
+
+
+Release 94b - 1994-02-07 10:04:33 -0500
+
+ work by Paul Eggert who notes:
+
+ I found another book of time zone histories by E W Whitman; it's not
+ as extensive as Shanks but has a few goodies of its own. I used it
+ to update the tables. I also fixed some more as a result of
+ correspondence with Adam David and Peter Ilieve, and move some stray
+ links from 'europe' to 'backward'. I corrected some scanning errors
+ in usno1989.
+
+ As far as the code goes, I fixed zic to allow years in the range
+ INT_MIN to INT_MAX; this fixed a few boundary conditions around 1900.
+ And I cleaned up the zic documentation a little bit.
+
+
+Release data94a - 1994-02-03 08:58:54 -0500
+
+ It simply incorporates the recently announced leap second into the
+ "leapseconds" file.
+
+
+Release 93g - 1993-11-22 17:28:27 -0500
+
+ Paul Eggert has provided a good deal of historic information (based
+ on Shanks), and there are some code changes to deal with the buglets
+ that crawled out in dealing with the new information.
+
+
+Release 93f - 1993-10-15 12:27:46 -0400
+
+ Paul Eggert's changes
+
+
+Release 93e - 1993-09-05 21:21:44 -0400
+
+ This has updated data for Israel, England, and Kwajalein. There's
+ also an update to "zdump" to cope with Kwajalein's 24-hour jump.
+ Thanks to Paul Eggert and Peter Ilieve for the changes.
+
+
+Release 93d - 1993-06-17 23:34:17 -0400
+
+ new fix and new data on Israel
+
+
+Release 93c - 1993-06-06 19:31:55 -0400
+
+ [not summarized]
+
+
+Release 93b - 1993-02-02 14:53:58 -0500
+
+ updated "leapseconds" file
+
+
+Release 93 - 1993-01-08 07:01:06 -0500
+
+ At kre's suggestion, the package has been split in two - a code piece
+ (which also includes documentation) that's only of use to folks who
+ want to recompile things and a data piece useful to anyone who can
+ run "zic".
+
+ The new version has a few changes to the data files, a few
+ portability changes, and an off-by-one fix (with thanks to
+ Tom Karzes at deshaw.com for providing a description and a
+ solution).
+
+
+Release 92c - 1992-11-21 17:35:36 -0000
+ [tz92c.tar.Z is missing!]
+
+ The fallout from the latest round of DST transitions.
+
+ There are changes for Portugal, Saskatchewan, and "Pacific-New";
+ there's also a change to "zic.c" that makes it portable to more systems.
+
+
+Release 92 - 1992-04-25 18:17:03 -0000
+ [tz92.tar.Z is missing!]
+
+ By popular demand (well, at any rate, following a request by kre at munnari)
+
+
+The 1989 update of the time zone package featured:
+
+ * POSIXization (including interpretation of POSIX-style TZ environment
+ variables, provided by Guy Harris),
+ * ANSIfication (including versions of "mktime" and "difftime"),
+ * SVIDulation (an "altzone" variable)
+ * MACHination (the "gtime" function)
+ * corrections to some time zone data (including corrections to the rules
+ for Great Britain and New Zealand)
+ * reference data from the United States Naval Observatory for folks who
+ want to do additional time zones
+ * and the 1989 data for Saudi Arabia.
+
+ (Since this code will be treated as "part of the implementation" in some
+ places and as "part of the application" in others, there's no good way to
+ name functions, such as timegm, that are not part of the proposed ANSI C
+ standard; such functions have kept their old, underscore-free names in this
+ update.)
+
+ And the "dysize" function has disappeared; it was present to allow
+ compilation of the "date" command on old BSD systems, and a version of "date"
+ is now provided in the package. The "date" command is not created when you
+ "make all" since it may lack options provided by the version distributed with
+ your operating system, or may not interact with the system in the same way
+ the native version does.
+
+ Since POSIX frowns on correct leap second handling, the default behavior of
+ the "zic" command (in the absence of a "-L" option) has been changed to omit
+ leap second information from its output files.
+
+
+-----
+Notes
+
+This file contains copies of the part of each release announcement
+that talks about the changes in that release. The text has been
+adapted and reformatted for the purposes of this file.
+
+Traditionally a release R consists of a pair of tarball files,
+tzcodeR.tar.gz and tzdataR.tar.gz. However, some releases (e.g.,
+code2010a, data2012c) consist of just one or the other tarball, and a
+few (e.g., code2012c-data2012d) have tarballs with mixed version
+numbers. Recent releases also come in an experimental format
+consisting of a single tarball tzdb-R.tar.lz with extra data.
+
+Release timestamps are taken from the release's commit (for newer,
+Git-based releases), from the newest file in the tarball (for older
+releases, where this info is available) or from the email announcing
+the release (if all else fails; these are marked with a time zone
+abbreviation of -0000 and an "is missing!" comment).
+
+Earlier versions of the code and data were not announced on the tz
+list and are not summarized here.
+
+This file is in the public domain.
+
+Local Variables:
+coding: utf-8
+End:
diff --git a/contrib/tzcode/README b/contrib/tzcode/README
new file mode 100644
index 000000000000..145aacd495b7
--- /dev/null
+++ b/contrib/tzcode/README
@@ -0,0 +1,52 @@
+README for the tz distribution
+
+"Where do I set the hands of the clock?" -- Les Tremayne as The King
+"Oh that--you can set them any place you want." -- Frank Baxter as The Scientist
+ (from the Bell System film "About Time")
+
+The Time Zone Database (called tz, tzdb or zoneinfo) contains code and
+data that represent the history of local time for many representative
+locations around the globe. It is updated periodically to reflect
+changes made by political bodies to time zone boundaries, UTC offsets,
+and daylight-saving rules.
+
+See <https://www.iana.org/time-zones/repository/tz-link.html> or the
+file tz-link.html for how to acquire the code and data. Once acquired,
+read the comments in the file 'Makefile' and make any changes needed
+to make things right for your system, especially if you are using some
+platform other than GNU/Linux. Then run the following commands,
+substituting your desired installation directory for "$HOME/tzdir":
+
+ make TOPDIR=$HOME/tzdir install
+ $HOME/tzdir/usr/bin/zdump -v America/Los_Angeles
+
+See the file tz-how-to.html for examples of how to read the data files.
+
+This database of historical local time information has several goals:
+
+ * Provide a compendium of data about the history of civil time that
+ is useful even if not 100% accurate.
+
+ * Give an idea of the variety of local time rules that have existed
+ in the past and thus may be expected in the future.
+
+ * Test the generality of the local time rule description system.
+
+The information in the time zone data files is by no means authoritative;
+fixes and enhancements are welcome. Please see the file CONTRIBUTING
+for details.
+
+Thanks to these Time Zone Caballeros who've made major contributions to the
+time conversion package: Keith Bostic; Bob Devine; Paul Eggert; Robert Elz;
+Guy Harris; Mark Horton; John Mackin; and Bradley White. Thanks also to
+Michael Bloom, Art Neilson, Stephen Prince, John Sovereign, and Frank Wales
+for testing work, and to Gwillim Law for checking local mean time data.
+Thanks in particular to Arthur David Olson, the project's founder and first
+maintainer, to whom the time zone community owes the greatest debt of all.
+None of them are responsible for remaining errors.
+
+-----
+
+This file is in the public domain, so clarified as of 2009-05-17 by
+Arthur David Olson. The other files in this distribution are either
+public domain or BSD licensed; see the file LICENSE for details.
diff --git a/contrib/tzcode/SECURITY b/contrib/tzcode/SECURITY
new file mode 100644
index 000000000000..40128bc86dd2
--- /dev/null
+++ b/contrib/tzcode/SECURITY
@@ -0,0 +1,15 @@
+Please report any sensitive security-related bugs via email to the
+tzdb designated coordinators, currently Paul Eggert
+<eggert@cs.ucla.edu> and Tim Parenti <tim@timtimeonline.com>.
+Put "tzdb security" at the start of your email's subject line.
+We prefer communications to be in English.
+
+You should receive a response within a week. If not, please follow up
+via email to make sure we received your original message.
+
+If we confirm the bug, we plan to notify affected third-party services
+or software that we know about, prepare an advisory, commit fixes to
+the main development branch as quickly as is practical, and finally
+publish the advisory on tz@iana.org. As with all tzdb contributions,
+we give credit to security contributors unless they wish to remain
+anonymous.
diff --git a/contrib/tzcode/stdtime/asctime.c b/contrib/tzcode/asctime.c
index be865fb807c1..1a6486f38163 100644
--- a/contrib/tzcode/stdtime/asctime.c
+++ b/contrib/tzcode/asctime.c
@@ -1,3 +1,5 @@
+/* asctime and asctime_r a la POSIX and ISO C, except pad years before 1000. */
+
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
@@ -9,28 +11,14 @@
** 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.5";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-__FBSDID("$FreeBSD$");
-
/*LINTLIBRARY*/
#include "namespace.h"
#include "private.h"
#include "un-namespace.h"
-#include "tzfile.h"
+#include <stdio.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
@@ -39,61 +27,45 @@ __FBSDID("$FreeBSD$");
** 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,
+** The ISO C and POSIX 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__ */
+static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
/*
** 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__ */
+static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n";
-#define STD_ASCTIME_BUF_SIZE 26
+enum { 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).
+** and a trailing NUL byte).
** 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
+** as an example; the size expression below is a bound 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.
-*/
+static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
char *
-asctime_r(timeptr, buf)
-const struct tm * timeptr;
-char * buf;
+asctime_r(register const struct tm *timeptr, char *buf)
{
- static const char wday_name[][3] = {
+ static const char wday_name[][4] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
- static const char mon_name[][3] = {
+ static const char mon_name[][4] = {
"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];
+ char result[sizeof buf_asctime];
if (timeptr == NULL) {
errno = EINVAL;
@@ -111,11 +83,11 @@ char * buf;
** 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);
+ strftime(year, sizeof year, "%Y", timeptr);
/*
** We avoid using snprintf since it's not available on all systems.
*/
- (void) sprintf(result,
+ sprintf(result,
((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
@@ -124,22 +96,13 @@ char * buf;
if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime)
return strcpy(buf, result);
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;
+asctime(register const struct tm *timeptr)
{
return asctime_r(timeptr, buf_asctime);
}
diff --git a/contrib/tzcode/calendars b/contrib/tzcode/calendars
new file mode 100644
index 000000000000..f4ed9e434e50
--- /dev/null
+++ b/contrib/tzcode/calendars
@@ -0,0 +1,173 @@
+----- Calendrical issues -----
+
+As mentioned in Theory.html, although calendrical issues are out of
+scope for tzdb, they indicate the sort of problems that we would run
+into if we extended tzdb further into the past. The following
+information and sources go beyond Theory.html's brief discussion.
+They sometimes disagree.
+
+
+France
+
+Gregorian calendar adopted 1582-12-20.
+French Revolutionary calendar used 1793-11-24 through 1805-12-31,
+and (in Paris only) 1871-05-06 through 1871-05-23.
+
+
+Russia
+
+From Chris Carrier (1996-12-02):
+On 1929-10-01 the Soviet Union instituted an "Eternal Calendar"
+with 30-day months plus 5 holidays, with a 5-day week.
+On 1931-12-01 it changed to a 6-day week; in 1934 it reverted to the
+Gregorian calendar while retaining the 6-day week; on 1940-06-27 it
+reverted to the 7-day week. With the 6-day week the usual days
+off were the 6th, 12th, 18th, 24th and 30th of the month.
+(Source: Evitiar Zerubavel, _The Seven Day Circle_)
+
+
+Mark Brader reported a similar story in "The Book of Calendars", edited
+by Frank Parise (1982, Facts on File, ISBN 0-8719-6467-8), page 377. But:
+
+From: Petteri Sulonen (via Usenet)
+Date: 14 Jan 1999 00:00:00 GMT
+...
+
+If your source is correct, how come documents between 1929 and 1940 were
+still dated using the conventional, Gregorian calendar?
+
+I can post a scan of a document dated December 1, 1934, signed by
+Yenukidze, the secretary, on behalf of Kalinin, the President of the
+Executive Committee of the Supreme Soviet, if you like.
+
+
+
+Sweden (and Finland)
+
+From: Mark Brader
+Subject: Re: Gregorian reform - a part of locale?
+<news:1996Jul6.012937.29190@sq.com>
+Date: 1996-07-06
+
+In 1700, Denmark made the transition from Julian to Gregorian. Sweden
+decided to *start* a transition in 1700 as well, but rather than have one of
+those unsightly calendar gaps :-), they simply decreed that the next leap
+year after 1696 would be in 1744 - putting the whole country on a calendar
+different from both Julian and Gregorian for a period of 40 years.
+
+However, in 1704 something went wrong and the plan was not carried through;
+they did, after all, have a leap year that year. And one in 1708. In 1712
+they gave it up and went back to Julian, putting 30 days in February that
+year!...
+
+Then in 1753, Sweden made the transition to Gregorian in the usual manner,
+getting there only 13 years behind the original schedule.
+
+(A previous posting of this story was challenged, and Swedish readers
+produced the following references to support it: "Tideräkning och historia"
+by Natanael Beckman (1924) and "Tid, en bok om tideräkning och
+kalenderväsen" by Lars-Olof Lodén (1968).
+
+
+Grotefend's data
+
+From: "Michael Palmer" [with two obvious typos fixed]
+Subject: Re: Gregorian Calendar (was Re: Another FHC related question
+Newsgroups: soc.genealogy.german
+Date: Tue, 9 Feb 1999 02:32:48 -800
+...
+
+The following is a(n incomplete) listing, arranged chronologically, of
+European states, with the date they converted from the Julian to the
+Gregorian calendar:
+
+04/15 Oct 1582 - Italy (with exceptions), Spain, Portugal, Poland (Roman
+ Catholics and Danzig only)
+09/20 Dec 1582 - France, Lorraine
+
+21 Dec 1582/
+ 01 Jan 1583 - Holland, Brabant, Flanders, Hennegau
+10/21 Feb 1583 - bishopric of Liege (Lüttich)
+13/24 Feb 1583 - bishopric of Augsburg
+04/15 Oct 1583 - electorate of Trier
+05/16 Oct 1583 - Bavaria, bishoprics of Freising, Eichstedt, Regensburg,
+ Salzburg, Brixen
+13/24 Oct 1583 - Austrian Oberelsaß and Breisgau
+20/31 Oct 1583 - bishopric of Basel
+02/13 Nov 1583 - duchy of Jülich-Berg
+02/13 Nov 1583 - electorate and city of Köln
+04/15 Nov 1583 - bishopric of Würzburg
+11/22 Nov 1583 - electorate of Mainz
+16/27 Nov 1583 - bishopric of Strassburg and the margraviate of Baden
+17/28 Nov 1583 - bishopric of Münster and duchy of Cleve
+14/25 Dec 1583 - Steiermark
+
+06/17 Jan 1584 - Austria and Bohemia
+11/22 Jan 1584 - Lucerne, Uri, Schwyz, Zug, Freiburg, Solothurn
+12/23 Jan 1584 - Silesia and the Lausitz
+22 Jan/
+ 02 Feb 1584 - Hungary (legally on 21 Oct 1587)
+ Jun 1584 - Unterwalden
+01/12 Jul 1584 - duchy of Westfalen
+
+16/27 Jun 1585 - bishopric of Paderborn
+
+14/25 Dec 1590 - Transylvania
+
+22 Aug/
+ 02 Sep 1612 - duchy of Prussia
+
+13/24 Dec 1614 - Pfalz-Neuburg
+
+ 1617 - duchy of Kurland (reverted to the Julian calendar in
+ 1796)
+
+ 1624 - bishopric of Osnabrück
+
+ 1630 - bishopric of Minden
+
+15/26 Mar 1631 - bishopric of Hildesheim
+
+ 1655 - Kanton Wallis
+
+05/16 Feb 1682 - city of Strassburg
+
+18 Feb/
+ 01 Mar 1700 - Protestant Germany (including Swedish possessions in
+ Germany), Denmark, Norway
+30 Jun/
+ 12 Jul 1700 - Gelderland, Zutphen
+10 Nov/
+ 12 Dec 1700 - Utrecht, Overijssel
+
+31 Dec 1700/
+ 12 Jan 1701 - Friesland, Groningen, Zürich, Bern, Basel, Geneva,
+ Thurgau, and Schaffhausen
+
+ 1724 - Glarus, Appenzell, and the city of St. Gallen
+
+01 Jan 1750 - Pisa and Florence
+
+02/14 Sep 1752 - Great Britain
+
+17 Feb/
+ 01 Mar 1753 - Sweden
+
+1760-1812 - Graubünden
+
+The Russian empire (including Finland and the Baltic states) did not
+convert to the Gregorian calendar until the Soviet revolution of 1917.
+
+Source: H. Grotefend, _Taschenbuch der Zeitrechnung des deutschen
+Mittelalters und der Neuzeit_, herausgegeben von Dr. O. Grotefend
+(Hannover: Hahnsche Buchhandlung, 1941), pp. 26-28.
+
+-----
+
+This file is in the public domain, so clarified as of 2009-05-17 by
+Arthur David Olson.
+
+-----
+Local Variables:
+coding: utf-8
+End:
diff --git a/contrib/tzcode/date.1 b/contrib/tzcode/date.1
new file mode 100644
index 000000000000..043e568117df
--- /dev/null
+++ b/contrib/tzcode/date.1
@@ -0,0 +1,167 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH date 1
+.SH NAME
+date \- show and set date and time
+.SH SYNOPSIS
+.if n .nh
+.if n .na
+.ie \n(.g .ds - \f(CR-\fP
+.el .ds - \-
+.B date
+[
+.B \*-u
+] [
+.B \*-c
+] [
+.B \*-r
+.I seconds
+] [
+.BI + format
+] [
+\fR[\fIyyyy\fR]\fImmddhhmm\fR[\fIyy\fR][\fB.\fIss\fR]
+]
+.SH DESCRIPTION
+.ie '\(lq'' .ds lq \&"\"
+.el .ds lq \(lq\"
+.ie '\(rq'' .ds rq \&"\"
+.el .ds rq \(rq\"
+.de q
+\\$3\*(lq\\$1\*(rq\\$2
+..
+The
+.B date
+command
+without arguments writes the date and time to the standard output in
+the form
+.ce 1
+Wed Mar 8 14:54:40 EST 1989
+.br
+with
+.B EST
+replaced by the local time zone's abbreviation
+(or by the abbreviation for the time zone specified in the
+.B TZ
+environment variable if set).
+The exact output format depends on the locale.
+.PP
+If a command-line argument starts with a plus sign (\c
+.q "\fB+\fP" ),
+the rest of the argument is used as a
+.I format
+that controls what appears in the output.
+In the format, when a percent sign (\c
+.q "\fB%\fP"
+appears,
+it and the character after it are not output,
+but rather identify part of the date or time
+to be output in a particular way
+(or identify a special character to output):
+.nf
+.sp
+.if t .in +.5i
+.if n .in +2
+.ta \w'%M\0\0'u +\w'Wed Mar 8 14:54:40 EST 1989\0\0'u
+ Sample output Explanation
+%a Wed Abbreviated weekday name*
+%A Wednesday Full weekday name*
+%b Mar Abbreviated month name*
+%B March Full month name*
+%c Wed Mar 08 14:54:40 1989 Date and time*
+%C 19 Century
+%d 08 Day of month (always two digits)
+%D 03/08/89 Month/day/year (eight characters)
+%e 8 Day of month (leading zero blanked)
+%h Mar Abbreviated month name*
+%H 14 24-hour-clock hour (two digits)
+%I 02 12-hour-clock hour (two digits)
+%j 067 Julian day number (three digits)
+%k 2 12-hour-clock hour (leading zero blanked)
+%l 14 24-hour-clock hour (leading zero blanked)
+%m 03 Month number (two digits)
+%M 54 Minute (two digits)
+%n \\n newline character
+%p PM AM/PM designation
+%r 02:54:40 PM Hour:minute:second AM/PM designation
+%R 14:54 Hour:minute
+%S 40 Second (two digits)
+%t \\t tab character
+%T 14:54:40 Hour:minute:second
+%U 10 Sunday-based week number (two digits)
+%w 3 Day number (one digit, Sunday is 0)
+%W 10 Monday-based week number (two digits)
+%x 03/08/89 Date*
+%X 14:54:40 Time*
+%y 89 Last two digits of year
+%Y 1989 Year in full
+%z -0500 Numeric time zone
+%Z EST Time zone abbreviation
+%+ Wed Mar 8 14:54:40 EST 1989 Default output format*
+.if t .in -.5i
+.if n .in -2
+* The exact output depends on the locale.
+.sp
+.fi
+If a character other than one of those shown above appears after
+a percent sign in the format,
+that following character is output.
+All other characters in the format are copied unchanged to the output;
+a newline character is always added at the end of the output.
+.PP
+In Sunday-based week numbering,
+the first Sunday of the year begins week 1;
+days preceding it are part of
+.q "week 0" .
+In Monday-based week numbering,
+the first Monday of the year begins week 1.
+.PP
+To set the date, use a command line argument with one of the following forms:
+.nf
+.if t .in +.5i
+.if n .in +2
+.ta \w'198903081454\0'u
+1454 24-hour-clock hours (first two digits) and minutes
+081454 Month day (first two digits), hours, and minutes
+03081454 Month (two digits, January is 01), month day, hours, minutes
+8903081454 Year, month, month day, hours, minutes
+0308145489 Month, month day, hours, minutes, year
+ (on System V-compatible systems)
+030814541989 Month, month day, hours, minutes, four-digit year
+198903081454 Four-digit year, month, month day, hours, minutes
+.if t .in -.5i
+.if n .in -2
+.fi
+If the century, year, month, or month day is not given,
+the current value is used.
+Any of the above forms may be followed by a period and two digits that give
+the seconds part of the new time; if no seconds are given, zero is assumed.
+.PP
+These options are available:
+.TP
+.BR \*-u " or " \*-c
+Use Universal Time when setting and showing the date and time.
+.TP
+.BI "\*-r " seconds
+Output the date that corresponds to
+.I seconds
+past the epoch of 1970-01-01 00:00:00 UTC, where
+.I seconds
+should be an integer, either decimal, octal (leading 0), or
+hexadecimal (leading 0x), preceded by an optional sign.
+.SH FILES
+.ta \w'/usr/share/zoneinfo/posixrules\0\0'u
+/etc/localtime local timezone file
+.br
+/usr/lib/locale/\f2L\fP/LC_TIME description of time locale \f2L\fP
+.br
+/usr/share/zoneinfo timezone information directory
+.br
+/usr/share/zoneinfo/posixrules used with POSIX-style TZ's
+.br
+/usr/share/zoneinfo/GMT for UTC leap seconds
+.sp
+If
+.B /usr/share/zoneinfo/GMT
+is absent,
+UTC leap seconds are loaded from
+.BR /usr/share/zoneinfo/posixrules .
diff --git a/contrib/tzcode/date.c b/contrib/tzcode/date.c
new file mode 100644
index 000000000000..11c5e5fe8d49
--- /dev/null
+++ b/contrib/tzcode/date.c
@@ -0,0 +1,216 @@
+/* Display or set the current time and date. */
+
+/* Copyright 1985, 1987, 1988 The Regents of the University of California.
+ 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. 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. */
+
+#include "private.h"
+#include <locale.h>
+#include <stdio.h>
+
+#if !HAVE_POSIX_DECLS
+extern char * optarg;
+extern int optind;
+#endif
+
+static int retval = EXIT_SUCCESS;
+
+static void display(const char *, time_t);
+static void dogmt(void);
+static void errensure(void);
+static void timeout(FILE *, const char *, const struct tm *);
+static ATTRIBUTE_NORETURN void usage(void);
+
+int
+main(const int argc, char *argv[])
+{
+ register const char * format = "+%+";
+ register int ch;
+ register bool rflag = false;
+ time_t t;
+ intmax_t secs;
+ char * endarg;
+
+#ifdef LC_ALL
+ setlocale(LC_ALL, "");
+#endif /* defined(LC_ALL) */
+#if HAVE_GETTEXT
+# ifdef TZ_DOMAINDIR
+ bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+# endif /* defined(TEXTDOMAINDIR) */
+ textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT */
+ t = time(NULL);
+ while ((ch = getopt(argc, argv, "ucr:")) != EOF && ch != -1) {
+ switch (ch) {
+ default:
+ usage();
+ case 'u': /* do it in UT */
+ case 'c':
+ dogmt();
+ break;
+ case 'r': /* seconds since 1970 */
+ if (rflag) {
+ fprintf(stderr,
+ _("date: error: multiple -r's used"));
+ usage();
+ }
+ rflag = true;
+ errno = 0;
+ secs = strtoimax(optarg, &endarg, 0);
+ if (*endarg || optarg == endarg)
+ errno = EINVAL;
+ else if (! (TIME_T_MIN <= secs && secs <= TIME_T_MAX))
+ errno = ERANGE;
+ if (errno) {
+ perror(optarg);
+ errensure();
+ exit(retval);
+ }
+ t = secs;
+ break;
+ }
+ }
+ if (optind < argc) {
+ if (argc - optind != 1) {
+ fprintf(stderr,
+ _("date: error: multiple operands in command line\n"));
+ usage();
+ }
+ format = argv[optind];
+ if (*format != '+') {
+ fprintf(stderr, _("date: unknown operand: %s\n"), format);
+ usage();
+ }
+ }
+
+ display(format, t);
+ return retval;
+}
+
+static void
+dogmt(void)
+{
+ static char ** fakeenv;
+
+ if (fakeenv == NULL) {
+ static char tzeutc0[] = "TZ=UTC0";
+ ptrdiff_t from, to, n;
+
+ for (n = 0; environ[n] != NULL; ++n)
+ continue;
+#if defined ckd_add && defined ckd_mul
+ if (!ckd_add(&n, n, 2) && !ckd_mul(&n, n, sizeof *fakeenv)
+ && n <= SIZE_MAX)
+ fakeenv = malloc(n);
+#else
+ if (n <= min(PTRDIFF_MAX, SIZE_MAX) / sizeof *fakeenv - 2)
+ fakeenv = malloc((n + 2) * sizeof *fakeenv);
+#endif
+ if (fakeenv == NULL) {
+ fprintf(stderr, _("date: Memory exhausted\n"));
+ errensure();
+ exit(retval);
+ }
+ to = 0;
+ fakeenv[to++] = tzeutc0;
+ for (from = 1; environ[from] != NULL; ++from)
+ if (strncmp(environ[from], "TZ=", 3) != 0)
+ fakeenv[to++] = environ[from];
+ fakeenv[to] = NULL;
+ environ = fakeenv;
+ }
+}
+
+static void
+errensure(void)
+{
+ if (retval == EXIT_SUCCESS)
+ retval = EXIT_FAILURE;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ _("date: usage: date [-u] [-c] [-r seconds]"
+ " [+format]\n"));
+ errensure();
+ exit(retval);
+}
+
+static void
+display(char const *format, time_t now)
+{
+ struct tm *tmp;
+
+ tmp = localtime(&now);
+ if (!tmp) {
+ fprintf(stderr,
+ _("date: error: time out of range\n"));
+ errensure();
+ return;
+ }
+ timeout(stdout, format, tmp);
+ putchar('\n');
+ fflush(stdout);
+ fflush(stderr);
+ if (ferror(stdout) || ferror(stderr)) {
+ fprintf(stderr,
+ _("date: error: couldn't write results\n"));
+ errensure();
+ }
+}
+
+static void
+timeout(FILE *fp, char const *format, struct tm const *tmp)
+{
+ char *cp = NULL;
+ ptrdiff_t result;
+ ptrdiff_t size = 1024 / 2;
+
+ for ( ; ; ) {
+#ifdef ckd_mul
+ bool bigger = !ckd_mul(&size, size, 2) && size <= SIZE_MAX;
+#else
+ bool bigger = (size <= min(PTRDIFF_MAX, SIZE_MAX) / 2
+ && (size *= 2, true));
+#endif
+ char *newcp = bigger ? realloc(cp, size) : NULL;
+ if (!newcp) {
+ fprintf(stderr,
+ _("date: error: can't get memory\n"));
+ errensure();
+ exit(retval);
+ }
+ cp = newcp;
+ result = strftime(cp, size, format, tmp);
+ if (result != 0)
+ break;
+ }
+ fwrite(cp + 1, 1, result - 1, fp);
+ free(cp);
+}
diff --git a/contrib/tzcode/difftime.c b/contrib/tzcode/difftime.c
new file mode 100644
index 000000000000..43af402cdc12
--- /dev/null
+++ b/contrib/tzcode/difftime.c
@@ -0,0 +1,62 @@
+/* Return the difference between two timestamps. */
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*LINTLIBRARY*/
+
+#include "namespace.h"
+#include "private.h" /* for time_t and TYPE_SIGNED */
+#include "un-namespace.h"
+
+/* Return -X as a double. Using this avoids casting to 'double'. */
+static double
+dminus(double x)
+{
+ return -x;
+}
+
+double
+difftime(time_t time1, time_t time0)
+{
+ /*
+ ** If double is large enough, simply convert and subtract
+ ** (assuming that the larger type has more precision).
+ */
+ if (sizeof(time_t) < sizeof(double)) {
+ double t1 = time1, t0 = time0;
+ return t1 - t0;
+ }
+
+ /*
+ ** The difference of two unsigned values can't overflow
+ ** if the minuend is greater than or equal to the subtrahend.
+ */
+ if (!TYPE_SIGNED(time_t))
+ return time0 <= time1 ? time1 - time0 : dminus(time0 - time1);
+
+ /* Use uintmax_t if wide enough. */
+ if (sizeof(time_t) <= sizeof(uintmax_t)) {
+ uintmax_t t1 = time1, t0 = time0;
+ return time0 <= time1 ? t1 - t0 : dminus(t0 - t1);
+ }
+
+ /*
+ ** 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;
+
+ /*
+ ** The values have opposite signs and uintmax_t is too narrow.
+ ** This suffers from double rounding; attempt to lessen that
+ ** by using long double temporaries.
+ */
+ {
+ long double t1 = time1, t0 = time0;
+ return t1 - t0;
+ }
+}
diff --git a/contrib/tzcode/localtime.c b/contrib/tzcode/localtime.c
new file mode 100644
index 000000000000..2dbab26509d3
--- /dev/null
+++ b/contrib/tzcode/localtime.c
@@ -0,0 +1,2669 @@
+/* Convert timestamp from time_t to struct tm. */
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/*LINTLIBRARY*/
+
+#define LOCALTIME_IMPLEMENTATION
+#include "namespace.h"
+#ifdef DETECT_TZ_CHANGES
+#ifndef DETECT_TZ_CHANGES_INTERVAL
+#define DETECT_TZ_CHANGES_INTERVAL 61
+#endif
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+#if THREAD_SAFE
+#include <pthread.h>
+#endif
+#include "private.h"
+#include "un-namespace.h"
+
+#include "tzfile.h"
+
+#include "libc_private.h"
+
+#if defined THREAD_SAFE && THREAD_SAFE
+static pthread_mutex_t locallock = PTHREAD_MUTEX_INITIALIZER;
+static int lock(void) {
+ if (__isthreaded)
+ return _pthread_mutex_lock(&locallock);
+ return 0;
+}
+static void unlock(void) {
+ if (__isthreaded)
+ _pthread_mutex_unlock(&locallock);
+}
+#else
+static int lock(void) { return 0; }
+static void unlock(void) { }
+#endif
+
+#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 */
+
+/*
+** Support non-POSIX platforms that distinguish between text and binary files.
+*/
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#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 const char wildabbr[] = WILDABBR;
+
+static char const etc_utc[] = "Etc/UTC";
+static char const *utc = etc_utc + sizeof "Etc/" - 1;
+
+/*
+** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+** Default to US rules as of 2017-05-07.
+** POSIX does not specify the default DST rules;
+** for historical reasons, US rules are a common default.
+*/
+#ifndef TZDEFRULESTRING
+# define TZDEFRULESTRING ",M3.2.0,M11.1.0"
+#endif
+
+struct ttinfo { /* time type information */
+ int_fast32_t tt_utoff; /* UT offset in seconds */
+ bool tt_isdst; /* used to set tm_isdst */
+ int tt_desigidx; /* abbreviation list index */
+ bool tt_ttisstd; /* transition is std time */
+ bool tt_ttisut; /* transition is UT */
+};
+
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ int_fast32_t ls_corr; /* correction to apply */
+};
+
+/* This abbreviation means local time is unspecified. */
+static char const UNSPEC[] = "-00";
+
+/* How many extra bytes are needed at the end of struct state's chars array.
+ This needs to be at least 1 for null termination in case the input
+ data isn't properly terminated, and it also needs to be big enough
+ for ttunspecified to work without crashing. */
+enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
+
+#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;
+ bool goback;
+ bool goahead;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[max(max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof "UTC"),
+ 2 * (MY_TZNAME_MAX + 1))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+
+ /* The time type to use for early times or if no transitions.
+ It is always zero for recent tzdb releases.
+ It might be nonzero for data from tzdb 2018e or earlier. */
+ int defaulttype;
+};
+
+enum r_type {
+ JULIAN_DAY, /* Jn = Julian day */
+ DAY_OF_YEAR, /* n = day of year */
+ MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */
+};
+
+struct rule {
+ enum r_type r_type; /* type of rule */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ int_fast32_t r_time; /* transition time of rule */
+};
+
+static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t,
+ struct tm *);
+static bool increment_overflow(int *, int);
+static bool increment_overflow_time(time_t *, int_fast32_t);
+static int_fast32_t leapcorr(struct state const *, time_t);
+static bool normalize_overflow32(int_fast32_t *, int *, int);
+static struct tm *timesub(time_t const *, int_fast32_t, struct state const *,
+ struct tm *);
+static bool typesequiv(struct state const *, int, int);
+static bool tzparse(char const *, struct state *, struct state *);
+
+#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;
+static struct state *const lclptr = &lclmem;
+static struct state *const 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_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;
+
+/*
+** 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;
+
+#if 2 <= HAVE_TZNAME + TZ_TIME_T
+char * tzname[2] = {
+ (char *) wildabbr,
+ (char *) wildabbr
+};
+#endif
+#if 2 <= USG_COMPAT + TZ_TIME_T
+long timezone;
+int daylight;
+#endif
+#if 2 <= ALTZONE + TZ_TIME_T
+long altzone;
+#endif
+
+/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */
+static void
+init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
+{
+ s->tt_utoff = utoff;
+ s->tt_isdst = isdst;
+ s->tt_desigidx = desigidx;
+ s->tt_ttisstd = false;
+ s->tt_ttisut = false;
+}
+
+/* Return true if SP's time type I does not specify local time. */
+static bool
+ttunspecified(struct state const *sp, int i)
+{
+ char const *abbr = &sp->chars[sp->ttis[i].tt_desigidx];
+ /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */
+ return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0;
+}
+
+static int_fast32_t
+detzcode(const char *const codep)
+{
+ register int_fast32_t result;
+ register int i;
+ int_fast32_t one = 1;
+ int_fast32_t halfmaxval = one << (32 - 2);
+ int_fast32_t maxval = halfmaxval - 1 + halfmaxval;
+ int_fast32_t minval = -1 - maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80) {
+ /* Do two's-complement negation even on non-two's-complement machines.
+ If the result would be minval - 1, return minval. */
+ result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0;
+ result += minval;
+ }
+ return result;
+}
+
+static int_fast64_t
+detzcode64(const char *const codep)
+{
+ register int_fast64_t result;
+ register int i;
+ int_fast64_t one = 1;
+ int_fast64_t halfmaxval = one << (64 - 2);
+ int_fast64_t maxval = halfmaxval - 1 + halfmaxval;
+ int_fast64_t minval = -TWOS_COMPLEMENT(int_fast64_t) - maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 8; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80) {
+ /* Do two's-complement negation even on non-two's-complement machines.
+ If the result would be minval - 1, return minval. */
+ result -= !TWOS_COMPLEMENT(int_fast64_t) && result != 0;
+ result += minval;
+ }
+ return result;
+}
+
+static void
+update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp)
+{
+#if HAVE_TZNAME
+ tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx];
+#endif
+#if USG_COMPAT
+ if (!ttisp->tt_isdst)
+ timezone = - ttisp->tt_utoff;
+#endif
+#if ALTZONE
+ if (ttisp->tt_isdst)
+ altzone = - ttisp->tt_utoff;
+#endif
+}
+
+/* If STDDST_MASK indicates that SP's TYPE provides useful info,
+ update tzname, timezone, and/or altzone and return STDDST_MASK,
+ diminished by the provided info if it is a specified local time.
+ Otherwise, return STDDST_MASK. See settzname for STDDST_MASK. */
+static int
+may_update_tzname_etc(int stddst_mask, struct state *sp, int type)
+{
+ struct ttinfo *ttisp = &sp->ttis[type];
+ int this_bit = 1 << ttisp->tt_isdst;
+ if (stddst_mask & this_bit) {
+ update_tzname_etc(sp, ttisp);
+ if (!ttunspecified(sp, type))
+ return stddst_mask & ~this_bit;
+ }
+ return stddst_mask;
+}
+
+static void
+settzname(void)
+{
+ register struct state * const sp = lclptr;
+ register int i;
+
+ /* If STDDST_MASK & 1 we need info about a standard time.
+ If STDDST_MASK & 2 we need info about a daylight saving time.
+ When STDDST_MASK becomes zero we can stop looking. */
+ int stddst_mask = 0;
+
+#if HAVE_TZNAME
+ tzname[0] = tzname[1] = (char *) (sp ? wildabbr : utc);
+ stddst_mask = 3;
+#endif
+#if USG_COMPAT
+ timezone = 0;
+ stddst_mask = 3;
+#endif
+#if ALTZONE
+ altzone = 0;
+ stddst_mask |= 2;
+#endif
+ /*
+ ** And to get the latest time zone abbreviations into tzname. . .
+ */
+ if (sp) {
+ for (i = sp->timecnt - 1; stddst_mask && 0 <= i; i--)
+ stddst_mask = may_update_tzname_etc(stddst_mask, sp, sp->types[i]);
+ for (i = sp->typecnt - 1; stddst_mask && 0 <= i; i--)
+ stddst_mask = may_update_tzname_etc(stddst_mask, sp, i);
+ }
+#if USG_COMPAT
+ daylight = stddst_mask >> 1 ^ 1;
+#endif
+}
+
+static void
+scrub_abbrs(struct state *sp)
+{
+ int i;
+ /*
+ ** 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];
+ char *cp = &sp->chars[ttisp->tt_desigidx];
+
+ if (strlen(cp) > TZ_ABBR_MAX_LEN &&
+ strcmp(cp, GRANDPARENTED) != 0)
+ *(cp + TZ_ABBR_MAX_LEN) = '\0';
+ }
+}
+
+#ifdef DETECT_TZ_CHANGES
+/*
+ * Determine if there's a change in the timezone since the last time we checked.
+ * Returns: -1 on error
+ * 0 if the timezone has not changed
+ * 1 if the timezone has changed
+ */
+static int
+change_in_tz(const char *name)
+{
+ static char old_name[PATH_MAX];
+ static struct stat old_sb;
+ struct stat sb;
+ int error;
+
+ error = stat(name, &sb);
+ if (error != 0)
+ return -1;
+
+ if (strcmp(name, old_name) != 0) {
+ strlcpy(old_name, name, sizeof(old_name));
+ old_sb = sb;
+ return 1;
+ }
+
+ if (sb.st_dev != old_sb.st_dev ||
+ sb.st_ino != old_sb.st_ino ||
+ sb.st_ctime != old_sb.st_ctime ||
+ sb.st_mtime != old_sb.st_mtime) {
+ old_sb = sb;
+ return 1;
+ }
+
+ return 0;
+}
+#else /* !DETECT_TZ_CHANGES */
+#define change_in_tz(X) 1
+#endif /* !DETECT_TZ_CHANGES */
+
+/* Input buffer for data read from a compiled tz file. */
+union input_buffer {
+ /* The first part of the buffer, interpreted as a header. */
+ struct tzhead tzhead;
+
+ /* The entire buffer. */
+ char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
+ + 4 * TZ_MAX_TIMES];
+};
+
+/* TZDIR with a trailing '/' rather than a trailing '\0'. */
+static char const tzdirslash[sizeof TZDIR] = TZDIR "/";
+
+/* Local storage needed for 'tzloadbody'. */
+union local_storage {
+ /* The results of analyzing the file's contents after it is opened. */
+ struct file_analysis {
+ /* The input buffer. */
+ union input_buffer u;
+
+ /* A temporary state used for parsing a TZ string in the file. */
+ struct state st;
+ } u;
+
+ /* The file name to be opened. */
+ char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)];
+};
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
+ success, an errno value on failure. */
+static int
+tzloadbody(char const *name, struct state *sp, bool doextend,
+ union local_storage *lsp)
+{
+ register int i;
+ register int fid;
+ register int stored;
+ register ssize_t nread;
+ register bool doaccess;
+ register union input_buffer *up = &lsp->u.u;
+ register int tzheadsize = sizeof(struct tzhead);
+
+ sp->goback = sp->goahead = false;
+
+ if (! name) {
+ name = TZDEFAULT;
+ if (! name)
+ return EINVAL;
+ }
+
+ if (name[0] == ':')
+ ++name;
+#ifdef SUPPRESS_TZDIR
+ /* Do not prepend TZDIR. This is intended for specialized
+ applications only, due to its security implications. */
+ doaccess = true;
+#else
+ doaccess = name[0] == '/';
+#endif
+ if (!doaccess) {
+ char const *dot;
+ if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name))
+ return ENAMETOOLONG;
+
+ /* Create a string "TZDIR/NAME". Using sprintf here
+ would pull in stdio (and would fail if the
+ resulting string length exceeded INT_MAX!). */
+ memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash);
+ strcpy(lsp->fullname + sizeof tzdirslash, name);
+
+ /* Set doaccess if NAME contains a ".." file name
+ component, as such a name could read a file outside
+ the TZDIR virtual subtree. */
+ for (dot = name; (dot = strchr(dot, '.')); dot++)
+ if ((dot == name || dot[-1] == '/') && dot[1] == '.'
+ && (dot[2] == '/' || !dot[2])) {
+ doaccess = true;
+ break;
+ }
+
+ name = lsp->fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return errno;
+ if (doextend) {
+ /*
+ * Detect if the timezone file has changed. Check
+ * 'doextend' to ignore TZDEFRULES; the change_in_tz()
+ * function can only keep state for a single file.
+ */
+ int ret = change_in_tz(name);
+ if (ret <= 0) {
+ /*
+ * Returns an errno value if there was an error,
+ * and 0 if the timezone had not changed.
+ */
+ return errno;
+ }
+ }
+ fid = _open(name, O_RDONLY | O_BINARY);
+ if (fid < 0)
+ return errno;
+
+ nread = _read(fid, up->buf, sizeof up->buf);
+ if (nread < tzheadsize) {
+ int err = nread < 0 ? errno : EINVAL;
+ _close(fid);
+ return err;
+ }
+ if (_close(fid) < 0)
+ return errno;
+ for (stored = 4; stored <= 8; stored *= 2) {
+ char version = up->tzhead.tzh_version[0];
+ bool skip_datablock = stored == 4 && version;
+ int_fast32_t datablock_size;
+ int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+ int_fast64_t prevtr = -1;
+ int_fast32_t prevcorr;
+ int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+ int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+ int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
+ char const *p = up->buf + tzheadsize;
+ /* Although tzfile(5) currently requires typecnt to be nonzero,
+ support future formats that may allow zero typecnt
+ in files that have a TZ string and no transitions. */
+ if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
+ && 0 <= typecnt && typecnt < TZ_MAX_TYPES
+ && 0 <= timecnt && timecnt < TZ_MAX_TIMES
+ && 0 <= charcnt && charcnt < TZ_MAX_CHARS
+ && 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES
+ && 0 <= ttisutcnt && ttisutcnt < TZ_MAX_TYPES))
+ return EINVAL;
+ datablock_size
+ = (timecnt * stored /* ats */
+ + timecnt /* types */
+ + typecnt * 6 /* ttinfos */
+ + charcnt /* chars */
+ + leapcnt * (stored + 4) /* lsinfos */
+ + ttisstdcnt /* ttisstds */
+ + ttisutcnt); /* ttisuts */
+ if (nread < tzheadsize + datablock_size)
+ return EINVAL;
+ if (skip_datablock)
+ p += datablock_size;
+ else {
+ if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0)
+ && (ttisutcnt == typecnt || ttisutcnt == 0)))
+ return EINVAL;
+
+ sp->leapcnt = leapcnt;
+ sp->timecnt = timecnt;
+ sp->typecnt = typecnt;
+ sp->charcnt = charcnt;
+
+ /* Read transitions, discarding those out of time_t range.
+ But pretend the last transition before TIME_T_MIN
+ occurred at TIME_T_MIN. */
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ int_fast64_t at
+ = stored == 4 ? detzcode(p) : detzcode64(p);
+ sp->types[i] = at <= TIME_T_MAX;
+ if (sp->types[i]) {
+ time_t attime
+ = ((TYPE_SIGNED(time_t) ? at < TIME_T_MIN : at < 0)
+ ? TIME_T_MIN : at);
+ if (timecnt && attime <= sp->ats[timecnt - 1]) {
+ if (attime < sp->ats[timecnt - 1])
+ return EINVAL;
+ sp->types[i - 1] = 0;
+ timecnt--;
+ }
+ sp->ats[timecnt++] = attime;
+ }
+ p += stored;
+ }
+
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ unsigned char typ = *p++;
+ if (sp->typecnt <= typ)
+ return EINVAL;
+ if (sp->types[i])
+ sp->types[timecnt++] = typ;
+ }
+ sp->timecnt = timecnt;
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+ unsigned char isdst, desigidx;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_utoff = detzcode(p);
+ p += 4;
+ isdst = *p++;
+ if (! (isdst < 2))
+ return EINVAL;
+ ttisp->tt_isdst = isdst;
+ desigidx = *p++;
+ if (! (desigidx < sp->charcnt))
+ return EINVAL;
+ ttisp->tt_desigidx = desigidx;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ /* Ensure '\0'-terminated, and make it safe to call
+ ttunspecified later. */
+ memset(&sp->chars[i], 0, CHARS_EXTRA);
+
+ /* Read leap seconds, discarding those out of time_t range. */
+ leapcnt = 0;
+ for (i = 0; i < sp->leapcnt; ++i) {
+ int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
+ int_fast32_t corr = detzcode(p + stored);
+ p += stored + 4;
+
+ /* Leap seconds cannot occur before the Epoch,
+ or out of order. */
+ if (tr <= prevtr)
+ return EINVAL;
+
+ /* To avoid other botches in this code, each leap second's
+ correction must differ from the previous one's by 1
+ second or less, except that the first correction can be
+ any value; these requirements are more generous than
+ RFC 8536, to allow future RFC extensions. */
+ if (! (i == 0
+ || (prevcorr < corr
+ ? corr == prevcorr + 1
+ : (corr == prevcorr
+ || corr == prevcorr - 1))))
+ return EINVAL;
+ prevtr = tr;
+ prevcorr = corr;
+
+ if (tr <= TIME_T_MAX) {
+ sp->lsis[leapcnt].ls_trans = tr;
+ sp->lsis[leapcnt].ls_corr = corr;
+ leapcnt++;
+ }
+ }
+ sp->leapcnt = leapcnt;
+
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = false;
+ else {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisstd = *p++;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ register struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisutcnt == 0)
+ ttisp->tt_ttisut = false;
+ else {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisut = *p++;
+ }
+ }
+ }
+
+ nread -= p - up->buf;
+ memmove(up->buf, p, nread);
+
+ /* If this is an old file, we're done. */
+ if (!version)
+ break;
+ }
+ if (doextend && nread > 2 &&
+ up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state *ts = &lsp->u.st;
+
+ up->buf[nread - 1] = '\0';
+ if (tzparse(&up->buf[1], ts, sp)) {
+
+ /* Attempt to reuse existing abbreviations.
+ Without this, America/Anchorage would be right on
+ the edge after 2037 when TZ_MAX_CHARS is 50, as
+ sp->charcnt equals 40 (for LMT AST AWT APT AHST
+ AHDT YST AKDT AKST) and ts->charcnt equals 10
+ (for AKST AKDT). Reusing means sp->charcnt can
+ stay 40 in this example. */
+ int gotabbr = 0;
+ int charcnt = sp->charcnt;
+ for (i = 0; i < ts->typecnt; i++) {
+ char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx;
+ int j;
+ for (j = 0; j < charcnt; j++)
+ if (strcmp(sp->chars + j, tsabbr) == 0) {
+ ts->ttis[i].tt_desigidx = j;
+ gotabbr++;
+ break;
+ }
+ if (! (j < charcnt)) {
+ int tsabbrlen = strlen(tsabbr);
+ if (j + tsabbrlen < TZ_MAX_CHARS) {
+ strcpy(sp->chars + j, tsabbr);
+ charcnt = j + tsabbrlen + 1;
+ ts->ttis[i].tt_desigidx = j;
+ gotabbr++;
+ }
+ }
+ }
+ if (gotabbr == ts->typecnt) {
+ sp->charcnt = charcnt;
+
+ /* Ignore any trailing, no-op transitions generated
+ by zic as they don't help here and can run afoul
+ of bugs in zic 2016j or earlier. */
+ while (1 < sp->timecnt
+ && (sp->types[sp->timecnt - 1]
+ == sp->types[sp->timecnt - 2]))
+ sp->timecnt--;
+
+ for (i = 0;
+ i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES;
+ i++) {
+ time_t t = ts->ats[i];
+ if (increment_overflow_time(&t, leapcorr(sp, t))
+ || (0 < sp->timecnt
+ && t <= sp->ats[sp->timecnt - 1]))
+ continue;
+ sp->ats[sp->timecnt] = t;
+ sp->types[sp->timecnt] = (sp->typecnt
+ + ts->types[i]);
+ sp->timecnt++;
+ }
+ for (i = 0; i < ts->typecnt; i++)
+ sp->ttis[sp->typecnt++] = ts->ttis[i];
+ }
+ }
+ }
+ if (sp->typecnt == 0)
+ return EINVAL;
+ if (sp->timecnt > 1) {
+ if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) {
+ time_t repeatat = sp->ats[0] + SECSPERREPEAT;
+ int repeattype = sp->types[0];
+ for (i = 1; i < sp->timecnt; ++i)
+ if (sp->ats[i] == repeatat
+ && typesequiv(sp, sp->types[i], repeattype)) {
+ sp->goback = true;
+ break;
+ }
+ }
+ if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) {
+ time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT;
+ int repeattype = sp->types[sp->timecnt - 1];
+ for (i = sp->timecnt - 2; i >= 0; --i)
+ if (sp->ats[i] == repeatat
+ && typesequiv(sp, sp->types[i], repeattype)) {
+ sp->goahead = true;
+ break;
+ }
+ }
+ }
+
+ /* Infer sp->defaulttype from the data. Although this default
+ type is always zero for data from recent tzdb releases,
+ things are trickier for data from tzdb 2018e or earlier.
+
+ The first set of heuristics work around bugs in 32-bit data
+ generated by tzdb 2013c or earlier. The workaround is for
+ zones like Australia/Macquarie where timestamps before the
+ first transition have a time type that is not the earliest
+ standard-time type. See:
+ https://mm.icann.org/pipermail/tz/2013-May/019368.html */
+ /*
+ ** If type 0 does not specify local time, or is unused in transitions,
+ ** it's the type to use for early times.
+ */
+ for (i = 0; i < sp->timecnt; ++i)
+ if (sp->types[i] == 0)
+ break;
+ i = i < sp->timecnt && ! ttunspecified(sp, 0) ? -1 : 0;
+ /*
+ ** Absent the above,
+ ** if there are transition times
+ ** and the first transition is to a daylight time
+ ** find the standard type less than and closest to
+ ** the type of the first transition.
+ */
+ if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst) {
+ i = sp->types[0];
+ while (--i >= 0)
+ if (!sp->ttis[i].tt_isdst)
+ break;
+ }
+ /* The next heuristics are for data generated by tzdb 2018e or
+ earlier, for zones like EST5EDT where the first transition
+ is to DST. */
+ /*
+ ** If no result yet, find the first standard type.
+ ** If there is none, punt to type zero.
+ */
+ if (i < 0) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ }
+ /* A simple 'sp->defaulttype = 0;' would suffice here if we
+ didn't have to worry about 2018e-or-earlier data. Even
+ simpler would be to remove the defaulttype member and just
+ use 0 in its place. */
+ sp->defaulttype = i;
+
+ return 0;
+}
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ format if DOEXTEND. Return 0 on success, an errno value on failure. */
+static int
+tzload(char const *name, struct state *sp, bool doextend)
+{
+#ifdef ALL_STATE
+ union local_storage *lsp = malloc(sizeof *lsp);
+ if (!lsp) {
+ return HAVE_MALLOC_ERRNO ? errno : ENOMEM;
+ } else {
+ int err = tzloadbody(name, sp, doextend, lsp);
+ free(lsp);
+ return err;
+ }
+#else
+ union local_storage ls;
+ return tzloadbody(name, sp, doextend, &ls);
+#endif
+}
+
+static bool
+typesequiv(const struct state *sp, int a, int b)
+{
+ register bool result;
+
+ if (sp == NULL ||
+ a < 0 || a >= sp->typecnt ||
+ b < 0 || b >= sp->typecnt)
+ result = false;
+ else {
+ /* Compare the relevant members of *AP and *BP.
+ Ignore tt_ttisstd and tt_ttisut, as they are
+ irrelevant now and counting them could cause
+ sp->goahead to mistakenly remain false. */
+ register const struct ttinfo * ap = &sp->ttis[a];
+ register const struct ttinfo * bp = &sp->ttis[b];
+ result = (ap->tt_utoff == bp->tt_utoff
+ && ap->tt_isdst == bp->tt_isdst
+ && (strcmp(&sp->chars[ap->tt_desigidx],
+ &sp->chars[bp->tt_desigidx])
+ == 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
+};
+
+/* Is C an ASCII digit? */
+static bool
+is_digit(char c)
+{
+ return '0' <= c && c <= '9';
+}
+
+/*
+** Given a pointer into a timezone string, scan until a character that is not
+** a valid character in a time zone abbreviation is found.
+** Return a pointer to that character.
+*/
+
+static ATTRIBUTE_REPRODUCIBLE const char *
+getzname(register const char *strp)
+{
+ register char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*
+** Given a pointer into an extended timezone string, scan until the ending
+** delimiter of the time zone abbreviation 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 ATTRIBUTE_REPRODUCIBLE 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 timezone 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(register const char *strp, int *const nump, const int min, const int max)
+{
+ register char c;
+ register 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 timezone 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(register const char *strp, int_fast32_t *const secsp)
+{
+ int num;
+ int_fast32_t secsperhour = SECSPERHOUR;
+
+ /*
+ ** '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 * 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 timezone 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(register const char *strp, int_fast32_t *const offsetp)
+{
+ register bool neg = false;
+
+ if (*strp == '-') {
+ neg = true;
+ ++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 timezone 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(const char *strp, register 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 = getoffset(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+** Given a year, a rule, and the offset from UT at the time that rule takes
+** effect, calculate the year-relative time that rule takes effect.
+*/
+
+static int_fast32_t
+transtime(const int year, register const struct rule *const rulep,
+ const int_fast32_t offset)
+{
+ register bool leapyear;
+ register int_fast32_t value;
+ register int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ 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 = (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 = rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+
+ /*
+ ** 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;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+ break;
+
+ default: unreachable();
+ }
+
+ /*
+ ** "value" is the year-relative time of 00:00:00 UT on the day in
+ ** question. To get the year-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UT.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static bool
+tzparse(const char *name, struct state *sp, struct state *basep)
+{
+ const char * stdname;
+ const char * dstname;
+ int_fast32_t stdoffset;
+ int_fast32_t dstoffset;
+ register char * cp;
+ register bool load_ok;
+ ptrdiff_t stdlen, dstlen, charcnt;
+ time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
+
+ stdname = name;
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (!stdlen)
+ return false;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return false;
+ charcnt = stdlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ if (basep) {
+ if (0 < basep->timecnt)
+ atlo = basep->ats[basep->timecnt - 1];
+ load_ok = false;
+ sp->leapcnt = basep->leapcnt;
+ memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis);
+ } else {
+ load_ok = tzload(TZDEFRULES, sp, false) == 0;
+ if (!load_ok)
+ sp->leapcnt = 0; /* So, we're off a little. */
+ }
+ if (0 < sp->leapcnt)
+ leaplo = sp->lsis[sp->leapcnt - 1].ls_trans;
+ if (*name != '\0') {
+ if (*name == '<') {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ dstlen = name - dstname;
+ name++;
+ } else {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST abbr. */
+ }
+ if (!dstlen)
+ return false;
+ charcnt += dstlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return false;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && !load_ok)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ register int year;
+ register int timecnt;
+ time_t janfirst;
+ int_fast32_t janoffset = 0;
+ int yearbeg, yearlim;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return false;
+ if (*name++ != ',')
+ return false;
+ if ((name = getrule(name, &end)) == NULL)
+ return false;
+ if (*name != '\0')
+ return false;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
+ sp->defaulttype = 0;
+ timecnt = 0;
+ janfirst = 0;
+ yearbeg = EPOCH_YEAR;
+
+ do {
+ int_fast32_t yearsecs
+ = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
+ yearbeg--;
+ if (increment_overflow_time(&janfirst, -yearsecs)) {
+ janoffset = -yearsecs;
+ break;
+ }
+ } while (atlo < janfirst
+ && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
+
+ while (true) {
+ int_fast32_t yearsecs
+ = year_lengths[isleap(yearbeg)] * SECSPERDAY;
+ int yearbeg1 = yearbeg;
+ time_t janfirst1 = janfirst;
+ if (increment_overflow_time(&janfirst1, yearsecs)
+ || increment_overflow(&yearbeg1, 1)
+ || atlo <= janfirst1)
+ break;
+ yearbeg = yearbeg1;
+ janfirst = janfirst1;
+ }
+
+ yearlim = yearbeg;
+ if (increment_overflow(&yearlim, YEARSPERREPEAT + 1))
+ yearlim = INT_MAX;
+ for (year = yearbeg; year < yearlim; year++) {
+ int_fast32_t
+ starttime = transtime(year, &start, stdoffset),
+ endtime = transtime(year, &end, dstoffset);
+ int_fast32_t
+ yearsecs = (year_lengths[isleap(year)]
+ * SECSPERDAY);
+ bool reversed = endtime < starttime;
+ if (reversed) {
+ int_fast32_t swap = starttime;
+ starttime = endtime;
+ endtime = swap;
+ }
+ if (reversed
+ || (starttime < endtime
+ && endtime - starttime < yearsecs)) {
+ if (TZ_MAX_TIMES - 2 < timecnt)
+ break;
+ sp->ats[timecnt] = janfirst;
+ if (! increment_overflow_time
+ (&sp->ats[timecnt],
+ janoffset + starttime)
+ && atlo <= sp->ats[timecnt])
+ sp->types[timecnt++] = !reversed;
+ sp->ats[timecnt] = janfirst;
+ if (! increment_overflow_time
+ (&sp->ats[timecnt],
+ janoffset + endtime)
+ && atlo <= sp->ats[timecnt]) {
+ sp->types[timecnt++] = reversed;
+ }
+ }
+ if (endtime < leaplo) {
+ yearlim = year;
+ if (increment_overflow(&yearlim,
+ YEARSPERREPEAT + 1))
+ yearlim = INT_MAX;
+ }
+ if (increment_overflow_time
+ (&janfirst, janoffset + yearsecs))
+ break;
+ janoffset = 0;
+ }
+ sp->timecnt = timecnt;
+ if (! timecnt) {
+ sp->ttis[0] = sp->ttis[1];
+ sp->typecnt = 1; /* Perpetual DST. */
+ } else if (YEARSPERREPEAT < year - yearbeg)
+ sp->goback = sp->goahead = true;
+ } else {
+ register int_fast32_t theirstdoffset;
+ register int_fast32_t theirdstoffset;
+ register int_fast32_t theiroffset;
+ register bool isdst;
+ register int i;
+ register int j;
+
+ if (*name != '\0')
+ return false;
+ /*
+ ** 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_utoff;
+ 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_utoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = false;
+ /*
+ ** 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_ttisut) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If daylight saving time is in
+ ** effect, and the transition time was
+ ** not specified as standard time, add
+ ** the daylight saving 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_utoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
+ sp->typecnt = 2;
+ sp->defaulttype = 0;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ sp->defaulttype = 0;
+ }
+ sp->charcnt = charcnt;
+ cp = sp->chars;
+ memcpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ memcpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return true;
+}
+
+static void
+gmtload(struct state *const sp)
+{
+ if (tzload(etc_utc, sp, true) != 0)
+ tzparse("UTC0", sp, NULL);
+}
+
+#ifdef DETECT_TZ_CHANGES
+static int
+recheck_tzdata()
+{
+ static time_t last_checked;
+ struct timespec now;
+ time_t current_time;
+ int error;
+
+ /*
+ * We want to recheck the timezone file every 61 sec.
+ */
+ error = clock_gettime(CLOCK_MONOTONIC, &now);
+ if (error < 0) {
+ /* XXX: Can we somehow report this? */
+ return 0;
+ }
+
+ current_time = now.tv_sec;
+ if ((current_time - last_checked > DETECT_TZ_CHANGES_INTERVAL) ||
+ (last_checked > current_time)) {
+ last_checked = current_time;
+ return 1;
+ }
+
+ return 0;
+}
+#else /* !DETECT_TZ_CHANGES */
+#define recheck_tzdata() 0
+#endif /* !DETECT_TZ_CHANGES */
+
+/* Initialize *SP to a value appropriate for the TZ setting NAME.
+ Return 0 on success, an errno value on failure. */
+static int
+zoneinit(struct state *sp, char const *name)
+{
+ if (name && ! name[0]) {
+ /*
+ ** User wants it fast rather than right.
+ */
+ sp->leapcnt = 0; /* so, we're off a little */
+ sp->timecnt = 0;
+ sp->typecnt = 0;
+ sp->charcnt = 0;
+ sp->goback = sp->goahead = false;
+ init_ttinfo(&sp->ttis[0], 0, false, 0);
+ strcpy(sp->chars, utc);
+ sp->defaulttype = 0;
+ return 0;
+ } else {
+ int err = tzload(name, sp, true);
+ if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
+ err = 0;
+ if (err == 0)
+ scrub_abbrs(sp);
+ return err;
+ }
+}
+
+static void
+tzset_unlocked(void)
+{
+ char const *name = getenv("TZ");
+ struct state *sp = lclptr;
+ int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
+ if (lcl < 0
+ ? lcl_is_set < 0
+ : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
+ if (recheck_tzdata() == 0)
+ return;
+#ifdef ALL_STATE
+ if (! sp)
+ lclptr = sp = malloc(sizeof *lclptr);
+#endif /* defined ALL_STATE */
+ if (sp) {
+ if (zoneinit(sp, name) != 0)
+ zoneinit(sp, "");
+ if (0 < lcl)
+ strcpy(lcl_TZname, name);
+ }
+ settzname();
+ lcl_is_set = lcl;
+}
+
+void
+tzset(void)
+{
+ if (lock() != 0)
+ return;
+ tzset_unlocked();
+ unlock();
+}
+
+static void
+gmtcheck(void)
+{
+ static bool gmt_is_set;
+ if (lock() != 0)
+ return;
+ if (! gmt_is_set) {
+#ifdef ALL_STATE
+ gmtptr = malloc(sizeof *gmtptr);
+#endif
+ if (gmtptr)
+ gmtload(gmtptr);
+ gmt_is_set = true;
+ }
+ unlock();
+}
+
+#if NETBSD_INSPIRED
+
+timezone_t
+tzalloc(char const *name)
+{
+ timezone_t sp = malloc(sizeof *sp);
+ if (sp) {
+ int err = zoneinit(sp, name);
+ if (err != 0) {
+ free(sp);
+ errno = err;
+ return NULL;
+ }
+ } else if (!HAVE_MALLOC_ERRNO)
+ errno = ENOMEM;
+ return sp;
+}
+
+void
+tzfree(timezone_t sp)
+{
+ free(sp);
+}
+
+/*
+** NetBSD 6.1.4 has ctime_rz, but omit it because POSIX says ctime and
+** ctime_r are obsolescent and have potential security problems that
+** ctime_rz would share. Callers can instead use localtime_rz + strftime.
+**
+** NetBSD 6.1.4 has tzgetname, but omit it because it doesn't work
+** in zones with three or more time zone abbreviations.
+** Callers can instead use localtime_rz + strftime.
+*/
+
+#endif
+
+/*
+** 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.)
+**
+** If successful and SETNAME is nonzero,
+** set the applicable parts of tzname, timezone and altzone;
+** however, it's OK to omit this step if the timezone is POSIX-compatible,
+** since in that case tzset should have already done this step correctly.
+** SETNAME's type is int_fast32_t for compatibility with gmtsub,
+** but it is actually a boolean and its value should be 0 or 1.
+*/
+
+/*ARGSUSED*/
+static struct tm *
+localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
+ struct tm *const tmp)
+{
+ register const struct ttinfo * ttisp;
+ register int i;
+ register struct tm * result;
+ const time_t t = *timep;
+
+ if (sp == NULL) {
+ /* Don't bother to set tzname etc.; tzset has already done it. */
+ return gmtsub(gmtptr, timep, 0, tmp);
+ }
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t newt;
+ register time_t seconds;
+ register time_t years;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+
+ /* Beware integer overflow, as SECONDS might
+ be close to the maximum time_t. */
+ years = seconds / SECSPERREPEAT * YEARSPERREPEAT;
+ seconds = years * AVGSECSPERYEAR;
+ years += YEARSPERREPEAT;
+ if (t < sp->ats[0])
+ newt = t + seconds + SECSPERREPEAT;
+ else
+ newt = t - seconds - SECSPERREPEAT;
+
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(sp, &newt, setname, tmp);
+ if (result) {
+#if defined ckd_add && defined ckd_sub
+ if (t < sp->ats[0]
+ ? ckd_sub(&result->tm_year,
+ result->tm_year, years)
+ : ckd_add(&result->tm_year,
+ result->tm_year, years))
+ return NULL;
+#else
+ register int_fast64_t newy;
+
+ newy = result->tm_year;
+ if (t < sp->ats[0])
+ newy -= years;
+ else newy += years;
+ if (! (INT_MIN <= newy && newy <= INT_MAX))
+ return NULL;
+ result->tm_year = newy;
+#endif
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ i = sp->defaulttype;
+ } 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 = 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_utoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_utoff, sp, tmp);
+ if (result) {
+ result->tm_isdst = ttisp->tt_isdst;
+#ifdef TM_ZONE
+ result->TM_ZONE = (char *) &sp->chars[ttisp->tt_desigidx];
+#endif /* defined TM_ZONE */
+ if (setname)
+ update_tzname_etc(sp, ttisp);
+ }
+ return result;
+}
+
+#if NETBSD_INSPIRED
+
+struct tm *
+localtime_rz(struct state *sp, time_t const *timep, struct tm *tmp)
+{
+ return localsub(sp, timep, 0, tmp);
+}
+
+#endif
+
+static struct tm *
+localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
+{
+ int err = lock();
+ if (err) {
+ errno = err;
+ return NULL;
+ }
+#ifndef DETECT_TZ_CHANGES
+ if (setname || !lcl_is_set)
+#endif
+ tzset_unlocked();
+ tmp = localsub(lclptr, timep, setname, tmp);
+ unlock();
+ return tmp;
+}
+
+static void
+localtime_key_init(void)
+{
+
+ localtime_key_error = _pthread_key_create(&localtime_key, free);
+}
+
+struct tm *
+localtime(const time_t *timep)
+{
+ struct tm *p_tm = &tm;
+
+ if (__isthreaded != 0) {
+ _pthread_once(&localtime_once, localtime_key_init);
+ if (localtime_key_error != 0) {
+ errno = localtime_key_error;
+ return (NULL);
+ }
+ if ((p_tm = _pthread_getspecific(localtime_key)) == NULL) {
+ if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
+ return (NULL);
+ }
+ _pthread_setspecific(localtime_key, p_tm);
+ }
+ }
+ return localtime_tzset(timep, p_tm, true);
+}
+
+struct tm *
+localtime_r(const time_t *timep, struct tm *tmp)
+{
+ return localtime_tzset(timep, tmp, false);
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct tm *
+gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
+ int_fast32_t offset, struct tm *tmp)
+{
+ register struct tm * result;
+
+ result = timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "+xx" or "-xx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ tmp->TM_ZONE = ((char *)
+ (offset ? wildabbr : gmtptr ? gmtptr->chars : utc));
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+/*
+* Re-entrant version of gmtime.
+*/
+
+struct tm *
+gmtime_r(const time_t *timep, struct tm *tmp)
+{
+ _once(&gmt_once, gmtcheck);
+ return gmtsub(gmtptr, timep, 0, tmp);
+}
+
+static void
+gmtime_key_init(void)
+{
+
+ gmtime_key_error = _pthread_key_create(&gmtime_key, free);
+}
+
+struct tm *
+gmtime(const time_t *timep)
+{
+ struct tm *p_tm = &tm;
+
+ if (__isthreaded != 0) {
+ _pthread_once(&gmtime_once, gmtime_key_init);
+ if (gmtime_key_error != 0) {
+ errno = gmtime_key_error;
+ return (NULL);
+ }
+ if ((p_tm = _pthread_getspecific(gmtime_key)) == NULL) {
+ if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
+ return (NULL);
+ }
+ _pthread_setspecific(gmtime_key, p_tm);
+ }
+ }
+ return gmtime_r(timep, p_tm);
+}
+
+#ifdef STD_INSPIRED
+
+struct tm *
+offtime(const time_t *timep, long offset)
+{
+ _once(&gmt_once, gmtcheck);
+ return gmtsub(gmtptr, 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 time_t
+leaps_thru_end_of_nonneg(time_t y)
+{
+ return y / 4 - y / 100 + y / 400;
+}
+
+static time_t
+leaps_thru_end_of(time_t y)
+{
+ return (y < 0
+ ? -1 - leaps_thru_end_of_nonneg(-1 - y)
+ : leaps_thru_end_of_nonneg(y));
+}
+
+static struct tm *
+timesub(const time_t *timep, int_fast32_t offset,
+ const struct state *sp, struct tm *tmp)
+{
+ register const struct lsinfo * lp;
+ register time_t tdays;
+ register const int * ip;
+ register int_fast32_t corr;
+ register int i;
+ int_fast32_t idays, rem, dayoff, dayrem;
+ time_t y;
+
+ /* If less than SECSPERMIN, the number of seconds since the
+ most recent positive leap second; otherwise, do not add 1
+ to localtime tm_sec because of leap seconds. */
+ time_t secs_since_posleap = SECSPERMIN;
+
+ corr = 0;
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (*timep >= lp->ls_trans) {
+ corr = lp->ls_corr;
+ if ((i == 0 ? 0 : lp[-1].ls_corr) < corr)
+ secs_since_posleap = *timep - lp->ls_trans;
+ break;
+ }
+ }
+
+ /* Calculate the year, avoiding integer overflow even if
+ time_t is unsigned. */
+ tdays = *timep / SECSPERDAY;
+ rem = *timep % SECSPERDAY;
+ rem += offset % SECSPERDAY - corr % SECSPERDAY + 3 * SECSPERDAY;
+ dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3;
+ rem %= SECSPERDAY;
+ /* y = (EPOCH_YEAR
+ + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
+ sans overflow. But calculate against 1570 (EPOCH_YEAR -
+ YEARSPERREPEAT) instead of against 1970 so that things work
+ for localtime values before 1970 when time_t is unsigned. */
+ dayrem = tdays % DAYSPERREPEAT;
+ dayrem += dayoff % DAYSPERREPEAT;
+ y = (EPOCH_YEAR - YEARSPERREPEAT
+ + ((1 + dayoff / DAYSPERREPEAT + dayrem / DAYSPERREPEAT
+ - ((dayrem % DAYSPERREPEAT) < 0)
+ + tdays / DAYSPERREPEAT)
+ * YEARSPERREPEAT));
+ /* idays = (tdays + dayoff) mod DAYSPERREPEAT, sans overflow. */
+ idays = tdays % DAYSPERREPEAT;
+ idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT;
+ idays %= DAYSPERREPEAT;
+ /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */
+ while (year_lengths[isleap(y)] <= idays) {
+ int tdelta = idays / DAYSPERLYEAR;
+ int_fast32_t ydelta = tdelta + !tdelta;
+ time_t newy = y + ydelta;
+ register int leapdays;
+ leapdays = leaps_thru_end_of(newy - 1) -
+ leaps_thru_end_of(y - 1);
+ idays -= ydelta * DAYSPERNYEAR;
+ idays -= leapdays;
+ y = newy;
+ }
+
+#ifdef ckd_add
+ if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+#else
+ if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) {
+ int signed_y = y;
+ tmp->tm_year = signed_y - TM_YEAR_BASE;
+ } else if ((!TYPE_SIGNED(time_t) || INT_MIN + TM_YEAR_BASE <= y)
+ && y - TM_YEAR_BASE <= INT_MAX)
+ tmp->tm_year = y - TM_YEAR_BASE;
+ else {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+#endif
+ tmp->tm_yday = idays;
+ /*
+ ** The "extra" mods below avoid overflow problems.
+ */
+ tmp->tm_wday = (TM_WDAY_BASE
+ + ((tmp->tm_year % DAYSPERWEEK)
+ * (DAYSPERNYEAR % DAYSPERWEEK))
+ + leaps_thru_end_of(y - 1)
+ - leaps_thru_end_of(TM_YEAR_BASE - 1)
+ + idays);
+ tmp->tm_wday %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = rem / SECSPERHOUR;
+ rem %= SECSPERHOUR;
+ tmp->tm_min = rem / SECSPERMIN;
+ tmp->tm_sec = rem % SECSPERMIN;
+
+ /* Use "... ??:??:60" at the end of the localtime minute containing
+ the second just before the positive leap second. */
+ tmp->tm_sec += secs_since_posleap <= tmp->tm_sec;
+
+ 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 = idays + 1;
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+ return tmp;
+}
+
+char *
+ctime(const time_t *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))
+*/
+ struct tm *tmp = localtime(timep);
+ return tmp ? asctime(tmp) : NULL;
+}
+
+char *
+ctime_r(const time_t *timep, char *buf)
+{
+ struct tm mytm;
+ struct tm *tmp = localtime_r(timep, &mytm);
+ return tmp ? asctime_r(tmp, buf) : NULL;
+}
+
+/*
+** 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 */
+
+/*
+** Normalize logic courtesy Paul Eggert.
+*/
+
+static bool
+increment_overflow(int *ip, int j)
+{
+#ifdef ckd_add
+ return ckd_add(ip, *ip, j);
+#else
+ register int const i = *ip;
+
+ /*
+ ** If i >= 0 there can only be overflow if i + j > INT_MAX
+ ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
+ ** If i < 0 there can only be overflow if i + j < INT_MIN
+ ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
+ */
+ if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
+ return true;
+ *ip += j;
+ return false;
+#endif
+}
+
+static bool
+increment_overflow32(int_fast32_t *const lp, int const m)
+{
+#ifdef ckd_add
+ return ckd_add(lp, *lp, m);
+#else
+ register int_fast32_t const l = *lp;
+
+ if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
+ return true;
+ *lp += m;
+ return false;
+#endif
+}
+
+static bool
+increment_overflow_time(time_t *tp, int_fast32_t j)
+{
+#ifdef ckd_add
+ return ckd_add(tp, *tp, j);
+#else
+ /*
+ ** This is like
+ ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
+ ** except that it does the right thing even if *tp + j would overflow.
+ */
+ if (! (j < 0
+ ? (TYPE_SIGNED(time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
+ : *tp <= TIME_T_MAX - j))
+ return true;
+ *tp += j;
+ return false;
+#endif
+}
+
+static bool
+normalize_overflow(int *const tensptr, int *const unitsptr, const int base)
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static bool
+normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base)
+{
+ register int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow32(tensptr, tensdelta);
+}
+
+static int
+tmcomp(register const struct tm *const atmp,
+ register const struct tm *const btmp)
+{
+ register int result;
+
+ if (atmp->tm_year != btmp->tm_year)
+ return atmp->tm_year < btmp->tm_year ? -1 : 1;
+ if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0)
+ result = atmp->tm_sec - btmp->tm_sec;
+ return result;
+}
+
+/* Copy to *DEST from *SRC. Copy only the members needed for mktime,
+ as other members might not be initialized. */
+static void
+mktmcpy(struct tm *dest, struct tm const *src)
+{
+ dest->tm_sec = src->tm_sec;
+ dest->tm_min = src->tm_min;
+ dest->tm_hour = src->tm_hour;
+ dest->tm_mday = src->tm_mday;
+ dest->tm_mon = src->tm_mon;
+ dest->tm_year = src->tm_year;
+ dest->tm_isdst = src->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ dest->TM_GMTOFF = src->TM_GMTOFF;
+#endif
+}
+
+static time_t
+time2sub(struct tm *const tmp,
+ struct tm *(*funcp)(struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
+ struct state const *sp,
+ const int_fast32_t offset,
+ bool *okayp,
+ bool do_norm_secs)
+{
+ register int dir;
+ register int i, j;
+ register int saved_seconds;
+ register int_fast32_t li;
+ register time_t lo;
+ register time_t hi;
+ int_fast32_t y;
+ time_t newt;
+ time_t t;
+ struct tm yourtm, mytm;
+
+ *okayp = false;
+ mktmcpy(&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 (normalize_overflow32(&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 (increment_overflow32(&y, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (increment_overflow32(&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 (increment_overflow32(&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 (increment_overflow32(&y, 1))
+ return WRONG;
+ }
+ }
+#ifdef ckd_add
+ if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE))
+ return WRONG;
+#else
+ if (increment_overflow32(&y, -TM_YEAR_BASE))
+ return WRONG;
+ if (! (INT_MIN <= y && y <= INT_MAX))
+ return WRONG;
+ yourtm.tm_year = y;
+#endif
+ if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+ saved_seconds = 0;
+ else if (yourtm.tm_year < EPOCH_YEAR - TM_YEAR_BASE) {
+ /*
+ ** 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).
+ */
+ lo = TIME_T_MIN;
+ hi = TIME_T_MAX;
+ for ( ; ; ) {
+ t = lo / 2 + hi / 2;
+ if (t < lo)
+ t = lo;
+ else if (t > hi)
+ t = hi;
+ if (! funcp(sp, &t, offset, &mytm)) {
+ /*
+ ** 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) {
+ if (t == TIME_T_MAX)
+ return WRONG;
+ ++t;
+ ++lo;
+ } else if (t == hi) {
+ if (t == TIME_T_MIN)
+ return WRONG;
+ --t;
+ --hi;
+ }
+ if (lo > hi)
+ return WRONG;
+ if (dir > 0)
+ hi = t;
+ else lo = t;
+ continue;
+ }
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ if (mytm.TM_GMTOFF != yourtm.TM_GMTOFF
+ && (yourtm.TM_GMTOFF < 0
+ ? (-SECSPERDAY <= yourtm.TM_GMTOFF
+ && (mytm.TM_GMTOFF <=
+ (min(INT_FAST32_MAX, LONG_MAX)
+ + yourtm.TM_GMTOFF)))
+ : (yourtm.TM_GMTOFF <= SECSPERDAY
+ && ((max(INT_FAST32_MIN, LONG_MIN)
+ + yourtm.TM_GMTOFF)
+ <= mytm.TM_GMTOFF)))) {
+ /* MYTM matches YOURTM except with the wrong UT offset.
+ YOURTM.TM_GMTOFF is plausible, so try it instead.
+ It's OK if YOURTM.TM_GMTOFF contains uninitialized data,
+ since the guess gets checked. */
+ time_t altt = t;
+ int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF;
+ if (!increment_overflow_time(&altt, diff)) {
+ struct tm alttm;
+ if (funcp(sp, &altt, offset, &alttm)
+ && alttm.tm_isdst == mytm.tm_isdst
+ && alttm.TM_GMTOFF == yourtm.TM_GMTOFF
+ && tmcomp(&alttm, &yourtm) == 0) {
+ t = altt;
+ mytm = alttm;
+ }
+ }
+ }
+#endif
+ 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.
+ */
+ if (sp == NULL)
+ return WRONG;
+ 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;
+ if (ttunspecified(sp, j))
+ continue;
+ newt = (t + sp->ttis[j].tt_utoff
+ - sp->ttis[i].tt_utoff);
+ if (! funcp(sp, &newt, offset, &mytm))
+ 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(sp, &t, offset, tmp))
+ *okayp = true;
+ return t;
+}
+
+static time_t
+time2(struct tm * const tmp,
+ struct tm *(*funcp)(struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
+ struct state const *sp,
+ const int_fast32_t offset,
+ bool *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, sp, offset, okayp, false);
+ return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true);
+}
+
+static time_t
+time1(struct tm *const tmp,
+ struct tm *(*funcp)(struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
+ struct state const *sp,
+ const int_fast32_t offset)
+{
+ register time_t t;
+ register int samei, otheri;
+ register int sameind, otherind;
+ register int i;
+ register int nseen;
+ char seen[TZ_MAX_TYPES];
+ unsigned char types[TZ_MAX_TYPES];
+ bool okay;
+
+ if (tmp == NULL) {
+ errno = EINVAL;
+ return WRONG;
+ }
+
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, sp, offset, &okay);
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+#ifdef PCTS
+ /*
+ ** POSIX Conformance Test Suite code courtesy Grant Sullivan.
+ */
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#else
+ 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.
+ */
+ if (sp == NULL)
+ return WRONG;
+ 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]] && !ttunspecified(sp, 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_utoff
+ - sp->ttis[samei].tt_utoff);
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, sp, offset, &okay);
+ if (okay)
+ return t;
+ tmp->tm_sec -= (sp->ttis[otheri].tt_utoff
+ - sp->ttis[samei].tt_utoff);
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+static time_t
+mktime_tzname(struct state *sp, struct tm *tmp, bool setname)
+{
+ if (sp)
+ return time1(tmp, localsub, sp, setname);
+ else {
+ _once(&gmt_once, gmtcheck);
+ return time1(tmp, gmtsub, gmtptr, 0);
+ }
+}
+
+#if NETBSD_INSPIRED
+
+time_t
+mktime_z(struct state *sp, struct tm *tmp)
+{
+ return mktime_tzname(sp, tmp, false);
+}
+
+#endif
+
+time_t
+mktime(struct tm *tmp)
+{
+ time_t t;
+ int err = lock();
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ tzset_unlocked();
+ t = mktime_tzname(lclptr, tmp, true);
+ unlock();
+ return t;
+}
+
+#ifdef STD_INSPIRED
+time_t
+timelocal(struct tm *tmp)
+{
+ if (tmp != NULL)
+ tmp->tm_isdst = -1; /* in case it wasn't initialized */
+ return mktime(tmp);
+}
+#else
+static
+#endif
+time_t
+timeoff(struct tm *tmp, long offset)
+{
+ if (tmp)
+ tmp->tm_isdst = 0;
+ _once(&gmt_once, gmtcheck);
+ return time1(tmp, gmtsub, gmtptr, offset);
+}
+
+time_t
+timegm(struct tm *tmp)
+{
+ time_t t;
+ struct tm tmcpy;
+ mktmcpy(&tmcpy, tmp);
+ tmcpy.tm_wday = -1;
+ t = timeoff(&tmcpy, 0);
+ if (0 <= tmcpy.tm_wday)
+ *tmp = tmcpy;
+ return t;
+}
+
+static int_fast32_t
+leapcorr(struct state const *sp, time_t t)
+{
+ register struct lsinfo const * lp;
+ register int i;
+
+ i = sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (t >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+/*
+** XXX--is the below the right way to conditionalize??
+*/
+
+#ifdef STD_INSPIRED
+
+/* NETBSD_INSPIRED_EXTERN functions are exported to callers if
+ NETBSD_INSPIRED is defined, and are private otherwise. */
+# if NETBSD_INSPIRED
+# define NETBSD_INSPIRED_EXTERN
+# else
+# define NETBSD_INSPIRED_EXTERN static
+# endif
+
+/*
+** IEEE Std 1003.1 (POSIX) says 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.
+*/
+
+NETBSD_INSPIRED_EXTERN time_t
+time2posix_z(struct state *sp, time_t t)
+{
+ return t - leapcorr(sp, t);
+}
+
+time_t
+time2posix(time_t t)
+{
+ int err = lock();
+ if (err) {
+ errno = err;
+ return -1;
+ }
+#ifndef DETECT_TZ_CHANGES
+ if (!lcl_is_set)
+#endif
+ tzset_unlocked();
+ if (lclptr)
+ t = time2posix_z(lclptr, t);
+ unlock();
+ return t;
+}
+
+NETBSD_INSPIRED_EXTERN time_t
+posix2time_z(struct state *sp, time_t t)
+{
+ time_t x;
+ time_t y;
+ /*
+ ** 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(sp, t);
+ y = x - leapcorr(sp, x);
+ if (y < t) {
+ do {
+ x++;
+ y = x - leapcorr(sp, x);
+ } while (y < t);
+ x -= y != t;
+ } else if (y > t) {
+ do {
+ --x;
+ y = x - leapcorr(sp, x);
+ } while (y > t);
+ x += y != t;
+ }
+ return x;
+}
+
+time_t
+posix2time(time_t t)
+{
+ int err = lock();
+ if (err) {
+ errno = err;
+ return -1;
+ }
+#ifndef DETECT_TZ_CHANGES
+ if (!lcl_is_set)
+#endif
+ tzset_unlocked();
+ if (lclptr)
+ t = posix2time_z(lclptr, t);
+ unlock();
+ return t;
+}
+
+#endif /* defined STD_INSPIRED */
+
+#if TZ_TIME_T
+
+# if !USG_COMPAT
+# define daylight 0
+# define timezone 0
+# endif
+# if !ALTZONE
+# define altzone 0
+# endif
+
+/* Convert from the underlying system's time_t to the ersatz time_tz,
+ which is called 'time_t' in this file. Typically, this merely
+ converts the time's integer width. On some platforms, the system
+ time is local time not UT, or uses some epoch other than the POSIX
+ epoch.
+
+ Although this code appears to define a function named 'time' that
+ returns time_t, the macros in private.h cause this code to actually
+ define a function named 'tz_time' that returns tz_time_t. The call
+ to sys_time invokes the underlying system's 'time' function. */
+
+time_t
+time(time_t *p)
+{
+ time_t r = sys_time(0);
+ if (r != (time_t) -1) {
+ int_fast32_t offset = EPOCH_LOCAL ? (daylight ? timezone : altzone) : 0;
+ if (increment_overflow32(&offset, -EPOCH_OFFSET)
+ || increment_overflow_time(&r, offset)) {
+ errno = EOVERFLOW;
+ r = -1;
+ }
+ }
+ if (p)
+ *p = r;
+ return r;
+}
+
+#endif
diff --git a/contrib/tzcode/newctime.3 b/contrib/tzcode/newctime.3
new file mode 100644
index 000000000000..e25d841ef537
--- /dev/null
+++ b/contrib/tzcode/newctime.3
@@ -0,0 +1,344 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH NEWCTIME 3
+.SH NAME
+asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
+.SH SYNOPSIS
+.nf
+.ie \n(.g .ds - \f(CR-\fP
+.el .ds - \-
+.B #include <time.h>
+.PP
+.BR "extern char *tzname[];" " /\(** (optional) \(**/"
+.PP
+.B char *ctime(time_t const *clock);
+.PP
+.B char *ctime_r(time_t const *clock, char *buf);
+.PP
+.B double difftime(time_t time1, time_t time0);
+.PP
+.B char *asctime(struct tm const *tm);
+.PP
+.B "char *asctime_r(struct tm const *restrict tm,"
+.B " char *restrict result);"
+.PP
+.B struct tm *localtime(time_t const *clock);
+.PP
+.B "struct tm *localtime_r(time_t const *restrict clock,"
+.B " struct tm *restrict result);"
+.PP
+.B "struct tm *localtime_rz(timezone_t restrict zone,"
+.B " time_t const *restrict clock,"
+.B " struct tm *restrict result);"
+.PP
+.B struct tm *gmtime(time_t const *clock);
+.PP
+.B "struct tm *gmtime_r(time_t const *restrict clock,"
+.B " struct tm *restrict result);"
+.PP
+.B time_t mktime(struct tm *tm);
+.PP
+.B "time_t mktime_z(timezone_t restrict zone,"
+.B " struct tm *restrict tm);"
+.PP
+.B cc ... \*-ltz
+.fi
+.SH DESCRIPTION
+.ie '\(en'' .ds en \-
+.el .ds en \(en
+.ie '\(lq'' .ds lq \&"\"
+.el .ds lq \(lq\"
+.ie '\(rq'' .ds rq \&"\"
+.el .ds rq \(rq\"
+.de q
+\\$3\*(lq\\$1\*(rq\\$2
+..
+The
+.B ctime
+function
+converts a long integer, pointed to by
+.IR clock ,
+and returns a pointer to a
+string of the form
+.br
+.ce
+.eo
+Thu Nov 24 18:22:48 1986\n\0
+.br
+.ec
+Years requiring fewer than four characters are padded with leading zeroes.
+For years longer than four characters, the string is of the form
+.br
+.ce
+.eo
+Thu Nov 24 18:22:48 81986\n\0
+.ec
+.br
+with five spaces before the year.
+These unusual formats are designed to make it less likely that older
+software that expects exactly 26 bytes of output will mistakenly output
+misleading values for out-of-range years.
+.PP
+The
+.BI * clock
+timestamp represents the time in seconds since 1970-01-01 00:00:00
+Coordinated Universal Time (UTC).
+The POSIX standard says that timestamps must be nonnegative
+and must ignore leap seconds.
+Many implementations extend POSIX by allowing negative timestamps,
+and can therefore represent timestamps that predate the
+introduction of UTC and are some other flavor of Universal Time (UT).
+Some implementations support leap seconds, in contradiction to POSIX.
+.PP
+The
+.B localtime
+and
+.B gmtime
+functions
+return pointers to
+.q "tm"
+structures, described below.
+The
+.B localtime
+function
+corrects for the time zone and any time zone adjustments
+(such as Daylight Saving Time in the United States).
+After filling in the
+.q "tm"
+structure,
+.B localtime
+sets the
+.BR tm_isdst 'th
+element of
+.B tzname
+to a pointer to a string that's the time zone abbreviation to be used with
+.BR localtime 's
+return value.
+.PP
+The
+.B gmtime
+function
+converts to Coordinated Universal Time.
+.PP
+The
+.B asctime
+function
+converts a time value contained in a
+.q "tm"
+structure to a string,
+as shown in the above example,
+and returns a pointer to the string.
+.PP
+The
+.B mktime
+function
+converts the broken-down time,
+expressed as local time,
+in the structure pointed to by
+.I tm
+into a calendar time value with the same encoding as that of the values
+returned by the
+.B time
+function.
+The original values of the
+.B tm_wday
+and
+.B tm_yday
+components of the structure are ignored,
+and the original values of the other components are not restricted
+to their normal ranges.
+(A positive or zero value for
+.B tm_isdst
+causes
+.B mktime
+to presume initially that daylight saving time
+respectively,
+is or is not in effect for the specified time.
+A negative value for
+.B tm_isdst
+causes the
+.B mktime
+function to attempt to divine whether daylight saving time is in effect
+for the specified time; in this case it does not use a consistent
+rule and may give a different answer when later
+presented with the same argument.)
+On successful completion, the values of the
+.B tm_wday
+and
+.B 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
+.B tm_mday
+is not set until
+.B tm_mon
+and
+.B tm_year
+are determined.
+The
+.B mktime
+function
+returns the specified calendar time;
+If the calendar time cannot be represented,
+it returns \-1.
+.PP
+The
+.B difftime
+function
+returns the difference between two calendar times,
+.RI ( time1
+\-
+.IR time0 ),
+expressed in seconds.
+.PP
+The
+.BR ctime_r ,
+.BR localtime_r ,
+.BR gmtime_r ,
+and
+.B asctime_r
+functions
+are like their unsuffixed counterparts, except that they accept an
+additional argument specifying where to store the result if successful.
+.PP
+The
+.B localtime_rz
+and
+.B mktime_z
+functions
+are like their unsuffixed counterparts, except that they accept an
+extra initial
+.B zone
+argument specifying the timezone to be used for conversion.
+If
+.B zone
+is null, UT is used; otherwise,
+.B zone
+should be have been allocated by
+.B tzalloc
+and should not be freed until after all uses (e.g., by calls to
+.BR strftime )
+of the filled-in
+.B tm_zone
+fields.
+.PP
+Declarations of all the functions and externals, and the
+.q "tm"
+structure,
+are in the
+.B <time.h>
+header file.
+The structure (of type)
+.B struct tm
+includes the following fields:
+.RS
+.PP
+.nf
+.ta 2n +\w'long tm_gmtoff;nn'u
+ int tm_sec; /\(** seconds (0\*(en60) \(**/
+ int tm_min; /\(** minutes (0\*(en59) \(**/
+ int tm_hour; /\(** hours (0\*(en23) \(**/
+ int tm_mday; /\(** day of month (1\*(en31) \(**/
+ int tm_mon; /\(** month of year (0\*(en11) \(**/
+ int tm_year; /\(** year \- 1900 \(**/
+ int tm_wday; /\(** day of week (Sunday = 0) \(**/
+ int tm_yday; /\(** day of year (0\*(en365) \(**/
+ int tm_isdst; /\(** is daylight saving time in effect? \(**/
+ char \(**tm_zone; /\(** time zone abbreviation (optional) \(**/
+ long tm_gmtoff; /\(** offset from UT in seconds (optional) \(**/
+.fi
+.RE
+.PP
+The
+.B tm_isdst
+field
+is non-zero if daylight saving time is in effect.
+.PP
+The
+.B tm_gmtoff
+field
+is the offset (in seconds) of the time represented
+from UT, with positive values indicating east
+of the Prime Meridian.
+The field's name is derived from Greenwich Mean Time, a precursor of UT.
+.PP
+In
+.B "struct tm"
+the
+.B tm_zone
+and
+.B tm_gmtoff
+fields exist, and are filled in, only if arrangements to do
+so were made when the library containing these functions was
+created.
+Similarly, the
+.B tzname
+variable is optional; also, there is no guarantee that
+.B tzname
+will
+continue to exist in this form in future releases of this code.
+.SH FILES
+.ta \w'/usr/share/zoneinfo/posixrules\0\0'u
+/usr/share/zoneinfo timezone information directory
+.br
+/usr/share/zoneinfo/localtime local timezone file
+.br
+/usr/share/zoneinfo/posixrules used with POSIX-style TZ's
+.br
+/usr/share/zoneinfo/GMT for UTC leap seconds
+.sp
+If
+.B /usr/share/zoneinfo/GMT
+is absent,
+UTC leap seconds are loaded from
+.BR /usr/share/zoneinfo/posixrules .
+.SH SEE ALSO
+getenv(3),
+newstrftime(3),
+newtzset(3),
+time(2),
+tzfile(5)
+.SH NOTES
+The return values of
+.BR asctime ,
+.BR ctime ,
+.BR gmtime ,
+and
+.B localtime
+point to static data
+overwritten by each call.
+The
+.B tzname
+variable (once set) and the
+.B tm_zone
+field of a returned
+.B "struct tm"
+both point to an array of characters that
+can be freed or overwritten by later calls to the functions
+.BR localtime ,
+.BR tzfree ,
+and
+.BR tzset ,
+if these functions affect the timezone information that specifies the
+abbreviation in question.
+The remaining functions and data are thread-safe.
+.PP
+The
+.BR asctime ,
+.BR asctime_r ,
+.BR ctime ,
+and
+.B ctime_r
+functions
+behave strangely for years before 1000 or after 9999.
+The 1989 and 1999 editions of the C Standard say
+that years from \-99 through 999 are converted without
+extra spaces, but this conflicts with longstanding
+tradition and with this implementation.
+The 2011 edition says that the behavior
+is undefined if the year is before 1000 or after 9999.
+Traditional implementations of these two functions are
+restricted to years in the range 1900 through 2099.
+To avoid this portability mess, new programs should use
+.B strftime
+instead.
diff --git a/contrib/tzcode/newstrftime.3 b/contrib/tzcode/newstrftime.3
new file mode 100644
index 000000000000..d5d8ee104d9f
--- /dev/null
+++ b/contrib/tzcode/newstrftime.3
@@ -0,0 +1,290 @@
+.\" strftime man page
+.\"
+.\" Based on the UCB file whose corrected copyright information appears below.
+.\" Copyright 1989, 1991 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.
+.\" 3. 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: @(#)strftime.3 5.12 (Berkeley) 6/29/91
+.\" $Id: strftime.3,v 1.4 1993/12/15 20:33:00 jtc Exp $
+.\"
+.TH NEWSTRFTIME 3
+.SH NAME
+strftime \- format date and time
+.SH SYNOPSIS
+.nf
+.ie \n(.g .ds - \f(CR-\fP
+.el .ds - \-
+.B #include <time.h>
+.PP
+.B "size_t strftime(char *restrict buf, size_t maxsize,"
+.B " char const *restrict format, struct tm const *restrict timeptr);"
+.PP
+.B cc ... \-ltz
+.fi
+.SH DESCRIPTION
+.ie '\(lq'' .ds lq \&"\"
+.el .ds lq \(lq\"
+.ie '\(rq'' .ds rq \&"\"
+.el .ds rq \(rq\"
+.de c
+.ie \n(.g \f(CR\\$1\fP\\$2
+.el \\$1\\$2
+..
+.de q
+\\$3\*(lq\\$1\*(rq\\$2
+..
+The
+.B strftime
+function formats the information from
+.I timeptr
+into the array pointed to by
+.I buf
+according to the string pointed to by
+.IR format .
+.PP
+The
+.I format
+string consists of zero or more conversion specifications and
+ordinary characters.
+All ordinary characters are copied directly into the array.
+A conversion specification consists of a percent sign
+.Ql %
+and one other character.
+.PP
+No more than
+.I maxsize
+bytes are placed into the array.
+.PP
+Each conversion specification is replaced by the characters as
+follows which are then copied into the array.
+.TP
+%A
+is replaced by the locale's full weekday name.
+.TP
+%a
+is replaced by the locale's abbreviated weekday name.
+.TP
+%B
+is replaced by the locale's full month name.
+.TP
+%b or %h
+is replaced by the locale's abbreviated month name.
+.TP
+%C
+is replaced by the century (a year divided by 100 and truncated to an integer)
+as a decimal number [00,99].
+.TP
+%c
+is replaced by the locale's appropriate date and time representation.
+.TP
+%D
+is equivalent to
+.c %m/%d/%y .
+.TP
+%d
+is replaced by the day of the month as a decimal number [01,31].
+.TP
+%e
+is replaced by the day of month as a decimal number [1,31];
+single digits are preceded by a blank.
+.TP
+%F
+is equivalent to
+.c %Y-%m-%d
+(the ISO 8601 date format).
+.TP
+%G
+is replaced by the ISO 8601 year with century as a decimal number.
+See also the
+.c %V
+conversion specification.
+.TP
+%g
+is replaced by the ISO 8601 year without century as a decimal number [00,99].
+This is the year that includes the greater part of the week.
+(Monday as the first day of a week).
+See also the
+.c %V
+conversion specification.
+.TP
+%H
+is replaced by the hour (24-hour clock) as a decimal number [00,23].
+.TP
+%I
+is replaced by the hour (12-hour clock) as a decimal number [01,12].
+.TP
+%j
+is replaced by the day of the year as a decimal number [001,366].
+.TP
+%k
+is replaced by the hour (24-hour clock) as a decimal number [0,23];
+single digits are preceded by a blank.
+.TP
+%l
+is replaced by the hour (12-hour clock) as a decimal number [1,12];
+single digits are preceded by a blank.
+.TP
+%M
+is replaced by the minute as a decimal number [00,59].
+.TP
+%m
+is replaced by the month as a decimal number [01,12].
+.TP
+%n
+is replaced by a newline.
+.TP
+%p
+is replaced by the locale's equivalent of either
+.q AM
+or
+.q PM .
+.TP
+%R
+is replaced by the time in the format
+.c %H:%M .
+.TP
+%r
+is replaced by the locale's representation of 12-hour clock time
+using AM/PM notation.
+.TP
+%S
+is replaced by the second as a decimal number [00,60].
+The range of
+seconds is [00,60] instead of [00,59] to allow for the periodic occurrence
+of leap seconds.
+.TP
+%s
+is replaced by the number of seconds since the Epoch (see
+.BR ctime (3)).
+.TP
+%T
+is replaced by the time in the format
+.c %H:%M:%S .
+.TP
+%t
+is replaced by a tab.
+.TP
+%U
+is replaced by the week number of the year (Sunday as the first day of
+the week) as a decimal number [00,53].
+.TP
+%u
+is replaced by the weekday (Monday as the first day of the week)
+as a decimal number [1,7].
+.TP
+%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 week 53 of the previous year, and the next week is week 1.
+The year is given by the
+.c %G
+conversion specification.
+.TP
+%W
+is replaced by the week number of the year (Monday as the first day of
+the week) as a decimal number [00,53].
+.TP
+%w
+is replaced by the weekday (Sunday as the first day of the week)
+as a decimal number [0,6].
+.TP
+%X
+is replaced by the locale's appropriate time representation.
+.TP
+%x
+is replaced by the locale's appropriate date representation.
+.TP
+%Y
+is replaced by the year with century as a decimal number.
+.TP
+%y
+is replaced by the year without century as a decimal number [00,99].
+.TP
+%Z
+is replaced by the time zone abbreviation,
+or by the empty string if this is not determinable.
+.TP
+%z
+is replaced by the offset from the Prime Meridian
+in the format +HHMM or \*-HHMM (ISO 8601) as appropriate,
+with positive values representing locations east of Greenwich,
+or by the empty string if this is not determinable.
+The numeric time zone abbreviation \*-0000 is used when the time is
+Universal Time
+but local time is indeterminate; by convention this is used for
+locations while uninhabited, and corresponds to a zero offset when the
+time zone abbreviation begins with
+.q "\*-" .
+.TP
+%%
+is replaced by a single %.
+.TP
+%+
+is replaced by the locale's date and time in
+.BR date (1)
+format.
+.SH "RETURN VALUE"
+If the conversion is successful,
+.B strftime
+returns the number of bytes placed into the array, not counting the
+terminating NUL;
+.B errno
+is unchanged if the returned value is zero.
+Otherwise,
+.B errno
+is set to indicate the error, zero is returned,
+and the array contents are unspecified.
+.SH ERRORS
+This function fails if:
+.TP
+[ERANGE]
+The total number of resulting bytes, including the terminating
+NUL character, is more than
+.IR maxsize .
+.PP
+This function may fail if:
+.TP
+[EOVERFLOW]
+The format includes an
+.c %s
+conversion and the number of seconds since the Epoch cannot be represented
+in a
+.c time_t .
+.SH SEE ALSO
+date(1),
+getenv(3),
+newctime(3),
+newtzset(3),
+time(2),
+tzfile(5)
+.SH BUGS
+There is no conversion specification for the phase of the moon.
diff --git a/contrib/tzcode/newtzset.3 b/contrib/tzcode/newtzset.3
new file mode 100644
index 000000000000..1e75acf0e6e3
--- /dev/null
+++ b/contrib/tzcode/newtzset.3
@@ -0,0 +1,350 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH NEWTZSET 3
+.SH NAME
+tzset \- initialize time conversion information
+.SH SYNOPSIS
+.nf
+.ie \n(.g .ds - \f(CR-\fP
+.el .ds - \-
+.B #include <time.h>
+.PP
+.B timezone_t tzalloc(char const *TZ);
+.PP
+.B void tzfree(timezone_t tz);
+.PP
+.B void tzset(void);
+.PP
+.B cc ... \*-ltz
+.fi
+.SH DESCRIPTION
+.ie '\(en'' .ds en \-
+.el .ds en \(en
+.ie '\(lq'' .ds lq \&"\"
+.el .ds lq \(lq\"
+.ie '\(rq'' .ds rq \&"\"
+.el .ds rq \(rq\"
+.de q
+\\$3\*(lq\\$1\*(rq\\$2
+..
+The
+.B tzalloc
+function
+allocates and returns a timezone object described by
+.BR TZ .
+If
+.B TZ
+is not a valid timezone description, or if the object cannot be allocated,
+.B tzalloc
+returns a null pointer and sets
+.BR errno .
+.PP
+The
+.B tzfree
+function
+frees a timezone object
+.BR tz ,
+which should have been successfully allocated by
+.BR tzalloc .
+This invalidates any
+.B tm_zone
+pointers that
+.B tz
+was used to set.
+.PP
+The
+.B tzset
+function
+acts like
+.BR tzalloc(getenv("TZ")) ,
+except it saves any resulting timezone object into internal
+storage that is accessed by
+.BR localtime ,
+.BR localtime_r ,
+and
+.BR mktime .
+The anonymous shared timezone object is freed by the next call to
+.BR tzset .
+If the implied call to
+.B tzalloc
+fails,
+.B tzset
+falls back on Universal Time (UT).
+.PP
+If
+.B TZ
+is null, the best available approximation to local (wall
+clock) time, as specified by the
+.BR tzfile (5)-format
+file
+.B localtime
+in the system time conversion information directory, is used.
+If
+.B TZ
+is the empty string,
+UT is used, with the abbreviation "UTC"
+and without leap second correction; please see
+.BR newctime (3)
+for more about UT, UTC, and leap seconds. If
+.B TZ
+is nonnull and nonempty:
+.IP
+if the value begins with a colon, it is used as a pathname of a file
+from which to read the time conversion information;
+.IP
+if the value does not begin with a colon, it is first used as the
+pathname of a file from which to read the time conversion information,
+and, if that file cannot be read, is used directly as a specification of
+the time conversion information.
+.PP
+When
+.B TZ
+is used as a pathname, if it begins with a slash,
+it is used as an absolute pathname; otherwise,
+it is used as a pathname relative to a system time conversion information
+directory.
+The file must be in the format specified in
+.BR tzfile (5).
+.PP
+When
+.B TZ
+is used directly as a specification of the time conversion information,
+it must have the following syntax (spaces inserted for clarity):
+.IP
+\fIstd\|offset\fR[\fIdst\fR[\fIoffset\fR][\fB,\fIrule\fR]]
+.PP
+Where:
+.RS
+.TP 15
+.IR std " and " dst
+Three or more bytes that are the designation for the standard
+.RI ( std )
+or the alternative
+.RI ( dst ,
+such as daylight saving time)
+time zone. Only
+.I std
+is required; if
+.I dst
+is missing, then daylight saving time does not apply in this locale.
+Upper- and lowercase letters are explicitly allowed. Any characters
+except a leading colon
+.RB ( : ),
+digits, comma
+.RB ( , ),
+ASCII minus
+.RB ( \*- ),
+ASCII plus
+.RB ( + ),
+and NUL bytes are allowed.
+Alternatively, a designation can be surrounded by angle brackets
+.B <
+and
+.BR > ;
+in this case, the designation can contain any characters other than
+.B >
+and NUL.
+.TP
+.I offset
+Indicates the value one must add to the local time to arrive at
+Coordinated Universal Time. The
+.I offset
+has the form:
+.RS
+.IP
+\fIhh\fR[\fB:\fImm\fR[\fB:\fIss\fR]]
+.RE
+.IP
+The minutes
+.RI ( mm )
+and seconds
+.RI ( ss )
+are optional. The hour
+.RI ( hh )
+is required and may be a single digit. The
+.I offset
+following
+.I std
+is required. If no
+.I offset
+follows
+.IR dst ,
+daylight saving time is assumed to be one hour ahead of standard time. One or
+more digits may be used; the value is always interpreted as a decimal
+number. The hour must be between zero and 24, and the minutes (and
+seconds) \*(en if present \*(en between zero and 59. If preceded by a
+.q "\*-" ,
+the time zone shall be east of the Prime Meridian; otherwise it shall be
+west (which may be indicated by an optional preceding
+.q "+" .
+.TP
+.I rule
+Indicates when to change to and back from daylight saving time. The
+.I rule
+has the form:
+.RS
+.IP
+\fIdate\fB/\fItime\fB,\fIdate\fB/\fItime\fR
+.RE
+.IP
+where the first
+.I date
+describes when the change from standard to daylight saving time occurs and the
+second
+.I date
+describes when the change back happens. Each
+.I time
+field describes when, in current local time, the change to the other
+time is made.
+As an extension to POSIX, daylight saving is assumed to be in effect
+all year if it begins January 1 at 00:00 and ends December 31 at
+24:00 plus the difference between daylight saving and standard time,
+leaving no room for standard time in the calendar.
+.IP
+The format of
+.I date
+is one of the following:
+.RS
+.TP 10
+.BI J n
+The Julian day
+.I n
+.RI "(1\ \(<=" "\ n\ " "\(<=\ 365).
+Leap days are not counted; that is, in all years \*(en including leap
+years \*(en February 28 is day 59 and March 1 is day 60. It is
+impossible to explicitly refer to the occasional February 29.
+.TP
+.I n
+The zero-based Julian day
+.RI "(0\ \(<=" "\ n\ " "\(<=\ 365).
+Leap days are counted, and it is possible to refer to February 29.
+.TP
+.BI M m . n . d
+The
+.IR d' th
+day
+.RI "(0\ \(<=" "\ d\ " "\(<=\ 6)
+of week
+.I n
+of month
+.I m
+of the year
+.RI "(1\ \(<=" "\ n\ " "\(<=\ 5,
+.RI "1\ \(<=" "\ m\ " "\(<=\ 12,
+where week 5 means
+.q "the last \fId\fP day in month \fIm\fP"
+which may occur in either the fourth or the fifth week). Week 1 is the
+first week in which the
+.IR d' th
+day occurs. Day zero is Sunday.
+.RE
+.IP "" 15
+The
+.I time
+has the same format as
+.I offset
+except that POSIX does not allow a leading sign (\c
+.q "\*-"
+or
+.q "+" ).
+As an extension to POSIX, the hours part of
+.I time
+can range from \-167 through 167; this allows for unusual rules such
+as
+.q "the Saturday before the first Sunday of March" .
+The default, if
+.I time
+is not given, is
+.BR 02:00:00 .
+.RE
+.LP
+Here are some examples of
+.B TZ
+values that directly specify the timezone; they use some of the
+extensions to POSIX.
+.TP
+.B EST5
+stands for US Eastern Standard
+Time (EST), 5 hours behind UT, without daylight saving.
+.TP
+.B <+12>\*-12<+13>,M11.1.0,M1.2.1/147
+stands for Fiji time, 12 hours ahead
+of UT, springing forward on November's first Sunday at 02:00, and
+falling back on January's second Monday at 147:00 (i.e., 03:00 on the
+first Sunday on or after January 14). The abbreviations for standard
+and daylight saving time are
+.q "+12"
+and
+.q "+13".
+.TP
+.B IST\*-2IDT,M3.4.4/26,M10.5.0
+stands for Israel Standard Time (IST) and Israel Daylight Time (IDT),
+2 hours ahead of UT, springing forward on March's fourth
+Thursday at 26:00 (i.e., 02:00 on the first Friday on or after March
+23), and falling back on October's last Sunday at 02:00.
+.TP
+.B <\*-04>4<\*-03>,J1/0,J365/25
+stands for permanent daylight saving time, 3 hours behind UT with
+abbreviation
+.q "\*-03".
+There is a dummy fall-back transition on December 31 at 25:00 daylight
+saving time (i.e., 24:00 standard time, equivalent to January 1 at
+00:00 standard time), and a simultaneous spring-forward transition on
+January 1 at 00:00 standard time, so daylight saving time is in effect
+all year and the initial
+.B <\*-04>
+is a placeholder.
+.TP
+.B <\*-03>3<\*-02>,M3.5.0/\*-2,M10.5.0/\*-1
+stands for time in western Greenland, 3 hours behind UT, where clocks
+follow the EU rules of
+springing forward on March's last Sunday at 01:00 UT (\-02:00 local
+time, i.e., 22:00 the previous day) and falling back on October's last
+Sunday at 01:00 UT (\-01:00 local time, i.e., 23:00 the previous day).
+The abbreviations for standard and daylight saving time are
+.q "\*-03"
+and
+.q "\*-02".
+.PP
+If no
+.I rule
+is present in
+.BR TZ ,
+the rules specified
+by the
+.BR tzfile (5)-format
+file
+.B posixrules
+in the system time conversion information directory are used, with the
+standard and daylight saving time offsets from UT replaced by those specified by
+the
+.I offset
+values in
+.BR TZ .
+.PP
+For compatibility with System V Release 3.1, a semicolon
+.RB ( ; )
+may be used to separate the
+.I rule
+from the rest of the specification.
+.SH FILES
+.ta \w'/usr/share/zoneinfo/posixrules\0\0'u
+/usr/share/zoneinfo timezone information directory
+.br
+/usr/share/zoneinfo/localtime local timezone file
+.br
+/usr/share/zoneinfo/posixrules used with POSIX-style TZ
+.br
+/usr/share/zoneinfo/GMT for UTC leap seconds
+.sp
+If
+.B /usr/share/zoneinfo/GMT
+is absent,
+UTC leap seconds are loaded from
+.BR /usr/share/zoneinfo/posixrules .
+.SH SEE ALSO
+getenv(3),
+newctime(3),
+newstrftime(3),
+time(2),
+tzfile(5)
diff --git a/contrib/tzcode/private.h b/contrib/tzcode/private.h
new file mode 100644
index 000000000000..0bb70056a9d7
--- /dev/null
+++ b/contrib/tzcode/private.h
@@ -0,0 +1,957 @@
+/* Private header for tzdb code. */
+
+#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 PCTS 1
+#define HAVE_LONG_DOUBLE 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!
+*/
+
+#ifndef __STDC_VERSION__
+# define __STDC_VERSION__ 0
+#endif
+
+/* Define true, false and bool if they don't work out of the box. */
+#if __STDC_VERSION__ < 199901
+# define true 1
+# define false 0
+# define bool int
+#elif __STDC_VERSION__ < 202311
+# include <stdbool.h>
+#endif
+
+/*
+** zdump has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use zdump to help in verifying other implementations.
+** To do this, compile with -DUSE_LTZ=0 and link without the tz library.
+*/
+#ifndef USE_LTZ
+# define USE_LTZ 1
+#endif
+
+/* This string was in the Factory zone through version 2016f. */
+#define GRANDPARENTED "Local time zone must be set--use tzsetup"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. '-DHAVE_GETTEXT=1'.
+*/
+
+#ifndef HAVE_DECL_ASCTIME_R
+# define HAVE_DECL_ASCTIME_R 1
+#endif
+
+#if !defined HAVE_GENERIC && defined __has_extension
+# if __has_extension(c_generic_selections)
+# define HAVE_GENERIC 1
+# else
+# define HAVE_GENERIC 0
+# endif
+#endif
+/* _Generic is buggy in pre-4.9 GCC. */
+#if !defined HAVE_GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__
+# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
+#endif
+#ifndef HAVE_GENERIC
+# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
+#endif
+
+#if !defined HAVE_GETTEXT && defined __has_include
+# if __has_include(<libintl.h>)
+# define HAVE_GETTEXT true
+# endif
+#endif
+#ifndef HAVE_GETTEXT
+# define HAVE_GETTEXT false
+#endif
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+# define HAVE_INCOMPATIBLE_CTIME_R 0
+#endif
+
+#ifndef HAVE_LINK
+# define HAVE_LINK 1
+#endif /* !defined HAVE_LINK */
+
+#ifndef HAVE_MALLOC_ERRNO
+# define HAVE_MALLOC_ERRNO 1
+#endif
+
+#ifndef HAVE_POSIX_DECLS
+# define HAVE_POSIX_DECLS 1
+#endif
+
+#ifndef HAVE_SETENV
+# define HAVE_SETENV 1
+#endif
+
+#ifndef HAVE_STRDUP
+# define HAVE_STRDUP 1
+#endif
+
+#ifndef HAVE_STRTOLL
+# define HAVE_STRTOLL 1
+#endif
+
+#ifndef HAVE_SYMLINK
+# define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#if !defined HAVE_SYS_STAT_H && defined __has_include
+# if !__has_include(<sys/stat.h>)
+# define HAVE_SYS_STAT_H false
+# endif
+#endif
+#ifndef HAVE_SYS_STAT_H
+# define HAVE_SYS_STAT_H true
+#endif
+
+#if !defined HAVE_UNISTD_H && defined __has_include
+# if !__has_include(<unistd.h>)
+# define HAVE_UNISTD_H false
+# endif
+#endif
+#ifndef HAVE_UNISTD_H
+# define HAVE_UNISTD_H true
+#endif
+
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+# define asctime_r _incompatible_asctime_r
+# define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
+#define _GNU_SOURCE 1
+/* Fix asctime_r on Solaris 11. */
+#define _POSIX_PTHREAD_SEMANTICS 1
+/* Enable strtoimax on pre-C99 Solaris 11. */
+#define __EXTENSIONS__ 1
+
+/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
+ On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
+ setting _TIME_BITS to 64 does not work. The code does not
+ otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
+ use off_t or functions like 'stat' that depend on off_t. */
+#ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+#endif
+#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
+# define _TIME_BITS 64
+#endif
+
+/*
+** Nested includes
+*/
+
+/* Avoid clashes with NetBSD by renaming NetBSD's declarations.
+ If defining the 'timezone' variable, avoid a clash with FreeBSD's
+ 'timezone' function by renaming its declaration. */
+#define localtime_rz sys_localtime_rz
+#define mktime_z sys_mktime_z
+#define posix2time_z sys_posix2time_z
+#define time2posix_z sys_time2posix_z
+#if defined USG_COMPAT && USG_COMPAT == 2
+# define timezone sys_timezone
+#endif
+#define timezone_t sys_timezone_t
+#define tzalloc sys_tzalloc
+#define tzfree sys_tzfree
+#include <time.h>
+#undef localtime_rz
+#undef mktime_z
+#undef posix2time_z
+#undef time2posix_z
+#if defined USG_COMPAT && USG_COMPAT == 2
+# undef timezone
+#endif
+#undef timezone_t
+#undef tzalloc
+#undef tzfree
+
+#include <stddef.h>
+#include <string.h>
+#include <limits.h> /* for CHAR_BIT et al. */
+#include <stdlib.h>
+
+#include <errno.h>
+
+#ifndef EINVAL
+# define EINVAL ERANGE
+#endif
+
+#ifndef ELOOP
+# define ELOOP EINVAL
+#endif
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+#ifndef ENOMEM
+# define ENOMEM EINVAL
+#endif
+#ifndef ENOTSUP
+# define ENOTSUP EINVAL
+#endif
+#ifndef EOVERFLOW
+# define EOVERFLOW EINVAL
+#endif
+
+#if HAVE_GETTEXT
+# include <libintl.h>
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_UNISTD_H
+# include <unistd.h> /* for R_OK, and other POSIX goodness */
+#endif /* HAVE_UNISTD_H */
+
+#ifndef HAVE_STRFTIME_L
+# if _POSIX_VERSION < 200809
+# define HAVE_STRFTIME_L 0
+# else
+# define HAVE_STRFTIME_L 1
+# endif
+#endif
+
+#ifndef USG_COMPAT
+# ifndef _XOPEN_VERSION
+# define USG_COMPAT 0
+# else
+# define USG_COMPAT 1
+# endif
+#endif
+
+#ifndef HAVE_TZNAME
+# if _POSIX_VERSION < 198808 && !USG_COMPAT
+# define HAVE_TZNAME 0
+# else
+# define HAVE_TZNAME 1
+# endif
+#endif
+
+#ifndef ALTZONE
+# if defined __sun || defined _M_XENIX
+# define ALTZONE 1
+# else
+# define ALTZONE 0
+# endif
+#endif
+
+#ifndef R_OK
+# define R_OK 4
+#endif /* !defined R_OK */
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__ and INTMAX_MAX's values depend on
+** previously-included files. glibc 2.1 and Solaris 10 and later have
+** stdint.h, even with pre-C99 compilers.
+*/
+#if !defined HAVE_STDINT_H && defined __has_include
+# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
+#endif
+#ifndef HAVE_STDINT_H
+# define HAVE_STDINT_H \
+ (199901 <= __STDC_VERSION__ \
+ || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
+ || __CYGWIN__ || INTMAX_MAX)
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif /* !HAVE_STDINT_H */
+
+#ifndef HAVE_INTTYPES_H
+# define HAVE_INTTYPES_H HAVE_STDINT_H
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
+#if defined __LONG_LONG_MAX__ && !defined __STRICT_ANSI__
+# ifndef LLONG_MAX
+# define LLONG_MAX __LONG_LONG_MAX__
+# endif
+# ifndef LLONG_MIN
+# define LLONG_MIN (-1 - LLONG_MAX)
+# endif
+# ifndef ULLONG_MAX
+# define ULLONG_MAX (LLONG_MAX * 2ull + 1)
+# endif
+#endif
+
+#ifndef INT_FAST64_MAX
+# if 1 <= LONG_MAX >> 31 >> 31
+typedef long int_fast64_t;
+# define INT_FAST64_MIN LONG_MIN
+# define INT_FAST64_MAX LONG_MAX
+# else
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef long long int_fast64_t;
+# define INT_FAST64_MIN LLONG_MIN
+# define INT_FAST64_MAX LLONG_MAX
+# endif
+#endif
+
+#ifndef PRIdFAST64
+# if INT_FAST64_MAX == LONG_MAX
+# define PRIdFAST64 "ld"
+# else
+# define PRIdFAST64 "lld"
+# endif
+#endif
+
+#ifndef SCNdFAST64
+# define SCNdFAST64 PRIdFAST64
+#endif
+
+#ifndef INT_FAST32_MAX
+# if INT_MAX >> 31 == 0
+typedef long int_fast32_t;
+# define INT_FAST32_MAX LONG_MAX
+# define INT_FAST32_MIN LONG_MIN
+# else
+typedef int int_fast32_t;
+# define INT_FAST32_MAX INT_MAX
+# define INT_FAST32_MIN INT_MIN
+# endif
+#endif
+
+#ifndef INTMAX_MAX
+# ifdef LLONG_MAX
+typedef long long intmax_t;
+# if HAVE_STRTOLL
+# define strtoimax strtoll
+# endif
+# define INTMAX_MAX LLONG_MAX
+# define INTMAX_MIN LLONG_MIN
+# else
+typedef long intmax_t;
+# define INTMAX_MAX LONG_MAX
+# define INTMAX_MIN LONG_MIN
+# endif
+# ifndef strtoimax
+# define strtoimax strtol
+# endif
+#endif
+
+#ifndef PRIdMAX
+# if INTMAX_MAX == LLONG_MAX
+# define PRIdMAX "lld"
+# else
+# define PRIdMAX "ld"
+# endif
+#endif
+
+#ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t))
+#endif
+
+#ifndef UINT_FAST32_MAX
+typedef unsigned long uint_fast32_t;
+#endif
+
+#ifndef UINT_FAST64_MAX
+# if 3 <= ULONG_MAX >> 31 >> 31
+typedef unsigned long uint_fast64_t;
+# define UINT_FAST64_MAX ULONG_MAX
+# else
+/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
+typedef unsigned long long uint_fast64_t;
+# define UINT_FAST64_MAX ULLONG_MAX
+# endif
+#endif
+
+#ifndef UINTMAX_MAX
+# ifdef ULLONG_MAX
+typedef unsigned long long uintmax_t;
+# else
+typedef unsigned long uintmax_t;
+# endif
+#endif
+
+#ifndef PRIuMAX
+# ifdef ULLONG_MAX
+# define PRIuMAX "llu"
+# else
+# define PRIuMAX "lu"
+# endif
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+/* Support ckd_add, ckd_sub, ckd_mul on C23 or recent-enough GCC-like
+ hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
+#if !defined HAVE_STDCKDINT_H && defined __has_include
+# if __has_include(<stdckdint.h>)
+# define HAVE_STDCKDINT_H true
+# endif
+#endif
+#ifdef HAVE_STDCKDINT_H
+# if HAVE_STDCKDINT_H
+# include <stdckdint.h>
+# endif
+#elif defined __EDG__
+/* Do nothing, to work around EDG bug <https://bugs.gnu.org/53256>. */
+#elif defined __has_builtin
+# if __has_builtin(__builtin_add_overflow)
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_sub_overflow)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# endif
+# if __has_builtin(__builtin_mul_overflow)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+# endif
+#elif 7 <= __GNUC__
+# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
+# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
+# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
+#endif
+
+#if 3 <= __GNUC__
+# define ATTRIBUTE_MALLOC __attribute__((malloc))
+# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
+#else
+# define ATTRIBUTE_MALLOC /* empty */
+# define ATTRIBUTE_FORMAT(spec) /* empty */
+#endif
+
+#if (defined __has_c_attribute \
+ && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__))
+# define HAVE_HAS_C_ATTRIBUTE true
+#else
+# define HAVE_HAS_C_ATTRIBUTE false
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(fallthrough)
+# define ATTRIBUTE_FALLTHROUGH [[fallthrough]]
+# endif
+#endif
+#ifndef ATTRIBUTE_FALLTHROUGH
+# if 7 <= __GNUC__
+# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough))
+# else
+# define ATTRIBUTE_FALLTHROUGH ((void) 0)
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(maybe_unused)
+# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
+# endif
+#endif
+#ifndef ATTRIBUTE_MAYBE_UNUSED
+# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused))
+# else
+# define ATTRIBUTE_MAYBE_UNUSED /* empty */
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(noreturn)
+# define ATTRIBUTE_NORETURN [[noreturn]]
+# endif
+#endif
+#ifndef ATTRIBUTE_NORETURN
+# if 201112 <= __STDC_VERSION__
+# define ATTRIBUTE_NORETURN _Noreturn
+# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
+# define ATTRIBUTE_NORETURN __attribute__((noreturn))
+# else
+# define ATTRIBUTE_NORETURN /* empty */
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(reproducible)
+# define ATTRIBUTE_REPRODUCIBLE [[reproducible]]
+# endif
+#endif
+#ifndef ATTRIBUTE_REPRODUCIBLE
+# if 3 <= __GNUC__
+# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure))
+# else
+# define ATTRIBUTE_REPRODUCIBLE /* empty */
+# endif
+#endif
+
+#if HAVE_HAS_C_ATTRIBUTE
+# if __has_c_attribute(unsequenced)
+# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
+# endif
+#endif
+#ifndef ATTRIBUTE_UNSEQUENCED
+# if 3 <= __GNUC__
+# define ATTRIBUTE_UNSEQUENCED __attribute__((const))
+# else
+# define ATTRIBUTE_UNSEQUENCED /* empty */
+# endif
+#endif
+
+#if __STDC_VERSION__ < 199901 && !defined restrict
+# define restrict /* empty */
+#endif
+
+#if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
+# define ATTRIBUTE_PURE __attribute__ ((__pure__))
+#else
+# define ATTRIBUTE_PURE /* empty */
+#endif
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+#ifndef EPOCH_LOCAL
+# define EPOCH_LOCAL 0
+#endif
+#ifndef EPOCH_OFFSET
+# define EPOCH_OFFSET 0
+#endif
+#ifndef RESERVE_STD_EXT_IDS
+# define RESERVE_STD_EXT_IDS 0
+#endif
+
+/* If standard C identifiers with external linkage (e.g., localtime)
+ are reserved and are not already being renamed anyway, rename them
+ as if compiling with '-Dtime_tz=time_t'. */
+#if !defined time_tz && RESERVE_STD_EXT_IDS && USE_LTZ
+# define time_tz time_t
+#endif
+
+/*
+** Compile with -Dtime_tz=T to build the tz package with a private
+** time_t type equivalent to T rather than the system-supplied time_t.
+** This debugging feature can test unusual design decisions
+** (e.g., time_t wider than 'long', or unsigned time_t) even on
+** typical platforms.
+*/
+#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
+# define TZ_TIME_T 1
+#else
+# define TZ_TIME_T 0
+#endif
+
+#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
+static time_t sys_time(time_t *x) { return time(x); }
+#endif
+
+#if TZ_TIME_T
+
+typedef time_tz tz_time_t;
+
+# undef asctime
+# define asctime tz_asctime
+# undef asctime_r
+# define asctime_r tz_asctime_r
+# undef ctime
+# define ctime tz_ctime
+# undef ctime_r
+# define ctime_r tz_ctime_r
+# undef difftime
+# define difftime tz_difftime
+# undef gmtime
+# define gmtime tz_gmtime
+# undef gmtime_r
+# define gmtime_r tz_gmtime_r
+# undef localtime
+# define localtime tz_localtime
+# undef localtime_r
+# define localtime_r tz_localtime_r
+# undef localtime_rz
+# define localtime_rz tz_localtime_rz
+# undef mktime
+# define mktime tz_mktime
+# undef mktime_z
+# define mktime_z tz_mktime_z
+# undef offtime
+# define offtime tz_offtime
+# undef posix2time
+# define posix2time tz_posix2time
+# undef posix2time_z
+# define posix2time_z tz_posix2time_z
+# undef strftime
+# define strftime tz_strftime
+# undef time
+# define time tz_time
+# undef time2posix
+# define time2posix tz_time2posix
+# undef time2posix_z
+# define time2posix_z tz_time2posix_z
+# undef time_t
+# define time_t tz_time_t
+# undef timegm
+# define timegm tz_timegm
+# undef timelocal
+# define timelocal tz_timelocal
+# undef timeoff
+# define timeoff tz_timeoff
+# undef tzalloc
+# define tzalloc tz_tzalloc
+# undef tzfree
+# define tzfree tz_tzfree
+# undef tzset
+# define tzset tz_tzset
+# if HAVE_STRFTIME_L
+# undef strftime_l
+# define strftime_l tz_strftime_l
+# endif
+# if HAVE_TZNAME
+# undef tzname
+# define tzname tz_tzname
+# endif
+# if USG_COMPAT
+# undef daylight
+# define daylight tz_daylight
+# undef timezone
+# define timezone tz_timezone
+# endif
+# if ALTZONE
+# undef altzone
+# define altzone tz_altzone
+# endif
+
+char *asctime(struct tm const *);
+char *asctime_r(struct tm const *restrict, char *restrict);
+char *ctime(time_t const *);
+char *ctime_r(time_t const *, char *);
+double difftime(time_t, time_t) ATTRIBUTE_UNSEQUENCED;
+size_t strftime(char *restrict, size_t, char const *restrict,
+ struct tm const *restrict);
+# if HAVE_STRFTIME_L
+size_t strftime_l(char *restrict, size_t, char const *restrict,
+ struct tm const *restrict, locale_t);
+# endif
+struct tm *gmtime(time_t const *);
+struct tm *gmtime_r(time_t const *restrict, struct tm *restrict);
+struct tm *localtime(time_t const *);
+struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
+time_t mktime(struct tm *);
+time_t time(time_t *);
+time_t timegm(struct tm *);
+void tzset(void);
+#endif
+
+#ifndef HAVE_DECL_TIMEGM
+# if (202311 <= __STDC_VERSION__ \
+ || defined __GLIBC__ || defined __tm_zone /* musl */ \
+ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# define HAVE_DECL_TIMEGM true
+# else
+# define HAVE_DECL_TIMEGM false
+# endif
+#endif
+#if !HAVE_DECL_TIMEGM && !defined timegm
+time_t timegm(struct tm *);
+#endif
+
+#if !HAVE_DECL_ASCTIME_R && !defined asctime_r
+extern char *asctime_r(struct tm const *restrict, char *restrict);
+#endif
+
+#ifndef HAVE_DECL_ENVIRON
+# if defined environ || defined __USE_GNU
+# define HAVE_DECL_ENVIRON 1
+# else
+# define HAVE_DECL_ENVIRON 0
+# endif
+#endif
+
+#if !HAVE_DECL_ENVIRON
+extern char **environ;
+#endif
+
+#if 2 <= HAVE_TZNAME + (TZ_TIME_T || !HAVE_POSIX_DECLS)
+extern char *tzname[];
+#endif
+#if 2 <= USG_COMPAT + (TZ_TIME_T || !HAVE_POSIX_DECLS)
+extern long timezone;
+extern int daylight;
+#endif
+#if 2 <= ALTZONE + (TZ_TIME_T || !HAVE_POSIX_DECLS)
+extern long altzone;
+#endif
+
+/*
+** The STD_INSPIRED functions are similar, but most also need
+** declarations if time_tz is defined.
+*/
+
+#ifdef STD_INSPIRED
+# if TZ_TIME_T || !defined offtime
+struct tm *offtime(time_t const *, long);
+# endif
+# if TZ_TIME_T || !defined timelocal
+time_t timelocal(struct tm *);
+# endif
+# if TZ_TIME_T || !defined timeoff
+time_t timeoff(struct tm *, long);
+# endif
+# if TZ_TIME_T || !defined time2posix
+time_t time2posix(time_t);
+# endif
+# if TZ_TIME_T || !defined posix2time
+time_t posix2time(time_t);
+# endif
+#endif
+
+/* Infer TM_ZONE on systems where this information is known, but suppress
+ guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
+#if (defined __GLIBC__ \
+ || defined __tm_zone /* musl */ \
+ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
+ || (defined __APPLE__ && defined __MACH__))
+# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
+# define TM_GMTOFF tm_gmtoff
+# endif
+# if !defined TM_ZONE && !defined NO_TM_ZONE
+# define TM_ZONE tm_zone
+# endif
+#endif
+
+/*
+** Define functions that are ABI compatible with NetBSD but have
+** better prototypes. NetBSD 6.1.4 defines a pointer type timezone_t
+** and labors under the misconception that 'const timezone_t' is a
+** pointer to a constant. This use of 'const' is ineffective, so it
+** is not done here. What we call 'struct state' NetBSD calls
+** 'struct __state', but this is a private name so it doesn't matter.
+*/
+#if NETBSD_INSPIRED
+typedef struct state *timezone_t;
+struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
+ struct tm *restrict);
+time_t mktime_z(timezone_t restrict, struct tm *restrict);
+timezone_t tzalloc(char const *);
+void tzfree(timezone_t);
+# ifdef STD_INSPIRED
+# if TZ_TIME_T || !defined posix2time_z
+time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_REPRODUCIBLE;
+# endif
+# if TZ_TIME_T || !defined time2posix_z
+time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_REPRODUCIBLE;
+# endif
+# endif
+#endif
+
+/*
+** Finally, some convenience items.
+*/
+
+#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+
+/* Minimum and maximum of two values. Use lower case to avoid
+ naming clashes with standard include files. */
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+/* Max and min values of the integer type T, of which only the bottom
+ B bits are used, and where the highest-order used bit is considered
+ to be a sign bit if T is signed. */
+#define MAXVAL(t, b) \
+ ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
+ - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b) \
+ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+
+/* The extreme time values, assuming no padding. */
+#define TIME_T_MIN_NO_PADDING MINVAL(time_t, TYPE_BIT(time_t))
+#define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t))
+
+/* The extreme time values. These are macros, not constants, so that
+ any portability problems occur only when compiling .c files that use
+ the macros, which is safer for applications that need only zdump and zic.
+ This implementation assumes no padding if time_t is signed and
+ either the compiler lacks support for _Generic or time_t is not one
+ of the standard signed integer types. */
+#if HAVE_GENERIC
+# define TIME_T_MIN \
+ _Generic((time_t) 0, \
+ signed char: SCHAR_MIN, short: SHRT_MIN, \
+ int: INT_MIN, long: LONG_MIN, long long: LLONG_MIN, \
+ default: TIME_T_MIN_NO_PADDING)
+# define TIME_T_MAX \
+ (TYPE_SIGNED(time_t) \
+ ? _Generic((time_t) 0, \
+ signed char: SCHAR_MAX, short: SHRT_MAX, \
+ int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
+ default: TIME_T_MAX_NO_PADDING) \
+ : (time_t) -1)
+#else
+# define TIME_T_MIN TIME_T_MIN_NO_PADDING
+# define TIME_T_MAX TIME_T_MAX_NO_PADDING
+#endif
+
+/*
+** 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))
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifdef GCC_LINT
+# define INITIALIZE(x) ((x) = 0)
+#else
+# define INITIALIZE(x)
+#endif
+
+/* Whether memory access must strictly follow the C standard.
+ If 0, it's OK to read uninitialized storage so long as the value is
+ not relied upon. Defining it to 0 lets mktime access parts of
+ struct tm that might be uninitialized, as a heuristic when the
+ standard doesn't say what to return and when tm_gmtoff can help
+ mktime likely infer a better value. */
+#ifndef UNINIT_TRAP
+# define UNINIT_TRAP 0
+#endif
+
+#ifdef DEBUG
+# undef unreachable
+# define unreachable() abort()
+#elif !defined unreachable
+# ifdef __has_builtin
+# if __has_builtin(__builtin_unreachable)
+# define unreachable() __builtin_unreachable()
+# endif
+# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
+# define unreachable() __builtin_unreachable()
+# endif
+# ifndef unreachable
+# define unreachable() ((void) 0)
+# endif
+#endif
+
+/*
+** 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.
+*/
+
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+
+#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
+# define TZ_DOMAIN "tz"
+#endif
+
+#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 */
+
+/* Handy macros that are independent of tzfile implementation. */
+
+enum {
+ SECSPERMIN = 60,
+ MINSPERHOUR = 60,
+ SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
+ HOURSPERDAY = 24,
+ DAYSPERWEEK = 7,
+ DAYSPERNYEAR = 365,
+ DAYSPERLYEAR = DAYSPERNYEAR + 1,
+ MONSPERYEAR = 12,
+ YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
+};
+
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+
+#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
+#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
+#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
+
+enum {
+ TM_SUNDAY,
+ TM_MONDAY,
+ TM_TUESDAY,
+ TM_WEDNESDAY,
+ TM_THURSDAY,
+ TM_FRIDAY,
+ TM_SATURDAY
+};
+
+enum {
+ TM_JANUARY,
+ TM_FEBRUARY,
+ TM_MARCH,
+ TM_APRIL,
+ TM_MAY,
+ TM_JUNE,
+ TM_JULY,
+ TM_AUGUST,
+ TM_SEPTEMBER,
+ TM_OCTOBER,
+ TM_NOVEMBER,
+ TM_DECEMBER
+};
+
+enum {
+ TM_YEAR_BASE = 1900,
+ TM_WDAY_BASE = TM_MONDAY,
+ EPOCH_YEAR = 1970,
+ 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 by C99 or later).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined PRIVATE_H */
diff --git a/contrib/tzcode/stdtime/difftime.c b/contrib/tzcode/stdtime/difftime.c
deleted file mode 100644
index 0480849c3ffe..000000000000
--- a/contrib/tzcode/stdtime/difftime.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-** 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(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
deleted file mode 100644
index 6a0e0ce4fe4b..000000000000
--- a/contrib/tzcode/stdtime/localtime.c
+++ /dev/null
@@ -1,2317 +0,0 @@
-/*
-** 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.14";
-#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) ATTRIBUTE_PURE;
-static const char * getqzname(const char * strp, const int delim)
- ATTRIBUTE_PURE;
-static const char * getnum(const char * strp, int * nump, int min,
- int max);
-static const char * getsecs(const char * strp, long * secsp);
-static const char * getoffset(const char * strp, long * offsetp);
-static const char * getrule(const char * strp, struct rule * rulep);
-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) ATTRIBUTE_PURE;
-static int long_increment_overflow(long * number, int delta);
-static int long_normalize_overflow(long * tensptr,
- int * unitsptr, int base);
-static int normalize_overflow(int * tensptr, int * unitsptr,
- int base);
-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)
- ATTRIBUTE_PURE;
-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(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(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 */
- /*
- ** And to get the latest zone names into tzname. . .
- */
- for (i = 0; i < sp->typecnt; ++i) {
- const struct ttinfo * const ttisp = &sp->ttis[sp->types[i]];
-
- tzname[ttisp->tt_isdst] =
- &sp->chars[ttisp->tt_abbrind];
-#ifdef USG_COMPAT
- if (ttisp->tt_isdst)
- daylight = 1;
- if (!ttisp->tt_isdst)
- timezone = -(ttisp->tt_gmtoff);
-#endif /* defined USG_COMPAT */
-#ifdef ALTZONE
- if (ttisp->tt_isdst)
- altzone = -(ttisp->tt_gmtoff);
-#endif /* defined ALTZONE */
- }
- /*
- ** 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';
- }
-}
-
-#ifdef DETECT_TZ_CHANGES
-/*
- * Determine if there's a change in the timezone since the last time we checked.
- * Returns: -1 on error
- * 0 if the timezone has not changed
- * 1 if the timezone has changed
- */
-static int
-change_in_tz(const char *name)
-{
- static char old_name[PATH_MAX];
- static struct stat old_sb;
- struct stat sb;
- int error;
-
- error = stat(name, &sb);
- if (error != 0)
- return -1;
-
- if (strcmp(name, old_name) != 0) {
- strlcpy(old_name, name, sizeof(old_name));
- old_sb = sb;
- return 1;
- }
-
- if (sb.st_dev != old_sb.st_dev ||
- sb.st_ino != old_sb.st_ino ||
- sb.st_ctime != old_sb.st_ctime ||
- sb.st_mtime != old_sb.st_mtime) {
- old_sb = sb;
- return 1;
- }
-
- return 0;
-}
-#else /* !DETECT_TZ_CHANGES */
-#define change_in_tz(X) 1
-#endif /* !DETECT_TZ_CHANGES */
-
-static int
-differ_by_repeat(const time_t t1, const time_t t0)
-{
- int_fast64_t _t0 = t0;
- int_fast64_t _t1 = t1;
-
- if (TYPE_INTEGRAL(time_t) &&
- TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
- return 0;
- //turn ((int_fast64_t)(t1 - t0) == SECSPERREPEAT);
- return _t1 - _t0 == SECSPERREPEAT;
-}
-
-static int
-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;
- int res;
- int ret;
- union {
- struct tzhead tzhead;
- char buf[2 * sizeof(struct tzhead) +
- 2 * sizeof *sp +
- 4 * TZ_MAX_TIMES];
- } *u;
-
- u = NULL;
- res = -1;
- sp->goback = sp->goahead = FALSE;
-
- if (name != NULL && issetugid() != 0)
- if ((name[0] == ':' && name[1] == '/') ||
- name[0] == '/' || strchr(name, '.'))
- name = NULL;
- if (name == NULL && (name = TZDEFAULT) == NULL)
- return -1;
- {
- 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;
-
- fullname = malloc(FILENAME_MAX + 1);
- if (fullname == NULL)
- goto out;
-
- if (name[0] == ':')
- ++name;
- if (name[0] != '/') {
- if ((p = TZDIR) == NULL) {
- free(fullname);
- return -1;
- }
- if (strlen(p) + 1 + strlen(name) >= FILENAME_MAX) {
- free(fullname);
- return -1;
- }
- (void) strcpy(fullname, p);
- (void) strcat(fullname, "/");
- (void) strcat(fullname, name);
- name = fullname;
- }
- if (doextend == TRUE) {
- /*
- * Detect if the timezone file has changed. Check
- * 'doextend' to ignore TZDEFRULES; the change_in_tz()
- * function can only keep state for a single file.
- */
- ret = change_in_tz(name);
- if (ret <= 0) {
- /*
- * Returns -1 if there was an error,
- * and 0 if the timezone had not changed.
- */
- free(fullname);
- return ret;
- }
- }
- if ((fid = _open(name, OPEN_MODE)) == -1) {
- free(fullname);
- return -1;
- }
- if ((_fstat(fid, &stab) < 0) || !S_ISREG(stab.st_mode)) {
- free(fullname);
- _close(fid);
- return -1;
- }
- free(fullname);
- }
- u = malloc(sizeof(*u));
- if (u == NULL)
- goto out;
- nread = _read(fid, u->buf, sizeof u->buf);
- if (_close(fid) < 0 || nread <= 0)
- goto out;
- for (stored = 4; stored <= 8; stored *= 2) {
- int ttisstdcnt;
- int ttisgmtcnt;
-
- 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))
- goto out;
- 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 */
- goto out;
- 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)
- goto out;
- }
- 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)
- goto out;
- ttisp->tt_abbrind = (unsigned char) *p++;
- if (ttisp->tt_abbrind < 0 ||
- ttisp->tt_abbrind > sp->charcnt)
- goto out;
- }
- 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)
- goto out;
- }
- }
- 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)
- goto out;
- }
- }
- /*
- ** Out-of-sort ats should mean we're running on a
- ** signed time_t system but using a data file with
- ** unsigned values (or vice versa).
- */
- for (i = 0; i < sp->timecnt - 2; ++i)
- if (sp->ats[i] > sp->ats[i + 1]) {
- ++i;
- if (TYPE_SIGNED(time_t)) {
- /*
- ** Ignore the end (easy).
- */
- sp->timecnt = i;
- } else {
- /*
- ** Ignore the beginning (harder).
- */
- register int j;
-
- for (j = 0; j + i < sp->timecnt; ++j) {
- sp->ats[j] = sp->ats[j + i];
- sp->types[j] = sp->types[j + i];
- }
- sp->timecnt = j;
- }
- break;
- }
- /*
- ** If this is an old file, we're done.
- */
- if (u->tzhead.tzh_version[0] == '\0')
- break;
- nread -= p - u->buf;
- for (i = 0; i < nread; ++i)
- u->buf[i] = p[i];
- /*
- ** If this is a narrow integer time_t system, we're done.
- */
- if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
- break;
- }
- if (doextend && nread > 2 &&
- u->buf[0] == '\n' && u->buf[nread - 1] == '\n' &&
- sp->typecnt + 2 <= TZ_MAX_TYPES) {
- struct state *ts;
- register int result;
-
- ts = malloc(sizeof(*ts));
- if (ts == NULL)
- goto out;
- u->buf[nread - 1] = '\0';
- result = tzparse(&u->buf[1], ts, FALSE);
- if (result == 0 && ts->typecnt == 2 &&
- sp->charcnt + ts->charcnt <= TZ_MAX_CHARS) {
- for (i = 0; i < 2; ++i)
- ts->ttis[i].tt_abbrind +=
- sp->charcnt;
- for (i = 0; i < ts->charcnt; ++i)
- sp->chars[sp->charcnt++] =
- ts->chars[i];
- i = 0;
- while (i < ts->timecnt &&
- ts->ats[i] <=
- sp->ats[sp->timecnt - 1])
- ++i;
- while (i < ts->timecnt &&
- sp->timecnt < TZ_MAX_TIMES) {
- sp->ats[sp->timecnt] =
- ts->ats[i];
- sp->types[sp->timecnt] =
- sp->typecnt +
- ts->types[i];
- ++sp->timecnt;
- ++i;
- }
- sp->ttis[sp->typecnt++] = ts->ttis[0];
- sp->ttis[sp->typecnt++] = ts->ttis[1];
- }
- free(ts);
- }
- if (sp->timecnt > 1) {
- for (i = 1; i < sp->timecnt; ++i)
- if (typesequiv(sp, sp->types[i], sp->types[0]) &&
- differ_by_repeat(sp->ats[i], sp->ats[0])) {
- sp->goback = TRUE;
- break;
- }
- for (i = sp->timecnt - 2; i >= 0; --i)
- if (typesequiv(sp, sp->types[sp->timecnt - 1],
- sp->types[i]) &&
- differ_by_repeat(sp->ats[sp->timecnt - 1],
- sp->ats[i])) {
- sp->goahead = TRUE;
- break;
- }
- }
- res = 0;
-out:
- free(u);
- return (res);
-}
-
-static int
-typesequiv(sp, a, b)
-const struct state * const sp;
-const int a;
-const int b;
-{
- register int result;
-
- if (sp == NULL ||
- a < 0 || a >= sp->typecnt ||
- b < 0 || b >= sp->typecnt)
- result = FALSE;
- else {
- register const struct ttinfo * ap = &sp->ttis[a];
- register const struct ttinfo * bp = &sp->ttis[b];
- result = ap->tt_gmtoff == bp->tt_gmtoff &&
- ap->tt_isdst == bp->tt_isdst &&
- ap->tt_ttisstd == bp->tt_ttisstd &&
- ap->tt_ttisgmt == bp->tt_ttisgmt &&
- strcmp(&sp->chars[ap->tt_abbrind],
- &sp->chars[bp->tt_abbrind]) == 0;
- }
- return result;
-}
-
-static const int mon_lengths[2][MONSPERYEAR] = {
- { 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(struct state *const sp)
-{
- if (tzload(gmt, sp, TRUE) != 0)
- (void) tzparse(gmt, sp, TRUE);
-}
-
-#ifdef DETECT_TZ_CHANGES
-static int
-recheck_tzdata()
-{
- static time_t last_checked;
- struct timespec now;
- time_t current_time;
- int error;
-
- /*
- * We want to recheck the timezone file every 61 sec.
- */
- error = clock_gettime(CLOCK_MONOTONIC, &now);
- if (error < 0) {
- /* XXX: Can we somehow report this? */
- return 0;
- }
-
- current_time = now.tv_sec;
- if ((current_time - last_checked > 61) ||
- (last_checked > current_time)) {
- last_checked = current_time;
- return 1;
- }
-
- return 0;
-}
-#else /* !DETECT_TZ_CHANGES */
-#define recheck_tzdata() 0
-#endif /* !DETECT_TZ_CHANGES */
-
-static void
-tzsetwall_basic(int rdlocked)
-{
- if (!rdlocked)
- _RWLOCK_RDLOCK(&lcl_rwlock);
- if (lcl_is_set < 0 && recheck_tzdata() == 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 = calloc(1, 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 *) calloc(1, 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(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(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);
- p_tm = localsub(timep, 0L, p_tm);
- _RWLOCK_UNLOCK(&lcl_rwlock);
- } else {
- tzset_basic(0);
- p_tm = localsub(timep, 0L, &tm);
- }
- return(p_tm);
-}
-
-/*
-** Re-entrant version of localtime.
-*/
-
-struct tm *
-localtime_r(const time_t *const timep, struct tm *tmp)
-{
- _RWLOCK_RDLOCK(&lcl_rwlock);
- tzset_basic(1);
- tmp = localsub(timep, 0L, tmp);
- _RWLOCK_UNLOCK(&lcl_rwlock);
- return tmp;
-}
-
-static void
-gmt_init(void)
-{
-
-#ifdef ALL_STATE
- gmtptr = (struct state *) calloc(1, 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(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(const time_t *const timep, struct tm *tmp)
-{
- return gmtsub(timep, 0L, tmp);
-}
-
-#ifdef STD_INSPIRED
-
-struct tm *
-offtime(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(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(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(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(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(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(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 == NULL) {
- errno = EINVAL;
- return WRONG;
- }
-
- 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(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(struct tm *const tmp)
-{
- if (tmp != NULL)
- tmp->tm_isdst = -1; /* in case it wasn't initialized */
- return mktime(tmp);
-}
-
-time_t
-timegm(struct tm *const tmp)
-{
- if (tmp != NULL)
- tmp->tm_isdst = 0;
- return time1(tmp, gmtsub, 0L);
-}
-
-time_t
-timeoff(struct tm *const tmp, const long offset)
-{
- if (tmp != NULL)
- 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(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(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(time_t t)
-{
- tzset();
- return t - leapcorr(&t);
-}
-
-time_t
-posix2time(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
deleted file mode 100644
index 8f8f725f8193..000000000000
--- a/contrib/tzcode/stdtime/private.h
+++ /dev/null
@@ -1,318 +0,0 @@
-#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 */
-
-#if 2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)
-# define ATTRIBUTE_PURE __attribute__ ((__pure__))
-#else
-# define ATTRIBUTE_PURE /* empty */
-#endif
-
-/*
-** 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 * icatalloc(char * old, const char * new);
-char * icpyalloc(const char * string);
-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 */
-
-#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/tzfile.5 b/contrib/tzcode/stdtime/tzfile.5
deleted file mode 100644
index 5e911a5ee5c1..000000000000
--- a/contrib/tzcode/stdtime/tzfile.5
+++ /dev/null
@@ -1,152 +0,0 @@
-.\" $FreeBSD$
-.Dd September 13, 1994
-.Dt TZFILE 5
-.Os
-.Sh NAME
-.Nm tzfile
-.Nd timezone information
-.Sh SYNOPSIS
-.Fd #include \&"/usr/src/contrib/tzcode/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
deleted file mode 100644
index 85b945e6dd75..000000000000
--- a/contrib/tzcode/stdtime/tzfile.h
+++ /dev/null
@@ -1,184 +0,0 @@
-#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 */
diff --git a/contrib/tzcode/strftime.c b/contrib/tzcode/strftime.c
new file mode 100644
index 000000000000..b23b6101b150
--- /dev/null
+++ b/contrib/tzcode/strftime.c
@@ -0,0 +1,657 @@
+/* Convert a broken-down timestamp to a string. */
+
+/* Copyright 1989 The Regents of the University of California.
+ 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. 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. */
+
+/*
+** Based on the UCB version with the copyright notice appearing above.
+**
+** This is ANSIish only when "multibyte character == plain character".
+*/
+
+#include "private.h"
+
+#include <fcntl.h>
+#include <locale.h>
+#include <stdio.h>
+
+#ifndef DEPRECATE_TWO_DIGIT_YEARS
+# define DEPRECATE_TWO_DIGIT_YEARS false
+#endif
+
+struct lc_time_T {
+ const char * mon[MONSPERYEAR];
+ const char * month[MONSPERYEAR];
+ const char * wday[DAYSPERWEEK];
+ const char * weekday[DAYSPERWEEK];
+ const char * X_fmt;
+ const char * x_fmt;
+ const char * c_fmt;
+ const char * am;
+ const char * pm;
+ const char * date_fmt;
+};
+
+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
+ ** C99 and later require this format.
+ ** Using just numbers (as here) makes Quakers happier;
+ ** it's also compatible with SVR4.
+ */
+ "%m/%d/%y",
+
+ /*
+ ** c_fmt
+ ** C99 and later require this format.
+ ** Previously this code used "%D %X", but we now conform to C99.
+ ** Note that
+ ** "%a %b %d %H:%M:%S %Y"
+ ** is used by Solaris 2.3.
+ */
+ "%a %b %e %T %Y",
+
+ /* am */
+ "AM",
+
+ /* pm */
+ "PM",
+
+ /* date_fmt */
+ "%a %b %e %H:%M:%S %Z %Y"
+};
+
+enum warn { IN_NONE, IN_SOME, IN_THIS, IN_ALL };
+
+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 *,
+ enum warn *);
+static char * _yconv(int, int, bool, bool, char *, char const *);
+
+#ifndef YEAR_2000_NAME
+# define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
+#endif /* !defined YEAR_2000_NAME */
+
+#if HAVE_STRFTIME_L
+size_t
+strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
+ ATTRIBUTE_MAYBE_UNUSED locale_t locale)
+{
+ /* Just call strftime, as only the C locale is supported. */
+ return strftime(s, maxsize, format, t);
+}
+#endif
+
+size_t
+strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
+{
+ char * p;
+ int saved_errno = errno;
+ enum warn warn = IN_NONE;
+
+ tzset();
+ p = _fmt(format, t, s, s + maxsize, &warn);
+ if (!p) {
+ errno = EOVERFLOW;
+ return 0;
+ }
+ if (DEPRECATE_TWO_DIGIT_YEARS
+ && warn != IN_NONE && getenv(YEAR_2000_NAME)) {
+ fprintf(stderr, "\n");
+ fprintf(stderr, "strftime format \"%s\" ", format);
+ fprintf(stderr, "yields only two digits of years in ");
+ if (warn == IN_SOME)
+ fprintf(stderr, "some locales");
+ else if (warn == IN_THIS)
+ fprintf(stderr, "the current locale");
+ else fprintf(stderr, "all locales");
+ fprintf(stderr, "\n");
+ }
+ if (p == s + maxsize) {
+ errno = ERANGE;
+ return 0;
+ }
+ *p = '\0';
+ errno = saved_errno;
+ return p - s;
+}
+
+static char *
+_fmt(const char *format, const struct tm *t, char *pt,
+ const char *ptlim, enum warn *warnp)
+{
+ struct lc_time_T const *Locale = &C_time_locale;
+
+ for ( ; *format; ++format) {
+ if (*format == '%') {
+label:
+ switch (*++format) {
+ case '\0':
+ --format;
+ break;
+ case 'A':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : Locale->weekday[t->tm_wday],
+ pt, ptlim);
+ continue;
+ case 'a':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : Locale->wday[t->tm_wday],
+ pt, ptlim);
+ continue;
+ case 'B':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->month[t->tm_mon],
+ pt, ptlim);
+ continue;
+ case 'b':
+ case 'h':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->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,
+ true, false, pt, ptlim);
+ continue;
+ case 'c':
+ {
+ enum warn warn2 = IN_SOME;
+
+ pt = _fmt(Locale->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, "%02d", pt, ptlim);
+ continue;
+ case 'E':
+ case 'O':
+ /*
+ ** Locale modifiers of C99 and later.
+ ** 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 alternative
+ ** representations.
+ */
+ goto label;
+ case 'e':
+ pt = _conv(t->tm_mday, "%2d", pt, ptlim);
+ continue;
+ case 'F':
+ pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
+ continue;
+ case 'H':
+ pt = _conv(t->tm_hour, "%02d", pt, ptlim);
+ continue;
+ case 'I':
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ "%02d", pt, ptlim);
+ continue;
+ case 'j':
+ pt = _conv(t->tm_yday + 1, "%03d", 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, "%2d", 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,
+ "%2d", pt, ptlim);
+ continue;
+ case 'M':
+ pt = _conv(t->tm_min, "%02d", pt, ptlim);
+ continue;
+ case 'm':
+ pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
+ continue;
+ case 'n':
+ pt = _add("\n", pt, ptlim);
+ continue;
+ case 'p':
+ pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+ Locale->pm :
+ Locale->am,
+ pt, ptlim);
+ continue;
+ case 'R':
+ pt = _fmt("%H:%M", t, pt, ptlim, warnp);
+ continue;
+ case 'r':
+ pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
+ continue;
+ case 'S':
+ pt = _conv(t->tm_sec, "%02d", pt, ptlim);
+ continue;
+ case 's':
+ {
+ struct tm tm;
+ char buf[INT_STRLEN_MAXIMUM(
+ time_t) + 1];
+ time_t mkt;
+
+ tm.tm_sec = t->tm_sec;
+ tm.tm_min = t->tm_min;
+ tm.tm_hour = t->tm_hour;
+ tm.tm_mday = t->tm_mday;
+ tm.tm_mon = t->tm_mon;
+ tm.tm_year = t->tm_year;
+ tm.tm_isdst = t->tm_isdst;
+#if defined TM_GMTOFF && ! UNINIT_TRAP
+ tm.TM_GMTOFF = t->TM_GMTOFF;
+#endif
+ mkt = mktime(&tm);
+ /* If mktime fails, %s expands to the
+ value of (time_t) -1 as a failure
+ marker; this is better in practice
+ than strftime failing. */
+ if (TYPE_SIGNED(time_t)) {
+ intmax_t n = mkt;
+ sprintf(buf, "%"PRIdMAX, n);
+ } else {
+ uintmax_t n = mkt;
+ sprintf(buf, "%"PRIuMAX, n);
+ }
+ 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,
+ "%02d", 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 <https://www.cl.cam.ac.uk/~mgk25/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, "%02d",
+ pt, ptlim);
+ else if (*format == 'g') {
+ *warnp = IN_ALL;
+ pt = _yconv(year, base,
+ false, true,
+ pt, ptlim);
+ } else pt = _yconv(year, base,
+ true, true,
+ 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,
+ "%02d", pt, ptlim);
+ continue;
+ case 'w':
+ pt = _conv(t->tm_wday, "%d", pt, ptlim);
+ continue;
+ case 'X':
+ pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
+ continue;
+ case 'x':
+ {
+ enum warn warn2 = IN_SOME;
+
+ pt = _fmt(Locale->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,
+ false, true,
+ pt, ptlim);
+ continue;
+ case 'Y':
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ true, true,
+ pt, ptlim);
+ continue;
+ case 'Z':
+#ifdef TM_ZONE
+ pt = _add(t->TM_ZONE, pt, ptlim);
+#elif HAVE_TZNAME
+ if (t->tm_isdst >= 0)
+ pt = _add(tzname[t->tm_isdst != 0],
+ pt, ptlim);
+#endif
+ /*
+ ** C99 and later say that %Z must be
+ ** replaced by the empty string if the
+ ** time zone abbreviation is not
+ ** determinable.
+ */
+ continue;
+ case 'z':
+#if defined TM_GMTOFF || USG_COMPAT || ALTZONE
+ {
+ long diff;
+ char const * sign;
+ bool negative;
+
+# ifdef TM_GMTOFF
+ diff = t->TM_GMTOFF;
+# else
+ /*
+ ** C99 and later say that the UT 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 the standard;
+ ** see GNU C strftime for details.
+ ** For now, punt and conform to the
+ ** standard, even though it's incorrect.
+ **
+ ** C99 and later say 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)
+ continue;
+ if (t->tm_isdst == 0)
+# if USG_COMPAT
+ diff = -timezone;
+# else
+ continue;
+# endif
+ else
+# if ALTZONE
+ diff = -altzone;
+# else
+ continue;
+# endif
+# endif
+ negative = diff < 0;
+ if (diff == 0) {
+# ifdef TM_ZONE
+ negative = t->TM_ZONE[0] == '-';
+# else
+ negative = t->tm_isdst < 0;
+# if HAVE_TZNAME
+ if (tzname[t->tm_isdst != 0][0] == '-')
+ negative = true;
+# endif
+# endif
+ }
+ if (negative) {
+ sign = "-";
+ diff = -diff;
+ } else sign = "+";
+ pt = _add(sign, pt, ptlim);
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 +
+ (diff % MINSPERHOUR);
+ pt = _conv(diff, "%04d", pt, ptlim);
+ }
+#endif
+ continue;
+ case '+':
+ pt = _fmt(Locale->date_fmt, t, pt, ptlim,
+ warnp);
+ continue;
+ 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(int n, const char *format, char *pt, const char *ptlim)
+{
+ char buf[INT_STRLEN_MAXIMUM(int) + 1];
+
+ sprintf(buf, format, n);
+ return _add(buf, pt, ptlim);
+}
+
+static char *
+_add(const char *str, char *pt, const char *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(int a, int b, bool convert_top, bool convert_yy,
+ char *pt, const char *ptlim)
+{
+ register int lead;
+ register int trail;
+
+ int 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/theory.html b/contrib/tzcode/theory.html
new file mode 100644
index 000000000000..75e347f0f9d1
--- /dev/null
+++ b/contrib/tzcode/theory.html
@@ -0,0 +1,1479 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Theory and pragmatics of the tz code and data</title>
+ <meta charset="UTF-8">
+ <style>
+ pre {margin-left: 2em; white-space: pre-wrap;}
+ </style>
+</head>
+
+<body>
+<h1>Theory and pragmatics of the <code><abbr>tz</abbr></code> code and data</h1>
+ <h3>Outline</h3>
+ <nav>
+ <ul>
+ <li><a href="#scope">Scope of the <code><abbr>tz</abbr></code>
+ database</a></li>
+ <li><a href="#naming">Timezone identifiers</a></li>
+ <li><a href="#abbreviations">Time zone abbreviations</a></li>
+ <li><a href="#accuracy">Accuracy of the <code><abbr>tz</abbr></code>
+ database</a></li>
+ <li><a href="#functions">Time and date functions</a></li>
+ <li><a href="#stability">Interface stability</a></li>
+ <li><a href="#leapsec">Leap seconds</a></li>
+ <li><a href="#calendar">Calendrical issues</a></li>
+ <li><a href="#planets">Time and time zones on other planets</a></li>
+ </ul>
+ </nav>
+
+<section>
+ <h2 id="scope">Scope of the <code><abbr>tz</abbr></code> database</h2>
+<p>
+The <a
+href="https://www.iana.org/time-zones"><code><abbr>tz</abbr></code>
+database</a> attempts to record the history and predicted future of
+civil time scales.
+It organizes <a href="tz-link.html">time zone and daylight saving time
+data</a> by partitioning the world into <a
+href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"><dfn>timezones</dfn></a>
+whose clocks all agree about timestamps that occur after the <a
+href="https://en.wikipedia.org/wiki/Unix_time">POSIX Epoch</a>
+(1970-01-01 00:00:00 <a
+href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time"><abbr
+title="Coordinated Universal Time">UTC</abbr></a>).
+Although 1970 is a somewhat-arbitrary cutoff, there are significant
+challenges to moving the cutoff earlier even by a decade or two, due
+to the wide variety of local practices before computer timekeeping
+became prevalent.
+Most timezones correspond to a notable location and the database
+records all known clock transitions for that location;
+some timezones correspond instead to a fixed <abbr>UTC</abbr> offset.
+</p>
+
+<p>
+Each timezone typically corresponds to a geographical region that is
+smaller than a traditional time zone, because clocks in a timezone
+all agree after 1970 whereas a traditional time zone merely
+specifies current standard time. For example, applications that deal
+with current and future timestamps in the traditional North
+American mountain time zone can choose from the timezones
+<code>America/Denver</code> which observes US-style daylight saving
+time (<abbr>DST</abbr>),
+and <code>America/Phoenix</code> which does not observe <abbr>DST</abbr>.
+Applications that also deal with past timestamps in the mountain time
+zone can choose from over a dozen timezones, such as
+<code>America/Boise</code>, <code>America/Edmonton</code>, and
+<code>America/Hermosillo</code>, each of which currently uses mountain
+time but differs from other timezones for some timestamps after 1970.
+</p>
+
+<p>
+Clock transitions before 1970 are recorded for location-based timezones,
+because most systems support timestamps before 1970 and could
+misbehave if data entries were omitted for pre-1970 transitions.
+However, the database is not designed for and does not suffice for
+applications requiring accurate handling of all past times everywhere,
+as it would take far too much effort and guesswork to record all
+details of pre-1970 civil timekeeping.
+Although some information outside the scope of the database is
+collected in a file <code>backzone</code> that is distributed along
+with the database proper, this file is less reliable and does not
+necessarily follow database guidelines.
+</p>
+
+<p>
+As described below, reference source code for using the
+<code><abbr>tz</abbr></code> database is also available.
+The <code><abbr>tz</abbr></code> code is upwards compatible with <a
+href="https://en.wikipedia.org/wiki/POSIX">POSIX</a>, an international
+standard for <a
+href="https://en.wikipedia.org/wiki/Unix">UNIX</a>-like systems.
+As of this writing, the current edition of POSIX is: <a
+href="https://pubs.opengroup.org/onlinepubs/9699919799/"> The Open
+Group Base Specifications Issue 7</a>, IEEE Std 1003.1-2017, 2018
+Edition.
+Because the database's scope encompasses real-world changes to civil
+timekeeping, its model for describing time is more complex than the
+standard and daylight saving times supported by POSIX.
+A <code><abbr>tz</abbr></code> timezone corresponds to a ruleset that can
+have more than two changes per year, these changes need not merely
+flip back and forth between two alternatives, and the rules themselves
+can change at times.
+Whether and when a timezone changes its clock,
+and even the timezone's notional base offset from <abbr>UTC</abbr>,
+are variable.
+It does not always make sense to talk about a timezone's
+"base offset", which is not necessarily a single number.
+</p>
+
+</section>
+
+<section>
+ <h2 id="naming">Timezone identifiers</h2>
+<p>
+Each timezone has a name that uniquely identifies the timezone.
+Inexperienced users are not expected to select these names unaided.
+Distributors should provide documentation and/or a simple selection
+interface that explains each name via a map or via descriptive text like
+"Czech Republic" instead of the timezone name "<code>Europe/Prague</code>".
+If geolocation information is available, a selection interface can
+locate the user on a timezone map or prioritize names that are
+geographically close. For an example selection interface, see the
+<code>tzselect</code> program in the <code><abbr>tz</abbr></code> code.
+The <a href="https://cldr.unicode.org">Unicode Common Locale Data
+Repository</a> contains data that may be useful for other selection
+interfaces; it maps timezone names like <code>Europe/Prague</code> to
+locale-dependent strings like "Prague", "Praha", "Прага", and "布拉格".
+</p>
+
+<p>
+The naming conventions attempt to strike a balance
+among the following goals:
+</p>
+
+<ul>
+ <li>
+ Uniquely identify every timezone where clocks have agreed since 1970.
+ This is essential for the intended use: static clocks keeping local
+ civil time.
+ </li>
+ <li>
+ Indicate to experts where the timezone's clocks typically are.
+ </li>
+ <li>
+ Be robust in the presence of political changes.
+ For example, names are typically not tied to countries, to avoid
+ incompatibilities when countries change their name (e.g.,
+ Swaziland&rarr;Eswatini) or when locations change countries (e.g., Hong
+ Kong from UK colony to China).
+ There is no requirement that every country or national
+ capital must have a timezone name.
+ </li>
+ <li>
+ Be portable to a wide variety of implementations.
+ </li>
+ <li>
+ Use a consistent naming conventions over the entire world.
+ </li>
+</ul>
+
+<p>
+Names normally have the form
+<var>AREA</var><code>/</code><var>LOCATION</var>, where
+<var>AREA</var> is a continent or ocean, and
+<var>LOCATION</var> is a specific location within the area.
+North and South America share the same area, '<code>America</code>'.
+Typical names are '<code>Africa/Cairo</code>',
+'<code>America/New_York</code>', and '<code>Pacific/Honolulu</code>'.
+Some names are further qualified to help avoid confusion; for example,
+'<code>America/Indiana/Petersburg</code>' distinguishes Petersburg,
+Indiana from other Petersburgs in America.
+</p>
+
+<p>
+Here are the general guidelines used for
+choosing timezone names,
+in decreasing order of importance:
+</p>
+
+<ul>
+ <li>
+ Use only valid POSIX file name components (i.e., the parts of
+ names other than '<code>/</code>').
+ Do not use the file name components '<code>.</code>' and
+ '<code>..</code>'.
+ Within a file name component, use only <a
+ href="https://en.wikipedia.org/wiki/ASCII">ASCII</a> letters,
+ '<code>.</code>', '<code>-</code>' and '<code>_</code>'.
+ Do not use digits, as that might create an ambiguity with <a
+ href="https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03">POSIX
+ <code>TZ</code> strings</a>.
+ A file name component must not exceed 14 characters or start with
+ '<code>-</code>'.
+ E.g., prefer <code>America/Noronha</code> to
+ <code>America/Fernando_de_Noronha</code>.
+ Exceptions: see the discussion of legacy names below.
+ </li>
+ <li>
+ A name must not be empty, or contain '<code>//</code>', or
+ start or end with '<code>/</code>'.
+ </li>
+ <li>
+ Do not use names that differ only in case.
+ Although the reference implementation is case-sensitive, some
+ other implementations are not, and they would mishandle names
+ differing only in case.
+ </li>
+ <li>
+ If one name <var>A</var> is an initial prefix of another
+ name <var>AB</var> (ignoring case), then <var>B</var> must not
+ start with '<code>/</code>', as a regular file cannot have the
+ same name as a directory in POSIX.
+ For example, <code>America/New_York</code> precludes
+ <code>America/New_York/Bronx</code>.
+ </li>
+ <li>
+ Uninhabited regions like the North Pole and Bouvet Island
+ do not need locations, since local time is not defined there.
+ </li>
+ <li>
+ If all the clocks in a timezone have agreed since 1970,
+ do not bother to include more than one timezone
+ even if some of the clocks disagreed before 1970.
+ Otherwise these tables would become annoyingly large.
+ </li>
+ <li>
+ If boundaries between regions are fluid, such as during a war or
+ insurrection, do not bother to create a new timezone merely
+ because of yet another boundary change. This helps prevent table
+ bloat and simplifies maintenance.
+ </li>
+ <li>
+ If a name is ambiguous, use a less ambiguous alternative;
+ e.g., many cities are named San José and Georgetown, so
+ prefer <code>America/Costa_Rica</code> to
+ <code>America/San_Jose</code> and <code>America/Guyana</code>
+ to <code>America/Georgetown</code>.
+ </li>
+ <li>
+ Keep locations compact.
+ Use cities or small islands, not countries or regions, so that any
+ future changes do not split individual locations into different
+ timezones.
+ E.g., prefer <code>Europe/Paris</code> to <code>Europe/France</code>,
+ since
+ <a href="https://en.wikipedia.org/wiki/Time_in_France#History">France
+ has had multiple time zones</a>.
+ </li>
+ <li>
+ Use mainstream English spelling, e.g., prefer
+ <code>Europe/Rome</code> to <code>Europa/Roma</code>, and
+ prefer <code>Europe/Athens</code> to the Greek
+ <code>Ευρώπη/Αθήνα</code> or the Romanized
+ <code>Evrópi/Athína</code>.
+ The POSIX file name restrictions encourage this guideline.
+ </li>
+ <li>
+ Use the most populous among locations in a region,
+ e.g., prefer <code>Asia/Shanghai</code> to
+ <code>Asia/Beijing</code>.
+ Among locations with similar populations, pick the best-known
+ location, e.g., prefer <code>Europe/Rome</code> to
+ <code>Europe/Milan</code>.
+ </li>
+ <li>
+ Use the singular form, e.g., prefer <code>Atlantic/Canary</code> to
+ <code>Atlantic/Canaries</code>.
+ </li>
+ <li>
+ Omit common suffixes like '<code>_Islands</code>' and
+ '<code>_City</code>', unless that would lead to ambiguity.
+ E.g., prefer <code>America/Cayman</code> to
+ <code>America/Cayman_Islands</code> and
+ <code>America/Guatemala</code> to
+ <code>America/Guatemala_City</code>, but prefer
+ <code>America/Mexico_City</code> to
+ <code>America/Mexico</code>
+ because <a href="https://en.wikipedia.org/wiki/Time_in_Mexico">the
+ country of Mexico has several time zones</a>.
+ </li>
+ <li>
+ Use '<code>_</code>' to represent a space.
+ </li>
+ <li>
+ Omit '<code>.</code>' from abbreviations in names.
+ E.g., prefer <code>Atlantic/St_Helena</code> to
+ <code>Atlantic/St._Helena</code>.
+ </li>
+ <li>
+ Do not change established names if they only marginally violate
+ the above guidelines.
+ For example, do not change the existing name <code>Europe/Rome</code> to
+ <code>Europe/Milan</code> merely because Milan's population has grown
+ to be somewhat greater than Rome's.
+ </li>
+ <li>
+ If a name is changed, put its old spelling in the
+ '<code>backward</code>' file as a link to the new spelling.
+ This means old spellings will continue to work.
+ Ordinarily a name change should occur only in the rare case when
+ a location's consensus English-language spelling changes; for example,
+ in 2008 <code>Asia/Calcutta</code> was renamed to <code>Asia/Kolkata</code>
+ due to long-time widespread use of the new city name instead of the old.
+ </li>
+</ul>
+
+<p>
+Guidelines have evolved with time, and names following old versions of
+these guidelines might not follow the current version. When guidelines
+have changed, old names continue to be supported. Guideline changes
+have included the following:
+</p>
+
+<ul>
+<li>
+Older versions of this package used a different naming scheme.
+See the file '<code>backward</code>' for most of these older names
+(e.g., '<code>US/Eastern</code>' instead of '<code>America/New_York</code>').
+The other old-fashioned names still supported are
+'<code>WET</code>', '<code>CET</code>', '<code>MET</code>', and
+'<code>EET</code>' (see the file '<code>europe</code>').
+</li>
+
+<li>
+Older versions of this package defined legacy names that are
+incompatible with the first guideline of location names, but which are
+still supported.
+These legacy names are mostly defined in the file
+'<code>etcetera</code>'.
+Also, the file '<code>backward</code>' defines the legacy names
+'<code>Etc/GMT0</code>', '<code>Etc/GMT-0</code>', '<code>Etc/GMT+0</code>',
+'<code>GMT0</code>', '<code>GMT-0</code>' and '<code>GMT+0</code>',
+and the file '<code>northamerica</code>' defines the legacy names
+'<code>EST5EDT</code>', '<code>CST6CDT</code>',
+'<code>MST7MDT</code>', and '<code>PST8PDT</code>'.
+</li>
+
+<li>
+Older versions of these guidelines said that
+there should typically be at least one name for each <a
+href="https://en.wikipedia.org/wiki/ISO_3166-1"><abbr
+title="International Organization for Standardization">ISO</abbr>
+3166-1</a> officially assigned two-letter code for an inhabited
+country or territory.
+This old guideline has been dropped, as it was not needed to handle
+timestamps correctly and it increased maintenance burden.
+</li>
+</ul>
+
+<p>
+The file <code>zone1970.tab</code> lists geographical locations used
+to name timezones.
+It is intended to be an exhaustive list of names for geographic
+regions as described above; this is a subset of the timezones in the data.
+Although a <code>zone1970.tab</code> location's
+<a href="https://en.wikipedia.org/wiki/Longitude">longitude</a>
+corresponds to
+its <a href="https://en.wikipedia.org/wiki/Local_mean_time">local mean
+time (<abbr>LMT</abbr>)</a> offset with one hour for every 15&deg;
+east longitude, this relationship is not exact.
+The backward-compatibility file <code>zone.tab</code> is similar
+but conforms to the older-version guidelines related to <abbr>ISO</abbr> 3166-1;
+it lists only one country code per entry and unlike <code>zone1970.tab</code>
+it can list names defined in <code>backward</code>.
+</p>
+
+<p>
+The database defines each timezone name to be a zone, or a link to a zone.
+The source file <code>backward</code> defines links for backward
+compatibility; it does not define zones.
+Although <code>backward</code> was originally designed to be optional,
+nowadays distributions typically use it
+and no great weight should be attached to whether a link
+is defined in <code>backward</code> or in some other file.
+The source file <code>etcetera</code> defines names that may be useful
+on platforms that do not support POSIX-style <code>TZ</code> strings;
+no other source file other than <code>backward</code>
+contains links to its zones.
+One of <code>etcetera</code>'s names is <code>Etc/UTC</code>,
+used by functions like <code>gmtime</code> to obtain leap
+second information on platforms that support leap seconds.
+Another <code>etcetera</code> name, <code>GMT</code>,
+is used by older code releases.
+</p>
+</section>
+
+<section>
+ <h2 id="abbreviations">Time zone abbreviations</h2>
+<p>
+When this package is installed, it generates time zone abbreviations
+like '<code>EST</code>' to be compatible with human tradition and POSIX.
+Here are the general guidelines used for choosing time zone abbreviations,
+in decreasing order of importance:
+</p>
+
+<ul>
+ <li>
+ Use three to six characters that are ASCII alphanumerics or
+ '<code>+</code>' or '<code>-</code>'.
+ Previous editions of this database also used characters like
+ space and '<code>?</code>', but these characters have a
+ special meaning to the
+ <a href="https://en.wikipedia.org/wiki/Unix_shell">UNIX shell</a>
+ and cause commands like
+ '<code><a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set">set</a>
+ `<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/date.html">date</a>`</code>'
+ to have unexpected effects.
+ Previous editions of this guideline required upper-case letters, but the
+ Congressman who introduced
+ <a href="https://en.wikipedia.org/wiki/Chamorro_Time_Zone">Chamorro
+ Standard Time</a> preferred "ChST", so lower-case letters are now
+ allowed.
+ Also, POSIX from 2001 on relaxed the rule to allow '<code>-</code>',
+ '<code>+</code>', and alphanumeric characters from the portable
+ character set in the current locale.
+ In practice ASCII alphanumerics and '<code>+</code>' and
+ '<code>-</code>' are safe in all locales.
+
+ <p>
+ In other words, in the C locale the POSIX extended regular
+ expression <code>[-+[:alnum:]]{3,6}</code> should match the
+ abbreviation.
+ This guarantees that all abbreviations could have been specified by a
+ POSIX <code>TZ</code> string.
+ </p>
+ </li>
+ <li>
+ Use abbreviations that are in common use among English-speakers,
+ e.g., 'EST' for Eastern Standard Time in North America.
+ We assume that applications translate them to other languages
+ as part of the normal localization process; for example,
+ a French application might translate 'EST' to 'HNE'.
+
+ <p>
+ <small>These abbreviations (for standard/daylight/etc. time) are:
+ ACST/ACDT Australian Central,
+ AST/ADT/APT/AWT/ADDT Atlantic,
+ AEST/AEDT Australian Eastern,
+ AHST/AHDT Alaska-Hawaii,
+ AKST/AKDT Alaska,
+ AWST/AWDT Australian Western,
+ BST/BDT Bering,
+ CAT/CAST Central Africa,
+ CET/CEST/CEMT Central European,
+ ChST Chamorro,
+ CST/CDT/CWT/CPT/CDDT Central [North America],
+ CST/CDT China,
+ GMT/BST/IST/BDST Greenwich,
+ EAT East Africa,
+ EST/EDT/EWT/EPT/EDDT Eastern [North America],
+ EET/EEST Eastern European,
+ GST/GDT Guam,
+ HST/HDT/HWT/HPT Hawaii,
+ HKT/HKST/HKWT Hong Kong,
+ IST India,
+ IST/GMT Irish,
+ IST/IDT/IDDT Israel,
+ JST/JDT Japan,
+ KST/KDT Korea,
+ MET/MEST Middle European (a backward-compatibility alias for
+ Central European),
+ MSK/MSD Moscow,
+ MST/MDT/MWT/MPT/MDDT Mountain,
+ NST/NDT/NWT/NPT/NDDT Newfoundland,
+ NST/NDT/NWT/NPT Nome,
+ NZMT/NZST New Zealand through 1945,
+ NZST/NZDT New Zealand 1946&ndash;present,
+ PKT/PKST Pakistan,
+ PST/PDT/PWT/PPT/PDDT Pacific,
+ PST/PDT Philippine,
+ SAST South Africa,
+ SST Samoa,
+ UTC Universal,
+ WAT/WAST West Africa,
+ WET/WEST/WEMT Western European,
+ WIB Waktu Indonesia Barat,
+ WIT Waktu Indonesia Timur,
+ WITA Waktu Indonesia Tengah,
+ YST/YDT/YWT/YPT/YDDT Yukon</small>.
+ </p>
+ </li>
+ <li>
+ <p>
+ For times taken from a city's longitude, use the
+ traditional <var>x</var>MT notation.
+ The only abbreviation like this in current use is '<abbr>GMT</abbr>'.
+ The others are for timestamps before 1960,
+ except that Monrovia Mean Time persisted until 1972.
+ Typically, numeric abbreviations (e.g., '<code>-</code>004430' for
+ MMT) would cause trouble here, as the numeric strings would exceed
+ the POSIX length limit.
+ </p>
+
+ <p>
+ <small>These abbreviations are:
+ AMT Asunción, Athens;
+ BMT Baghdad, Bangkok, Batavia, Bermuda, Bern, Bogotá, Bridgetown,
+ Brussels, Bucharest;
+ CMT Calamarca, Caracas, Chisinau, Colón, Córdoba;
+ DMT Dublin/Dunsink;
+ EMT Easter;
+ FFMT Fort-de-France;
+ FMT Funchal;
+ GMT Greenwich;
+ HMT Havana, Helsinki, Horta, Howrah;
+ IMT Irkutsk, Istanbul;
+ JMT Jerusalem;
+ KMT Kaunas, Kyiv, Kingston;
+ LMT Lima, Lisbon, local, Luanda;
+ MMT Macassar, Madras, Malé, Managua, Minsk, Monrovia, Montevideo,
+ Moratuwa, Moscow;
+ PLMT Phù Liễn;
+ PMT Paramaribo, Paris, Perm, Pontianak, Prague;
+ PMMT Port Moresby;
+ QMT Quito;
+ RMT Rangoon, Riga, Rome;
+ SDMT Santo Domingo;
+ SJMT San José;
+ SMT Santiago, Simferopol, Singapore, Stanley;
+ TBMT Tbilisi;
+ TMT Tallinn, Tehran;
+ WMT Warsaw;
+ ZMT Zomba.</small>
+ </p>
+
+ <p>
+ <small>A few abbreviations also follow the pattern that
+ <abbr>GMT</abbr>/<abbr>BST</abbr> established for time in the UK.
+ They are:
+ BMT/BST for Bermuda 1890&ndash;1930,
+ CMT/BST for Calamarca Mean Time and Bolivian Summer Time
+ 1890&ndash;1932,
+ DMT/IST for Dublin/Dunsink Mean Time and Irish Summer Time
+ 1880&ndash;1916,
+ MMT/MST/MDST for Moscow 1880&ndash;1919, and
+ RMT/LST for Riga Mean Time and Latvian Summer time 1880&ndash;1926.
+ </small>
+ </p>
+ </li>
+ <li>
+ Use '<abbr>LMT</abbr>' for local mean time of locations before the
+ introduction of standard time; see "<a href="#scope">Scope of the
+ <code><abbr>tz</abbr></code> database</a>".
+ </li>
+ <li>
+ If there is no common English abbreviation, use numeric offsets like
+ <code>-</code>05 and <code>+</code>0530 that are generated
+ by <code>zic</code>'s <code>%z</code> notation.
+ </li>
+ <li>
+ Use current abbreviations for older timestamps to avoid confusion.
+ For example, in 1910 a common English abbreviation for time
+ in central Europe was 'MEZ' (short for both "Middle European
+ Zone" and for "Mitteleuropäische Zeit" in German).
+ Nowadays 'CET' ("Central European Time") is more common in
+ English, and the database uses 'CET' even for circa-1910
+ timestamps as this is less confusing for modern users and avoids
+ the need for determining when 'CET' supplanted 'MEZ' in common
+ usage.
+ </li>
+ <li>
+ Use a consistent style in a timezone's history.
+ For example, if a history tends to use numeric
+ abbreviations and a particular entry could go either way, use a
+ numeric abbreviation.
+ </li>
+ <li>
+ Use
+ <a href="https://en.wikipedia.org/wiki/Universal_Time">Universal Time</a>
+ (<abbr>UT</abbr>) (with time zone abbreviation '<code>-</code>00') for
+ locations while uninhabited.
+ The leading '<code>-</code>' is a flag that the <abbr>UT</abbr> offset is in
+ some sense undefined; this notation is derived
+ from <a href="https://datatracker.ietf.org/doc/html/rfc3339">Internet
+ <abbr title="Request For Comments">RFC</abbr> 3339</a>.
+ </li>
+</ul>
+
+<p>
+Application writers should note that these abbreviations are ambiguous
+in practice: e.g., 'CST' means one thing in China and something else
+in North America, and 'IST' can refer to time in India, Ireland or
+Israel.
+To avoid ambiguity, use numeric <abbr>UT</abbr> offsets like
+'<code>-</code>0600' instead of time zone abbreviations like 'CST'.
+</p>
+</section>
+
+<section>
+ <h2 id="accuracy">Accuracy of the <code><abbr>tz</abbr></code> database</h2>
+<p>
+The <code><abbr>tz</abbr></code> database is not authoritative, and it
+surely has errors.
+Corrections are welcome and encouraged; see the file <code>CONTRIBUTING</code>.
+Users requiring authoritative data should consult national standards
+bodies and the references cited in the database's comments.
+</p>
+
+<p>
+Errors in the <code><abbr>tz</abbr></code> database arise from many sources:
+</p>
+
+<ul>
+ <li>
+ The <code><abbr>tz</abbr></code> database predicts future
+ timestamps, and current predictions
+ will be incorrect after future governments change the rules.
+ For example, if today someone schedules a meeting for 13:00 next
+ October 1, Casablanca time, and tomorrow Morocco changes its
+ daylight saving rules, software can mess up after the rule change
+ if it blithely relies on conversions made before the change.
+ </li>
+ <li>
+ The pre-1970 entries in this database cover only a tiny sliver of how
+ clocks actually behaved; the vast majority of the necessary
+ information was lost or never recorded.
+ Thousands more timezones would be needed if
+ the <code><abbr>tz</abbr></code> database's scope were extended to
+ cover even just the known or guessed history of standard time; for
+ example, the current single entry for France would need to split
+ into dozens of entries, perhaps hundreds.
+ And in most of the world even this approach would be misleading
+ due to widespread disagreement or indifference about what times
+ should be observed.
+ In her 2015 book
+ <cite><a
+ href="https://www.hup.harvard.edu/catalog.php?isbn=9780674286146">The
+ Global Transformation of Time, 1870&ndash;1950</a></cite>,
+ Vanessa Ogle writes
+ "Outside of Europe and North America there was no system of time
+ zones at all, often not even a stable landscape of mean times,
+ prior to the middle decades of the twentieth century".
+ See: Timothy Shenk, <a
+href="https://www.dissentmagazine.org/blog/booked-a-global-history-of-time-vanessa-ogle">Booked:
+ A Global History of Time</a>. <cite>Dissent</cite> 2015-12-17.
+ </li>
+ <li>
+ Most of the pre-1970 data entries come from unreliable sources, often
+ astrology books that lack citations and whose compilers evidently
+ invented entries when the true facts were unknown, without
+ reporting which entries were known and which were invented.
+ These books often contradict each other or give implausible entries,
+ and on the rare occasions when they are checked they are
+ typically found to be incorrect.
+ </li>
+ <li>
+ For the UK the <code><abbr>tz</abbr></code> database relies on
+ years of first-class work done by
+ Joseph Myers and others; see
+ "<a href="https://www.polyomino.org.uk/british-time/">History of
+ legal time in Britain</a>".
+ Other countries are not done nearly as well.
+ </li>
+ <li>
+ Sometimes, different people in the same city maintain clocks
+ that differ significantly.
+ Historically, railway time was used by railroad companies (which
+ did not always
+ agree with each other), church-clock time was used for birth
+ certificates, etc.
+ More recently, competing political groups might disagree about
+ clock settings. Often this is merely common practice, but
+ sometimes it is set by law.
+ For example, from 1891 to 1911 the <abbr>UT</abbr> offset in France
+ was legally <abbr>UT</abbr> +00:09:21 outside train stations and
+ <abbr>UT</abbr> +00:04:21 inside. Other examples include
+ Chillicothe in 1920, Palm Springs in 1946/7, and Jerusalem and
+ Ürümqi to this day.
+ </li>
+ <li>
+ Although a named location in the <code><abbr>tz</abbr></code>
+ database stands for the containing region, its pre-1970 data
+ entries are often accurate for only a small subset of that region.
+ For example, <code>Europe/London</code> stands for the United
+ Kingdom, but its pre-1847 times are valid only for locations that
+ have London's exact meridian, and its 1847 transition
+ to <abbr>GMT</abbr> is known to be valid only for the L&amp;NW and
+ the Caledonian railways.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> database does not record the
+ earliest time for which a timezone's
+ data entries are thereafter valid for every location in the region.
+ For example, <code>Europe/London</code> is valid for all locations
+ in its region after <abbr>GMT</abbr> was made the standard time,
+ but the date of standardization (1880-08-02) is not in the
+ <code><abbr>tz</abbr></code> database, other than in commentary.
+ For many timezones the earliest time of
+ validity is unknown.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> database does not record a
+ region's boundaries, and in many cases the boundaries are not known.
+ For example, the timezone
+ <code>America/Kentucky/Louisville</code> represents a region
+ around the city of Louisville, the boundaries of which are
+ unclear.
+ </li>
+ <li>
+ Changes that are modeled as instantaneous transitions in the
+ <code><abbr>tz</abbr></code>
+ database were often spread out over hours, days, or even decades.
+ </li>
+ <li>
+ Even if the time is specified by law, locations sometimes
+ deliberately flout the law.
+ </li>
+ <li>
+ Early timekeeping practices, even assuming perfect clocks, were
+ often not specified to the accuracy that the
+ <code><abbr>tz</abbr></code> database requires.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> database cannot represent stopped clocks.
+ However, on 1911-03-11 at 00:00, some public-facing French clocks
+ were changed by stopping them for a few minutes to effect a transition.
+ The <code><abbr>tz</abbr></code> database models this via a
+ backward transition; the relevant French legislation does not
+ specify exactly how the transition was to occur.
+ </li>
+ <li>
+ Sometimes historical timekeeping was specified more precisely
+ than what the <code><abbr>tz</abbr></code> code can handle.
+ For example, from 1880 to 1916 clocks in Ireland observed Dublin Mean
+ Time (estimated to be <abbr>UT</abbr>
+ &minus;00:25:21.1); although the <code><abbr>tz</abbr></code>
+ source data can represent the .1 second, TZif files and the code cannot.
+ In practice these old specifications were rarely if ever
+ implemented to subsecond precision.
+ </li>
+ <li>
+ Even when all the timestamp transitions recorded by the
+ <code><abbr>tz</abbr></code> database are correct, the
+ <code><abbr>tz</abbr></code> rules that generate them may not
+ faithfully reflect the historical rules.
+ For example, from 1922 until World War II the UK moved clocks
+ forward the day following the third Saturday in April unless that
+ was Easter, in which case it moved clocks forward the previous
+ Sunday.
+ Because the <code><abbr>tz</abbr></code> database has no
+ way to specify Easter, these exceptional years are entered as
+ separate <code><abbr>tz</abbr> Rule</code> lines, even though the
+ legal rules did not change.
+ When transitions are known but the historical rules behind them are not,
+ the database contains <code>Zone</code> and <code>Rule</code>
+ entries that are intended to represent only the generated
+ transitions, not any underlying historical rules; however, this
+ intent is recorded at best only in commentary.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> database models time
+ using the <a
+ href="https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar">proleptic
+ Gregorian calendar</a> with days containing 24 equal-length hours
+ numbered 00 through 23, except when clock transitions occur.
+ Pre-standard time is modeled as local mean time.
+ However, historically many people used other calendars and other timescales.
+ For example, the Roman Empire used
+ the <a href="https://en.wikipedia.org/wiki/Julian_calendar">Julian
+ calendar</a>,
+ and <a href="https://en.wikipedia.org/wiki/Roman_timekeeping">Roman
+ timekeeping</a> had twelve varying-length daytime hours with a
+ non-hour-based system at night.
+ And even today, some local practices diverge from the Gregorian
+ calendar with 24-hour days. These divergences range from
+ relatively minor, such as Japanese bars giving times like "24:30" for the
+ wee hours of the morning, to more-significant differences such as <a
+ href="https://www.pri.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time">the
+ east African practice of starting the day at dawn</a>, renumbering
+ the Western 06:00 to be 12:00. These practices are largely outside
+ the scope of the <code><abbr>tz</abbr></code> code and data, which
+ provide only limited support for date and time localization
+ such as that required by POSIX.
+ If <abbr>DST</abbr> is not used a different time zone
+ can often do the trick; for example, in Kenya a <code>TZ</code> setting
+ like <code>&lt;-03&gt;3</code> or <code>America/Cayenne</code> starts
+ the day six hours later than <code>Africa/Nairobi</code> does.
+ </li>
+ <li>
+ Early clocks were less reliable, and data entries do not represent
+ clock error.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> database assumes Universal Time
+ (<abbr>UT</abbr>) as an origin, even though <abbr>UT</abbr> is not
+ standardized for older timestamps.
+ In the <code><abbr>tz</abbr></code> database commentary,
+ <abbr>UT</abbr> denotes a family of time standards that includes
+ Coordinated Universal Time (<abbr>UTC</abbr>) along with other
+ variants such as <abbr>UT1</abbr> and <abbr>GMT</abbr>,
+ with days starting at midnight.
+ Although <abbr>UT</abbr> equals <abbr>UTC</abbr> for modern
+ timestamps, <abbr>UTC</abbr> was not defined until 1960, so
+ commentary uses the more-general abbreviation <abbr>UT</abbr> for
+ timestamps that might predate 1960.
+ Since <abbr>UT</abbr>, <abbr>UT1</abbr>, etc. disagree slightly,
+ and since pre-1972 <abbr>UTC</abbr> seconds varied in length,
+ interpretation of older timestamps can be problematic when
+ subsecond accuracy is needed.
+ </li>
+ <li>
+ Civil time was not based on atomic time before 1972, and we do not
+ know the history of
+ <a href="https://en.wikipedia.org/wiki/Earth's_rotation">earth's
+ rotation</a> accurately enough to map <a
+ href="https://en.wikipedia.org/wiki/International_System_of_Units"><abbr
+ title="International System of Units">SI</abbr></a> seconds to
+ historical <a href="https://en.wikipedia.org/wiki/Solar_time">solar time</a>
+ to more than about one-hour accuracy.
+ See: Stephenson FR, Morrison LV, Hohenkerk CY.
+ <a href="https://dx.doi.org/10.1098/rspa.2016.0404">Measurement of
+ the Earth's rotation: 720 BC to AD 2015</a>.
+ <cite>Proc Royal Soc A</cite>. 2016;472:20160404.
+ Also see: Espenak F. <a
+ href="https://eclipse.gsfc.nasa.gov/SEhelp/uncertainty2004.html">Uncertainty
+ in Delta T (ΔT)</a>.
+ </li>
+ <li>
+ The relationship between POSIX time (that is, <abbr>UTC</abbr> but
+ ignoring <a href="https://en.wikipedia.org/wiki/Leap_second">leap
+ seconds</a>) and <abbr>UTC</abbr> is not agreed upon after 1972.
+ Although the POSIX
+ clock officially stops during an inserted leap second, at least one
+ proposed standard has it jumping back a second instead; and in
+ practice POSIX clocks more typically either progress glacially during
+ a leap second, or are slightly slowed while near a leap second.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> database does not represent how
+ uncertain its information is.
+ Ideally it would contain information about when data entries are
+ incomplete or dicey.
+ Partial temporal knowledge is a field of active research, though,
+ and it is not clear how to apply it here.
+ </li>
+</ul>
+
+<p>
+In short, many, perhaps most, of the <code><abbr>tz</abbr></code>
+database's pre-1970 and future timestamps are either wrong or
+misleading.
+Any attempt to pass the
+<code><abbr>tz</abbr></code> database off as the definition of time
+should be unacceptable to anybody who cares about the facts.
+In particular, the <code><abbr>tz</abbr></code> database's
+<abbr>LMT</abbr> offsets should not be considered meaningful, and
+should not prompt creation of timezones
+merely because two locations
+differ in <abbr>LMT</abbr> or transitioned to standard time at
+different dates.
+</p>
+</section>
+
+<section>
+ <h2 id="functions">Time and date functions</h2>
+<p>
+The <code><abbr>tz</abbr></code> code contains time and date functions
+that are upwards compatible with those of POSIX.
+Code compatible with this package is already
+<a href="tz-link.html#tzdb">part of many platforms</a>, where the
+primary use of this package is to update obsolete time-related files.
+To do this, you may need to compile the time zone compiler
+'<code>zic</code>' supplied with this package instead of using the
+system '<code>zic</code>', since the format of <code>zic</code>'s
+input is occasionally extended, and a platform may still be shipping
+an older <code>zic</code>.
+</p>
+
+<h3 id="POSIX">POSIX properties and limitations</h3>
+<ul>
+ <li>
+ <p>
+ In POSIX, time display in a process is controlled by the
+ environment variable <code>TZ</code>.
+ Unfortunately, the POSIX
+ <code>TZ</code> string takes a form that is hard to describe and
+ is error-prone in practice.
+ Also, POSIX <code>TZ</code> strings cannot deal with daylight
+ saving time rules not based on the Gregorian calendar (as in
+ Iran), or with situations where more than two time zone
+ abbreviations or <abbr>UT</abbr> offsets are used in an area.
+ </p>
+
+ <p>
+ The POSIX <code>TZ</code> string takes the following form:
+ </p>
+
+ <p>
+ <var>stdoffset</var>[<var>dst</var>[<var>offset</var>][<code>,</code><var>date</var>[<code>/</code><var>time</var>]<code>,</code><var>date</var>[<code>/</code><var>time</var>]]]
+ </p>
+
+ <p>
+ where:
+ </p>
+
+ <dl>
+ <dt><var>std</var> and <var>dst</var></dt><dd>
+ are 3 or more characters specifying the standard
+ and daylight saving time (<abbr>DST</abbr>) zone abbreviations.
+ Starting with POSIX.1-2001, <var>std</var> and <var>dst</var>
+ may also be in a quoted form like '<code>&lt;+09&gt;</code>';
+ this allows "<code>+</code>" and "<code>-</code>" in the names.
+ </dd>
+ <dt><var>offset</var></dt><dd>
+ is of the form
+ '<code>[&plusmn;]<var>hh</var>:[<var>mm</var>[:<var>ss</var>]]</code>'
+ and specifies the offset west of <abbr>UT</abbr>.
+ '<var>hh</var>' may be a single digit;
+ 0&le;<var>hh</var>&le;24.
+ The default <abbr>DST</abbr> offset is one hour ahead of
+ standard time.
+ </dd>
+ <dt><var>date</var>[<code>/</code><var>time</var>]<code>,</code><var>date</var>[<code>/</code><var>time</var>]</dt><dd>
+ specifies the beginning and end of <abbr>DST</abbr>.
+ If this is absent, the system supplies its own ruleset
+ for <abbr>DST</abbr>, and its rules can differ from year to year;
+ typically <abbr>US</abbr> <abbr>DST</abbr> rules are used.
+ </dd>
+ <dt><var>time</var></dt><dd>
+ takes the form
+ '<var>hh</var><code>:</code>[<var>mm</var>[<code>:</code><var>ss</var>]]'
+ and defaults to 02:00.
+ This is the same format as the offset, except that a
+ leading '<code>+</code>' or '<code>-</code>' is not allowed.
+ </dd>
+ <dt><var>date</var></dt><dd>
+ takes one of the following forms:
+ <dl>
+ <dt>J<var>n</var> (1&le;<var>n</var>&le;365)</dt><dd>
+ origin-1 day number not counting February 29
+ </dd>
+ <dt><var>n</var> (0&le;<var>n</var>&le;365)</dt><dd>
+ origin-0 day number counting February 29 if present
+ </dd>
+ <dt><code>M</code><var>m</var><code>.</code><var>n</var><code>.</code><var>d</var>
+ (0[Sunday]&le;<var>d</var>&le;6[Saturday], 1&le;<var>n</var>&le;5,
+ 1&le;<var>m</var>&le;12)</dt><dd>
+ for the <var>d</var>th day of week <var>n</var> of
+ month <var>m</var> of the year, where week 1 is the first
+ week in which day <var>d</var> appears, and
+ '<code>5</code>' stands for the last week in which
+ day <var>d</var> appears (which may be either the 4th or
+ 5th week).
+ Typically, this is the only useful form; the <var>n</var>
+ and <code>J</code><var>n</var> forms are rarely used.
+ </dd>
+ </dl>
+ </dd>
+ </dl>
+
+ <p>
+ Here is an example POSIX <code>TZ</code> string for New
+ Zealand after 2007.
+ It says that standard time (<abbr>NZST</abbr>) is 12 hours ahead
+ of <abbr>UT</abbr>, and that daylight saving time
+ (<abbr>NZDT</abbr>) is observed from September's last Sunday at
+ 02:00 until April's first Sunday at 03:00:
+ </p>
+
+ <pre><code>TZ='NZST-12NZDT,M9.5.0,M4.1.0/3'</code></pre>
+
+ <p>
+ This POSIX <code>TZ</code> string is hard to remember, and
+ mishandles some timestamps before 2008.
+ With this package you can use this instead:
+ </p>
+
+ <pre><code>TZ='Pacific/Auckland'</code></pre>
+ </li>
+ <li>
+ POSIX does not define the <abbr>DST</abbr> transitions
+ for <code>TZ</code> values like
+ "<code>EST5EDT</code>".
+ Traditionally the current <abbr>US</abbr> <abbr>DST</abbr> rules
+ were used to interpret such values, but this meant that the
+ <abbr>US</abbr> <abbr>DST</abbr> rules were compiled into each
+ program that did time conversion. This meant that when
+ <abbr>US</abbr> time conversion rules changed (as in the United
+ States in 1987), all programs that did time conversion had to be
+ recompiled to ensure proper results.
+ </li>
+ <li>
+ The <code>TZ</code> environment variable is process-global, which
+ makes it hard to write efficient, thread-safe applications that
+ need access to multiple timezones.
+ </li>
+ <li>
+ In POSIX, there is no tamper-proof way for a process to learn the
+ system's best idea of local (wall clock) time.
+ This is important for applications that an administrator wants
+ used only at certain times &ndash; without regard to whether the
+ user has fiddled the
+ <code>TZ</code> environment variable.
+ While an administrator can "do everything in <abbr>UT</abbr>" to
+ get around the problem, doing so is inconvenient and precludes
+ handling daylight saving time shifts &ndash; as might be required to
+ limit phone calls to off-peak hours.
+ </li>
+ <li>
+ POSIX provides no convenient and efficient way to determine
+ the <abbr>UT</abbr> offset and time zone abbreviation of arbitrary
+ timestamps, particularly for timezones
+ that do not fit into the POSIX model.
+ </li>
+ <li>
+ POSIX requires that <code>time_t</code> clock counts exclude leap
+ seconds.
+ </li>
+ <li>
+ The <code><abbr>tz</abbr></code> code attempts to support all the
+ <code>time_t</code> implementations allowed by POSIX.
+ The <code>time_t</code> type represents a nonnegative count of seconds
+ since 1970-01-01 00:00:00 <abbr>UTC</abbr>, ignoring leap seconds.
+ In practice, <code>time_t</code> is usually a signed 64- or 32-bit
+ integer; 32-bit signed <code>time_t</code> values stop working after
+ 2038-01-19 03:14:07 <abbr>UTC</abbr>, so new implementations these
+ days typically use a signed 64-bit integer.
+ Unsigned 32-bit integers are used on one or two platforms, and 36-bit
+ and 40-bit integers are also used occasionally.
+ Although earlier POSIX versions allowed <code>time_t</code> to be a
+ floating-point type, this was not supported by any practical system,
+ and POSIX.1-2013 and the <code><abbr>tz</abbr></code> code both
+ require <code>time_t</code> to be an integer type.
+ </li>
+</ul>
+
+<h3 id="POSIX-extensions">Extensions to POSIX in the
+<code><abbr>tz</abbr></code> code</h3>
+<ul>
+ <li>
+ <p>
+ The <code>TZ</code> environment variable is used in generating
+ the name of a file from which time-related information is read
+ (or is interpreted à la POSIX); <code>TZ</code> is no longer
+ constrained to be a string containing abbreviations
+ and numeric data as described <a href="#POSIX">above</a>.
+ The file's format is <dfn><abbr>TZif</abbr></dfn>,
+ a timezone information format that contains binary data; see
+ <a href="https://datatracker.ietf.org/doc/html/8536">Internet
+ <abbr>RFC</abbr> 8536</a>.
+ The daylight saving time rules to be used for a
+ particular timezone are encoded in the
+ <abbr>TZif</abbr> file; the format of the file allows <abbr>US</abbr>,
+ Australian, and other rules to be encoded, and
+ allows for situations where more than two time zone
+ abbreviations are used.
+ </p>
+ <p>
+ It was recognized that allowing the <code>TZ</code> environment
+ variable to take on values such as '<code>America/New_York</code>'
+ might cause "old" programs (that expect <code>TZ</code> to have a
+ certain form) to operate incorrectly; consideration was given to using
+ some other environment variable (for example, <code>TIMEZONE</code>)
+ to hold the string used to generate the <abbr>TZif</abbr> file's name.
+ In the end, however, it was decided to continue using
+ <code>TZ</code>: it is widely used for time zone purposes;
+ separately maintaining both <code>TZ</code>
+ and <code>TIMEZONE</code> seemed a nuisance; and systems where
+ "new" forms of <code>TZ</code> might cause problems can simply
+ use legacy <code>TZ</code> values such as "<code>EST5EDT</code>" which
+ can be used by "new" programs as well as by "old" programs that
+ assume pre-POSIX <code>TZ</code> values.
+ </p>
+ </li>
+ <li>
+ The code supports platforms with a <abbr>UT</abbr> offset member
+ in <code>struct tm</code>, e.g., <code>tm_gmtoff</code>,
+ or with a time zone abbreviation member in
+ <code>struct tm</code>, e.g., <code>tm_zone</code>. As noted
+ in <a href="https://austingroupbugs.net/view.php?id=1533">Austin
+ Group defect 1533</a>, a future version of POSIX is planned to
+ require <code>tm_gmtoff</code> and <code>tm_zone</code>.
+ </li>
+ <li>
+ Functions <code>tzalloc</code>, <code>tzfree</code>,
+ <code>localtime_rz</code>, and <code>mktime_z</code> for
+ more-efficient thread-safe applications that need to use multiple
+ timezones.
+ The <code>tzalloc</code> and <code>tzfree</code> functions
+ allocate and free objects of type <code>timezone_t</code>,
+ and <code>localtime_rz</code> and <code>mktime_z</code> are
+ like <code>localtime_r</code> and <code>mktime</code> with an
+ extra <code>timezone_t</code> argument.
+ The functions were inspired by <a href="https://netbsd.org/">NetBSD</a>.
+ </li>
+ <li>
+ Negative <code>time_t</code> values are supported, on systems
+ where <code>time_t</code> is signed.
+ </li>
+ <li>
+ These functions can account for leap seconds;
+ see <a href="#leapsec">Leap seconds</a> below.
+ </li>
+</ul>
+
+<h3 id="vestigial">POSIX features no longer needed</h3>
+<p>
+POSIX and <a href="https://en.wikipedia.org/wiki/ISO_C"><abbr>ISO</abbr> C</a>
+define some <a href="https://en.wikipedia.org/wiki/API"><abbr
+title="application programming interface">API</abbr>s</a> that are vestigial:
+they are not needed, and are relics of a too-simple model that does
+not suffice to handle many real-world timestamps.
+Although the <code><abbr>tz</abbr></code> code supports these
+vestigial <abbr>API</abbr>s for backwards compatibility, they should
+be avoided in portable applications.
+The vestigial <abbr>API</abbr>s are:
+</p>
+<ul>
+ <li>
+ The POSIX <code>tzname</code> variable does not suffice and is no
+ longer needed.
+ To get a timestamp's time zone abbreviation, consult
+ the <code>tm_zone</code> member if available; otherwise,
+ use <code>strftime</code>'s <code>"%Z"</code> conversion
+ specification.
+ </li>
+ <li>
+ The POSIX <code>daylight</code> and <code>timezone</code>
+ variables do not suffice and are no longer needed.
+ To get a timestamp's <abbr>UT</abbr> offset, consult
+ the <code>tm_gmtoff</code> member if available; otherwise,
+ subtract values returned by <code>localtime</code>
+ and <code>gmtime</code> using the rules of the Gregorian calendar,
+ or use <code>strftime</code>'s <code>"%z"</code> conversion
+ specification if a string like <code>"+0900"</code> suffices.
+ </li>
+ <li>
+ The <code>tm_isdst</code> member is almost never needed and most of
+ its uses should be discouraged in favor of the abovementioned
+ <abbr>API</abbr>s.
+ Although it can still be used in arguments to
+ <code>mktime</code> to disambiguate timestamps near
+ a <abbr>DST</abbr> transition when the clock jumps back on
+ platforms lacking <code>tm_gmtoff</code>, this
+ disambiguation does not work when standard time itself jumps back,
+ which can occur when a location changes to a time zone with a
+ lesser <abbr>UT</abbr> offset.
+ </li>
+</ul>
+
+<h3 id="other-portability">Other portability notes</h3>
+<ul>
+ <li>
+ The <a href="https://en.wikipedia.org/wiki/Version_7_Unix">7th Edition
+ UNIX</a> <code>timezone</code> function is not present in this
+ package; it is impossible to reliably map <code>timezone</code>'s
+ arguments (a "minutes west of <abbr>GMT</abbr>" value and a
+ "daylight saving time in effect" flag) to a time zone
+ abbreviation, and we refuse to guess.
+ Programs that in the past used the <code>timezone</code> function
+ may now examine <code>localtime(&amp;clock)-&gt;tm_zone</code>
+ (if <code>TM_ZONE</code> is defined) or
+ <code>tzname[localtime(&amp;clock)-&gt;tm_isdst]</code>
+ (if <code>HAVE_TZNAME</code> is nonzero) to learn the correct time
+ zone abbreviation to use.
+ </li>
+ <li>
+ The <a
+ href="https://en.wikipedia.org/wiki/History_of_the_Berkeley_Software_Distribution#4.2BSD"><abbr>4.2BSD</abbr></a>
+ <code>gettimeofday</code> function is not
+ used in this package.
+ This formerly let users obtain the current <abbr>UTC</abbr> offset
+ and <abbr>DST</abbr> flag, but this functionality was removed in
+ later versions of <abbr>BSD</abbr>.
+ </li>
+ <li>
+ In <abbr>SVR2</abbr>, time conversion fails for near-minimum or
+ near-maximum <code>time_t</code> values when doing conversions
+ for places that do not use <abbr>UT</abbr>.
+ This package takes care to do these conversions correctly.
+ A comment in the source code tells how to get compatibly wrong
+ results.
+ </li>
+ <li>
+ The functions that are conditionally compiled
+ if <code>STD_INSPIRED</code> is defined should, at this point, be
+ looked on primarily as food for thought.
+ They are not in any sense "standard compatible" &ndash; some are
+ not, in fact, specified in <em>any</em> standard.
+ They do, however, represent responses of various authors to
+ standardization proposals.
+ </li>
+ <li>
+ Other time conversion proposals, in particular those supported by the
+ <a href="https://howardhinnant.github.io/date/tz.html">Time Zone
+ Database Parser</a>, offer a wider selection of functions
+ that provide capabilities beyond those provided here.
+ The absence of such functions from this package is not meant to
+ discourage the development, standardization, or use of such
+ functions.
+ Rather, their absence reflects the decision to make this package
+ contain valid extensions to POSIX, to ensure its broad
+ acceptability.
+ If more powerful time conversion functions can be standardized, so
+ much the better.
+ </li>
+</ul>
+</section>
+
+<section>
+ <h2 id="stability">Interface stability</h2>
+<p>
+The <code><abbr>tz</abbr></code> code and data supply the following interfaces:
+</p>
+
+<ul>
+ <li>
+ A set of timezone names as per
+ "<a href="#naming">Timezone identifiers</a>" above.
+ </li>
+ <li>
+ Library functions described in "<a href="#functions">Time and date
+ functions</a>" above.
+ </li>
+ <li>
+ The programs <code>tzselect</code>, <code>zdump</code>,
+ and <code>zic</code>, documented in their man pages.
+ </li>
+ <li>
+ The format of <code>zic</code> input files, documented in
+ the <code>zic</code> man page.
+ </li>
+ <li>
+ The format of <code>zic</code> output files, documented in
+ the <code>tzfile</code> man page.
+ </li>
+ <li>
+ The format of zone table files, documented in <code>zone1970.tab</code>.
+ </li>
+ <li>
+ The format of the country code file, documented in <code>iso3166.tab</code>.
+ </li>
+ <li>
+ The version number of the code and data, as the first line of
+ the text file '<code>version</code>' in each release.
+ </li>
+</ul>
+
+<p>
+Interface changes in a release attempt to preserve compatibility with
+recent releases.
+For example, <code><abbr>tz</abbr></code> data files typically do not
+rely on recently-added <code>zic</code> features, so that users can
+run older <code>zic</code> versions to process newer data files.
+<a href="tz-link.html#download">Downloading
+the <code><abbr>tz</abbr></code> database</a> describes how releases
+are tagged and distributed.
+</p>
+
+<p>
+Interfaces not listed above are less stable.
+For example, users should not rely on particular <abbr>UT</abbr>
+offsets or abbreviations for timestamps, as data entries are often
+based on guesswork and these guesses may be corrected or improved.
+</p>
+
+<p>
+Timezone boundaries are not part of the stable interface.
+For example, even though the <samp>Asia/Bangkok</samp> timezone
+currently includes Chang Mai, Hanoi, and Phnom Penh, this is not part
+of the stable interface and the timezone can split at any time.
+If a calendar application records a future event in some location other
+than Bangkok by putting "<samp>Asia/Bangkok</samp>" in the event's record,
+the application should be robust in the presence of timezone splits
+between now and the future time.
+</p>
+</section>
+
+<section>
+ <h2 id="leapsec">Leap seconds</h2>
+<p>
+The <code><abbr>tz</abbr></code> code and data can account for leap seconds,
+thanks to code contributed by Bradley White.
+However, the leap second support of this package is rarely used directly
+because POSIX requires leap seconds to be excluded and many
+software packages would mishandle leap seconds if they were present.
+Instead, leap seconds are more commonly handled by occasionally adjusting
+the operating system kernel clock as described in
+<a href="tz-link.html#precision">Precision timekeeping</a>,
+and this package by default installs a <samp>leapseconds</samp> file
+commonly used by
+<a href="https://www.ntp.org"><abbr title="Network Time Protocol">NTP</abbr></a>
+software that adjusts the kernel clock.
+However, kernel-clock twiddling approximates UTC only roughly,
+and systems needing more-precise UTC can use this package's leap
+second support directly.
+</p>
+
+<p>
+The directly-supported mechanism assumes that <code>time_t</code>
+counts of seconds since the POSIX epoch normally include leap seconds,
+as opposed to POSIX <code>time_t</code> counts which exclude leap seconds.
+This modified timescale is converted to <abbr>UTC</abbr>
+at the same point that time zone and <abbr>DST</abbr>
+adjustments are applied &ndash;
+namely, at calls to <code>localtime</code> and analogous functions &ndash;
+and the process is driven by leap second information
+stored in alternate versions of the <abbr>TZif</abbr> files.
+Because a leap second adjustment may be needed even
+if no time zone correction is desired,
+calls to <code>gmtime</code>-like functions
+also need to consult a <abbr>TZif</abbr> file,
+conventionally named <samp><abbr>Etc/UTC</abbr></samp>
+(<samp><abbr>GMT</abbr></samp> in previous versions),
+to see whether leap second corrections are needed.
+To convert an application's <code>time_t</code> timestamps to or from
+POSIX <code>time_t</code> timestamps (for use when, say,
+embedding or interpreting timestamps in portable
+<a href="https://en.wikipedia.org/wiki/Tar_(computing)"><code>tar</code></a>
+files),
+the application can call the utility functions
+<code>time2posix</code> and <code>posix2time</code>
+included with this package.
+</p>
+
+<p>
+If the POSIX-compatible <abbr>TZif</abbr> file set is installed
+in a directory whose basename is <samp>zoneinfo</samp>, the
+leap-second-aware file set is by default installed in a separate
+directory <samp>zoneinfo-leaps</samp>.
+Although each process can have its own time zone by setting
+its <code>TZ</code> environment variable, there is no support for some
+processes being leap-second aware while other processes are
+POSIX-compatible; the leap-second choice is system-wide.
+So if you configure your kernel to count leap seconds, you should also
+discard <samp>zoneinfo</samp> and rename <samp>zoneinfo-leaps</samp>
+to <samp>zoneinfo</samp>.
+Alternatively, you can install just one set of <abbr>TZif</abbr> files
+in the first place; see the <code>REDO</code> variable in this package's
+<a href="https://en.wikipedia.org/wiki/Makefile">makefile</a>.
+</p>
+</section>
+
+<section>
+ <h2 id="calendar">Calendrical issues</h2>
+<p>
+Calendrical issues are a bit out of scope for a time zone database,
+but they indicate the sort of problems that we would run into if we
+extended the time zone database further into the past.
+An excellent resource in this area is Edward M. Reingold
+and Nachum Dershowitz, <cite><a
+href="https://www.cambridge.org/fr/academic/subjects/computer-science/computing-general-interest/calendrical-calculations-ultimate-edition-4th-edition">Calendrical
+Calculations: The Ultimate Edition</a></cite>, Cambridge University Press (2018).
+Other information and sources are given in the file '<code>calendars</code>'
+in the <code><abbr>tz</abbr></code> distribution.
+They sometimes disagree.
+</p>
+</section>
+
+<section>
+ <h2 id="planets">Time and time zones on other planets</h2>
+<p>
+Some people's work schedules have used
+<a href="https://en.wikipedia.org/wiki/Timekeeping_on_Mars">Mars time</a>.
+Jet Propulsion Laboratory (JPL) coordinators kept Mars time on
+and off during the
+<a href="https://en.wikipedia.org/wiki/Mars_Pathfinder">Mars
+Pathfinder</a> mission (1997).
+Some of their family members also adapted to Mars time.
+Dozens of special Mars watches were built for JPL workers who kept
+Mars time during the
+<a href="https://en.wikipedia.org/wiki/Mars_Exploration_Rover">Mars
+Exploration Rovers (MER)</a> mission (2004&ndash;2018).
+These timepieces looked like normal Seikos and Citizens but were adjusted
+to use Mars seconds rather than terrestrial seconds, although
+unfortunately the adjusted watches were unreliable and appear to have
+had only limited use.
+</p>
+
+<p>
+A Mars solar day is called a "sol" and has a mean period equal to
+about 24 hours 39 minutes 35.244 seconds in terrestrial time.
+It is divided into a conventional 24-hour clock, so each Mars second
+equals about 1.02749125 terrestrial seconds.
+(One MER worker noted, "If I am working Mars hours, and Mars hours are
+2.5% more than Earth hours, shouldn't I get an extra 2.5% pay raise?")
+</p>
+
+<p>
+The <a href="https://en.wikipedia.org/wiki/Prime_meridian">prime
+meridian</a> of Mars goes through the center of the crater
+<a href="https://en.wikipedia.org/wiki/Airy-0">Airy-0</a>, named in
+honor of the British astronomer who built the Greenwich telescope that
+defines Earth's prime meridian.
+Mean solar time on the Mars prime meridian is
+called Mars Coordinated Time (<abbr>MTC</abbr>).
+</p>
+
+<p>
+Each landed mission on Mars has adopted a different reference for
+solar timekeeping, so there is no real standard for Mars time zones.
+For example, the MER mission defined two time zones "Local
+Solar Time A" and "Local Solar Time B" for its two missions, each zone
+designed so that its time equals local true solar time at
+approximately the middle of the nominal mission.
+The A and B zones differ enough so that an MER worker assigned to
+the A zone might suffer "Mars lag" when switching to work in the B zone.
+Such a "time zone" is not particularly suited for any application
+other than the mission itself.
+</p>
+
+<p>
+Many calendars have been proposed for Mars, but none have achieved
+wide acceptance.
+Astronomers often use Mars Sol Date (<abbr>MSD</abbr>) which is a
+sequential count of Mars solar days elapsed since about 1873-12-29
+12:00 <abbr>GMT</abbr>.
+</p>
+
+<p>
+In our solar system, Mars is the planet with time and calendar most
+like Earth's.
+On other planets, Sun-based time and calendars would work quite
+differently.
+For example, although Mercury's
+<a href="https://en.wikipedia.org/wiki/Rotation_period">sidereal
+rotation period</a> is 58.646 Earth days, Mercury revolves around the
+Sun so rapidly that an observer on Mercury's equator would see a
+sunrise only every 175.97 Earth days, i.e., a Mercury year is 0.5 of a
+Mercury day.
+Venus is more complicated, partly because its rotation is slightly
+<a href="https://en.wikipedia.org/wiki/Retrograde_motion">retrograde</a>:
+its year is 1.92 of its days.
+Gas giants like Jupiter are trickier still, as their polar and
+equatorial regions rotate at different rates, so that the length of a
+day depends on latitude.
+This effect is most pronounced on Neptune, where the day is about 12
+hours at the poles and 18 hours at the equator.
+</p>
+
+<p>
+Although the <code><abbr>tz</abbr></code> database does not support
+time on other planets, it is documented here in the hopes that support
+will be added eventually.
+</p>
+
+<p>
+Sources for time on other planets:
+</p>
+
+<ul>
+ <li>
+ Michael Allison and Robert Schmunk,
+ "<a href="https://www.giss.nasa.gov/tools/mars24/help/notes.html">Technical
+ Notes on Mars Solar Time as Adopted by the Mars24 Sunclock</a>"
+ (2020-03-08).
+ </li>
+ <li>
+ Zara Mirmalek,
+ <em><a href="https://mitpress.mit.edu/books/making-time-mars">Making
+ Time on Mars</a></em>, MIT Press (March 2020), ISBN 978-0262043854.
+ </li>
+ <li>
+ Jia-Rui Chong,
+ "<a href="https://www.latimes.com/archives/la-xpm-2004-jan-14-sci-marstime14-story.html">Workdays
+ Fit for a Martian</a>", <cite>Los Angeles Times</cite>
+ (2004-01-14), pp A1, A20&ndash;A21.
+ </li>
+ <li>
+ Tom Chmielewski,
+ "<a href="https://www.theatlantic.com/technology/archive/2015/02/jet-lag-is-worse-on-mars/386033/">Jet
+ Lag Is Worse on Mars</a>", <cite>The Atlantic</cite> (2015-02-26)
+ </li>
+ <li>
+ Matt Williams,
+ "<a href="https://www.universetoday.com/37481/days-of-the-planets/">How
+ long is a day on the other planets of the solar system?</a>"
+ (2016-01-20).
+ </li>
+</ul>
+</section>
+
+<footer>
+ <hr>
+ This file is in the public domain, so clarified as of 2009-05-17 by
+ Arthur David Olson.
+</footer>
+</body>
+</html>
diff --git a/contrib/tzcode/stdtime/time2posix.3 b/contrib/tzcode/time2posix.3
index 1a1ec95699f9..6959c890a65b 100644
--- a/contrib/tzcode/stdtime/time2posix.3
+++ b/contrib/tzcode/time2posix.3
@@ -1,6 +1,9 @@
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
+.\"
.\" $FreeBSD$
.\"
-.Dd September 11, 2005
+.Dd December 15, 2022
.Dt TIME2POSIX 3
.Os
.Sh NAME
@@ -17,9 +20,10 @@
.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
+requires the time_t value 536457599 to stand for 1986-12-31 23:59:59 UTC.
+This effectively implies that POSIX
+.Vt time_t
+values cannot include leap
seconds and,
therefore,
that the system time must be adjusted as each leap occurs.
@@ -28,15 +32,22 @@ 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).
+.Vt time_t
+values continue to increase over leap events
+(as a true
+.Dq "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
+Typically this is not a problem as the type
+.Vt time_t
+is intended
to be
(mostly)
-opaque\(emtime_t values should only be obtained-from and
+opaque \(em
+.Vt time_t
+values should only be obtained-from and
passed-to functions such as
.Xr time 3 ,
.Xr localtime 3 ,
@@ -46,11 +57,15 @@ and
However,
.St -p1003.1-88
gives an arithmetic
-expression for directly computing a time_t value from a given date/time,
+expression for directly computing a
+.Vt 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
+Any programs creating/dissecting
+.Vt time_t
+values
using such a relationship will typically not handle intervals
over leap seconds correctly.
.Pp
@@ -58,8 +73,12 @@ 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.
+functions are provided to address this
+.Vt time_t
+mismatch by converting
+between local
+.Vt 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.
@@ -69,21 +88,27 @@ or when communicating with POSIX-compliant systems.
.Pp
The
.Fn time2posix
-function is single-valued.
+function
+is single-valued.
That is,
-every local time_t
-corresponds to a single POSIX time_t.
+every local
+.Vt time_t
+corresponds to a single POSIX
+.Vt time_t .
The
.Fn posix2time
-function is less well-behaved:
+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.
+POSIX
+.Vt 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,
+The following table summarizes the relationship between a time
+T and its conversion to,
and back from,
the POSIX representation over the leap second inserted at the end of June,
1993.
@@ -106,8 +131,12 @@ A leap second deletion would look like...
.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,
+local
+.Vt time_t
+and
+POSIX
+.Vt time_t
+values are equivalent,
and both
.Fn time2posix
and
@@ -118,6 +147,3 @@ degenerate to the identity function.
.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/tz-art.html b/contrib/tzcode/tz-art.html
new file mode 100644
index 000000000000..567953415f63
--- /dev/null
+++ b/contrib/tzcode/tz-art.html
@@ -0,0 +1,636 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>Time and the Arts</title>
+</head>
+<body>
+<h1>Time and the Arts</h1>
+<h2>Documentaries</h2>
+<ul>
+<li>
+"<a href="https://www.youtube.com/watch?v=84aWtseb2-4">Daylight
+Saving Time Explained</a>" (2011; 6:39) lightly covers daylight saving
+time's theory, history, pros and cons. Among other things, it explains
+Arizona's daylight-saving enclaves quite well.</li>
+<li>
+"<a href="https://www.youtube.com/watch?v=-5wpm-gesOY">The Problem
+with Time &amp; Timezones &ndash; Computerphile</a>" (2013; 10:12) delves
+into problems that programmers have with timekeeping.</li>
+<li>
+"<a href="https://www.rferl.org/a/28375932.html">All The Time In The World:
+Explaining The Mysteries Of Time Zones</a>" (2017; 2:15)
+briefly says why France has more time zones than Russia.
+<li>
+"<a href="https://www.youtube.com/watch?v=yRz-Dl60Lfc">Why Denmark is
+.17 Seconds Behind The World</a>" (2019; 6:29) explains why Denmark and
+the United Kingdom don't exactly follow their own law about civil time.
+<li>
+"About Time" (1962; 59 minutes) is part of the
+Bell Science extravaganza, with Frank Baxter, Richard Deacon, and Les Tremayne.
+Its advisor was Richard Feynman, and it was voiced by Mel Blanc.
+(<a href="https://www.imdb.com/title/tt0154110/">IMDb entry</a>.)</li>
+</ul>
+<h2>Movies</h2>
+<ul>
+<li>
+In the 1946 movie <em>A Matter of Life and Death</em>
+(U.S. title <em>Stairway to Heaven</em>)
+there is a reference to British Double Summer Time.
+The time does not play a large part in the plot;
+it's just a passing reference to the time when one of the
+characters was supposed to have died (but didn't).
+(<a href="https://www.imdb.com/title/tt0038733/">IMDb entry.</a>)
+(Dave Cantor)
+<li>
+The 1953 railway comedy movie <em>The Titfield Thunderbolt</em> includes a
+play on words on British Double Summer Time. Valentine's wife wants
+him to leave the pub and asks him, "Do you know what time it is?"
+And he, happy where he is, replies: "Yes, my love. Summer double time."
+(<a href="https://www.imdb.com/title/tt0046436/">IMDb entry.</a>)
+(Mark Brader, 2009-10-02)
+</li>
+<li>
+The premise of the 1999 caper movie <em>Entrapment</em> involves computers
+in an international banking network being shut down briefly at
+midnight in each time zone to avoid any problems at the transition
+from the year 1999 to 2000 in that zone. (Hmmmm.) If this shutdown
+is extended by 10 seconds, it will create a one-time opportunity for
+a gigantic computerized theft. To achieve this, at one location the
+crooks interfere with the microwave system supplying time signals to
+the computer, advancing the time by 0.1 second each minute over the
+last hour of 1999. (So this movie teaches us that 0.1 &times; 60 = 10.)
+(<a href="https://www.imdb.com/title/tt0137494/">IMDb entry.</a>)
+(Mark Brader, 2009-10-02)
+</li>
+<li>
+One mustn't forget the
+<a href="https://www.youtube.com/watch?v=k4EUTMPuvHo">trailer</a>
+(2014; 2:23) for the movie <em>Daylight Saving</em>.
+</li>
+</ul>
+<h2>TV episodes</h2>
+<ul>
+<li>
+An episode of <em>The Adventures of Superman</em> entitled "The Mysterious
+Cube," first aired 1958-02-24, had Superman convincing the controllers
+of the Arlington Time Signal to broadcast ahead of actual time;
+doing so got a crook trying to be declared dead to
+emerge a bit too early from the titular enclosure.
+(<a href="https://www.imdb.com/title/tt0506628/">IMDb entry</a>.)
+</li>
+<li>
+"<a href="https://en.wikipedia.org/wiki/The_Chimes_of_Big_Ben">The Chimes
+of Big Ben</a>", <em>The Prisoner</em>, episode 2, ITC, 1967-10-06.
+Our protagonist tumbles to
+the fraudulent nature of a Poland-to-England escape upon hearing "Big
+Ben" chiming on Polish local time.
+(<a href="https://www.imdb.com/title/tt0679185/">IMDb entry.</a>)
+</li>
+<li>
+"The Susie", <em>Seinfeld</em>, season 8, episode 15, NBC, 1997-02-13.
+Kramer decides that daylight saving time
+isn't coming fast enough, so he sets his watch ahead an hour.
+</li>
+<li>
+"20 Hours in America", <em>The West Wing</em>, season 4, episodes 1&ndash;2,
+2002-09-25, contained a <a
+href="https://www.youtube.com/watch?v=-J1NHzQ1sgc">scene</a> that
+saw White House staffers stranded in Indiana; they thought they had time to
+catch Air Force One but were done in by intra-Indiana local time changes.
+</li>
+<li>
+"In what time zone would you find New York City?" was a $200 question on
+the 1999-11-13 United States airing of <em>Who Wants to Be a Millionaire?</em>,
+and "In 1883, what industry led the movement to divide the U.S. into four time
+zones?" was a $32,000 question on the 2001-05-23 United States airing of
+the same show. At this rate, the million-dollar time-zone
+question should have been asked 2002-06-04.
+</li>
+<li>
+A private jet's mid-flight change of time zones distorts Alison Dubois'
+premonition in the "We Had a Dream" episode of <em>Medium</em>
+(originally aired 2007-02-28).
+</li>
+<li>
+A criminal's failure to account for the start of daylight saving is pivotal
+in "<a href="https://monk.fandom.com/wiki/Mr._Monk_and_the_Rapper">Mr. Monk
+and the Rapper</a>" (first aired 2007-07-20).
+</li>
+<li>
+In the <em>30 Rock</em> episode "Anna Howard Shaw Day"
+(first broadcast 2010-02-11),
+Jack Donaghy's date realizes that a Geneva-to-New-York business phone call
+received in the evening must be fake given the difference in local times.
+</li>
+<li>
+In the "Run by the Monkeys" episode of <em>Da Vinci's Inquest</em>
+(first broadcast 2002-11-17),
+a witness in a five-year-old fire case realizes they may not have set
+their clock back when daylight saving ended on the day of the fire,
+introducing the possibility of an hour when arson might have occurred.
+</li>
+<li>
+In "The Todd Couple" episode of <em>Outsourced</em> (first aired 2011-02-10),
+Manmeet sets up Valentine's Day teledates for 6:00 and 9:00pm;
+since one is with a New Yorker and the other with a San Franciscan,
+hilarity ensues.
+(Never mind that this should be 7:30am in Mumbai, yet for some reason the show
+proceeds as though it's also mid-evening there.)
+</li>
+<li>
+In the "14 Days to Go"/"T Minus..." episode of
+<em>You, Me and the Apocalypse</em>
+(first aired 2015-11-11 in the UK, 2016-03-10 in the US),
+the success of a mission to deal with a comet
+hinges on whether or not Russia observes daylight saving time.
+(In the US,
+the episode first aired in the week before the switch to <abbr>DST</abbr>.)
+</li>
+<li>
+"The Lost Hour", <em>Eerie, Indiana</em>, episode 10, NBC, 1991-12-01.
+Despite Indiana's then-lack of <abbr>DST</abbr>,
+Marshall changes his clock with unusual consequences.
+See "<a
+href="https://www.avclub.com/eerie-indiana-was-a-few-dimensions-ahead-of-its-time-1819833380"><em>Eerie,
+Indiana</em> was a few dimensions ahead of its time</a>".
+</li>
+<li>
+"Time Tunnel", <em>The Adventures of Pete &amp; Pete</em>, season 2, episode 5,
+Nickelodeon, 1994-10-23.
+The two Petes travel back in time an hour
+on the day that <abbr>DST</abbr> ends.
+</li>
+<li>
+"King-Size Homer", <em>The Simpsons</em>, episode 135, Fox, 1995-11-05.
+Homer, working from home, remarks "8:58, first
+time I've ever been early for work. Except for all those daylight
+savings days. Lousy farmers."
+</li>
+<li>
+<em>Last Week Tonight with John Oliver</em>, season 2, episode 5, 2015-03-08,
+asked, "<a href="https://www.youtube.com/watch?v=br0NW9ufUUw">Daylight Saving
+Time &ndash; How Is This Still A Thing?</a>"
+</li>
+<li>
+"Tracks", <em>The Good Wife</em>, season 7, episode 12,
+CBS, 2016-01-17.
+The applicability of a contract hinges on the
+time zone associated with a video timestamp.
+</li>
+<li>
+"Justice", <em>Veep</em>, season 6, episode 4, HBO, 2017-05-07.
+Jonah's inability to understand <abbr>DST</abbr> ends up impressing a wealthy
+backer who sets him up for a 2020 presidential run.
+</li>
+</ul>
+<h2>Books, plays, and magazines</h2>
+<ul>
+<li>
+Jules Verne, <em>Around the World in Eighty Days</em>
+(<em>Le tour du monde en quatre-vingts jours</em>), 1873.
+Wall-clock time plays a central role in the plot.
+European readers of the 1870s clearly held the U.S. press in
+deep contempt; the protagonists cross the U.S. without once
+reading a paper.
+Available versions include
+<a href="https://www.gutenberg.org/ebooks/103">an English
+translation</a>, and
+<a href="https://fourmilab.ch/etexts/www/tdm80j">the original French</a>
+"with illustrations from the original 1873 French-language edition".
+</li>
+<li>
+Nick Enright, <em>Daylight Saving</em>, 1989.
+A fast-paced comedy about love and loneliness as the clocks turn back.
+</li>
+<li>
+Umberto Eco,
+<a href="https://en.wikipedia.org/wiki/The_Island_of_the_Day_Before"><em>The
+Island of the Day Before</em></a>
+(<em>L'isola del giorno prima</em>), 1994.
+"...the story of a 17th century Italian nobleman trapped near an island
+on the International Date Line. Time and time zones play an integral
+part in the novel." (Paul Eggert, 2006-04-22)
+</li>
+<li>
+John Dunning, <a
+href="https://www.simonandschuster.com/books/Two-OClock-Eastern-Wartime/John-Dunning/9781439171530"><em>Two
+O'Clock, Eastern Wartime</em></a>, 2001.
+Mystery, history, daylight saving time, and old-time radio.
+</li>
+<li>
+Surrealist artist Guy Billout's work "Date Line" appeared on page 103
+of the 1999-11 <em>Atlantic Monthly</em>.
+</li>
+<li>
+"Gloom, Gloom, Go Away" by Walter Kirn appeared on page 106 of <em>Time</em>
+magazine's 2002-11-11 issue; among other things, it proposed
+year-round <abbr>DST</abbr> as a way of lessening wintertime despair.
+</li>
+</ul>
+<h2>Music</h2>
+<p>
+Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:</p>
+<table>
+<tr><td>Artist</td><td>Karrin Allyson</td></tr>
+<tr><td>CD</td><td>I Didn't Know About You</td></tr>
+<tr><td>Copyright Date</td><td>1993</td></tr>
+<tr><td>Label</td><td>Concord Jazz, Inc.</td></tr>
+<tr><td>ID</td><td>CCD-4543</td></tr>
+<tr><td>Track Time</td><td>3:44</td></tr>
+<tr><td>Personnel</td><td>Karrin Allyson, vocal;
+Russ Long, piano;
+Gerald Spaits, bass;
+Todd Strait, drums</td></tr>
+<tr><td>Notes</td><td>CD notes "additional lyric by Karrin Allyson;
+arranged by Russ Long and Karrin Allyson"</td></tr>
+<tr><td>ADO Rating</td><td>1 star</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/i-didnt-know-about-you-mw0000618657">AMG Rating</a></td><td>4 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3.5 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Kevin Mahogany</td></tr>
+<tr><td>CD</td><td>Double Rainbow</td></tr>
+<tr><td>Copyright Date</td><td>1993</td></tr>
+<tr><td>Label</td><td>Enja Records</td></tr>
+<tr><td>ID</td><td>ENJ-7097 2</td></tr>
+<tr><td>Track Time</td><td>6:27</td></tr>
+<tr><td>Personnel</td><td>Kevin Mahogany, vocal;
+Kenny Barron, piano;
+Ray Drummond, bass;
+Ralph Moore, tenor saxophone;
+Lewis Nash, drums</td></tr>
+<tr><td>ADO Rating</td><td>1.5 stars</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/double-rainbow-mw0000620371">AMG Rating</a></td><td>3 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Joe Williams</td></tr>
+<tr><td>CD</td><td>Here's to Life</td></tr>
+<tr><td>Copyright Date</td><td>1994</td></tr>
+<tr><td>Label</td><td>Telarc International Corporation</td></tr>
+<tr><td>ID</td><td>CD-83357</td></tr>
+<tr><td>Track Time</td><td>3:58</td></tr>
+<tr><td>Personnel</td><td>Joe Williams, vocal
+The Robert Farnon [39 piece] Orchestra</td></tr>
+<tr><td>Notes</td><td>This CD is also available as part of a 3-CD package from
+Telarc, "Triple Play" (CD-83461)</td></tr>
+<tr><td>ADO Rating</td><td>black dot</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/heres-to-life-mw0000623648">AMG Rating</a></td><td>2 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Charles Fambrough</td></tr>
+<tr><td>CD</td><td>Keeper of the Spirit</td></tr>
+<tr><td>Copyright Date</td><td>1995</td></tr>
+<tr><td>Label</td><td>AudioQuest Music</td></tr>
+<tr><td>ID</td><td>AQ-CD1033</td></tr>
+<tr><td>Track Time</td><td>7:07</td></tr>
+<tr><td>Personnel</td><td>Charles Fambrough, bass;
+Joel Levine, tenor recorder;
+Edward Simon, piano;
+Lenny White, drums;
+Marion Simon, percussion</td></tr>
+<tr><td>ADO Rating</td><td>2 stars</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/keeper-of-the-spirit-mw0000176559">AMG Rating</a></td><td>unrated</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+</table>
+<hr>
+<p>Also of note:</p>
+<table>
+<tr><td>Artist</td><td>Holly Cole Trio</td></tr>
+<tr><td>CD</td><td>Blame It On My Youth</td></tr>
+<tr><td>Copyright Date</td><td>1992</td></tr>
+<tr><td>Label</td><td>Manhattan</td></tr>
+<tr><td>ID</td><td>CDP 7 97349 2</td></tr>
+<tr><td>Total Time</td><td>37:45</td></tr>
+<tr><td>Personnel</td><td>Holly Cole, voice;
+Aaron Davis, piano;
+David Piltch, string bass</td></tr>
+<tr><td>Notes</td><td>Lyrical reference to "Eastern Standard Time" in
+Tom Waits' "Purple Avenue"</td></tr>
+<tr><td>ADO Rating</td><td>2.5 stars</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/blame-it-on-my-youth-mw0000274303">AMG Rating</a></td><td>3 stars</td></tr>
+<tr><td>Penguin Rating</td><td>unrated</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Milt Hinton</td></tr>
+<tr><td>CD</td><td>Old Man Time</td></tr>
+<tr><td>Copyright Date</td><td>1990</td></tr>
+<tr><td>Label</td><td>Chiaroscuro</td></tr>
+<tr><td>ID</td><td>CR(D) 310</td></tr>
+<tr><td>Total Time</td><td>149:38 (two CDs)</td></tr>
+<tr><td>Personnel</td><td>Milt Hinton, bass;
+Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet;
+Al Grey, trombone;
+Eddie Barefield, Joe Camel (Flip Phillips), Buddy Tate,
+clarinet and saxophone;
+John Bunch, Red Richards, Norman Simmons, Derek Smith,
+Ralph Sutton, piano;
+Danny Barker, Al Casey, guitar;
+Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
+drums;
+Lionel Hampton, vibraphone;
+Cab Calloway, Joe Williams, vocal;
+Buck Clayton, arrangements</td></tr>
+<tr><td>Notes</td><td>tunes include Old Man Time, Time After Time,
+Sometimes I'm Happy,
+A Hot Time in the Old Town Tonight,
+Four or Five Times, Now's the Time,
+Time on My Hands, This Time It's Us,
+and Good Time Charlie.
+<a href="http://www.chiaroscurojazz.com/album.php?C=310">Album info</a>
+is available.</td></tr>
+<tr><td>ADO Rating</td><td>3 stars</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/old-man-time-mw0000269353">AMG Rating</a></td><td>4.5 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Alan Broadbent</td></tr>
+<tr><td>CD</td><td>Pacific Standard Time</td></tr>
+<tr><td>Copyright Date</td><td>1995</td></tr>
+<tr><td>Label</td><td>Concord Jazz, Inc.</td></tr>
+<tr><td>ID</td><td>CCD-4664</td></tr>
+<tr><td>Total Time</td><td>62:42</td></tr>
+<tr><td>Personnel</td><td>Alan Broadbent, piano;
+Putter Smith, Bass;
+Frank Gibson, Jr., drums</td></tr>
+<tr><td>Notes</td><td>The CD cover features an analemma for equation-of-time fans</td></tr>
+<tr><td>ADO Rating</td><td>1 star</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/pacific-standard-time-mw0000645433">AMG Rating</a></td><td>4 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3.5 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Anthony Braxton/Richard Teitelbaum</td></tr>
+<tr><td>CD</td><td>Silence/Time Zones</td></tr>
+<tr><td>Copyright Date</td><td>1996</td></tr>
+<tr><td>Label</td><td>Black Lion</td></tr>
+<tr><td>ID</td><td>BLCD 760221</td></tr>
+<tr><td>Total Time</td><td>72:58</td></tr>
+<tr><td>Personnel</td><td>Anthony Braxton, sopranino and alto saxophones,
+contrebasse clarinet, miscellaneous instruments;
+Leo Smith, trumpet and miscellaneous instruments;
+Leroy Jenkins, violin and miscellaneous instruments;
+Richard Teitelbaum, modular moog and micromoog synthesizer</td></tr>
+<tr><td>ADO Rating</td><td>black dot</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/silence-time-zones-mw0000595735">AMG Rating</a></td><td>4 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Charles Gayle</td></tr>
+<tr><td>CD</td><td>Time Zones</td></tr>
+<tr><td>Copyright Date</td><td>2006</td></tr>
+<tr><td>Label</td><td>Tompkins Square</td></tr>
+<tr><td>ID</td><td>TSQ2839</td></tr>
+<tr><td>Total Time</td><td>49:06</td></tr>
+<tr><td>Personnel</td><td>Charles Gayle, piano</td></tr>
+<tr><td>ADO Rating</td><td>1 star</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/time-zones-mw0000349642">AMG Rating</a></td><td>4.5 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>The Get Up Kids</td></tr>
+<tr><td>CD</td><td>Eudora</td></tr>
+<tr><td>Copyright Date</td><td>2001</td></tr>
+<tr><td>Label</td><td>Vagrant</td></tr>
+<tr><td>ID</td><td>357</td></tr>
+<tr><td>Total Time</td><td>65:12</td></tr>
+<tr><td>Notes</td><td>Includes the song "Central Standard Time." Thanks to Colin Bowern for this information.</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/eudora-mw0000592063">AMG Rating</a></td><td>2.5 stars</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+
+<tr><td>Artist</td><td>Coldplay</td></tr>
+<tr><td>Song</td><td>Clocks</td></tr>
+<tr><td>Copyright Date</td><td>2003</td></tr>
+<tr><td>Label</td><td>Capitol Records</td></tr>
+<tr><td>ID</td><td>52608</td></tr>
+<tr><td>Total Time</td><td>4:13</td></tr>
+<tr><td>Notes</td><td>Won the 2004 Record of the Year honor at the
+Grammy Awards. Co-written and performed by Chris Martin,
+great-great-grandson of <abbr>DST</abbr> inventor William Willett.
+The song's first line is "Lights go out and I can't be saved".</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+
+<tr><td>Artist</td><td>Jaime Guevara</td></tr>
+<tr><td>Song</td><td><a
+href="https://www.youtube.com/watch?v=ZfN4Fe_A50U">Qu&eacute;
+hora es</a></td></tr>
+<tr><td>Date</td><td>1993</td></tr>
+<tr><td>Total Time</td><td>3:04</td></tr>
+<tr><td>Notes</td><td>The song protested "Sixto Hour" in Ecuador
+(1992&ndash;3). Its lyrics include "Amanec&iacute;a en mitad de la noche, los
+guaguas iban a clase sin sol" ("It was dawning in the middle of the
+night, the buses went to class without sun").
+<tr><td>&nbsp;</td><td></td></tr>
+
+<tr><td>Artist</td><td>Irving Kahal and Harry Richman</td></tr>
+<tr><td>Song</td><td>There Ought to be a Moonlight Saving Time</td></tr>
+<tr><td>Copyright Date</td><td>1931</td>
+<tr><td>Notes</td><td>This musical standard was a No. 1 hit for Guy Lombardo
+in 1931, and was also performed by Maurice Chevalier, Blossom Dearie
+and many others. The phrase "Moonlight saving time" also appears in
+the 1995 country song "Not Enough Hours in the Night" written by Aaron
+Barker, Kim Williams and Rob Harbin and performed by Doug
+Supernaw.</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+
+<tr><td>Artist</td><td>The Microscopic Septet</td></tr>
+<tr><td>CD</td><td>Lobster Leaps In</td></tr>
+<tr><td>Copyright Date</td><td>2008</td></tr>
+<tr><td>Label</td><td>Cuneiform</td></tr>
+<tr><td>ID</td><td>272</td></tr>
+<tr><td>Total Time</td><td>73:05</td></tr>
+<tr><td>Notes</td><td>Includes the song "Twilight Time Zone."</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/lobster-leaps-in-mw0000794929">AMG Rating</a></td><td>3.5 stars</td></tr>
+<tr><td>ADO Rating</td><td>2 stars</td></tr>
+
+<tr><td>&nbsp;</td><td></td></tr>
+
+<tr><td>Artist</td><td>Bob Dylan</td></tr>
+<tr><td>CD</td><td>The Times They Are a-Changin'</td></tr>
+<tr><td>Copyright Date</td><td>1964</td></tr>
+<tr><td>Label</td><td>Columbia</td></tr>
+<tr><td>ID</td><td>CK-8905</td></tr>
+<tr><td>Total Time</td><td>45:36</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/the-times-they-a-changin-mw0000202344">AMG Rating</a></td><td>4.5 stars</td></tr>
+<tr><td>ADO Rating</td><td>1.5 stars</td></tr>
+<tr><td>Notes<td>The title song is also available on "Bob Dylan's Greatest Hits" and "The Essential Bob Dylan."</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+
+<tr><td>Artist</td><td>Luciana Souza</td></tr>
+<tr><td>CD</td><td>Tide</td></tr>
+<tr><td>Copyright Date</td><td>2009</td></tr>
+<tr><td>Label</td><td>Universal Jazz France</td></tr>
+<tr><td>ID</td><td>B0012688-02</td></tr>
+<tr><td>Total Time</td><td>42:31</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/tide-mw0000815692">AMG Rating</a></td><td>3.5 stars</td></tr>
+<tr><td>ADO Rating</td><td>2.5 stars</td></tr>
+<tr><td>Notes<td>Includes the song "Fire and Wood" with the lyric
+"The clocks were turned back you remember/Think it's still November."
+</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Ken Nordine</td></tr>
+<tr><td>CD</td><td>You're Getting Better: The Word Jazz Dot Masters</td></tr>
+<tr><td>Copyright Date</td><td>2005</td></tr>
+<tr><td>Label</td><td>Geffen</td></tr>
+<tr><td>ID</td><td>B0005171-02</td></tr>
+<tr><td>Total Time</td><td>156:22</td></tr>
+<tr><td>ADO Rating</td><td>1 star</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/youre-getting-better-the-word-jazz-dot-masters-mw0000736197">AMG Rating</a></td><td>4.5 stars</td></tr>
+<tr><td>Notes</td><td>Includes the piece "What Time Is It"
+("He knew what time it was everywhere...that counted").</td></tr>
+<tr><td>&nbsp;</td><td></td></tr>
+<tr><td>Artist</td><td>Chicago</td></tr>
+<tr><td>CD</td><td>Chicago Transit Authority</td></tr>
+<tr><td>Copyright Date</td><td>1969</td></tr>
+<tr><td>Label</td><td>Columbia</td></tr>
+<tr><td>ID</td><td>64409</td></tr>
+<tr><td>Total Time</td><td>1:16:20</td></tr>
+<tr><td><a href="https://www.allmusic.com/album/chicago-transit-authority-mw0000189364">AMG Rating</a></td><td>4 stars</td></tr>
+<tr><td>Notes</td><td>Includes the song "Does Anybody Really Know What Time It Is?"</td></tr>
+</table>
+<h2>Comics</h2>
+<ul>
+<li>
+The webcomic <em>xkcd</em> has the strip
+"<a href="https://xkcd.com/673/">The Sun</a>" (2009-12-09) and the panels
+"<a href="https://xkcd.com/1017/">Backward in Time</a>" (2012-02-14),
+"<a href="https://xkcd.com/1061/">EST</a>" (2012-05-28),
+"<a href="https://xkcd.com/1179/">ISO 8601</a>" (2013-02-27),
+"<a href="https://xkcd.com/1335/">Now</a>" (2014-02-26),
+"<a href="https://xkcd.com/1655/">Doomsday Clock</a>" (2016-03-14),
+"<a href="https://xkcd.com/1799/">Bad Map Projection: Time Zones</a>"
+(2017-02-15),
+"<a href="https://xkcd.com/1883/">Supervillain Plan</a>" (2017-08-30),
+"<a href="https://xkcd.com/2050/">6/6 Time</a>" (2018-09-24),
+and "<a href="https://xkcd.com/2266/">Leap Smearing</a>" (2020-02-10).
+The related book <em>What If?</em> has an entry
+"<a href="https://what-if.xkcd.com/26/">Leap Seconds</a>" (2012-12-31).
+</li>
+<li>
+Pig kills time in <a
+href="https://www.gocomics.com/pearlsbeforeswine/2016/11/06"><em>Pearls
+Before Swine</em> (2016-11-06)</a>.
+</li>
+<li>
+Stonehenge is abandoned in <a
+href="https://www.gocomics.com/nonsequitur/2017/03/12"><em>Non Sequitur</em>
+(2017-03-12)</a>.
+<li>
+The boss freaks out in <a
+href="https://dilbert.com/strip/1998-03-14"><em>Dilbert</em> (1998-03-14)</a>.
+</li>
+<li>
+Peppermint Patty: "What if the world comes to an end tonight, Marcie?"
+<br>
+Marcie: "I promise there'll be a tomorrow, sir ... in fact,
+it's already tomorrow in Australia!"
+<br>
+(Charles M. Schulz, <a href="https://www.gocomics.com/peanuts/1980/06/13"><em>Peanuts</em>, 1980-06-13</a>)
+</li>
+</ul>
+<h2>Jokes</h2>
+<ul>
+<li>
+The idea behind daylight saving time was first proposed as a joke by
+Benjamin Franklin. To enforce it, he suggested, "Every
+morning, as soon as the sun rises, let all the bells in every church
+be set ringing; and if that is not sufficient, let cannon be fired in
+every street, to wake the sluggards effectually, and make them open
+their eyes to see their true interest. All the difficulty will be in
+the first two or three days: after which the reformation will be as
+natural and easy as the present irregularity; for, <em>ce n'est que le
+premier pas qui co&ucirc;te</em>."
+<a href="http://www.webexhibits.org/daylightsaving/franklin3.html">Franklin's
+joke</a> was first published on 1784-04-26 by the
+<em>Journal de Paris</em> as <a
+href="https://en.wikipedia.org/wiki/File:Franklin-Benjamin-Journal-de-Paris-1784.jpg">an
+anonymous letter translated into French</a>.
+</li>
+<li>
+"We've been using the five-cent nickel in this country since 1492.
+Now that's pretty near 100 years, daylight saving."
+(Groucho Marx as Captain Spaulding in <em>Animal Crackers</em>, 1930,
+as noted by Will Fitzgerald)
+</li>
+<li>
+BRADY. ...[Bishop Usher] determined that the Lord began the Creation
+on the 23rd of October in the Year 4,004 B.C. at &ndash; uh, 9 A.M.!
+<br>
+DRUMMOND. That Eastern Standard Time? (<em>Laughter.</em>) Or Rocky Mountain
+Time? (<em>More laughter.</em>) It wasn't daylight-saving time, was it? Because
+the Lord didn't make the sun until the fourth day!
+<br>
+(From the play <em>Inherit the Wind</em> by Jerome Lawrence and Robert E. Lee,
+filmed in 1960 with Spencer Tracy as Drummond and Fredric March as
+Brady, and several other times. Thanks to Mark Brader.)
+</li>
+<li>
+"Good news."
+"What did they do? Extend Daylight Saving Time year round?"
+(Professional tanner George Hamilton, in dialog from a
+May, 1999 episode of the syndicated television series <em>Baywatch</em>)
+</li>
+<li>
+"A fundamental belief held by Americans is that if you are on land, you
+cannot be killed by a fish...So most Americans remain on land, believing
+they're safe. Unfortunately, this belief &ndash; like so many myths, such as that
+there's a reason for 'Daylight Saving Time' &ndash; is false."
+(Dave Barry column, 2000-07-02)
+</li>
+<li>
+"I once had sex for an hour and five minutes, but that was on the day
+when you turn the clocks ahead."
+(Garry Shandling, 52nd Annual Emmys, 2000-09-10)
+</li>
+<li>
+"Would it impress you if I told you I invented Daylight Savings Time?"
+("Sahjhan" to "Lilah" in dialog from the "Loyalty" episode of <em>Angel</em>,
+originally aired 2002-02-25)
+</li>
+<li>
+"I thought you said Tulsa was a three-hour flight."
+"Well, you're forgetting about the time difference."
+("Joey" and "Chandler" in dialog from the episode of <em>Friends</em>
+entitled "The One With Rachel's Phone Number," originally aired 2002-12-05)
+</li>
+<li>
+"Is that a pertinent fact,
+or are you just trying to dazzle me with your command of time zones?"
+(Kelsey Grammer as "Frasier Crane" to "Roz" from the episode of <em>Frasier</em>
+entitled "The Kid," originally aired 1997-11-04)
+</li>
+<li>
+"I put myself and my staff through this crazy, huge ordeal, all because
+I refused to go on at midnight, okay? And so I work, you know, and
+then I get this job at eleven, supposed to be a big deal. Then
+yesterday daylight [saving] time ended. Right now it's basically
+midnight." (Conan O'Brien on the 2010-11-08 premiere of <em>Conan</em>.)
+</li>
+<li>
+"The best method, I told folks, was to hang a large clock high on a
+barn wall where all the cows could see it. If you have Holsteins, you
+will need to use an analog clock." (Jerry Nelson, <a
+href="http://www.agriculture.com/family/farm-humor/how-to-adjust-dairy-cows-to-daylight-savings-time">How
+to adjust dairy cows to daylight saving time</a>", <em>Successful Farming</em>,
+2017-10-09.)
+</li>
+<li>
+"And now, driving to California, I find that I must enter a password
+in order to change the time zone on my laptop clock. Evidently,
+someone is out to mess up my schedule and my clock must be secured."
+(Garrison Keillor,
+"<a href="http://www.garrisonkeillor.com/weve-never-been-here-before/">We've
+never been here before</a>", 2017-08-22)
+</li>
+<li>
+"Well, in my time zone that's all the time I have,
+but maybe in your time zone I haven't finished yet. So stay tuned!"
+(Goldie Hawn, <em>Rowan &amp; Martin's Laugh-In</em> No. 65, 1970-03-09)
+</li>
+</ul>
+<h2>See also</h2>
+<ul>
+<li><a href="tz-link.html">Time Zone and Daylight Saving
+Time Data</a></li>
+</ul>
+<hr>
+<address>
+This web page is in the public domain, so clarified as of
+2009-05-17 by Arthur David Olson.
+<br>
+Please send corrections to this web page to the
+<a href="mailto:tz@iana.org">time zone mailing list</a>.
+</address>
+</body>
+</html>
diff --git a/contrib/tzcode/tz-how-to.html b/contrib/tzcode/tz-how-to.html
new file mode 100644
index 000000000000..e1e28f2e257d
--- /dev/null
+++ b/contrib/tzcode/tz-how-to.html
@@ -0,0 +1,719 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>How to Read the tz Database</title>
+<meta charset="UTF-8">
+<style>
+pre {margin-left: 2em; white-space: pre-wrap;}
+pre.td {margin-left: 0;}
+td {text-align: center;}
+table {border: 1px outset;}
+th, td {border: 1px inset;}
+table.rule {border: none; margin: auto;}
+td.footnote {text-align: left;}
+</style>
+</head>
+<body>
+<h2>How to Read the <a href="https://en.wikipedia.org/wiki/Tz_database">tz
+Database</a> Source Files</h2>
+<h3>by Bill Seymour</h3>
+<p>This guide uses the <code>America/Chicago</code> and
+<code>Pacific/Honolulu</code> zones as examples of how to infer
+times of day from the <a href="tz-link.html">tz database</a>
+source files. It might be helpful, but not absolutely necessary,
+for the reader to have already downloaded the
+latest release of the database and become familiar with the basic layout
+of the data files. The format is explained in the &ldquo;man
+page&rdquo; for the zic compiler, <code>zic.8.txt</code>, in
+the <code>code</code> subdirectory.
+Although this guide covers many of the common cases, it is not a
+complete summary of what zic accepts; the man page is the
+authoritative reference.</p>
+
+<p>We&rsquo;ll begin by talking about the rules for changing between standard
+and daylight saving time since we&rsquo;ll need that information when we talk
+about the zones.</p>
+
+<p>First, let&rsquo;s consider the special daylight saving time rules
+for Chicago (from the <code>northamerica</code> file in
+the <code>data</code> subdirectory):</p>
+
+<table>
+<tr>
+ <th colspan="6">From the Source File</th>
+</tr>
+<tr>
+ <td colspan="6">
+ <table class="rule">
+ <tr><td style="border:none;text-align:left">
+<pre class="td">
+#Rule NAME FROM TO - IN ON AT SAVE LETTER
+Rule Chicago 1920 only - Jun 13 2:00 1:00 D
+Rule Chicago 1920 1921 - Oct lastSun 2:00 0 S
+Rule Chicago 1921 only - Mar lastSun 2:00 1:00 D
+Rule Chicago 1922 1966 - Apr lastSun 2:00 1:00 D
+Rule Chicago 1922 1954 - Sep lastSun 2:00 0 S
+Rule Chicago 1955 1966 - Oct lastSun 2:00 0 S
+</pre>
+ </td></tr></table></td>
+</tr>
+<tr>
+ <th colspan="6">Reformatted a Bit</th>
+</tr>
+<tr>
+ <th>From</th>
+ <th>To</th>
+ <th colspan="2">On</th>
+ <th>At</th>
+ <th>Action</th>
+</tr>
+<tr>
+ <td colspan="2">1920 only</td>
+ <td colspan="2">June 13<small><sup>th</sup></small></td>
+ <td rowspan="6">02:00 local</td>
+ <td>go to daylight saving time</td>
+</tr>
+<tr>
+ <td>1920</td>
+ <td>1921</td>
+ <td rowspan="5">last Sunday</td>
+ <td>in October</td>
+ <td>return to standard time</td>
+</tr>
+<tr>
+ <td colspan="2">1921 only</td>
+ <td>in March</td>
+ <td rowspan="2">go to daylight saving time</td>
+</tr>
+<tr>
+ <td rowspan="2">1922</td>
+ <td>1966</td>
+ <td>in April</td>
+</tr>
+<tr>
+ <td>1954</td>
+ <td>in September</td>
+ <td rowspan="2">return to standard time</td>
+</tr>
+<tr>
+ <td>1955</td>
+ <td>1966</td>
+ <td>in October</td>
+</tr>
+</table>
+
+<p>The <code>FROM</code> and <code>TO</code> columns, respectively, specify the
+first and last calendar years defining a contiguous range over which a specific
+Rule line is to apply. The keyword <code>only</code> can be used in the
+<code>TO</code> field to repeat the value of the <code>FROM</code> field in the
+event that a rule should only apply to a single year. Often, the keyword
+<code>max</code> is used to extend a rule&rsquo;s application into the
+indefinite future; it is a platform-agnostic stand-in for the largest
+representable year.
+
+<p>The next column, <code>-</code>, is reserved; for compatibility with earlier
+releases, it always contains a hyphen, which acts as a kind of null value.
+Prior to the 2020b release, it was called the <code>TYPE</code> field, though
+it had not been used in the main data since the 2000e release.
+An obsolescent supplementary file used the
+field as a proof-of-concept to allow <code>zic</code> to apply a given Rule
+line only to certain &ldquo;types&rdquo; of years within the specified range as
+dictated by the output of a separate script, such as: only years which would
+have a US presidential election, or only years which wouldn&rsquo;t.
+
+<p>The <code>SAVE</code> column contains the local (wall clock) offset from
+local standard time.
+This is usually either zero for standard time or one hour for daylight
+saving time; but there&rsquo;s no reason, in principle, why it can&rsquo;t
+take on other values.
+
+<p>The <code>LETTER</code> (sometimes called <code>LETTER/S</code>)
+column can contain a variable
+part of the usual abbreviation of the time zone&rsquo;s name, or it can just
+be a hyphen if there&rsquo;s no variable part. For example, the abbreviation
+used in the central time zone will be either &ldquo;CST&rdquo; or
+&ldquo;CDT&rdquo;. The variable part is &lsquo;S&rsquo; or &lsquo;D&rsquo;;
+and, sure enough, that&rsquo;s just what we find in
+the <code>LETTER</code> column
+in the <code>Chicago</code> rules. More about this when we talk about
+&ldquo;Zone&rdquo; lines.
+
+<p>One important thing to notice is that &ldquo;Rule&rdquo; lines
+want at once to be both <i>transitions</i> and <i>steady states</i>:
+<ul>
+<li>On the one hand, they represent transitions between standard and
+daylight saving time; and any number of Rule lines can be in effect
+during a given period (which will always be a non-empty set of
+contiguous calendar years).</li>
+<li>On the other hand, the <code>SAVE</code> and <code>LETTER</code>
+columns contain state that exists between transitions. More about this
+when we talk about the US rules.</li>
+</ul>
+
+<p>In the example above, the transition to daylight saving time
+happened on the 13<small><sup>th</sup></small> of June in 1920, and on
+the last Sunday in March in 1921; but the return to standard time
+happened on the last Sunday in October in both of those
+years. Similarly, the rule for changing to daylight saving time was
+the same from 1922 to 1966; but the rule for returning to standard
+time changed in 1955. Got it?</p>
+
+<p>OK, now for the somewhat more interesting &ldquo;US&rdquo; rules:</p>
+
+<table>
+<tr>
+ <th colspan="6">From the Source File</th>
+</tr>
+<tr>
+ <td colspan="6">
+ <table class="rule">
+ <tr><td style="border:none;text-align:left">
+<pre class="td">
+#Rule NAME FROM TO - IN ON AT SAVE LETTER/S
+Rule US 1918 1919 - Mar lastSun 2:00 1:00 D
+Rule US 1918 1919 - Oct lastSun 2:00 0 S
+Rule US 1942 only - Feb 9 2:00 1:00 W # War
+Rule US 1945 only - Aug 14 23:00u 1:00 P # Peace
+Rule US 1945 only - Sep 30 2:00 0 S
+Rule US 1967 2006 - Oct lastSun 2:00 0 S
+Rule US 1967 1973 - Apr lastSun 2:00 1:00 D
+Rule US 1974 only - Jan 6 2:00 1:00 D
+Rule US 1975 only - Feb 23 2:00 1:00 D
+Rule US 1976 1986 - Apr lastSun 2:00 1:00 D
+Rule US 1987 2006 - Apr Sun&gt;=1 2:00 1:00 D
+Rule US 2007 max - Mar Sun&gt;=8 2:00 1:00 D
+Rule US 2007 max - Nov Sun&gt;=1 2:00 0 S
+</pre>
+ </td></tr></table></td>
+</tr>
+<tr>
+ <th colspan="6">Reformatted a Bit</th>
+</tr>
+<tr>
+ <th>From</th>
+ <th>To</th>
+ <th colspan="2">On</th>
+ <th>At</th>
+ <th>Action</th>
+</tr>
+<tr>
+ <td rowspan="2">1918</td>
+ <td rowspan="2">1919</td>
+ <td rowspan="2">last Sunday</td>
+ <td>in March</td>
+ <td rowspan="3">02:00 local</td>
+ <td>go to daylight saving time</td>
+</tr>
+<tr>
+ <td>in October</td>
+ <td>return to standard time</td>
+</tr>
+<tr>
+ <td colspan="2">1942 only</td>
+ <td colspan="2">February 9<small><sup>th</sup></small></td>
+ <td>go to &ldquo;war time&rdquo;</td>
+</tr>
+<tr>
+ <td colspan="2" rowspan="2">1945 only</td>
+ <td colspan="2">August 14<small><sup>th</sup></small></td>
+ <td>23:00 <a href="https://en.wikipedia.org/wiki/Universal_Time">UT</a></td>
+ <td>
+ rename &ldquo;war time&rdquo; to &ldquo;peace<br>time;&rdquo;
+ clocks don&rsquo;t change
+ </td>
+</tr>
+<tr>
+ <td colspan="2">September 30<small><sup>th</sup></small></td>
+ <td rowspan="9">02:00 local</td>
+ <td rowspan="2">return to standard time</td>
+</tr>
+<tr>
+ <td rowspan="2">1967</td>
+ <td>2006</td>
+ <td rowspan="2">last Sunday</td>
+ <td>in October</td>
+</tr>
+<tr>
+ <td>1973</td>
+ <td>in April</td>
+ <td rowspan="6">go to daylight saving time</td>
+</tr>
+<tr>
+ <td colspan="2">1974 only</td>
+ <td colspan="2">January 6<small><sup>th</sup></small></td>
+</tr>
+<tr>
+ <td colspan="2">1975 only</td>
+ <td colspan="2">February 23<small><sup>rd</sup></small></td>
+</tr>
+<tr>
+ <td>1976</td>
+ <td>1986</td>
+ <td>last Sunday</td>
+ <td rowspan="2">in April</td>
+</tr>
+<tr>
+ <td>1987</td>
+ <td>2006</td>
+ <td>first Sunday</td>
+</tr>
+<tr>
+ <td rowspan="2">2007</td>
+ <td rowspan="2">present</td>
+ <td colspan="2">second Sunday in March</td>
+</tr>
+<tr>
+ <td colspan="2">first Sunday in November</td>
+ <td>return to standard time</td>
+</tr>
+</table>
+
+<p>There are two interesting things to note here.</p>
+
+<p>First, the time that something happens (in the <code>AT</code>
+column) is not necessarily the local (wall clock) time. The time can be
+suffixed with &lsquo;s&rsquo; (for &ldquo;standard&rdquo;) to mean
+local standard time, different from local (wall clock) time when observing
+daylight saving time; or it can be suffixed with &lsquo;g&rsquo;,
+&lsquo;u&rsquo;, or &lsquo;z&rsquo;, all three of which mean the
+standard time at the
+<a href="https://en.wikipedia.org/wiki/Prime_Meridian">prime meridian</a>.
+&lsquo;g&rsquo; stands for &ldquo;<a
+href="https://en.wikipedia.org/wiki/Greenwich_Mean_Time">GMT</a>&rdquo;;
+&lsquo;u&rsquo; stands for &ldquo;<a
+href="https://en.wikipedia.org/wiki/Universal_Time">UT</a>&rdquo; or &ldquo;<a
+href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a>&rdquo;
+(whichever was official at the time); &lsquo;z&rsquo; stands for the
+<a href="https://en.wikipedia.org/wiki/Nautical_time">nautical time zone</a>
+Z (a.k.a. &ldquo;Zulu&rdquo; which, in turn, stands for &lsquo;Z&rsquo;).
+The time can also be suffixed with &lsquo;w&rsquo; meaning local (wall
+clock) time; but it usually isn&rsquo;t because that&rsquo;s the
+default.</p>
+
+<p>Second, the day in the <code>ON</code> column, in addition to
+&ldquo;<code>lastSun</code>&rdquo; or a particular day of the month,
+can have the form, &ldquo;<code>Sun&gt;=</code><i>x</i>&rdquo; or
+&ldquo;<code>Sun&lt;=</code><i>x</i>,&rdquo; where <i>x</i> is a day
+of the month. For example, &ldquo;<code>Sun&gt;=8</code>&rdquo; means
+&ldquo;the first Sunday on or after the eighth of the month,&rdquo; in
+other words, the second Sunday of the month. Furthermore, although
+there are no examples above, the weekday needn&rsquo;t be
+&ldquo;<code>Sun</code>&rdquo; in either form, but can be the usual
+three-character English abbreviation for any day of the week.</p>
+
+<p>And the US rules give us more examples of a couple of things
+already mentioned:</p>
+
+<ul>
+<li>The rules for changing to and from daylight saving time are
+actually <i>different sets</i> of rules; and the two sets can change
+independently. Consider, for example, that the rule for the return to
+standard time stayed the same from 1967 to 2006; but the rule for the
+transition to daylight saving time changed several times in the same
+period. There can also be periods, 1946 to 1966 for example, when no
+rule from this group is in effect, and so either no transition
+happened in those years, or some other rule is in effect (perhaps a
+state or other more local rule).</li>
+
+<li>The <code>SAVE</code> and <code>LETTER</code> columns
+contain <i>steady state</i>, not transitions. Consider, for example,
+the transition from &ldquo;war time&rdquo; to &ldquo;peace time&rdquo;
+that happened on August 14, 1945. The &ldquo;1:00&rdquo; in
+the <code>SAVE</code> column is <i>not</i> an instruction to advance
+the clock an hour. It means that clocks should <i>be</i> one hour
+ahead of standard time, which they already are because of the previous
+rule, so there should be no change.</li>
+
+</ul>
+
+<p>OK, now let&rsquo;s look at a Zone record:</p>
+
+<table>
+<tr>
+ <th colspan="5">From the Source File</th>
+</tr>
+<tr>
+ <td colspan="5">
+ <table class="rule">
+ <tr><td style="border:none;text-align:left">
+<pre class="td">
+#Zone NAME STDOFF RULES FORMAT [UNTIL]
+Zone America/Chicago -5:50:36 - LMT 1883 Nov 18 12:09:24
+ -6:00 US C%sT 1920
+ -6:00 Chicago C%sT 1936 Mar 1 2:00
+ -5:00 - EST 1936 Nov 15 2:00
+ -6:00 Chicago C%sT 1942
+ -6:00 US C%sT 1946
+ -6:00 Chicago C%sT 1967
+ -6:00 US C%sT
+</pre>
+ </td></tr></table></td>
+</tr>
+<tr>
+ <th colspan="5">Columns Renamed</th>
+</tr>
+<tr>
+ <th rowspan="2">Standard Offset<br>
+ from <a href="https://en.wikipedia.org/wiki/Prime_Meridian">Prime
+ Meridian</a></th>
+ <th rowspan="2">Daylight<br>Saving Time</th>
+ <th rowspan="2">Abbreviation(s)</th>
+ <th colspan="2">Ending at Local Time</th>
+</tr>
+<tr>
+ <th>Date</th>
+ <th>Time</th>
+</tr>
+<tr>
+ <td>&minus;5:50:36</td>
+ <td>not observed</td>
+ <td>LMT</td>
+ <td>1883-11-18</td>
+ <td>12:09:24</td>
+</tr>
+<tr>
+ <td rowspan="2">&minus;6:00:00</td>
+ <td>US rules</td>
+ <td rowspan="2">CST or CDT</td>
+ <td>1920-01-01</td>
+ <td>00:00:00</td>
+</tr>
+<tr>
+ <td>Chicago rules</td>
+ <td>1936-03-01</td>
+ <td rowspan="2">02:00:00</td>
+</tr>
+<tr>
+ <td>&minus;5:00:00</td>
+ <td>not observed</td>
+ <td>EST</td>
+ <td>1936-11-15</td>
+</tr>
+<tr>
+ <td rowspan="4">&minus;6:00:00</td>
+ <td>Chicago rules</td>
+ <td>CST or CDT</td>
+ <td>1942-01-01</td>
+ <td rowspan="3">00:00:00</td>
+</tr>
+<tr>
+ <td>US rules</td>
+ <td>CST, CWT or CPT</td>
+ <td>1946-01-01</td>
+</tr>
+<tr>
+ <td>Chicago rules</td>
+ <td rowspan="2">CST or CDT</td>
+ <td>1967-01-01</td>
+</tr>
+<tr>
+ <td>US rules</td>
+ <td colspan="2">&mdash;</td>
+</tr>
+</table>
+
+<p>There are a couple of interesting differences between Zones and Rules.</p>
+
+<p>First, and somewhat trivially, whereas Rules are considered to
+contain one or more records, a Zone is considered to be a single
+record with zero or more <i>continuation lines</i>. Thus, the keyword,
+&ldquo;<code>Zone</code>,&rdquo; and the zone name are not
+repeated. The last line is the one without anything in
+the <code>[UNTIL]</code> column.</p>
+
+<p>Second, and more fundamentally, each line of a Zone represents a
+steady state, not a transition between states. The state exists from
+the date and time in the previous line&rsquo;s <code>[UNTIL]</code>
+column up to the date and time in the current
+line&rsquo;s <code>[UNTIL]</code> column. In other words, the date and
+time in the <code>[UNTIL]</code> column is the instant that separates
+this state from the next. Where that would be ambiguous because
+we&rsquo;re setting our clocks back, the <code>[UNTIL]</code> column
+specifies the first occurrence of the instant. The state specified by
+the last line, the one without anything in the <code>[UNTIL]</code>
+column, continues to the present.</p>
+
+<p>The first line typically specifies the mean solar time observed
+before the introduction of standard time. Since there&rsquo;s no line before
+that, it has no beginning. <code>8-) </code> For some places near the <a
+href="https://en.wikipedia.org/wiki/International_Date_Line">International
+Date Line</a>, the first <i>two</i> lines will show solar times
+differing by 24 hours; this corresponds to a movement of the Date
+Line. For example:</p>
+
+<pre>
+#Zone NAME STDOFF RULES FORMAT [UNTIL]
+Zone America/Juneau 15:02:19 - LMT 1867 Oct 18
+ -8:57:41 - LMT ...
+</pre>
+
+<p>When Alaska was purchased from Russia in 1867, the Date Line moved
+from the Alaska/Canada border to the Bering Strait; and the time in
+Alaska was then 24 hours earlier than it had
+been. <code>&lt;aside&gt;</code>(6 October in the Julian calendar,
+which Russia was still using then for religious reasons, was followed
+by <i>a second instance of the same day with a different name</i>, 18
+October in the Gregorian calendar. Isn&rsquo;t civil time
+wonderful? <code>8-)</code>)<code>&lt;/aside&gt;</code></p>
+
+<p>The abbreviation, &ldquo;LMT&rdquo; stands for &ldquo;local mean
+time&rdquo;, which is an invention of
+the <a href="https://en.wikipedia.org/wiki/Tz_database">tz
+database</a> and was probably never actually used during the
+period. Furthermore, the value is almost certainly wrong except in the
+archetypal place after which the zone is named. (The tz database
+usually doesn&rsquo;t provide a separate Zone record for places where
+nothing significant happened after 1970.)</p>
+
+<p>The <code>RULES</code> column tells us whether daylight saving time is being observed:
+<ul>
+<li>A hyphen, a kind of null value, means that we have not set our
+clocks ahead of standard time.</li>
+
+<li>An amount of time (usually but not necessarily &ldquo;1:00&rdquo;
+meaning one hour) means that we have set our clocks ahead by that
+amount.</li>
+
+<li>Some alphabetic string means that we <i>might have</i> set our
+clocks ahead; and we need to check the rule the name of which is the
+given alphabetic string.</li>
+</ul>
+
+<p>An example of a specific amount of time is:</p>
+<pre>
+#Zone NAME STDOFF RULES FORMAT [UNTIL]
+Zone Pacific/Honolulu ... 1933 Apr 30 2:00
+ -10:30 1:00 HDT 1933 May 21 12:00
+ ...
+</pre>
+
+<p>Hawaii tried daylight saving time for three weeks in 1933 and
+decided they didn&rsquo;t like it. <code>8-) </code>Note that
+the <code>STDOFF</code> column always contains the standard time
+offset, so the local (wall clock) time during this period was GMT &minus;
+10:30 + 1:00 = GMT &minus; 9:30.</p>
+
+<p>The <code>FORMAT</code> column specifies the usual abbreviation of
+the time zone name. It should have one of four forms:</p>
+<ul>
+
+<li>a time zone abbreviation that is a string of three or more
+characters that are either ASCII alphanumerics,
+&ldquo;<code>+</code>&rdquo;, or &ldquo;<code>-</code>&rdquo;</li>
+
+<li>the string &ldquo;%z&rdquo;, in which case the
+&ldquo;<code>%z</code>&rdquo; will be replaced by a numeric time zone
+abbreviation</li>
+
+<li>a pair of time zone abbreviations separated by a slash
+(&lsquo;<code>/</code>&rsquo;), in which case the first string is the
+abbreviation for the standard time name and the second string is the
+abbreviation for the daylight saving time name</li>
+
+<li>a string containing &ldquo;<code>%s</code>&rdquo;, in which case
+the &ldquo;<code>%s</code>&rdquo; will be replaced by the text in the
+appropriate Rule&rsquo;s <code>LETTER</code> column, and the resulting
+string should be a time zone abbreviation</li>
+</ul>
+
+<p>The last two make sense only if there&rsquo;s a named rule in effect.</p>
+
+<p>An example of a slash is:</p>
+<pre>
+#Zone NAME STDOFF RULES FORMAT [UNTIL]
+Zone Europe/London ... 1996
+ 0:00 EU GMT/BST
+</pre>
+
+<p>The current time in the UK is called either Greenwich mean time or
+British summer time.</p>
+
+<p>One wrinkle, not fully explained in <code>zic.8.txt</code>, is what
+happens when switching to a named rule. To what values should
+the <code>SAVE</code> and <code>LETTER</code> data be initialized?</p>
+
+<ul>
+<li>If at least one transition has happened, use
+the <code>SAVE</code> and <code>LETTER</code> data from the most
+recent.</li>
+
+<li>If switching to a named rule before any transition has happened,
+assume standard time (<code>SAVE</code> zero), and use
+the <code>LETTER</code> data from the earliest transition with
+a <code>SAVE</code> of zero.
+
+</ul>
+
+<p>And three last things about the <code>FORMAT</code> column:</p>
+<ul>
+
+<li>The <a href="https://en.wikipedia.org/wiki/Tz_database">tz
+database</a> gives abbreviations for time zones in <i>popular
+usage</i>, which is not necessarily &ldquo;correct&rdquo; by law. For
+example, the last line in
+<code>Zone</code> <code>Pacific/Honolulu</code> (shown below) gives
+&ldquo;HST&rdquo; for &ldquo;Hawaii standard time&rdquo; even though the
+<a href="https://www.law.cornell.edu/uscode/text/15/263">legal</a>
+name for that time zone is &ldquo;Hawaii-Aleutian standard time.&rdquo;
+This author has read that there are also some places in Australia where
+popular time zone names differ from the legal ones.
+
+<li>No attempt is made to <a
+href="https://en.wikipedia.org/wiki/Internationalization_and_localization">localize</a>
+the abbreviations. They are intended to be the values returned through the
+<code>"%Z"</code> format specifier to
+<a href="https://en.wikipedia.org/wiki/C_(programming_language)">C</a>&rsquo;s
+<a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html"><code>strftime</code></a>
+function in the
+<a href="https://kirste.userpage.fu-berlin.de/chemnet/use/info/libc/libc_19.html#SEC324">&ldquo;C&rdquo; locale</a>.
+
+<li>If there is no generally-accepted abbreviation for a time zone,
+a numeric offset is used instead, e.g., <code>+07</code> for 7 hours
+ahead of Greenwich. By convention, <code>-00</code> is used in a
+zone while uninhabited, where the offset is zero but in some sense
+the true offset is undefined.
+</ul>
+
+<p>As a final example, here&rsquo;s the complete history for Hawaii:</p>
+
+<table>
+<tr>
+ <th colspan="6">Relevant Excerpts from the US Rules</th>
+</tr>
+<tr>
+ <td colspan="6">
+ <table class="rule">
+ <tr><td style="border:none;text-align:left">
+<pre class="td">
+#Rule NAME FROM TO - IN ON AT SAVE LETTER/S
+Rule US 1918 1919 - Oct lastSun 2:00 0 S
+Rule US 1942 only - Feb 9 2:00 1:00 W # War
+Rule US 1945 only - Aug 14 23:00u 1:00 P # Peace
+Rule US 1945 only - Sep lastSun 2:00 0 S
+</pre>
+ </td></tr></table></td>
+</tr>
+<tr>
+ <th colspan="6">The Zone Record</th>
+</tr>
+<tr>
+ <td colspan="6">
+ <table class="rule">
+ <tr><td style="border:none;text-align:left">
+<pre class="td">
+#Zone NAME STDOFF RULES FORMAT [UNTIL]
+Zone Pacific/Honolulu -10:31:26 - LMT 1896 Jan 13 12:00
+ -10:30 - HST 1933 Apr 30 2:00
+ -10:30 1:00 HDT 1933 May 21 2:00
+ -10:30 US H%sT 1947 Jun 8 2:00
+ -10:00 - HST
+</pre>
+ </td></tr></table></td>
+</tr>
+<tr>
+ <th colspan="6">What We Infer</th>
+</tr>
+<tr>
+ <th rowspan="2">Wall-Clock<br>Offset from<br>Prime Meridian</th>
+ <th rowspan="2">Adjust<br>Clocks</th>
+ <th colspan="2">Time Zone</th>
+ <th colspan="2">Ending at Local Time</th>
+</tr>
+<tr>
+ <th>Abbrv.</th>
+ <th>Name</th>
+ <th>Date</th>
+ <th>Time</th>
+</tr>
+<tr>
+ <td>&minus;10:31:26</td>
+ <td>&mdash;</td>
+ <td>LMT</td>
+ <td>local mean time</td>
+ <td>1896-01-13</td>
+ <td>12:00</td>
+</tr>
+<tr>
+ <td>&minus;10:30</td>
+ <td>+0:01:26</td>
+ <td>HST</td>
+ <td>Hawaii standard time</td>
+ <td>1933-04-30</td>
+ <td>02:00</td>
+</tr>
+<tr>
+ <td>&minus;9:30</td>
+ <td>+1:00</td>
+ <td>HDT</td>
+ <td>Hawaii daylight time</td>
+ <td>1933-05-21</td>
+ <td>12:00</td>
+</tr>
+<tr>
+ <td>&minus;10:30&sup1;</td>
+ <td>&minus;1:00&sup1;</td>
+ <td>HST&sup1;</td>
+ <td>Hawaii standard time</td>
+ <td>1942-02-09</td>
+ <td>02:00</td>
+</tr>
+<tr>
+ <td rowspan="2">&minus;9:30</td>
+ <td>+1:00</td>
+ <td>HWT</td>
+ <td>Hawaii war time</td>
+ <td>1945-08-14</td>
+ <td>13:30&sup2;</td>
+</tr>
+<tr>
+ <td>0</td>
+ <td>HPT</td>
+ <td>Hawaii peace time</td>
+ <td>1945-09-30</td>
+ <td rowspan="2">02:00</td>
+</tr>
+<tr>
+ <td>&minus;10:30</td>
+ <td>&minus;1:00</td>
+ <td rowspan="2">HST</td>
+ <td rowspan="2">Hawaii standard time</td>
+ <td>1947-06-08</td>
+</tr>
+<tr>
+ <td>&minus;10:00&sup3;</td>
+ <td>+0:30&sup3;</td>
+ <td colspan="2">&mdash;</td>
+</tr>
+<tr>
+ <td colspan="6" class="footnote">
+ &sup1;Switching to US rules&hellip;most recent transition (in 1919) was to standard time
+ </td>
+</tr>
+<tr>
+ <td colspan="6" class="footnote">
+ &sup2;23:00 <a href="https://en.wikipedia.org/wiki/Universal_Time">UT</a>
+ + (&minus;9:30) = 13:30 local
+ </td>
+</tr>
+<tr>
+ <td colspan="6" class="footnote">
+ &sup3;Since <a href="https://en.wikipedia.org/wiki/ISO_8601">1947&ndash;06&ndash;08T12:30Z</a>,
+ the civil time in Hawaii has been
+ <a href="https://en.wikipedia.org/wiki/Universal_Time">UT</a>/<a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a>
+ &minus; 10:00 year-round.
+ </td>
+</tr>
+</table>
+
+<p>There will be a short quiz later. <code>8-)</code></p>
+
+<hr>
+<address>
+This web page is in the public domain, so clarified as of
+2015-10-20 by Bill Seymour.
+<br>
+All suggestions and corrections will be welcome; all flames will be amusing.
+Mail to was at pobox dot com.
+</address>
+</body>
+</html>
diff --git a/contrib/tzcode/tz-link.html b/contrib/tzcode/tz-link.html
new file mode 100644
index 000000000000..d3b37664c71c
--- /dev/null
+++ b/contrib/tzcode/tz-link.html
@@ -0,0 +1,1175 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>Time zone and daylight saving time data</title>
+<meta charset="UTF-8">
+<style>
+pre {margin-left: 2em; white-space: pre-wrap;}
+</style>
+</head>
+<body>
+<h1>Time zone and daylight saving time data</h1>
+<p>
+<a href="https://en.wikipedia.org/wiki/Time_zone">Time zone</a> and
+<a href="https://en.wikipedia.org/wiki/Daylight_saving_time">daylight-saving</a>
+rules are controlled by individual
+governments. They are sometimes changed with little notice, and their
+histories and planned futures are often recorded only fitfully. Here
+is a summary of attempts to organize and record relevant data in this
+area.
+</p>
+ <h3>Outline</h3>
+ <nav>
+ <ul>
+ <li>The <code><abbr>tz</abbr></code> database product and process
+ <ul>
+ <li><a href="#tzdb">The <code><abbr>tz</abbr></code> database</a></li>
+ <li><a href="#download">Downloading the <code><abbr>tz</abbr></code> database</a></li>
+ <li><a href="#changes">Changes to the <code><abbr>tz</abbr></code> database</a></li>
+ <li><a href="#commentary">Commentary on the <code><abbr>tz</abbr></code> database</a></li>
+ </ul>
+ </li>
+ <li>Uses of the <code><abbr>tz</abbr></code> database
+ <ul>
+ <li><a href="#web">Web sites using recent versions of the <code><abbr>tz</abbr></code> database</a></li>
+ <li><a href="#protocols">Network protocols for <code><abbr>tz</abbr></code> data</a></li>
+ <li><a href="#compilers">Other <code><abbr>tz</abbr></code> compilers</a></li>
+ <li><a href="#TZif">Other <abbr>TZif</abbr> readers</a></li>
+ <li><a href="#software">Other <code><abbr>tz</abbr></code>-based time zone software</a></li>
+ </ul>
+ </li>
+ <li>Related data
+ <ul>
+ <li><a href="#other-dbs">Other time zone databases</a></li>
+ <li><a href="#maps">Maps</a></li>
+ <li><a href="#boundaries">Time zone boundaries</a></li>
+ </ul>
+ </li>
+ <li>Timekeeping concepts
+ <ul>
+ <li><a href="#civil">Civil time concepts and history</a></li>
+ <li><a href="#national">National histories of legal time</a></li>
+ <li><a href="#costs">Costs and benefits of time shifts</a></li>
+ <li><a href="#precision">Precision timekeeping</a></li>
+ <li><a href="#notation">Time notation</a></li>
+ </ul>
+ </li>
+ <li><a href="#see-also">See also</a></li>
+ </ul>
+ </nav>
+
+<section>
+<h2 id="tzdb">The <code><abbr title="time zone">tz</abbr></code> database</h2>
+<p>
+The <a href="https://en.wikipedia.org/wiki/Public_domain">public-domain</a>
+time zone database contains code and data
+that represent the history of local time
+for many representative locations around the globe.
+It is updated periodically to reflect changes made by political bodies
+to time zone boundaries and daylight saving rules.
+This database (known as <code><abbr>tz</abbr></code>,
+<code><abbr>tzdb</abbr></code>, or <code>zoneinfo</code>)
+is used by several implementations,
+including
+<a href="https://www.gnu.org/software/libc/">the
+<abbr title="GNU's Not Unix">GNU</abbr>
+C Library</a> (used in
+<a href="https://en.wikipedia.org/wiki/Linux"><abbr>GNU</abbr>/Linux</a>),
+<a href="https://www.android.com">Android</a>,
+<a href="https://www.freebsd.org">Free<abbr
+title="Berkeley Software Distribution">BSD</abbr></a>,
+<a href="https://netbsd.org">Net<abbr>BSD</abbr></a>,
+<a href="https://www.openbsd.org">Open<abbr>BSD</abbr></a>,
+<a href="https://www.chromium.org/chromium-os/">Chromium OS</a>,
+<a href="https://cygwin.com">Cygwin</a>,
+<a href="https://mariadb.org">MariaDB</a>,
+<a href="https://en.wikipedia.org/wiki/MINIX">MINIX</a>,
+<a href="https://www.mysql.com">MySQL</a>,
+<a href="https://en.wikipedia.org/wiki/WebOS"><abbr
+title="Web Operating System">webOS</abbr></a>,
+<a href="https://en.wikipedia.org/wiki/IBM_AIX"><abbr
+title="Advanced Interactive eXecutive">AIX</abbr></a>,
+<a href="https://www.apple.com/ios"><abbr
+title="iPhone OS">iOS</abbr></a>,
+<a href="https://www.apple.com/macos">macOS</a>,
+<a href="https://www.microsoft.com/en-us/windows">Microsoft Windows</a>,
+<a href="https://www.vmssoftware.com">Open<abbr
+title="Virtual Memory System">VMS</abbr></a>,
+<a href="https://www.oracle.com/database/">Oracle Database</a>, and
+<a href="https://www.oracle.com/solaris">Oracle Solaris</a>.</p>
+<p>
+Each main entry in the database represents a <dfn>timezone</dfn>
+for a set of civil-time clocks that have all agreed since 1970.
+Timezones are typically identified by continent or ocean and then by the
+name of the largest city within the region containing the clocks.
+For example, <code>America/New_York</code>
+represents most of the <abbr title="United States">US</abbr> eastern time zone;
+<code>America/Phoenix</code> represents most of Arizona, which
+uses mountain time without daylight saving time (<abbr>DST</abbr>);
+<code>America/Detroit</code> represents most of Michigan, which uses
+eastern time but with different <abbr>DST</abbr> rules in 1975;
+and other entries represent smaller regions like Starke County,
+Indiana, which switched from central to eastern time in 1991
+and switched back in 2006.
+To use the database on an extended <a
+href="https://en.wikipedia.org/wiki/POSIX"><abbr
+title="Portable Operating System Interface">POSIX</abbr></a>
+implementation set the <code><abbr>TZ</abbr></code>
+environment variable to the location's full name,
+e.g., <code><abbr>TZ</abbr>="America/New_York"</code>.</p>
+<p>
+Associated with each timezone is a history of offsets from
+<a href="https://en.wikipedia.org/wiki/Universal_Time">Universal
+Time</a> (<abbr>UT</abbr>), which is <a
+href="https://en.wikipedia.org/wiki/Greenwich_Mean_Time">Greenwich Mean
+Time</a> (<abbr>GMT</abbr>) with days beginning at midnight;
+for timestamps after 1960 this is more precisely <a
+href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">Coordinated
+Universal Time</a> (<abbr>UTC</abbr>).
+The database also records when daylight saving time was in use,
+along with some time zone abbreviations such as <abbr>EST</abbr>
+for Eastern Standard Time in the <abbr>US</abbr>.</p>
+</section>
+
+<section>
+<h2 id="download">Downloading the <code><abbr>tz</abbr></code> database</h2>
+<p>
+The following <a
+href="https://en.wikipedia.org/wiki/Unix_shell">shell</a> commands download
+the latest release's two
+<a href="https://en.wikipedia.org/wiki/Tar_(computing)">tarballs</a>
+to a <abbr>GNU</abbr>/Linux or similar host.</p>
+<pre><code>mkdir tzdb
+cd tzdb
+<a href="https://www.gnu.org/software/wget/">wget</a> https://www.iana.org/time-zones/repository/tzcode-latest.tar.gz
+wget https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz
+<a href="https://www.gnu.org/software/gzip/">gzip</a> -dc tzcode-latest.tar.gz | <a href="https://www.gnu.org/software/tar/">tar</a> -xf -
+gzip -dc tzdata-latest.tar.gz | tar -xf -
+</code></pre>
+<p>Alternatively, the following shell commands download the same
+release in a single-tarball format containing extra data
+useful for regression testing:</p>
+<pre><code>wget <a href="https://www.iana.org/time-zones/repository/tzdb-latest.tar.lz">https://www.iana.org/time-zones/repository/tzdb-latest.tar.lz</a>
+<a href="https://www.nongnu.org/lzip/">lzip</a> -dc tzdb-latest.tar.lz | tar -xf -
+</code></pre>
+<p>These commands use convenience links to the latest release
+of the <code><abbr>tz</abbr></code> database hosted by the
+<a href="https://www.iana.org/time-zones">Time Zone Database website</a>
+of the <a href="https://www.iana.org">Internet Assigned Numbers
+Authority (IANA)</a>.
+Older releases are in files named
+<code>tzcode<var>V</var>.tar.gz</code>,
+<code>tzdata<var>V</var>.tar.gz</code>, and
+<code>tzdb-<var>V</var>.tar.lz</code>,
+where <code><var>V</var></code> is the version.
+Since 1996, each version has been a four-digit year followed by
+lower-case letter (<samp>a</samp> through <samp>z</samp>,
+then <samp>za</samp> through <samp>zz</samp>, then <samp>zza</samp>
+through <samp>zzz</samp>, and so on).
+Since version 2022a, each release has been distributed in
+<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06">POSIX
+ustar interchange format</a>, compressed as described above;
+older releases use a nearly-compatible format.
+Since version 2016h, each release has contained a text file named
+"<samp>version</samp>" whose first (and currently only) line is the version.
+Older releases are <a href="https://ftp.iana.org/tz/releases/">archived</a>,
+and are also available in an
+<a href="ftp://ftp.iana.org/tz/releases/"><abbr
+title="File Transfer Protocol">FTP</abbr> directory</a> via a
+less-secure protocol.</p>
+<p>Alternatively, a development repository of code and data can be
+retrieved from <a href="https://github.com">GitHub</a> via the shell
+command:</p>
+<pre><code><a href="https://git-scm.com">git</a> clone <a href="https://github.com/eggert/tz">https://github.com/eggert/tz</a>
+</code></pre>
+<p>
+Since version 2012e, each release has been tagged in development repositories.
+Untagged commits are less well tested and probably contain
+more errors.</p>
+<p>
+After obtaining the code and data files, see the
+<code>README</code> file for what to do next.
+The code lets you compile the <code><abbr>tz</abbr></code> source files into
+machine-readable binary files, one for each location. The binary files
+are in a special timezone information format (<dfn><abbr>TZif</abbr></dfn>)
+specified by <a href="https://datatracker.ietf.org/doc/html/8536">Internet
+<abbr>RFC</abbr> 8536</a>.
+The code also lets
+you read a <abbr>TZif</abbr> file and interpret timestamps for that
+location.</p>
+</section>
+
+<section>
+<h2 id="changes">Changes to the <code><abbr>tz</abbr></code> database</h2>
+<p>
+The <code><abbr>tz</abbr></code> code and data
+are by no means authoritative. If you find errors, please
+send changes to <a href="mailto:tz@iana.org"><code>tz@iana.org</code></a>,
+the time zone mailing list. You can also <a
+href="https://mm.icann.org/mailman/listinfo/tz">subscribe</a> to it
+and browse the <a
+href="https://mm.icann.org/pipermail/tz/">archive of old
+messages</a>.
+<a href="https://tzdata-meta.timtimeonline.com/">Metadata for mailing list
+discussions</a> and corresponding data changes can be
+generated <a href="https://github.com/timparenti/tzdata-meta">automatically</a>.
+</p>
+<p>
+If your government plans to change its time zone boundaries or
+daylight saving rules, inform <code>tz@iana.org</code> well in
+advance, as this will coordinate updates to many cell phones,
+computers, and other devices around the world.
+The change should be officially announced at least a year before it affects
+how clocks operate; otherwise, there is a good chance that some
+clocks will operate incorrectly after the change, due
+to delays in propagating updates to software and data. The shorter
+the notice, the more likely clock problems will arise; see "<a
+href="https://codeofmatt.com/2016/04/23/on-the-timing-of-time-zone-changes/">On
+the Timing of Time Zone Changes</a>" for examples.
+The <code><abbr>tz</abbr></code> data can represent planned changes
+far into the future, and a long-planned change can easily be reverted
+or otherwise altered with a year's notice before the change would have
+affected clocks.
+</p>
+<p>
+Changes to the <code><abbr>tz</abbr></code> code and data are often
+propagated to clients via operating system updates, so
+client <code><abbr>tz</abbr></code> data can often be corrected by
+applying these updates. With GNU/Linux and similar systems, if your
+maintenance provider has not yet adopted the
+latest <code><abbr>tz</abbr></code> data, you can often short-circuit
+the process by tailoring the generic instructions in
+the <code><abbr>tz</abbr> README</code> file and installing the latest
+data yourself. System-specific instructions for installing the
+latest <code><abbr>tz</abbr></code> data have also been published
+for <a href="https://developer.ibm.com/articles/au-aix-olson-time-zone/"><abbr>AIX</abbr></a>,
+<a
+href="https://source.android.com/devices/tech/config/timezone-rules">Android</a>,
+<a
+href="https://unicode-org.github.io/icu/userguide/datetime/timezone/"><abbr
+title="International Components for Unicode">ICU</abbr></a>,
+<a href="https://www.ibm.com/support/pages/java-sdk-time-zone-update-utility"><abbr>IBM</abbr>
+JDK</a>,
+<a href="https://www.joda.org/joda-time/tz_update.html">Joda-Time</a>, <a
+href="https://dev.mysql.com/doc/refman/en/time-zone-support.html">MySQL</a>,
+<a href="https://nodatime.org/userguide/tzdb">Noda Time</a>, and <a
+href="https://www.oracle.com/java/technologies/javase/tzupdater-readme.html">OpenJDK/Oracle JDK</a>.
+</p>
+<p>Since version 2013a,
+sources for the <code><abbr>tz</abbr></code> database have been
+<a href="https://en.wikipedia.org/wiki/UTF-8"><abbr
+title="Unicode Transformation Format 8-bit">UTF-8</abbr></a>
+<a href="https://en.wikipedia.org/wiki/Text_file">text files</a>
+with lines terminated by <a href="https://en.wikipedia.org/wiki/Newline"><abbr
+title="linefeed">LF</abbr></a>,
+which can be modified by common text editors such
+as <a href="https://www.gnu.org/software/emacs/">GNU Emacs</a>,
+<a href="https://wiki.gnome.org/Apps/Gedit">gedit</a>, and
+<a href="https://www.vim.org">vim</a>.
+Specialized source-file editing can be done via the
+<a href="https://packagecontrol.io/packages/zoneinfo">Sublime
+zoneinfo</a> package for <a
+href="https://www.sublimetext.com">Sublime Text</a> and the <a
+href="https://marketplace.visualstudio.com/items?itemName=gilmoreorless.vscode-zoneinfo">VSCode
+zoneinfo</a> extension for <a href="https://code.visualstudio.com">Visual
+Studio Code</a>.
+</p>
+<p>
+For further information about updates, please see
+<a href="https://datatracker.ietf.org/doc/html/rfc6557">Procedures for
+Maintaining the Time Zone Database</a> (Internet <abbr
+title="Request For Comments">RFC</abbr> 6557). More detail can be
+found in <a href="theory.html">Theory and pragmatics of the
+<code><abbr>tz</abbr></code> code and data</a>.
+<a href="https://a0.github.io/a0-tzmigration/">A0 TimeZone Migration</a>
+displays changes between recent <code><abbr>tzdb</abbr></code> versions.
+</p>
+</section>
+
+<section>
+<h2 id="commentary">Commentary on the <code><abbr>tz</abbr></code> database</h2>
+<ul>
+<li>The article
+<a href="https://en.wikipedia.org/wiki/Tz_database">tz database</a> is
+an encyclopedic summary.</li>
+<li><a href="tz-how-to.html">How to Read the
+tz Database Source Files</a> explains the <code><abbr>tz</abbr></code>
+database format.</li>
+<li><a
+href="https://blog.jonudell.net/2009/10/23/a-literary-appreciation-of-the-olsonzoneinfotz-database/">A
+literary appreciation of the Olson/Zoneinfo/tz database</a> comments on the
+database's style.</li>
+<li><a href="https://doi.org/10.1145/3340301.3341125">What time is it:
+managing time in the internet</a> analyzes the database longitudinally.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="web">Web sites using recent versions of the
+<code><abbr>tz</abbr></code> database</h2>
+<p>
+These are listed roughly in ascending order of complexity and fanciness.
+</p>
+<ul>
+<li><a href="https://time.is">Time.is</a> shows locations'
+time and zones.</li>
+<li><a href="https://www.timejones.com">TimeJones.com</a>,
+<a href="https://timezoneconverterapp.com">Time Zone Converter</a> and
+<a href="https://www.worldclock.com">The World Clock</a>
+are time zone converters.</li>
+<li><a href="https://timezonedb.com/download">TimeZoneDB Database</a>
+publishes <code><abbr>tzdb</abbr></code>-derived data in
+<a href="https://en.wikipedia.org/wiki/Comma-separated_values"><abbr
+title="comma-separated values">CSV</abbr></a> and
+in <a href="https://en.wikipedia.org/wiki/SQL"><abbr
+title="Structured Query Language">SQL</abbr></a> form.</li>
+<li><a
+href="https://twiki.org/cgi-bin/xtra/tzdatepick.html">Date and Time Gateway</a>
+lets you see the <code><abbr>TZ</abbr></code> values directly.</li>
+<li><a
+href="https://www.convertit.com/Go/ConvertIt/World_Time/Current_Time.ASP">Current
+Time in 1000 Places</a> uses descriptions of the values.</li>
+<li><a href="https://home.kpn.nl/vanadovv/time/TZworld.html">Complete
+timezone information for all countries</a>
+displays tables of <abbr>DST</abbr> rules.
+<li><a href="https://www.timeanddate.com/worldclock/">The World Clock &ndash;
+Worldwide</a> lets you sort zone names and convert times.</li>
+<li><a href="https://24timezones.com">24TimeZones</a> has a world
+time map and a time converter.</li>
+<li><a href="https://www.zeitverschiebung.net/en/">Time Difference</a>
+calculates the current time difference between locations.</li>
+<li><a href="https://www.wx-now.com">Weather Now</a> and
+<a href="https://www.thetimenow.com">The Time Now</a> list the weather too.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="protocols">Network protocols for <code><abbr>tz</abbr></code> data</h2>
+<ul>
+<li>The <a href="https://www.ietf.org">Internet Engineering Task Force</a>'s
+<a href="https://datatracker.ietf.org/wg/tzdist/charter/">Time Zone Data
+Distribution Service (tzdist) working group</a> defined <a
+href="https://datatracker.ietf.org/doc/html/rfc7808">TZDIST</a>
+(Internet <abbr>RFC</abbr> 7808), a time zone data distribution service,
+along with <a href="https://datatracker.ietf.org/doc/html/rfc7809">CalDAV</a>
+(Internet <abbr>RFC</abbr> 7809), a calendar access protocol for
+transferring time zone data by reference.
+<a href="https://devguide.calconnect.org/Time-Zones/TZDS/">TZDIST
+implementations</a> are available.
+The <a href="https://www.ietf.org/mailman/listinfo/tzdist-bis">tzdist-bis
+mailing list</a> discusses possible extensions.</li>
+<li>The <a href="https://datatracker.ietf.org/doc/html/rfc5545">
+Internet Calendaring and Scheduling Core Object Specification
+(iCalendar)</a> (Internet <abbr>RFC</abbr> 5445)
+covers time zone
+data; see its VTIMEZONE calendar component.
+The iCalendar format requires specialized parsers and generators; a
+variant <a href="https://datatracker.ietf.org/doc/html/rfc6321">xCal</a>
+(Internet <abbr>RFC</abbr> 6321) uses
+<a href="https://www.w3.org/XML/"><abbr
+title="Extensible Markup Language">XML</abbr></a> format, and a variant
+<a href="https://datatracker.ietf.org/doc/html/rfc7265">jCal</a>
+(Internet <abbr>RFC</abbr> 7265)
+uses <a href="https://www.json.org"><abbr
+title="JavaScript Object Notation">JSON</abbr></a> format.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="compilers">Other <code><abbr>tz</abbr></code> compilers</h2>
+<p>Although some of these do not fully support
+<code><abbr>tz</abbr></code> data, in recent <code><abbr>tzdb</abbr></code>
+distributions you can generally work around compatibility problems by
+running the command <code>make rearguard_tarballs</code> and compiling
+from the resulting tarballs instead.</p>
+<ul>
+<li><a href="https://sourceforge.net/projects/vzic/">Vzic</a> is a <a
+href="https://en.wikipedia.org/wiki/C_%28programming_language%29">C</a>
+program that compiles
+<code><abbr>tz</abbr></code> source into iCalendar-compatible VTIMEZONE files.
+Vzic is freely
+available under the <a
+href="https://www.gnu.org/copyleft/gpl.html"><abbr>GNU</abbr>
+General Public License (<abbr
+title="General Public License">GPL</abbr>)</a>.</li>
+<li><a href="https://sourceforge.net/projects/tzical/">tziCal &ndash; tz
+database conversion utility</a> is like Vzic, except for the <a
+href="https://dotnet.microsoft.com">.NET framework</a>
+and with a <abbr>BSD</abbr>-style license.</li>
+<li><a
+href="https://metacpan.org/release/DateTime-TimeZone">DateTime::TimeZone</a>
+contains a script <code>parse_olson</code> that compiles
+<code><abbr>tz</abbr></code> source into <a href="https://www.perl.org">Perl</a>
+modules. It is part of the Perl <a
+href="https://github.com/houseabsolute/DateTime.pm/wiki">DateTime Project</a>,
+which is freely
+available under both the <abbr>GPL</abbr> and the Perl Artistic
+License. DateTime::TimeZone also contains a script
+<code>tests_from_zdump</code> that generates test cases for each clock
+transition in the <code><abbr>tz</abbr></code> database.</li>
+<li>The <a href="https://howardhinnant.github.io/date/tz.html">Time Zone
+Database Parser</a> is a
+<a href="https://en.wikipedia.org/wiki/C%2B%2B">C++</a> parser and
+runtime library with <a
+href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0355r7.html">API</a>
+adopted by
+<a href="https://en.wikipedia.org/wiki/C++20">C++20</a>,
+the current iteration of the C++ standard.
+It is freely available under the
+<abbr title="Massachusetts Institute of Technology">MIT</abbr> license.</li>
+<li><a id="ICU" href="https://icu.unicode.org">International Components for
+Unicode (<abbr>ICU</abbr>)</a> contains C/C++ and <a
+href="https://en.wikipedia.org/wiki/Java_%28programming_language%29">Java</a>
+libraries for internationalization that
+has a compiler from <code><abbr>tz</abbr></code> source
+and from <abbr title="Common Locale Data Repository">CLDR</abbr> data
+(mentioned <a href="#CLDR">below</a>)
+into an <abbr>ICU</abbr>-specific format.
+<abbr>ICU</abbr> is freely available under a
+<abbr>BSD</abbr>-style license.</li>
+<li>The <a href="https://github.com/lau/tzdata">Tzdata</a> package for
+the <a href="https://elixir-lang.org">Elixir</a> language downloads
+and compiles <code><abbr>tz</abbr></code> source and exposes <abbr
+title="Application Program Interface">API</abbr>s for use. It is
+freely available under the <abbr>MIT</abbr> license.</li>
+<li>Java-based compilers and libraries include:
+<ul>
+<li>The <a
+href="https://www.oracle.com/java/technologies/javase/tzupdater-readme.html">TZUpdater
+tool</a> compiles <code><abbr>tz</abbr></code> source into the format used by
+<a href="https://openjdk.java.net/">OpenJDK</a> and
+<a href="https://jdk.java.net/">Oracle JDK</a>.
+Although its source code is proprietary, its executable is available under the
+<a href="https://www.oracle.com/a/tech/docs/tzupdater-lic.html">Java SE
+Timezone Updater License Agreement</a>.</li>
+<li>The <a
+href="https://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html">Java
+SE 8 Date and Time</a> <abbr>API</abbr> can be supplemented by <a
+href="https://www.threeten.org/threeten-extra/">ThreeTen-Extra</a>,
+which is freely available under a <abbr>BSD</abbr>-style license.</li>
+<li><a href="https://www.joda.org/joda-time/">Joda-Time &ndash; Java date
+and time <abbr>API</abbr></a> contains a class
+<code>org.joda.time.tz.ZoneInfoCompiler</code> that compiles
+<code><abbr>tz</abbr></code> source into a binary format. It inspired
+Java 8 <code>java.time</code>, which its users should migrate to once
+they can assume Java 8 or later. It is available under the <a
+href="https://www.apache.org/licenses/LICENSE-2.0">Apache License</a>.</li>
+<li><a href="https://bell-sw.com/pages/iana-updater/">IANA Updater</a> and <a
+href="https://www.azul.com/products/open-source-tools/ziupdater-time-zone-tool/">ZIUpdater</a>
+are alternatives to TZUpdater. IANA Updater's license is unclear;
+ZIUpdater is licensed under the <abbr>GPL</abbr>.</li>
+<li><a href="https://github.com/MenoData/Time4A">Time4A: Advanced date and
+time library for Android</a> and
+<a href="https://github.com/MenoData/Time4J/">Time4J: Advanced date,
+time and interval library for Java</a> compile
+<code><abbr>tz</abbr></code> source into a binary format.
+Time4A is available under the Apache License and Time4J is
+available under the <a
+href="https://www.gnu.org/copyleft/lesser.html"><abbr>GNU</abbr> Lesser
+General Public License (<abbr title="Lesser General Public
+License">LGPL</abbr>)</a>.</li>
+<li><abbr>ICU</abbr> (mentioned <a href="#ICU">above</a>) contains compilers and
+Java-based libraries.</li>
+</ul>
+<li><a href="https://nodatime.org">Noda Time &ndash; Date and
+time <abbr>API</abbr> for .NET</a>
+is like Joda-Time and Time4J, but for the .NET framework instead of Java.
+It is freely available under the Apache License.</li>
+<li>Many modern
+<a href="https://en.wikipedia.org/wiki/JavaScript">JavaScript</a>
+runtimes support <code><abbr>tz</abbr></code> natively via the
+<samp>timeZone</samp> option of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat"><samp>Intl.DateTimeFormat</samp></a>.
+This can be used as-is or with most of the following libraries,
+many of which also support runtimes lacking the <samp>timeZone</samp> option.
+<ul>
+<li>The <a
+href="https://github.com/formatjs/date-time-format-timezone"><samp>Intl.DateTimeFormat</samp>
+timezone polyfill</a>
+is freely available under a <abbr>BSD</abbr>-style license.</li>
+<li>The <a href="https://date-fns.org/">date-fns</a>
+library manipulates timezone-aware timestamps in browsers and
+in <a href="https://nodejs.org/en/">Node.js</a>.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+<li><a href="https://github.com/iamkun/dayjs">Day.js</a> is a
+minimalist replacement for the date and time API of
+the <a href="https://momentjs.com/docs/">now-legacy Moment.js</a> date
+manipulation library.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+<li><a href="https://moment.github.io/luxon/">Luxon</a> improves
+timezone support for the <samp>Intl</samp> API.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+<li><a href="https://momentjs.com/timezone/">Moment Timezone</a> is a
+Moment.js plugin.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+<li><a href="https://github.com/bigeasy/timezone">Timezone</a> is a
+JavaScript library that supports date arithmetic that is time zone
+aware. It is freely available under the <abbr>MIT</abbr> license.</li>
+<li><a href="https://www.npmjs.com/package/@tubular/time">@tubular/time</a>
+supports live <code><abbr>tzdb</abbr></code> updates,
+astronomical and atomic time, a command-line interface,
+and full <a
+href="https://en.wikipedia.org/wiki/TypeScript">TypeScript</a>.
+Its companion <a
+href="https://www.npmjs.com/package/@tubular/time-tzdb">@tubular/time-tzdb</a>
+can generate <abbr>TZif</abbr> and other files, and a companion website
+<a href="https://tzexplorer.org">Timezone Database Explorer</a> lets you
+convert timestamps, view transition histories, and download code and data.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+</ul>
+The proposed <a
+href="https://github.com/tc39/proposal-temporal"><samp>Temporal</samp>
+objects</a> let programs access an abstract view of
+<code><abbr>tzdb</abbr></code> data, and are designed to replace <a
+href="https://codeofmatt.com/javascript-date-type-is-horribly-broken/">JavaScript's
+problematic <samp>Date</samp> objects</a> when working with dates and times.
+<li><a href="https://github.com/JuliaTime/">JuliaTime</a> contains a
+compiler from <code><abbr>tz</abbr></code> source into
+<a href="https://julialang.org/">Julia</a>. It is freely available
+under the <abbr>MIT</abbr> license.</li>
+<li><a href="https://github.com/pavkam/tzdb"><abbr>TZDB</abbr> &ndash;
+<abbr>IANA</abbr> Time Zone Database for Delphi/<abbr
+title="Free Pascal Compiler">FPC</abbr></a>
+compiles from <code><abbr>tz</abbr></code> source into
+<a href="https://en.wikipedia.org/wiki/Object_Pascal">Object Pascal</a>
+as compiled by <a href="https://en.wikipedia.org/wiki/Delphi_(IDE)">Delphi</a>
+and <a
+href="https://en.wikipedia.org/wiki/Free_Pascal"><abbr>FPC</abbr></a>.
+It is freely available under a <abbr>BSD</abbr>-style license.</li>
+<li><a href="http://pytz.sourceforge.net">pytz &ndash; World Timezone
+Definitions for Python</a> compiles <code><abbr>tz</abbr></code> source into
+<a href="https://www.python.org">Python</a>.
+It is freely available under a <abbr>BSD</abbr>-style license.
+In code that can assume Python 3.6 or later it is largely superseded; see <a
+href="https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html">pytz:
+The Fastest Footgun in the West</a>.</li>
+<li><a href="https://tzinfo.github.io">TZInfo &ndash;
+Ruby Timezone Library</a>
+compiles <code><abbr>tz</abbr></code> source into
+<a href="https://www.ruby-lang.org/en/">Ruby</a>.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+<li>The <a href="https://www.squeaksource.com/Chronos/">Chronos Date/Time
+Library</a> is
+a <a href="https://en.wikipedia.org/wiki/Smalltalk">Smalltalk</a> class
+library that compiles <code><abbr>tz</abbr></code> source into a time
+zone repository whose format
+is either proprietary or an <abbr>XML</abbr>-encoded
+representation.</li>
+<li><a id="Tcl" href="https://tcl.tk">Tcl</a>
+contains a developer-oriented parser that compiles <code><abbr>tz</abbr></code>
+source into text files, along with a runtime that can read those
+files. Tcl is freely available under a <abbr>BSD</abbr>-style
+license.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="TZif">Other <abbr>TZif</abbr> readers</h2>
+<ul>
+<li>The <a
+href="https://www.gnu.org/software/libc/"><abbr>GNU</abbr> C
+Library</a>
+has an independent, thread-safe implementation of
+a <abbr>TZif</abbr> file reader.
+This library is freely available under the LGPL
+and is widely used in <abbr>GNU</abbr>/Linux systems.</li>
+<li><a href="https://www.gnome.org">GNOME</a>'s
+<a href="https://developer.gnome.org/glib/">GLib</a> has
+a <abbr>TZif</abbr> file reader written in C that
+creates a <code>GTimeZone</code> object representing sets
+of <abbr>UT</abbr> offsets.
+It is freely available under the <abbr>LGPL</abbr>.</li>
+<li>The
+<a href="https://github.com/bloomberg/bde/wiki">BDE Standard Library</a>'s
+<code>baltzo::TimeZoneUtil</code> component contains a C++
+implementation of a <abbr>TZif</abbr> file reader. It is freely available under
+the Apache License.</li>
+<li><a href="https://github.com/google/cctz">CCTZ</a> is a simple C++
+library that translates between <abbr>UT</abbr> and civil time and
+can read <abbr>TZif</abbr> files. It is freely available under the Apache
+License.</li>
+<li><a href="https://github.com/derickr/timelib">Timelib</a> is a C
+library that reads <abbr>TZif</abbr> files and converts
+timestamps from one time zone or format to another.
+It is used by <a href="https://secure.php.net"><abbr
+title="PHP: Hypertext Preprocessor">PHP</abbr></a>,
+<a href="https://hhvm.com"><abbr title="HipHop Virtual Machine">HHVM</abbr></a>,
+and <a href="https://www.mongodb.com">MongoDB</a>.
+It is freely available under the <abbr>MIT</abbr> license.</li>
+<li>Tcl, mentioned <a href="#Tcl">above</a>, also contains a
+<abbr>TZif</abbr> file reader.</li>
+<li><a href="https://metacpan.org/pod/DateTime::TimeZone::Tzfile">
+DateTime::TimeZone::Tzfile</a>
+is a <abbr>TZif</abbr> file reader written in Perl.
+It is freely available under the same terms as Perl
+(dual <abbr>GPL</abbr> and Artistic license).</li>
+<li>Python has a <a id="python-zoneinfo"
+href="https://docs.python.org/3/library/zoneinfo.html"><code>zoneinfo.ZoneInfo</code>
+class</a> that reads <abbr>TZif</abbr> data and creates objects
+that represent <code><abbr>tzdb</abbr></code> timezones.
+Python is freely available under the
+<a href="https://docs.python.org/3/license.html">Python Software Foundation
+License</a>.
+A companion <a id="pypi-tzdata" href="https://pypi.org/">PyPI</a> module
+<a href="https://pypi.org/project/tzdata/"><code>tzdata</code></a>
+supplies TZif data if the underlying system data cannot be found;
+it is freely available under the Apache License.</li>
+<li>The
+public-domain <a href="https://github.com/dbaron/tz.js">tz.js</a>
+library contains a Python tool that
+converts <abbr>TZif</abbr> data into
+<abbr>JSON</abbr>-format data suitable for use
+in its JavaScript library for time zone conversion. Dates before 1970
+are not supported.</li>
+<li>The <a
+href="https://hackage.haskell.org/package/timezone-olson">timezone-olson</a>
+package contains <a href="https://www.haskell.org">Haskell</a> code that
+parses and uses <abbr>TZif</abbr> data. It is freely
+available under a <abbr>BSD</abbr>-style license.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="software">Other <code><abbr>tz</abbr></code>-based time zone software</h2>
+<ul>
+<li><a href="https://foxclocks.org">FoxClocks</a>
+is an extension for <a href="https://www.google.com/chrome/">Google
+Chrome</a> and for <a
+href="https://wiki.mozilla.org/Modules/Toolkit">Mozilla
+Toolkit</a> applications like <a
+href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> and <a
+href="https://www.mozilla.org/en-US/thunderbird/">Thunderbird</a>.
+It displays multiple clocks in the application window, and has a mapping
+interface to <a href="https://www.google.com/earth/">Google Earth</a>.
+It is freely available under the <abbr>GPL</abbr>.</li>
+<li><a href="https://golang.org">Go programming language</a>
+implementations contain a copy of a 32-bit subset of a recent
+<code><abbr>tz</abbr></code> database in a
+Go-specific format.</li>
+<li>Microsoft Windows 8.1
+and later has <code><abbr>tz</abbr></code> data and <abbr>CLDR</abbr>
+data (mentioned <a href="#CLDR">below</a>) used by the
+<a href="https://en.wikipedia.org/wiki/Windows_Runtime">Windows Runtime</a> /
+<a href="https://en.wikipedia.org/wiki/Universal_Windows_Platform">Universal Windows Platform</a> classes
+<a href="https://docs.microsoft.com/uwp/api/Windows.Globalization.DateTimeFormatting.DateTimeFormatter"><code>DateTimeFormatter</code></a> and
+<a href="https://docs.microsoft.com/uwp/api/windows.globalization.calendar"><code>Calendar</code></a>.
+<a id="System.TimeZoneInfo"
+href="https://blogs.msdn.microsoft.com/bclteam/2007/06/07/exploring-windows-time-zones-with-system-timezoneinfo-josh-free/">Exploring
+Windows Time Zones with <code>System.TimeZoneInfo</code></a> describes
+the older, proprietary method of Microsoft Windows 2000 and later,
+which stores time zone data in the
+<a href="https://en.wikipedia.org/wiki/Windows_Registry">Windows Registry</a>. The
+<a
+href="https://unicode.org/cldr/charts/latest/supplemental/zone_tzid.html">Zone &rarr;
+Tzid table</a> or <a
+href="https://github.com/unicode-org/cldr/blob/master/common/supplemental/windowsZones.xml"><abbr>XML</abbr>
+file</a> of the <abbr>CLDR</abbr> data maps proprietary zone IDs
+to <code><abbr>tz</abbr></code> names.
+These mappings can be performed programmatically via the <a href="https://github.com/mj1856/TimeZoneConverter">TimeZoneConverter</a> .NET library,
+or the ICU Java and C++ libraries mentioned <a href="#ICU">above</a>.
+<li><a
+href="https://www.oracle.com/java/index.html">Oracle
+Java</a> contains a copy of a subset of a recent
+<code><abbr>tz</abbr></code> database in a
+Java-specific format.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="other-dbs">Other time zone databases</h2>
+<ul>
+<li><a href="https://www.astro.com/atlas">Time-zone Atlas</a>
+is Astrodienst's Web version of Shanks and Pottenger's out-of-print
+time zone history atlases
+<a href="https://www.worldcat.org/oclc/468828649">for the US</a> and
+<a href="https://www.worldcat.org/oclc/76950459">for the world</a>.
+Although these extensive atlases
+<a href="https://astrologynewsservice.com/opinion/how-astrologers-contributed-to-the-information-age-a-brief-history-of-time/">were
+sources for much of the older <code><abbr>tz</abbr></code> data</a>,
+they are unreliable as Shanks appears to have
+guessed many <abbr>UT</abbr> offsets and transitions. The atlases cite no
+sources and do not indicate which entries are guesswork.</li>
+<li><a href="https://en.wikipedia.org/wiki/HP-UX">HP-UX</a> has a database in
+its own <code>tztab</code>(4) format.</li>
+<li>Microsoft Windows has proprietary data mentioned
+<a href="#System.TimeZoneInfo">above</a>.</li>
+<li><a href="https://www.worldtimeserver.com">World Time Server</a>
+is another time zone database.</li>
+<li>The <a
+href="https://www.iata.org/publications/store/Pages/standard-schedules-information.aspx">Standard
+Schedules Information Manual</a> of the
+International Air Transport Association
+gives current time zone rules for airports served by commercial aviation.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="maps">Maps</h2>
+<ul>
+<li>The <a
+href="https://www.cia.gov/the-world-factbook/maps/world-regional/">World
+and Regional Maps section</a> of <em>The World Factbook</em>, published by the
+<a href="https://www.cia.gov">US Central Intelligence
+Agency (<abbr
+title="Central Intelligence Agency">CIA</abbr>)</a>, contains a time
+zone map; the
+<a
+href="https://legacy.lib.utexas.edu/maps/world.html">Perry&ndash;Casta&ntilde;eda
+Library Map Collection</a>
+of the University of Texas at Austin has copies of
+recent editions.
+The pictorial quality is good,
+but the maps do not indicate daylight saving time,
+and parts of the data are a few years out of date.</li>
+<li><a href="https://www.worldtimezone.com">World Time Zone Map
+with current time</a>
+has several fancy time zone maps; it covers Russia particularly well.
+The maps' pictorial quality is not quite as good as the
+<abbr>CIA</abbr>'s
+but the maps are more up to date.</li>
+<li><a
+href="https://blog.poormansmath.net/how-much-is-time-wrong-around-the-world/">How
+much is time wrong around the world?</a> maps the difference between
+mean solar and standard time, highlighting areas such as western China
+where the two differ greatly. It's a bit out of date, unfortunately.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="boundaries">Time zone boundaries</h2>
+<p>Geographical boundaries between timezones are available
+from several <a
+href="https://en.wikipedia.org/wiki/Internet_geolocation">Internet
+geolocation</a>
+services and other sources.</p>
+<ul>
+<li><a href="https://github.com/evansiroky/timezone-boundary-builder">Timezone
+Boundary Builder</a> extracts
+<a href="https://www.openstreetmap.org">Open Street Map</a> data to build
+boundaries of <code><abbr>tzdb</abbr></code> timezones.
+Its code is freely available under the <abbr>MIT</abbr> license, and
+its data entries are freely available under the
+<a href="https://opendatacommons.org/licenses/odbl/">Open Data Commons
+Open Database License</a>. The maps' borders appear to be quite accurate.</li>
+<li>Programmatic interfaces that map geographical coordinates via tz_world to
+<code><abbr>tzdb</abbr></code> timezones include:
+<ul>
+<li><a href="https://github.com/mj1856/GeoTimeZone">GeoTimeZone</a> is
+written in <a
+href="https://en.wikipedia.org/wiki/C_Sharp_(programming_language)">C#</a>
+and is freely available under the <abbr>MIT</abbr> license.</li>
+<li>The <a href="https://github.com/bradfitz/latlong">latlong package</a>
+is written in Go and is freely available under the Apache License.</li>
+<li><a href="https://github.com/drtimcooper/LatLongToTimezone">LatLongToTimezone</a>,
+in both Java and
+<a href="https://en.wikipedia.org/wiki/Swift_(programming_language)">Swift</a>
+form, is freely available under the MIT license.</li>
+<li>For Node.js,
+the <a href="https://www.npmjs.com/package/geo-tz">geo-tz module</a>
+is freely available under the MIT license, and
+the <a href="https://www.npmjs.com/package/tz-lookup">tz-lookup module</a>
+is in the public domain.</li>
+<li>The <a
+href="https://github.com/MrMinimal64/timezonefinder">timezonefinder</a>
+library for Python is freely available under the MIT license.
+<li>The <a
+href="https://github.com/gunyarakun/timezone_finder">timezone_finder</a>
+library for Ruby is freely available under the MIT license.</li>
+</ul></li>
+<li>Free access via a network API, if you register a key, is provided by
+the <a
+href="https://www.geonames.org/export/web-services.html#timezone">GeoNames
+Timezone web service</a>, the <a
+href="https://developers.google.com/maps/documentation/timezone/intro">Google
+Maps Time Zone API</a>, and
+the <a href="https://timezonedb.com/api">TimeZoneDB API</a>.
+Commercial network API access is provided
+by <a href="https://askgeo.com">AskGeo</a>
+and <a href="https://www.geogarage.com/blog/news-1/post/geogarage-time-zone-api-31">GeoGarage</a>.
+</li>
+<li>"<a
+href="https://stackoverflow.com/questions/16086962/how-to-get-a-time-zone-from-a-location-using-latitude-and-longitude-coordinates/16086964">How
+to get a time zone from a location using latitude and longitude
+coordinates?</a>" discusses other geolocation possibilities.</li>
+<li><a href="http://statoids.com/statoids.html">Administrative
+Divisions of Countries ("Statoids")</a> lists
+political subdivision data related to time zones.</li>
+<li><a href="https://home.kpn.nl/vanadovv/time/Multizones.html">Time
+zone boundaries for multizone countries</a> summarizes legal
+boundaries between time zones within countries.</li>
+<li><a href="https://manifold.net/info/freestuff.shtml">Manifold Software
+&ndash; GIS and Database Tools</a> includes a Manifold-format map of
+world time zone boundaries distributed under the
+<abbr>GPL</abbr>.</li>
+<li>A ship within the <a
+href="https://en.wikipedia.org/wiki/Territorial_waters">territorial
+waters</a> of any nation uses that nation's time. In international
+waters, time zone boundaries are meridians 15&deg; apart, except that
+<abbr>UT</abbr>&minus;12 and <abbr>UT</abbr>+12 are each 7.5&deg;
+wide and are separated by
+the 180&deg; meridian (not by the International Date Line, which is
+for land and territorial waters only). A captain can change ship's
+clocks any time after entering a new time zone; midnight changes are
+common.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="civil">Civil time concepts and history</h2>
+<ul>
+<li><a href="https://www.nist.gov/pml/time-and-frequency-division/popular-links/walk-through-time">A
+Walk through Time</a>
+surveys the evolution of timekeeping.</li>
+<li>The history of daylight saving time is surveyed in <a
+href="http://www.webexhibits.org/daylightsaving/">About Daylight
+Saving Time &ndash; History, rationale, laws &amp; dates</a> and summarized in
+<a href="http://seizethedaylight.com/dst/">A Brief
+History of Daylight Saving Time</a>.</li>
+<li><a href="https://www.laphamsquarterly.org/roundtable/time-lords">Time
+Lords</a> discusses how authoritarians manipulate civil time.</li>
+<li><a href="https://www.w3.org/TR/timezone/">Working with Time Zones</a>
+contains guidelines and best practices for software applications that
+deal with civil time.</li>
+<li><a href="https://www.staff.science.uu.nl/~gent0113/idl/idl.htm">A History of
+the International Date Line</a> tells the story of the most important
+time zone boundary.</li>
+<li><a href="http://statoids.com/tconcept.html">Basic Time
+Zone Concepts</a> discusses terminological issues behind time zones.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="national">National histories of legal time</h2>
+<dl>
+<dt>Australia</dt>
+<dd>The Parliamentary Library commissioned a <a
+href="https://www.aph.gov.au/binaries/library/pubs/rp/2009-10/10rp10.pdf">research
+paper on daylight saving time in Australia</a>.
+The Bureau of Meteorology publishes a list of <a
+href="http://www.bom.gov.au/climate/averages/tables/dst_times.shtml">Implementation
+Dates of Daylight Savings Time within Australia</a>.</dd>
+<dt>Belgium</dt>
+<dd>The Royal Observatory of Belgium maintains a table of time in
+Belgium (in
+<a href="https://www.astro.oma.be/GENERAL/INFO/nli001a.html"
+hreflang="nl">Dutch</a> and <a
+href="https://www.astro.oma.be/GENERAL/INFO/fri001a.html"
+hreflang="fr">French</a>).</dd>
+<dt>Brazil</dt>
+<dd>The Time Service Department of the National Observatory
+records <a href="http://pcdsh01.on.br/DecHV.html"
+hreflang="pt-BR">Brazil's daylight saving time decrees (in
+Portuguese)</a>.</dd>
+<dt>Canada</dt>
+<dd>National Research Council Canada publishes current
+and some older information about <a
+href="https://nrc.canada.ca/en/certifications-evaluations-standards/canadas-official-time/time-zones-daylight-saving-time">time
+zones and daylight saving time</a>.</dd>
+<dt>Chile</dt>
+<dd>The Hydrographic and Oceanographic Service of the Chilean Navy publishes a
+<a href="https://www.horaoficial.cl/historia_hora.php" hreflang="es">history of
+Chile's official time (in Spanish)</a>.</dd>
+<dt>China</dt>
+<dd>The Hong Kong Observatory maintains a
+<a href="https://www.hko.gov.hk/en/gts/time/Summertime.htm">history of
+ summer time in Hong Kong</a>,
+and Macau's Meteorological and Geophysical Bureau maintains a <a
+href="https://www.smg.gov.mo/en/subpage/224/page/174">similar
+history for Macau</a>.
+Unfortunately the latter is incomplete and has errors.</dd>
+<dt>Czech Republic</dt>
+<dd><a href="https://kalendar.beda.cz/kdy-zacina-a-konci-letni-cas"
+hreflang="cs">When daylight saving time starts and ends (in Czech)</a>
+summarizes and cites historical <abbr>DST</abbr> regulations.</dd>
+<dt>Germany</dt>
+<dd>The National Institute for Science and Technology maintains the <a
+href="https://www.ptb.de/cms/en/fachabteilungen/abt4/fb-44/ag-441/realisation-of-legal-time-in-germany.html">Realisation
+of Legal Time in Germany</a>.</dd>
+<dt>Israel</dt>
+<dd>The Interior Ministry periodically issues <a
+href="ftp://ftp.cs.huji.ac.il/pub/tz/announcements"
+hreflang="he">announcements (in Hebrew)</a>.</dd>
+<dt>Malaysia</dt>
+<dd>See Singapore <a href="#Singapore">below</a>.</dd>
+<dt>Mexico</dt>
+<dd>The Investigation and Analysis Service of the Mexican Library of
+Congress has published a <a
+href="https://www.diputados.gob.mx/bibliot/publica/inveyana/polisoc/horver/index.htm"
+hreflang="es">history of Mexican local time (in Spanish)</a>.</dd>
+<dt>Netherlands</dt>
+<dd><a href="https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm"
+hreflang="nl">Legal time in the Netherlands (in Dutch)</a>
+covers the history of local time in the Netherlands from ancient times.</dd>
+<dt>New Zealand</dt>
+<dd>The Department of Internal Affairs maintains a brief <a
+href="https://www.dia.govt.nz/Daylight-Saving-History">History of
+Daylight Saving</a>.</dd>
+<dt>Portugal</dt>
+<dd>The Lisbon Astronomical Observatory publishes a
+<a href="https://oal.ul.pt/hora-legal/" hreflang="pt">history of
+legal time (in Portuguese)</a>.</dd>
+<dt>Singapore</dt>
+<dd><a id="Singapore"
+href="https://web.archive.org/web/20190822231045/http://www.math.nus.edu.sg/~mathelmr/teaching/timezone.html">Why
+is Singapore in the "Wrong" Time Zone?</a> details the
+history of legal time in Singapore and Malaysia.</dd>
+<dt>United Kingdom</dt>
+<dd><a
+href="https://www.polyomino.org.uk/british-time/">History of
+legal time in Britain</a> discusses in detail the country
+with perhaps the best-documented history of clock adjustments.</dd>
+<dt>United States</dt>
+<dd>The Department of Transportation's <a
+href="https://www.transportation.gov/regulations/recent-time-zone-proceedings">Recent
+Time Zone Proceedings</a> lists changes to time zone boundaries.</dd>
+<dt>Uruguay</dt>
+<dd>The Oceanography, Hydrography, and Meteorology Service of the Uruguayan
+Navy (SOHMA) publishes an annual <a
+href="https://sohma.armada.mil.uy/index.php/servicios/datos-astronomicos" hreflang="es">almanac
+(in Spanish)</a>.</dd>
+</dl>
+</section>
+
+<section>
+<h2 id="costs">Costs and benefits of time shifts</h2>
+<p>Various sources argue for and against daylight saving time and time
+zone shifts, and many scientific studies have been conducted. This
+section summarizes reviews and position statements based on
+scientific literature in the area.</p>
+<ul>
+<li>Carey RN, Sarma KM.
+<a href="https://bmjopen.bmj.com/content/7/6/e014319.long">Impact of
+daylight saving time on road traffic collision risk: a systematic
+review</a>.
+<em>BMJ Open.</em> 2017;7(6):e014319. doi:<a href="https://doi.org/10.1136/bmjopen-2016-014319">10.1136/bmjopen-2016-014319</a>.
+This reviews research literature and concludes that the evidence
+neither supports nor refutes road safety benefits from
+shifts in time zones.</li>
+<li>Havranek T, Herman D, Irsova D.
+<a href="https://www.iaee.org/en/publications/ejarticle.aspx?id=3051">Does
+daylight saving save electricity? A meta-analysis.</a>
+<em>Energy J.</em> 2018;39(2):35&ndash;61.
+doi:<a href="https://doi.org/10.5547/01956574.39.2.thav">10.5547/01956574.39.2.thav</a>.
+This analyzes research literature and concludes, "Electricity savings
+are larger for countries farther away from the equator, while
+subtropical regions consume more electricity because of <abbr>DST</abbr>."</li>
+<li>Rishi MA, Ahmed O, Barrantes Perez JH <em>et al</em>.
+<a href="https://jcsm.aasm.org/doi/10.5664/jcsm.8780">Daylight saving time:
+an American Academy of Sleep Medicine position statement</a>.
+<em>J Clin Sleep Med.</em>
+2020;<a href="https://doi.org/10.5664/jcsm.8780">10.5664/jcsm.8780</a>.
+This argues for permanent standard time due to health risks of both
+<abbr>DST</abbr> transitions and permanent <abbr>DST</abbr>.</li>
+<li>Roenneberg T, Wirz-Justice A, Skene DJ <em>et al</em>.
+<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7205184/">Why
+should we abolish Daylight Saving Time?</a>
+<em>J Biol Rhythms</em>. 2019;34(3):227&ndash;230.
+doi:<a href="https://doi.org/10.1177/0748730419854197">10.1177/0748730419854197</a>.
+This position paper of the Society for Research on Biological Rhythms
+opposes DST changes and permanent DST, and advocates that governments adopt
+"permanent Standard Time for the health and safety of their citizens".</li>
+</ul>
+</section>
+
+<section>
+<h2 id="precision">Precision timekeeping</h2>
+<ul>
+<li><a
+href="http://leapsecond.com/hpan/an1289.pdf">The
+Science of Timekeeping</a> is a thorough introduction
+to the theory and practice of precision timekeeping.</li>
+<li><a href="https://doi.org/10.1007/978-3-319-59909-0">The Science of
+Time 2016</a> contains several freely-readable papers.</li>
+<li><a href="https://www.ntp.org"><abbr
+title="Network Time Protocol">NTP</abbr>: The Network
+Time Protocol</a> (Internet <abbr>RFC</abbr> 5905)
+discusses how to synchronize clocks of
+Internet hosts.</li>
+<li>The <a href="https://www.usenix.org/system/files/conference/nsdi18/nsdi18-geng.pdf"><span style="font-variant: small-caps">Huygens</span></a>
+family of software algorithms can achieve accuracy to a few tens of
+nanoseconds in scalable server farms without special hardware.</li>
+<li>The <a
+href="https://www.nist.gov/intelligent-systems-division/ieee-1588">Precision
+Time Protocol</a> (<abbr
+title="Institute of Electrical and Electronics Engineers">IEEE</abbr> 1588)
+can achieve submicrosecond clock accuracy on a local area network
+with special-purpose hardware.</li>
+<li><a
+href="https://datatracker.ietf.org/doc/html/rfc4833">Timezone
+Options for <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr></a>
+(Internet <abbr>RFC</abbr> 4833)
+specifies a <a
+href="https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol"><abbr>DHCP</abbr></a>
+option for a server to configure
+a client's time zone and daylight saving settings automatically.</li>
+<li><a href="https://www.ucolick.org/~sla/leapsecs/timescales.html">Time
+Scales</a> describes astronomical time scales like
+<abbr title="Terrestrial Dynamic Time">TDT</abbr>,
+<abbr title="Geocentric Coordinate Time">TCG</abbr>, and
+<abbr title="Barycentric Dynamic Time">TDB</abbr>.
+<li>The <a href="https://www.iau.org"><abbr
+title="International Astronomical Union">IAU</abbr></a>'s <a
+href="https://www.iausofa.org"><abbr
+title="Standards Of Fundamental Astronomy">SOFA</abbr></a>
+collection contains C and <a
+href="https://en.wikipedia.org/wiki/Fortran">Fortran</a>
+code for converting among time scales like
+<abbr title="International Atomic Time">TAI</abbr>,
+<abbr>TDB</abbr>, <abbr>TDT</abbr> and
+<abbr>UTC</abbr>. It is freely available under the
+<a href="https://www.iausofa.org/tandc.html">SOFA license</a>.</li>
+<li><a
+href="https://www.giss.nasa.gov/tools/mars24/help/notes.html">Mars24 Sunclock
+&ndash; Time on Mars</a> describes Airy Mean Time (<abbr>AMT</abbr>) and the
+diverse local time
+scales used by each landed mission on Mars.</li>
+<li><a href="http://leapsecond.com">LeapSecond.com</a> is
+dedicated not only to leap seconds but to precise time and frequency
+in general. It covers the state of the art in amateur timekeeping, and
+how the art has progressed over the past few decades.</li>
+<li>The rules for leap seconds are specified in Annex 1 (Time scales) of <a
+href="https://www.itu.int/rec/R-REC-TF.460-6-200202-I/">Standard-frequency
+and time-signal emissions</a>, International Telecommunication Union &ndash;
+Radiocommunication Sector (ITU-R) Recommendation TF.460-6 (02/2002).</li>
+<li><a
+href="https://www.iers.org/IERS/EN/Publications/Bulletins/bulletins.html"><abbr
+title="International Earth Rotation and Reference Systems Service">IERS</abbr>
+Bulletins</a> contains official publications of the International
+Earth Rotation and Reference Systems Service, which decides when leap
+seconds occur. The <code><abbr>tz</abbr></code> code and data support leap seconds
+via an optional "<code>right</code>" configuration where a computer's internal
+<code>time_t</code> integer clock counts every <abbr>TAI</abbr> second,
+as opposed to the default "<code>posix</code>" configuration
+where the internal clock ignores leap seconds.
+The two configurations agree for timestamps starting with 1972-01-01 00:00:00
+<abbr>UTC</abbr> (<code>time_t</code> 63&thinsp;072&thinsp;000) and diverge for
+timestamps starting with <code>time_t</code> 78&thinsp;796&thinsp;800,
+which corresponds to the first leap second
+1972-06-30 23:59:60 <abbr>UTC</abbr> in the "<code>right</code>" configuration,
+and to
+1972-07-01 00:00:00 <abbr>UTC</abbr> in the "<code>posix</code>" configuration.
+In practice the two configurations also agree for timestamps before
+1972 even though the historical situation is messy, partly because
+neither <abbr>UTC</abbr> nor <abbr>TAI</abbr>
+is well-defined for sufficiently-old timestamps.</li>
+<li><a href="https://developers.google.com/time/smear">Leap Smear</a>
+discusses how to gradually adjust <abbr>POSIX</abbr> clocks near a
+leap second so that they disagree with <abbr>UTC</abbr> by at most a
+half second, even though every <abbr>POSIX</abbr> minute has exactly
+sixty seconds. This approach works with the default <code><abbr>tz</abbr></code>
+"<code>posix</code>" configuration, is <a
+href="http://bk1.ntp.org/ntp-stable/README.leapsmear">supported</a> by
+the <abbr>NTP</abbr> reference implementation, <a
+href="https://github.com/google/unsmear">supports</a> conversion between
+<abbr>UTC</abbr> and smeared <abbr>POSIX</abbr> timestamps, and is used by major
+cloud service providers. However, according to
+<a href="https://datatracker.ietf.org/doc/html/rfc8633#section-3.7.1">&sect;3.7.1 of
+Network Time Protocol Best Current Practices</a>
+(Internet <abbr>RFC</abbr> 8633), leap smearing is not suitable for
+applications requiring accurate <abbr>UTC</abbr> or civil time,
+and is intended for use only in single, well-controlled environments.</li>
+<li>The <a
+href="https://pairlist6.pair.net/mailman/listinfo/leapsecs">Leap
+Second Discussion List</a> covers <a
+href="https://www2.unb.ca/gge/Resources/gpsworld.november99.pdf">McCarthy
+and Klepczynski's 1999 proposal to discontinue leap seconds</a>,
+discussed further in
+<a href="https://www.cl.cam.ac.uk/~mgk25/time/metrologia-leapsecond.pdf">The
+leap second: its history and possible future</a>.
+<a href="https://www.ucolick.org/~sla/leapsecs/"><abbr>UTC</abbr>
+might be redefined
+without Leap Seconds</a> gives pointers on this
+contentious issue, which was active until 2015 and could become active
+again.</li>
+</ul>
+</section>
+
+<section>
+<h2 id="notation">Time notation</h2>
+<ul>
+<li>The <a id="CLDR" href="https://cldr.unicode.org">Unicode Common Locale Data
+Repository (<abbr>CLDR</abbr>) Project</a> has localizations for time
+zone names, abbreviations, identifiers, and formats. For example, it
+contains French translations for "Eastern European Summer Time",
+"<abbr title="Eastern European Summer Time">EEST</abbr>", and
+"Bucharest". Its
+<a href="https://unicode.org/cldr/charts/latest/by_type/">by-type
+charts</a> show these values for many locales. Data values are available in
+both <abbr title="Locale Data Markup Language">LDML</abbr>
+(an <abbr>XML</abbr> format) and <abbr>JSON</abbr>.
+<li>
+<a href="https://www.cl.cam.ac.uk/~mgk25/iso-time.html">A summary of
+the international standard date and time notation</a> covers
+<a
+href="https://www.iso.org/standard/70907.html"><em><abbr
+title="International Organization for Standardization">ISO</abbr>
+8601-1:2019 &ndash; Date and time &ndash; Representations for information
+interchange &ndash; Part 1: Basic rules</em></a>.</li>
+<li>
+<a href="https://www.w3.org/TR/xmlschema/#dateTime"><abbr>XML</abbr>
+Schema: Datatypes &ndash; dateTime</a> specifies a format inspired by
+<abbr>ISO</abbr> 8601 that is in common use in <abbr>XML</abbr> data.</li>
+<li><a href="https://datatracker.ietf.org/doc/html/rfc5322#section-3.3">&sect;3.3 of
+Internet Message Format</a> (Internet <abbr>RFC</abbr> 5322)
+specifies the time notation used in email and <a
+href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"><abbr>HTTP</abbr></a>
+headers.</li>
+<li>
+<a href="https://datatracker.ietf.org/doc/html/rfc3339">Date and Time
+on the Internet: Timestamps</a> (Internet <abbr>RFC</abbr> 3339)
+specifies an <abbr>ISO</abbr> 8601
+profile for use in new Internet
+protocols.</li>
+<li>
+<a href="https://web.archive.org/web/20190130042457/https://www.hackcraft.net/web/datetime/">Date &amp; Time
+Formats on the Web</a> surveys web- and Internet-oriented date and time
+formats.</li>
+<li>Alphabetic time zone abbreviations should not be used as unique
+identifiers for <abbr>UT</abbr> offsets as they are ambiguous in
+practice. For example, in English-speaking North America
+"<abbr>CST</abbr>" denotes 6 hours behind <abbr>UT</abbr>,
+but in China it denotes 8 hours ahead of <abbr>UT</abbr>,
+and French-speaking North Americans prefer
+"<abbr title="Heure Normale du Centre">HNC</abbr>" to
+"<abbr>CST</abbr>". The <code><abbr>tz</abbr></code>
+database contains English abbreviations for many timestamps;
+unfortunately some of these abbreviations were merely the database maintainers'
+inventions, and these have been removed when possible.</li>
+<li>Numeric time zone abbreviations typically count hours east of
+<abbr>UT</abbr>, e.g., +09 for Japan and
+&minus;10 for Hawaii. However, the <abbr>POSIX</abbr>
+<code><abbr>TZ</abbr></code> environment variable uses the opposite convention.
+For example, one might use <code><abbr>TZ</abbr>="<abbr
+title="Japan Standard Time">JST</abbr>-9"</code> and
+<code><abbr>TZ</abbr>="<abbr title="Hawaii Standard Time">HST</abbr>10"</code>
+for Japan and Hawaii, respectively. If the
+<code><abbr>tz</abbr></code> database is available, it is usually better to use
+settings like <code><abbr>TZ</abbr>="Asia/Tokyo"</code> and
+<code><abbr>TZ</abbr>="Pacific/Honolulu"</code> instead, as this should avoid
+confusion, handle old timestamps better, and insulate you better from
+any future changes to the rules. One should never set
+<abbr>POSIX</abbr> <code><abbr>TZ</abbr></code> to a value like
+<code>"GMT-9"</code>, though, since this would incorrectly imply that
+local time is nine hours ahead of <abbr>UT</abbr> and the time zone
+is called "<abbr>GMT</abbr>".</li>
+</ul>
+</section>
+
+<section>
+<h2 id="see-also">See also</h2>
+<ul>
+<li><a href="theory.html">Theory and pragmatics of the
+<code><abbr>tz</abbr></code> code and data</a></li>
+<li><a href="tz-art.html">Time and the Arts</a></li>
+</ul>
+</section>
+
+<footer>
+<hr>
+This web page is in the public domain, so clarified as of
+2009-05-17 by Arthur David Olson.
+<br>
+Please send corrections to this web page to the
+<a href="mailto:tz@iana.org">time zone mailing list</a>.
+</footer>
+</body>
+</html>
diff --git a/contrib/tzcode/tzfile.5 b/contrib/tzcode/tzfile.5
new file mode 100644
index 000000000000..b53abaab18d6
--- /dev/null
+++ b/contrib/tzcode/tzfile.5
@@ -0,0 +1,492 @@
+.\" This file is in the public domain, so clarified as of
+.\" 1996-06-05 by Arthur David Olson.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 15, 2022
+.Dt TZFILE 5
+.Os
+.Sh NAME
+.Nm tzfile
+.Nd timezone information
+.Sh DESCRIPTION
+The timezone information files used by
+.Xr tzset 3
+are found under
+.Pa /usr/share/zoneinfo .
+These files use the format described in Internet RFC 8536.
+Each file is a sequence of 8-bit bytes.
+In a file, a binary integer is represented by a sequence of one or
+more bytes in network order (bigendian, or high-order byte first),
+with all bits significant,
+a signed binary integer is represented using two's complement,
+and a boolean is represented by a one-byte binary integer that is
+either 0 (false) or 1 (true).
+The format begins with a 44-byte header containing the following fields:
+.Pp
+.Bl -bullet
+.It
+The magic four-byte ASCII sequence
+.Dq "TZif"
+identifies the file as a timezone information file.
+.It
+A byte identifying the version of the file's format
+(as of 2021, either an ASCII NUL,
+.Dq "2" ,
+.Dq "3" ,
+or
+.Dq "4" ) .
+.It
+Fifteen bytes containing zeros reserved for future use.
+.It
+Six four-byte integer values, in the following order:
+.Pp
+.Bl -tag -compat -width tzh_ttisstdcnt
+.It Va tzh_ttisutcnt
+The number of UT/local indicators stored in the file.
+(UT is Universal Time.)
+.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 entries are stored in the file.
+.It Va tzh_timecnt
+The number of transition times for which data entries are stored
+in the file.
+.It Va tzh_typecnt
+The number of local time types for which data entries are stored
+in the file (must not be zero).
+.It Va tzh_charcnt
+The number of bytes of time zone abbreviation strings
+stored in the file.
+.El
+.El
+.Pp
+The above header is followed by the following fields, whose lengths
+depend on the contents of the header:
+.Bl -tag -compat -width tzh_timecnt
+.It Va tzh_timecnt
+four-byte signed integer values sorted in ascending order.
+These values are written in network byte order.
+Each is used as a transition time (as returned by
+.Xt time 2 )
+at which the rules for computing local time change.
+.It Va tzh_timecnt
+one-byte unsigned integer values;
+each one but the last tells which of the different types of local time types
+described in the file is associated with the time period
+starting with the same-indexed transition time
+and continuing up to but not including the next transition time.
+(The last time type is present only for consistency checking with the
+POSIX-style TZ string described below.)
+These values serve as indices into the next field.
+.It Va tzh_typecnt
+.Vt ttinfo
+entries, each defined as follows:
+.Pp
+.Bd -literal -offset indent
+struct ttinfo {
+ int32_t tt_utoff;
+ unsigned char tt_isdst;
+ unsigned char tt_desigidx;
+};
+.Ed
+.Pp
+Each structure is written as a four-byte signed integer value for
+.Va tt_utoff ,
+in network byte order, followed by a one-byte boolean for
+.Va tt_isdst
+and a one-byte value for
+.Va tt_desigidx .
+In each structure,
+.Va tt_utoff
+gives the number of seconds to be added to UT,
+.Va tt_isdst
+tells whether
+.Va tm_isdst
+should be set by
+.Xr localtime 3
+and
+.Va tt_desigidx
+serves as an index into the array of time zone abbreviation bytes
+that follow the
+.Vt ttinfo
+entries in the file; if the designated string is "\*-00", the
+.Vt ttinfo
+entry is a placeholder indicating that local time is unspecified.
+The
+.Va tt_utoff
+value is never equal to \-2**31, to let 32-bit clients negate it without
+overflow.
+Also, in realistic applications
+.Va tt_utoff
+is in the range [\-89999, 93599] (i.e., more than \-25 hours and less
+than 26 hours); this allows easy support by implementations that
+already support the POSIX-required range [\-24:59:59, 25:59:59].
+.It Va tzh_charcnt
+bytes that represent time zone designations,
+which are null-terminated byte strings, each indexed by the
+.Va tt_desigidx
+values mentioned above.
+The byte strings can overlap if one is a suffix of the other.
+The encoding of these strings is not specified.
+.It Va tzh_leapcnt
+pairs of four-byte values, written in network byte order;
+the first value of each pair gives the nonnegative time
+(as returned by
+.Xr time 3 )
+at which a leap second occurs or at which the leap second table expires;
+the second is a signed integer specifying the correction, which is the
+.Em total
+number of leap seconds to be applied during the time period
+starting at the given time.
+The pairs of values are sorted in strictly ascending order by time.
+Each pair denotes one leap second, either positive or negative,
+except that if the last pair has the same correction as the previous one,
+the last pair denotes the leap second table's expiration time.
+Each leap second is at the end of a UTC calendar month.
+The first leap second has a nonnegative occurrence time,
+and is a positive leap second if and only if its correction is positive;
+the correction for each leap second after the first differs
+from the previous leap second by either 1 for a positive leap second,
+or \-1 for a negative leap second.
+If the leap second table is empty, the leap-second correction is zero
+for all timestamps;
+otherwise, for timestamps before the first occurrence time,
+the leap-second correction is zero if the first pair's correction is 1 or \-1,
+and is unspecified otherwise (which can happen only in files
+truncated at the start).
+.It Va tzh_ttisstdcnt
+standard/wall indicators, each stored as a one-byte boolean;
+they tell whether the transition times associated with local time types
+were specified as standard time or local (wall clock) time.
+.It Va tzh_ttisutcnt
+UT/local indicators, each stored as a one-byte boolean;
+they tell whether the transition times associated with local time types
+were specified as UT or local time.
+If a UT/local indicator is set, the corresponding standard/wall indicator
+must also be set.
+.El
+.Pp
+The standard/wall and UT/local indicators were designed for
+transforming a TZif file's transition times into transitions appropriate
+for another time zone specified via a POSIX-style TZ string that lacks rules.
+For example, when TZ="EET\*-2EEST" and there is no TZif file "EET\*-2EEST",
+the idea was to adapt the transition times from a TZif file with the
+well-known name "posixrules" that is present only for this purpose and
+is a copy of the file "Europe/Brussels", a file with a different UT offset.
+POSIX does not specify this obsolete transformational behavior,
+the default rules are installation-dependent, and no implementation
+is known to support this feature for timestamps past 2037,
+so users desiring (say) Greek time should instead specify
+TZ="Europe/Athens" for better historical coverage, falling back on
+TZ="EET\*-2EEST,M3.5.0/3,M10.5.0/4" if POSIX conformance is required
+and older timestamps need not be handled accurately.
+.Pp
+The
+.Xr localtime 3
+function
+normally uses the first
+.Vt ttinfo
+structure in the file
+if either
+.Va tzh_timecnt
+is zero or the time argument is less than the first transition time recorded
+in the file.
+.Ss Version 2 format
+For version-2-format timezone files,
+the above header and data are followed by a second header and data,
+identical in format except that
+eight bytes are used for each transition time or leap second time.
+(Leap second counts remain four bytes.)
+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
+or for all instants if the file has no transitions.
+The POSIX-style TZ string is empty (i.e., nothing between the newlines)
+if there is no POSIX-style representation for such instants.
+If nonempty, the POSIX-style TZ string must agree with the local time
+type after the last transition time if present in the eight-byte data;
+for example, given the string
+.Dq "WET0WEST,M3.5.0,M10.5.0/3"
+then if a last transition time is in July, the transition's local time
+type must specify a daylight-saving time abbreviated
+.Dq "WEST"
+that is one hour east of UT.
+Also, if there is at least one transition, time type 0 is associated
+with the time period from the indefinite past up to but not including
+the earliest transition time.
+.Ss Version 3 format
+For version-3-format timezone files, the POSIX-TZ-style string may
+use two minor extensions to the POSIX TZ format, as described in
+.Xr newtzset 3 .
+First, the hours part of its transition times may be signed and range from
+\-167 through 167 instead of the POSIX-required unsigned values
+from 0 through 24.
+Second, DST is in effect all year if it starts
+January 1 at 00:00 and ends December 31 at 24:00 plus the difference
+between daylight saving and standard time.
+.Ss Version 4 format
+For version-4-format TZif files,
+the first leap second record can have a correction that is neither
++1 nor \-1, to represent truncation of the TZif file at the start.
+Also, if two or more leap second transitions are present and the last
+entry's correction equals the previous one, the last entry
+denotes the expiration of the leap second table instead of a leap second;
+timestamps after this expiration are unreliable in that future
+releases will likely add leap second entries after the expiration, and
+the added leap seconds will change how post-expiration timestamps are treated.
+.Ss Interoperability considerations
+Future changes to the format may append more data.
+.Pp
+Version 1 files are considered a legacy format and
+should not be generated, as they do not support transition
+times after the year 2038.
+Readers that understand only Version 1 must ignore
+any data that extends beyond the calculated end of the version
+1 data block.
+.Pp
+Other than version 1, writers should generate
+the lowest version number needed by a file's data.
+For example, a writer should generate a version 4 file
+only if its leap second table either expires or is truncated at the start.
+Likewise, a writer not generating a version 4 file
+should generate a version 3 file only if
+TZ string extensions are necessary to accurately
+model transition times.
+.Pp
+The sequence of time changes defined by the version 1
+header and data block should be a contiguous sub-sequence
+of the time changes defined by the version 2+ header and data
+block, and by the footer.
+This guideline helps obsolescent version 1 readers
+agree with current readers about timestamps within the
+contiguous sub-sequence.
+It also lets writers not
+supporting obsolescent readers use a
+.Va tzh_timecnt
+of zero
+in the version 1 data block to save space.
+.Pp
+When a TZif file contains a leap second table expiration
+time, TZif readers should either refuse to process
+post-expiration timestamps, or process them as if the expiration
+time did not exist (possibly with an error indication).
+.Pp
+Time zone designations should consist of at least three (3)
+and no more than six (6) ASCII characters from the set of
+alphanumerics,
+.Dq "\*-" ,
+and
+.Dq "+" .
+This is for compatibility with POSIX requirements for
+time zone abbreviations.
+.Pp
+When reading a version 2 or higher file, readers
+should ignore the version 1 header and data block except for
+the purpose of skipping over them.
+.Pp
+Readers should calculate the total lengths of the
+headers and data blocks and check that they all fit within
+the actual file size, as part of a validity check for the file.
+.Pp
+When a positive leap second occurs, readers should append an extra
+second to the local minute containing the second just before the leap
+second.
+If this occurs when the UTC offset is not a multiple of 60
+seconds, the leap second occurs earlier than the last second of the
+local minute and the minute's remaining local seconds are numbered
+through 60 instead of the usual 59; the UTC offset is unaffected.
+.Ss Common interoperability issues
+This section documents common problems in reading or writing TZif files.
+Most of these are problems in generating TZif files for use by
+older readers.
+The goals of this section are:
+.Bl -bullet
+.It
+to help TZif writers output files that avoid common
+pitfalls in older or buggy TZif readers,
+.It
+to help TZif readers avoid common pitfalls when reading
+files generated by future TZif writers, and
+.It
+to help any future specification authors see what sort of
+problems arise when the TZif format is changed.
+.El
+.Pp
+When new versions of the TZif format have been defined, a
+design goal has been that a reader can successfully use a TZif
+file even if the file is of a later TZif version than what the
+reader was designed for.
+When complete compatibility was not achieved, an attempt was
+made to limit glitches to rarely used timestamps and allow
+simple partial workarounds in writers designed to generate
+new-version data useful even for older-version readers.
+This section attempts to document these compatibility issues and
+workarounds, as well as to document other common bugs in
+readers.
+.Pp
+Interoperability problems with TZif include the following:
+.Bl -bullet
+.It
+Some readers examine only version 1 data.
+As a partial workaround, a writer can output as much version 1
+data as possible.
+However, a reader should ignore version 1 data, and should use
+version 2+ data even if the reader's native timestamps have only
+32 bits.
+.It
+Some readers designed for version 2 might mishandle
+timestamps after a version 3 or higher file's last transition, because
+they cannot parse extensions to POSIX in the TZ-like string.
+As a partial workaround, a writer can output more transitions
+than necessary, so that only far-future timestamps are
+mishandled by version 2 readers.
+.It
+Some readers designed for version 2 do not support
+permanent daylight saving time with transitions after 24:00
+\(en e.g., a TZ string
+.Dq "EST5EDT,0/0,J365/25"
+denoting permanent Eastern Daylight Time
+(\-04).
+As a workaround, a writer can substitute standard time
+for two time zones east, e.g.,
+.Dq "XXX3EDT4,0/0,J365/23"
+for a time zone with a never-used standard time (XXX, \-03)
+and negative daylight saving time (EDT, \-04) all year.
+Alternatively,
+as a partial workaround a writer can substitute standard time
+for the next time zone east \(en e.g.,
+.Dq "AST4"
+for permanent
+Atlantic Standard Time (\-04).
+.It
+Some readers designed for version 2 or 3, and that require strict
+conformance to RFC 8536, reject version 4 files whose leap second
+tables are truncated at the start or that end in expiration times.
+.It
+Some readers ignore the footer, and instead predict future
+timestamps from the time type of the last transition.
+As a partial workaround, a writer can output more transitions
+than necessary.
+.It
+Some readers do not use time type 0 for timestamps before
+the first transition, in that they infer a time type using a
+heuristic that does not always select time type 0.
+As a partial workaround, a writer can output a dummy (no-op)
+first transition at an early time.
+.It
+Some readers mishandle timestamps before the first
+transition that has a timestamp not less than \-2**31.
+Readers that support only 32-bit timestamps are likely to be
+more prone to this problem, for example, when they process
+64-bit transitions only some of which are representable in 32
+bits.
+As a partial workaround, a writer can output a dummy
+transition at timestamp \-2**31.
+.It
+Some readers mishandle a transition if its timestamp has
+the minimum possible signed 64-bit value.
+Timestamps less than \-2**59 are not recommended.
+.It
+Some readers mishandle POSIX-style TZ strings that
+contain
+.Dq "<"
+or
+.Dq ">".
+As a partial workaround, a writer can avoid using
+.Dq "<"
+or
+.Dq ">"
+for time zone abbreviations containing only alphabetic
+characters.
+.It
+Many readers mishandle time zone abbreviations that contain
+non-ASCII characters.
+These characters are not recommended.
+.It
+Some readers may mishandle time zone abbreviations that
+contain fewer than 3 or more than 6 characters, or that
+contain ASCII characters other than alphanumerics,
+.Dq "\*-",
+and
+.Dq "+".
+These abbreviations are not recommended.
+.It
+Some readers mishandle TZif files that specify
+daylight-saving time UT offsets that are less than the UT
+offsets for the corresponding standard time.
+These readers do not support locations like Ireland, which
+uses the equivalent of the POSIX TZ string
+.Dq "IST\*-1GMT0,M10.5.0,M3.5.0/1" ,
+observing standard time
+(IST, +01) in summer and daylight saving time (GMT, +00) in winter.
+As a partial workaround, a writer can output data for the
+equivalent of the POSIX TZ string
+.Dq "GMT0IST,M3.5.0/1,M10.5.0" ,
+thus swapping standard and daylight saving time.
+Although this workaround misidentifies which part of the year
+uses daylight saving time, it records UT offsets and time zone
+abbreviations correctly.
+.It
+Some readers generate ambiguous timestamps for positive leap seconds
+that occur when the UTC offset is not a multiple of 60 seconds.
+For example, in a timezone with UTC offset +01:23:45 and with
+a positive leap second 78796801 (1972-06-30 23:59:60 UTC), some readers will
+map both 78796800 and 78796801 to 01:23:45 local time the next day
+instead of mapping the latter to 01:23:46, and they will map 78796815 to
+01:23:59 instead of to 01:23:60.
+This has not yet been a practical problem, since no civil authority
+has observed such UTC offsets since leap seconds were
+introduced in 1972.
+.El
+.Pp
+Some interoperability problems are reader bugs that
+are listed here mostly as warnings to developers of readers.
+.Bl -bullet
+.It
+Some readers do not support negative timestamps.
+Developers of distributed applications should keep this
+in mind if they need to deal with pre-1970 data.
+.It
+Some readers mishandle timestamps before the first
+transition that has a nonnegative timestamp.
+Readers that do not support negative timestamps are likely to
+be more prone to this problem.
+.It
+Some readers mishandle time zone abbreviations like
+.Dq "\*-08"
+that contain
+.Dq "+" ,
+.Dq "\*-" ,
+or digits.
+.It
+Some readers mishandle UT offsets that are out of the
+traditional range of \-12 through +12 hours, and so do not
+support locations like Kiritimati that are outside this
+range.
+.It
+Some readers mishandle UT offsets in the range [\-3599, \-1]
+seconds from UT, because they integer-divide the offset by
+3600 to get 0 and then display the hour part as
+.Dq "+00" .
+.It
+Some readers mishandle UT offsets that are not a multiple
+of one hour, or of 15 minutes, or of 1 minute.
+.El
+.Sh SEE ALSO
+.Xr time 3 ,
+.Xr localtime 3 ,
+.Xr tzset 3 ,
+.Xr tzsetup 8 ,
+.Xr zic 8 ,
+.Xr zdump 8
+.Rs
+.%A A. Olson
+.%A P. Eggert
+.%A K. Murchison
+.%T "The Time Zone Information Format (TZif)"
+.%R RFC 8536
+.%D February 2019
+.%U https://datatracker.ietf.org/doc/html/rfc8536
+.%U https://doi.org/10.17487/RFC8536
+.Re
diff --git a/contrib/tzcode/tzfile.h b/contrib/tzcode/tzfile.h
new file mode 100644
index 000000000000..e59d2ea4bb47
--- /dev/null
+++ b/contrib/tzcode/tzfile.h
@@ -0,0 +1,125 @@
+/* Layout and location of TZif files. */
+
+#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!
+*/
+
+/*
+** 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 */
+
+
+/* See Internet RFC 8536 for more details about the following format. */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */
+ char tzh_reserved[15]; /* reserved; must be zero */
+ char tzh_ttisutcnt[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 UT 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 1, transition
+** time is standard time, if 0,
+** transition time is local (wall clock)
+** time; if absent, transition times are
+** assumed to be local time
+** tzh_ttisutcnt (char)s indexed by type; if 1, transition
+** time is UT, if 0, transition time is
+** local time; if absent, transition
+** times are assumed to be local time.
+** When this is 1, the corresponding
+** std/wall indicator must also be 1.
+*/
+
+/*
+** 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).
+**
+** If tz_version is '3' or greater, the above is extended as follows.
+** First, the POSIX TZ string's hour offset may range from -167
+** through 167 as compared to the POSIX-required 0 through 24.
+** Second, its DST start time may be January 1 at 00:00 and its stop
+** time December 31 at 24:00 plus the difference between DST and
+** standard time, indicating DST all year.
+*/
+
+/*
+** 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 2000
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
+# define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#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 */
+
+#endif /* !defined TZFILE_H */
diff --git a/contrib/tzcode/tzselect.8 b/contrib/tzcode/tzselect.8
new file mode 100644
index 000000000000..846b867be1c0
--- /dev/null
+++ b/contrib/tzcode/tzselect.8
@@ -0,0 +1,125 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.TH TZSELECT 8
+.SH NAME
+tzselect \- select a timezone
+.SH SYNOPSIS
+.ie \n(.g .ds - \f(CR-\fP
+.el .ds - \-
+.ds d " degrees
+.ds m " minutes
+.ds s " seconds
+.ds _ " \&
+.if t \{\
+. if \n(.g .if c \(de .if c \(fm .if c \(sd \{\
+. ds d \(de
+. ds m \(fm
+. ds s \(sd
+. ds _ \|
+. \}
+.\}
+.B tzselect
+[
+.B \*-c
+.I coord
+] [
+.B \*-n
+.I limit
+] [
+.B \*-\*-help
+] [
+.B \*-\*-version
+]
+.SH DESCRIPTION
+The
+.B tzselect
+program asks the user for information about the current location,
+and outputs the resulting timezone to standard output.
+The output is suitable as a value for the TZ environment variable.
+.PP
+All interaction with the user is done via standard input and standard error.
+.SH OPTIONS
+.TP
+.BI "\*-c " coord
+Instead of asking for continent and then country and then city,
+ask for selection from time zones whose largest cities
+are closest to the location with geographical coordinates
+.I coord.
+Use ISO 6709 notation for
+.I coord,
+that is, a latitude immediately followed by a longitude. The latitude
+and longitude should be signed integers followed by an optional
+decimal point and fraction: positive numbers represent north and east,
+negative south and west. Latitudes with two and longitudes with three
+integer digits are treated as degrees; latitudes with four or six and
+longitudes with five or seven integer digits are treated as
+.I "DDMM, DDDMM, DDMMSS,"
+or
+.I DDDMMSS
+representing
+.I DD
+or
+.I DDD
+degrees,
+.I MM
+minutes,
+and zero or
+.I SS
+seconds, with any trailing fractions represent fractional minutes or
+(if
+.I SS
+is present) seconds. The decimal point is that of the current locale.
+For example, in the (default) C locale,
+.B "\*-c\ +40.689\*-074.045"
+specifies 40.689\*d\*_N, 74.045\*d\*_W,
+.B "\*-c\ +4041.4\*-07402.7"
+specifies 40\*d\*_41.4\*m\*_N, 74\*d\*_2.7\*m\*_W, and
+.B "\*-c\ +404121\*-0740240"
+specifies 40\*d\*_41\*m\*_21\*s\*_N, 74\*d\*_2\*m\*_40\*s\*_W.
+If
+.I coord
+is not one of the documented forms, the resulting behavior is unspecified.
+.TP
+.BI "\*-n " limit
+When
+.B \*-c
+is used, display the closest
+.I limit
+locations (default 10).
+.TP
+.B "\*-\*-help"
+Output help information and exit.
+.TP
+.B "\*-\*-version"
+Output version information and exit.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+\f3AWK\fP
+Name of a Posix-compliant
+.B awk
+program (default:
+.BR awk ).
+.TP
+\f3TZDIR\fP
+Name of the directory containing timezone data files (default:
+.BR /usr/share/zoneinfo ).
+.SH FILES
+.TP
+\f2TZDIR\fP\f3/iso3166.tab\fP
+Table of ISO 3166 2-letter country codes and country names.
+.TP
+\f2TZDIR\fP\f3/zone1970.tab\fP
+Table of country codes, latitude and longitude, timezones, and
+descriptive comments.
+.TP
+\f2TZDIR\fP\f3/\fP\f2TZ\fP
+Timezone data file for timezone \f2TZ\fP.
+.SH "EXIT STATUS"
+The exit status is zero if a timezone was successfully obtained from the user,
+nonzero otherwise.
+.SH "SEE ALSO"
+newctime(3), tzfile(5), zdump(8), zic(8)
+.SH NOTES
+Applications should not assume that
+.BR tzselect 's
+output matches the user's political preferences.
diff --git a/contrib/tzcode/tzselect.ksh b/contrib/tzcode/tzselect.ksh
new file mode 100644
index 000000000000..28c32a2430f8
--- /dev/null
+++ b/contrib/tzcode/tzselect.ksh
@@ -0,0 +1,586 @@
+#!/bin/bash
+# Ask the user about the time zone, and output the resulting TZ value to stdout.
+# Interact with the user via stderr and stdin.
+
+PKGVERSION='(tzcode) '
+TZVERSION=see_Makefile
+REPORT_BUGS_TO=tz@iana.org
+
+# Contributed by Paul Eggert. This file is in the public domain.
+
+# Porting notes:
+#
+# This script requires a Posix-like shell and prefers the extension of a
+# 'select' statement. The 'select' statement was introduced in the
+# Korn shell and is available in Bash and other shell implementations.
+# If your host lacks both Bash and the Korn shell, you can get their
+# source from one of these locations:
+#
+# Bash <https://www.gnu.org/software/bash/>
+# Korn Shell <http://www.kornshell.com/>
+# MirBSD Korn Shell <http://www.mirbsd.org/mksh.htm>
+#
+# For portability to Solaris 10 /bin/sh (supported by Oracle through
+# January 2024) this script avoids some POSIX features and common
+# extensions, such as $(...) (which works sometimes but not others),
+# $((...)), ! CMD, ${#ID}, ${ID##PAT}, ${ID%%PAT}, and $10.
+
+#
+# This script also uses several features of modern awk programs.
+# If your host lacks awk, or has an old awk that does not conform to Posix,
+# you can use either of the following free programs instead:
+#
+# Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
+# mawk <https://invisible-island.net/mawk/>
+# nawk <https://github.com/onetrueawk/awk>
+
+
+# Specify default values for environment variables if they are unset.
+: ${AWK=awk}
+: ${TZDIR=`pwd`}
+
+# Output one argument as-is to standard output.
+# Safer than 'echo', which can mishandle '\' or leading '-'.
+say() {
+ printf '%s\n' "$1"
+}
+
+# Check for awk Posix compliance.
+($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
+[ $? = 123 ] || {
+ say >&2 "$0: Sorry, your '$AWK' program is not Posix compatible."
+ exit 1
+}
+
+coord=
+location_limit=10
+zonetabtype=zone1970
+
+usage="Usage: tzselect [--version] [--help] [-c COORD] [-n LIMIT]
+Select a timezone interactively.
+
+Options:
+
+ -c COORD
+ Instead of asking for continent and then country and then city,
+ ask for selection from time zones whose largest cities
+ are closest to the location with geographical coordinates COORD.
+ COORD should use ISO 6709 notation, for example, '-c +4852+00220'
+ for Paris (in degrees and minutes, North and East), or
+ '-c -35-058' for Buenos Aires (in degrees, South and West).
+
+ -n LIMIT
+ Display at most LIMIT locations when -c is used (default $location_limit).
+
+ --version
+ Output version information.
+
+ --help
+ Output this help.
+
+Report bugs to $REPORT_BUGS_TO."
+
+# Ask the user to select from the function's arguments,
+# and assign the selected argument to the variable 'select_result'.
+# Exit on EOF or I/O error. Use the shell's 'select' builtin if available,
+# falling back on a less-nice but portable substitute otherwise.
+if
+ case $BASH_VERSION in
+ ?*) : ;;
+ '')
+ # '; exit' should be redundant, but Dash doesn't properly fail without it.
+ (eval 'set --; select x; do break; done; exit') </dev/null 2>/dev/null
+ esac
+then
+ # Do this inside 'eval', as otherwise the shell might exit when parsing it
+ # even though it is never executed.
+ eval '
+ doselect() {
+ select select_result
+ do
+ case $select_result in
+ "") echo >&2 "Please enter a number in range." ;;
+ ?*) break
+ esac
+ done || exit
+ }
+ '
+else
+ doselect() {
+ # Field width of the prompt numbers.
+ select_width=`expr $# : '.*'`
+
+ select_i=
+
+ while :
+ do
+ case $select_i in
+ '')
+ select_i=0
+ for select_word
+ do
+ select_i=`expr $select_i + 1`
+ printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
+ done ;;
+ *[!0-9]*)
+ echo >&2 'Please enter a number in range.' ;;
+ *)
+ if test 1 -le $select_i && test $select_i -le $#; then
+ shift `expr $select_i - 1`
+ select_result=$1
+ break
+ fi
+ echo >&2 'Please enter a number in range.'
+ esac
+
+ # Prompt and read input.
+ printf >&2 %s "${PS3-#? }"
+ read select_i || exit
+ done
+ }
+fi
+
+while getopts c:n:t:-: opt
+do
+ case $opt$OPTARG in
+ c*)
+ coord=$OPTARG ;;
+ n*)
+ location_limit=$OPTARG ;;
+ t*) # Undocumented option, used for developer testing.
+ zonetabtype=$OPTARG ;;
+ -help)
+ exec echo "$usage" ;;
+ -version)
+ exec echo "tzselect $PKGVERSION$TZVERSION" ;;
+ -*)
+ say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;;
+ *)
+ say >&2 "$0: try '$0 --help'"; exit 1 ;;
+ esac
+done
+
+shift `expr $OPTIND - 1`
+case $# in
+0) ;;
+*) say >&2 "$0: $1: unknown argument"; exit 1 ;;
+esac
+
+# Make sure the tables are readable.
+TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
+TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab
+for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
+do
+ <"$f" || {
+ say >&2 "$0: time zone files are not set up correctly"
+ exit 1
+ }
+done
+
+# If the current locale does not support UTF-8, convert data to current
+# locale's format if possible, as the shell aligns columns better that way.
+# Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI.
+$AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' || {
+ { tmp=`(mktemp -d) 2>/dev/null` || {
+ tmp=${TMPDIR-/tmp}/tzselect.$$ &&
+ (umask 77 && mkdir -- "$tmp")
+ };} &&
+ trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM &&
+ (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \
+ 2>/dev/null &&
+ TZ_COUNTRY_TABLE=$tmp/iso3166.tab &&
+ iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab &&
+ TZ_ZONE_TABLE=$tmp/$zonetabtype.tab
+}
+
+newline='
+'
+IFS=$newline
+
+
+# Awk script to read a time zone table and output the same table,
+# with each column preceded by its distance from 'here'.
+output_distances='
+ BEGIN {
+ FS = "\t"
+ while (getline <TZ_COUNTRY_TABLE)
+ if ($0 ~ /^[^#]/)
+ country[$1] = $2
+ country["US"] = "US" # Otherwise the strings get too long.
+ }
+ function abs(x) {
+ return x < 0 ? -x : x;
+ }
+ function min(x, y) {
+ return x < y ? x : y;
+ }
+ function convert_coord(coord, deg, minute, ilen, sign, sec) {
+ if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9][0-9][0-9]([^0-9]|$)/) {
+ degminsec = coord
+ intdeg = degminsec < 0 ? -int(-degminsec / 10000) : int(degminsec / 10000)
+ minsec = degminsec - intdeg * 10000
+ intmin = minsec < 0 ? -int(-minsec / 100) : int(minsec / 100)
+ sec = minsec - intmin * 100
+ deg = (intdeg * 3600 + intmin * 60 + sec) / 3600
+ } else if (coord ~ /^[-+]?[0-9]?[0-9][0-9][0-9][0-9]([^0-9]|$)/) {
+ degmin = coord
+ intdeg = degmin < 0 ? -int(-degmin / 100) : int(degmin / 100)
+ minute = degmin - intdeg * 100
+ deg = (intdeg * 60 + minute) / 60
+ } else
+ deg = coord
+ return deg * 0.017453292519943296
+ }
+ function convert_latitude(coord) {
+ match(coord, /..*[-+]/)
+ return convert_coord(substr(coord, 1, RLENGTH - 1))
+ }
+ function convert_longitude(coord) {
+ match(coord, /..*[-+]/)
+ return convert_coord(substr(coord, RLENGTH))
+ }
+ # Great-circle distance between points with given latitude and longitude.
+ # Inputs and output are in radians. This uses the great-circle special
+ # case of the Vicenty formula for distances on ellipsoids.
+ function gcdist(lat1, long1, lat2, long2, dlong, x, y, num, denom) {
+ dlong = long2 - long1
+ x = cos(lat2) * sin(dlong)
+ y = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dlong)
+ num = sqrt(x * x + y * y)
+ denom = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(dlong)
+ return atan2(num, denom)
+ }
+ # Parallel distance between points with given latitude and longitude.
+ # This is the product of the longitude difference and the cosine
+ # of the latitude of the point that is further from the equator.
+ # I.e., it considers longitudes to be further apart if they are
+ # nearer the equator.
+ function pardist(lat1, long1, lat2, long2) {
+ return abs(long1 - long2) * min(cos(lat1), cos(lat2))
+ }
+ # The distance function is the sum of the great-circle distance and
+ # the parallel distance. It could be weighted.
+ function dist(lat1, long1, lat2, long2) {
+ return gcdist(lat1, long1, lat2, long2) + pardist(lat1, long1, lat2, long2)
+ }
+ BEGIN {
+ coord_lat = convert_latitude(coord)
+ coord_long = convert_longitude(coord)
+ }
+ /^[^#]/ {
+ here_lat = convert_latitude($2)
+ here_long = convert_longitude($2)
+ line = $1 "\t" $2 "\t" $3
+ sep = "\t"
+ ncc = split($1, cc, /,/)
+ for (i = 1; i <= ncc; i++) {
+ line = line sep country[cc[i]]
+ sep = ", "
+ }
+ if (NF == 4)
+ line = line " - " $4
+ printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
+ }
+'
+
+# Begin the main loop. We come back here if the user wants to retry.
+while
+
+ echo >&2 'Please identify a location' \
+ 'so that time zone rules can be set correctly.'
+
+ continent=
+ country=
+ region=
+
+ case $coord in
+ ?*)
+ continent=coord;;
+ '')
+
+ # Ask the user for continent or ocean.
+
+ echo >&2 'Please select a continent, ocean, "coord", or "TZ".'
+
+ quoted_continents=`
+ $AWK '
+ function handle_entry(entry) {
+ entry = substr(entry, 1, index(entry, "/") - 1)
+ if (entry == "America")
+ entry = entry "s"
+ if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
+ entry = entry " Ocean"
+ printf "'\''%s'\''\n", entry
+ }
+ BEGIN { FS = "\t" }
+ /^[^#]/ {
+ handle_entry($3)
+ }
+ /^#@/ {
+ ncont = split($2, cont, /,/)
+ for (ci = 1; ci <= ncont; ci++) {
+ handle_entry(cont[ci])
+ }
+ }
+ ' <"$TZ_ZONE_TABLE" |
+ sort -u |
+ tr '\n' ' '
+ echo ''
+ `
+
+ eval '
+ doselect '"$quoted_continents"' \
+ "coord - I want to use geographical coordinates." \
+ "TZ - I want to specify the timezone using the Posix TZ format."
+ continent=$select_result
+ case $continent in
+ Americas) continent=America;;
+ *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''`
+ esac
+ '
+ esac
+
+ case $continent in
+ TZ)
+ # Ask the user for a Posix TZ string. Check that it conforms.
+ while
+ echo >&2 'Please enter the desired value' \
+ 'of the TZ environment variable.'
+ echo >&2 'For example, AEST-10 is abbreviated' \
+ 'AEST and is 10 hours'
+ echo >&2 'ahead (east) of Greenwich,' \
+ 'with no daylight saving time.'
+ read TZ
+ $AWK -v TZ="$TZ" 'BEGIN {
+ tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})"
+ time = "(2[0-4]|[0-1]?[0-9])" \
+ "(:[0-5][0-9](:[0-5][0-9])?)?"
+ offset = "[-+]?" time
+ mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
+ jdate = "((J[1-9]|[0-9]|J?[1-9][0-9]" \
+ "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])"
+ datetime = ",(" mdate "|" jdate ")(/" time ")?"
+ tzpattern = "^(:.*|" tzname offset "(" tzname \
+ "(" offset ")?(" datetime datetime ")?)?)$"
+ if (TZ ~ tzpattern) exit 1
+ exit 0
+ }'
+ do
+ say >&2 "'$TZ' is not a conforming Posix timezone string."
+ done
+ TZ_for_date=$TZ;;
+ *)
+ case $continent in
+ coord)
+ case $coord in
+ '')
+ echo >&2 'Please enter coordinates' \
+ 'in ISO 6709 notation.'
+ echo >&2 'For example, +4042-07403 stands for'
+ echo >&2 '40 degrees 42 minutes north,' \
+ '74 degrees 3 minutes west.'
+ read coord;;
+ esac
+ distance_table=`$AWK \
+ -v coord="$coord" \
+ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+ "$output_distances" <"$TZ_ZONE_TABLE" |
+ sort -n |
+ sed "${location_limit}q"
+ `
+ regions=`say "$distance_table" | $AWK '
+ BEGIN { FS = "\t" }
+ { print $NF }
+ '`
+ echo >&2 'Please select one of the following timezones,' \
+ echo >&2 'listed roughly in increasing order' \
+ "of distance from $coord".
+ doselect $regions
+ region=$select_result
+ TZ=`say "$distance_table" | $AWK -v region="$region" '
+ BEGIN { FS="\t" }
+ $NF == region { print $4 }
+ '`
+ ;;
+ *)
+ # Get list of names of countries in the continent or ocean.
+ countries=`$AWK \
+ -v continent_re="^$continent/" \
+ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+ '
+ BEGIN { FS = "\t" }
+ /^#$/ { next }
+ /^#[^@]/ { next }
+ {
+ commentary = $0 ~ /^#@/
+ if (commentary) {
+ col1ccs = substr($1, 3)
+ conts = $2
+ } else {
+ col1ccs = $1
+ conts = $3
+ }
+ ncc = split(col1ccs, cc, /,/)
+ ncont = split(conts, cont, /,/)
+ for (i = 1; i <= ncc; i++) {
+ elsewhere = commentary
+ for (ci = 1; ci <= ncont; ci++) {
+ if (cont[ci] ~ continent_re) {
+ if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i]
+ elsewhere = 0
+ }
+ }
+ if (elsewhere) {
+ for (i = 1; i <= ncc; i++) {
+ cc_elsewhere[cc[i]] = 1
+ }
+ }
+ }
+ }
+ END {
+ while (getline <TZ_COUNTRY_TABLE) {
+ if ($0 !~ /^#/) cc_name[$1] = $2
+ }
+ for (i = 1; i <= ccs; i++) {
+ country = cc_list[i]
+ if (cc_elsewhere[country]) continue
+ if (cc_name[country]) {
+ country = cc_name[country]
+ }
+ print country
+ }
+ }
+ ' <"$TZ_ZONE_TABLE" | sort -f`
+
+
+ # If there's more than one country, ask the user which one.
+ case $countries in
+ *"$newline"*)
+ echo >&2 'Please select a country' \
+ 'whose clocks agree with yours.'
+ doselect $countries
+ country=$select_result;;
+ *)
+ country=$countries
+ esac
+
+
+ # Get list of timezones in the country.
+ regions=`$AWK \
+ -v country="$country" \
+ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+ '
+ BEGIN {
+ FS = "\t"
+ cc = country
+ while (getline <TZ_COUNTRY_TABLE) {
+ if ($0 !~ /^#/ && country == $2) {
+ cc = $1
+ break
+ }
+ }
+ }
+ /^#/ { next }
+ $1 ~ cc { print $4 }
+ ' <"$TZ_ZONE_TABLE"`
+
+
+ # If there's more than one region, ask the user which one.
+ case $regions in
+ *"$newline"*)
+ echo >&2 'Please select one of the following timezones.'
+ doselect $regions
+ region=$select_result;;
+ *)
+ region=$regions
+ esac
+
+ # Determine TZ from country and region.
+ TZ=`$AWK \
+ -v country="$country" \
+ -v region="$region" \
+ -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
+ '
+ BEGIN {
+ FS = "\t"
+ cc = country
+ while (getline <TZ_COUNTRY_TABLE) {
+ if ($0 !~ /^#/ && country == $2) {
+ cc = $1
+ break
+ }
+ }
+ }
+ /^#/ { next }
+ $1 ~ cc && $4 == region { print $3 }
+ ' <"$TZ_ZONE_TABLE"`
+ esac
+
+ # Make sure the corresponding zoneinfo file exists.
+ TZ_for_date=$TZDIR/$TZ
+ <"$TZ_for_date" || {
+ say >&2 "$0: time zone files are not set up correctly"
+ exit 1
+ }
+ esac
+
+
+ # Use the proposed TZ to output the current date relative to UTC.
+ # Loop until they agree in seconds.
+ # Give up after 8 unsuccessful tries.
+
+ extra_info=
+ for i in 1 2 3 4 5 6 7 8
+ do
+ TZdate=`LANG=C TZ="$TZ_for_date" date`
+ UTdate=`LANG=C TZ=UTC0 date`
+ TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'`
+ UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'`
+ case $TZsec in
+ $UTsec)
+ extra_info="
+Selected time is now: $TZdate.
+Universal Time is now: $UTdate."
+ break
+ esac
+ done
+
+
+ # Output TZ info and ask the user to confirm.
+
+ echo >&2 ""
+ echo >&2 "The following information has been given:"
+ echo >&2 ""
+ case $country%$region%$coord in
+ ?*%?*%) say >&2 " $country$newline $region";;
+ ?*%%) say >&2 " $country";;
+ %?*%?*) say >&2 " coord $coord$newline $region";;
+ %%?*) say >&2 " coord $coord";;
+ *) say >&2 " TZ='$TZ'"
+ esac
+ say >&2 ""
+ say >&2 "Therefore TZ='$TZ' will be used.$extra_info"
+ say >&2 "Is the above information OK?"
+
+ doselect Yes No
+ ok=$select_result
+ case $ok in
+ Yes) break
+ esac
+do coord=
+done
+
+case $SHELL in
+*csh) file=.login line="setenv TZ '$TZ'";;
+*) file=.profile line="TZ='$TZ'; export TZ"
+esac
+
+test -t 1 && say >&2 "
+You can make this change permanent for yourself by appending the line
+ $line
+to the file '$file' in your home directory; then log out and log in again.
+
+Here is that TZ value again, this time on standard output so that you
+can use the $0 command in shell scripts:"
+
+say "$TZ"
diff --git a/contrib/tzcode/version b/contrib/tzcode/version
new file mode 100644
index 000000000000..b74fa117a223
--- /dev/null
+++ b/contrib/tzcode/version
@@ -0,0 +1 @@
+2022g
diff --git a/contrib/tzcode/version.h b/contrib/tzcode/version.h
new file mode 100644
index 000000000000..12997b874b5e
--- /dev/null
+++ b/contrib/tzcode/version.h
@@ -0,0 +1,3 @@
+static char const PKGVERSION[]="(tzcode) ";
+static char const TZVERSION[]="2022g";
+static char const REPORT_BUGS_TO[]="tz@iana.org";
diff --git a/contrib/tzcode/workman.sh b/contrib/tzcode/workman.sh
new file mode 100644
index 000000000000..6e2da3a80c8d
--- /dev/null
+++ b/contrib/tzcode/workman.sh
@@ -0,0 +1,41 @@
+#! /bin/sh
+# Convert manual page troff stdin to formatted .txt stdout.
+
+# This file is in the public domain, so clarified as of
+# 2009-05-17 by Arthur David Olson.
+
+if (type nroff && type perl) >/dev/null 2>&1; then
+
+ # Tell groff not to emit SGR escape sequences (ANSI color escapes).
+ GROFF_NO_SGR=1
+ export GROFF_NO_SGR
+
+ echo ".am TH
+.hy 0
+.na
+..
+.rm }H
+.rm }F" | nroff -man - ${1+"$@"} | perl -ne '
+ binmode STDIN, '\'':encoding(utf8)'\'';
+ binmode STDOUT, '\'':encoding(utf8)'\'';
+ chomp;
+ s/.\010//g;
+ s/\s*$//;
+ if (/^$/) {
+ $sawblank = 1;
+ next;
+ } else {
+ if ($sawblank && $didprint) {
+ print "\n";
+ $sawblank = 0;
+ }
+ print "$_\n";
+ $didprint = 1;
+ }
+ '
+elif (type mandoc && type col) >/dev/null 2>&1; then
+ mandoc -man -T ascii "$@" | col -bx
+else
+ echo >&2 "$0: please install nroff and perl, or mandoc and col"
+ exit 1
+fi
diff --git a/contrib/tzcode/zdump.8 b/contrib/tzcode/zdump.8
new file mode 100644
index 000000000000..0b9e956526cc
--- /dev/null
+++ b/contrib/tzcode/zdump.8
@@ -0,0 +1,231 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 15, 2022
+.Dt ZDUMP 8
+.Os
+.Sh NAME
+.Nm zdump
+.Nd timezone dumper
+.Sh SYNOPSIS
+.Nm
+.Op Fl -help
+.Op Fl -version
+.Op Fl ivV
+.Oo
+.Fl c
+.Op Ar loyear , Ns
+.Ar hiyear
+.Oc
+.Oo
+.Fl t
+.Op Ar lotime , Ns
+.Ar hitime
+.Oc
+.Op Ar timezone ...
+.Sh DESCRIPTION
+The
+.Nm
+program prints the current time in each
+.Ar timezone
+named on the command line.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl -version
+Output version information and exit.
+.It Fl -help
+Output short usage message and exit.
+.It Fl i
+Output a description of time intervals.
+For each
+.Ar timezone
+on the command line, output an interval-format description of the
+timezone.
+See
+.Sx "INTERVAL FORMAT"
+below.
+.It Fl v
+Output a verbose description of time intervals.
+For each
+.Ar timezone
+on the command line,
+print the times at the two extreme time values,
+the times (if present) at and just beyond the boundaries of years that
+.Xr localtime 3
+and
+.Xr gmtime 3
+can represent, and
+the times both one second before and exactly at
+each detected time discontinuity.
+Each line is followed by
+.Cm isdst= Ns Ar D
+where
+.Ar D
+is positive, zero, or negative depending on whether
+the given time is daylight saving time, standard time,
+or an unknown time type, respectively.
+Each line is also followed by
+.Cm gmtoff= Ns Ar N
+if the given local time is known to be
+.Ar N
+seconds east of Greenwich.
+.It Fl V
+Like
+.Fl v ,
+except omit output concerning extreme time and year values.
+This generates output that is easier to compare to that of
+implementations with different time representations.
+.It Fl c Oo Ar loyear , Oc Ns Ar hiyear
+Cut off interval output at the given year(s).
+Cutoff times are computed using the proleptic Gregorian calendar with year 0
+and with Universal Time (UT) ignoring leap seconds.
+Cutoffs are at the start of each year, where the lower-bound
+timestamp is inclusive and the upper is exclusive; for example,
+.Ql "-c 1970,2070"
+selects transitions on or after 1970-01-01 00:00:00 UTC
+and before 2070-01-01 00:00:00 UTC.
+The default cutoff is
+.Ql -500,2500 .
+.It Fl t Oo Ar lotime , Oc Ns Ar hitime
+Cut off interval output at the given time(s),
+given in decimal seconds since 1970-01-01 00:00:00
+Coordinated Universal Time (UTC).
+The
+.Ar timezone
+determines whether the count includes leap seconds.
+As with
+.Fl c ,
+the cutoff's lower bound is inclusive and its upper bound is exclusive.
+.El
+.Sh "INTERVAL FORMAT"
+The interval format is a compact text representation that is intended
+to be both human- and machine-readable.
+It consists of an empty line,
+then a line
+.Dq "TZ=\fIstring\fP"
+where
+.Ar string
+is a double-quoted string giving the timezone, a second line
+.Dq "\*- \*- \fIinterval\fP"
+describing the time interval before the first transition if any, and
+zero or more following lines
+.Dq "\fIdate time interval\fP",
+one line for each transition time and following interval.
+Fields are
+separated by single tabs.
+.Pp
+Dates are in
+.Ql "yyyy - mm - dd"
+format and times are in 24-hour
+.Ql "hh : mm : ss"
+format where
+.Ql "hh <24" .
+Times are in local time immediately after the transition.
+A
+time interval description consists of a UT offset in signed
+.Ql "\(+- hhmmss"
+format, a time zone abbreviation, and an isdst flag.
+An abbreviation
+that equals the UT offset is omitted; other abbreviations are
+double-quoted strings unless they consist of one or more alphabetic
+characters.
+An isdst flag is omitted for standard time, and otherwise
+is a decimal integer that is unsigned and positive (typically 1) for
+daylight saving time and negative for unknown.
+.Pp
+In times and in UT offsets with absolute value less than 100 hours,
+the seconds are omitted if they are zero, and
+the minutes are also omitted if they are also zero.
+Positive UT
+offsets are east of Greenwich.
+The UT offset \*-00 denotes a UT
+placeholder in areas where the actual offset is unspecified; by
+convention, this occurs when the UT offset is zero and the time zone
+abbreviation begins with
+.Dq "-"
+or is
+.Dq "zzz".
+.Pp
+In double-quoted strings, escape sequences represent unusual
+characters.
+The escape sequences are \es for space, and \e", \e\e,
+\ef, \en, \er, \et, and \ev with their usual meaning in the C
+programming language.
+E.g., the double-quoted string
+\*(lq"CET\es\e"\e\e"\*(rq represents the character sequence \*(lqCET
+"\e\*(rq.\""
+.Pp
+Here is an example of the output, with the leading empty line omitted.
+(This example is shown with tab stops set far enough apart so that the
+tabbed columns line up.)
+.Bd -literal -offset indent
+TZ="Pacific/Honolulu"
+- - -103126 LMT
+1896-01-13 12:01:26 -1030 HST
+1933-04-30 03 -0930 HDT 1
+1933-05-21 11 -1030 HST
+1942-02-09 03 -0930 HWT 1
+1945-08-14 13:30 -0930 HPT 1
+1945-09-30 01 -1030 HST
+1947-06-08 02:30 -10 HST
+.Ed
+.Pp
+Here, local time begins 10 hours, 31 minutes and 26 seconds west of
+UT, and is a standard time abbreviated LMT.
+Immediately after the
+first transition, the date is 1896-01-13 and the time is 12:01:26, and
+the following time interval is 10.5 hours west of UT, a standard time
+abbreviated HST.
+Immediately after the second transition, the date is
+1933-04-30 and the time is 03:00:00 and the following time interval is
+9.5 hours west of UT, is abbreviated HDT, and is daylight saving time.
+Immediately after the last transition the date is 1947-06-08 and the
+time is 02:30:00, and the following time interval is 10 hours west of
+UT, a standard time abbreviated HST.
+.Pp
+Here are excerpts from another example:
+.Bd -literal -offset indent
+TZ="Europe/Astrakhan"
+- - +031212 LMT
+1924-04-30 23:47:48 +03
+1930-06-21 01 +04
+1981-04-01 01 +05 1
+1981-09-30 23 +04
+\&...
+2014-10-26 01 +03
+2016-03-27 03 +04
+.Ed
+.Pp
+This time zone is east of UT, so its UT offsets are positive.
+Also,
+many of its time zone abbreviations are omitted since they duplicate
+the text of the UT offset.
+.Sh LIMITATIONS
+Time discontinuities are found by sampling the results returned by
+.Xr localtime 3
+at twelve-hour intervals.
+This works in all real-world cases;
+one can construct artificial time zones for which this fails.
+.Pp
+In the
+.Fl v
+and
+.Fl V
+output,
+.Dq "UT"
+denotes the value returned by
+.Xr gmtime 3 ,
+which uses UTC for modern timestamps and some other UT flavor for
+timestamps that predate the introduction of UTC.
+No attempt is currently made to have the output use
+.Dq "UTC"
+for newer and
+.Dq "UT"
+for older timestamps, partly because the exact date of the
+introduction of UTC is problematic.
+.Sh SEE ALSO
+.Xr tzfile 5 ,
+.Xr zic 8
diff --git a/contrib/tzcode/zdump.c b/contrib/tzcode/zdump.c
new file mode 100644
index 000000000000..d5ddfe537960
--- /dev/null
+++ b/contrib/tzcode/zdump.c
@@ -0,0 +1,1257 @@
+/* Dump time zone data in a textual format. */
+
+/*
+** This file is in the public domain, so clarified as of
+** 2009-05-17 by Arthur David Olson.
+*/
+
+#include "version.h"
+
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+
+#include "private.h"
+#include <stdio.h>
+
+#ifndef HAVE_SNPRINTF
+# define HAVE_SNPRINTF (199901 <= __STDC_VERSION__)
+#endif
+
+#ifndef HAVE_LOCALTIME_R
+# define HAVE_LOCALTIME_R 1
+#endif
+
+#ifndef HAVE_LOCALTIME_RZ
+# ifdef TM_ZONE
+# define HAVE_LOCALTIME_RZ (NETBSD_INSPIRED && USE_LTZ)
+# else
+# define HAVE_LOCALTIME_RZ 0
+# endif
+#endif
+
+#ifndef HAVE_TZSET
+# define HAVE_TZSET 1
+#endif
+
+#ifndef ZDUMP_LO_YEAR
+# define ZDUMP_LO_YEAR (-500)
+#endif /* !defined ZDUMP_LO_YEAR */
+
+#ifndef ZDUMP_HI_YEAR
+# define ZDUMP_HI_YEAR 2500
+#endif /* !defined ZDUMP_HI_YEAR */
+
+#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
+#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
+#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
+ + SECSPERLYEAR * (intmax_t) (100 - 3))
+
+/*
+** True if SECSPER400YEARS is known to be representable as an
+** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false
+** even if SECSPER400YEARS is representable, because when that happens
+** the code merely runs a bit more slowly, and this slowness doesn't
+** occur on any practical platform.
+*/
+enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
+
+#if HAVE_GETTEXT
+# include <locale.h> /* for setlocale */
+#endif /* HAVE_GETTEXT */
+
+#if ! HAVE_LOCALTIME_RZ
+# undef timezone_t
+# define timezone_t char **
+#endif
+
+#if !HAVE_POSIX_DECLS
+extern int getopt(int argc, char * const argv[],
+ const char * options);
+extern char * optarg;
+extern int optind;
+#endif
+
+/* The minimum and maximum finite time values. */
+enum { atime_shift = CHAR_BIT * sizeof(time_t) - 2 };
+static time_t const absolute_min_time =
+ ((time_t) -1 < 0
+ ? (- ((time_t) ~ (time_t) 0 < 0)
+ - (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)))
+ : 0);
+static time_t const absolute_max_time =
+ ((time_t) -1 < 0
+ ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
+ : -1);
+static size_t longest;
+static char const *progname;
+static bool warned;
+static bool errout;
+
+static char const *abbr(struct tm const *);
+static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_REPRODUCIBLE;
+static void dumptime(struct tm const *);
+static time_t hunt(timezone_t, time_t, time_t, bool);
+static void show(timezone_t, char *, time_t, bool);
+static void showextrema(timezone_t, char *, time_t, struct tm *, time_t);
+static void showtrans(char const *, struct tm const *, time_t, char const *,
+ char const *);
+static const char *tformat(void);
+static time_t yeartot(intmax_t) ATTRIBUTE_REPRODUCIBLE;
+
+/* Is C an ASCII digit? */
+static bool
+is_digit(char c)
+{
+ return '0' <= c && c <= '9';
+}
+
+/* Is A an alphabetic character in the C locale? */
+static bool
+is_alpha(char a)
+{
+ switch (a) {
+ default:
+ return false;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ return true;
+ }
+}
+
+static ATTRIBUTE_NORETURN void
+size_overflow(void)
+{
+ fprintf(stderr, _("%s: size overflow\n"), progname);
+ exit(EXIT_FAILURE);
+}
+
+/* Return A + B, exiting if the result would overflow either ptrdiff_t
+ or size_t. */
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+sumsize(size_t a, size_t b)
+{
+#ifdef ckd_add
+ ptrdiff_t sum;
+ if (!ckd_add(&sum, a, b) /* && sum <= SIZE_MAX */)
+ return sum;
+#else
+ ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX);
+ if (a <= sum_max && b <= sum_max - a)
+ return a + b;
+#endif
+ size_overflow();
+}
+
+/* Return a pointer to a newly allocated buffer of size SIZE, exiting
+ on failure. SIZE should be nonzero. */
+static void * ATTRIBUTE_MALLOC
+xmalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (!p) {
+ fprintf(stderr, _("%s: Memory exhausted\n"), progname);
+ exit(EXIT_FAILURE);
+ }
+ return p;
+}
+
+#if ! HAVE_TZSET
+# undef tzset
+# define tzset zdump_tzset
+static void tzset(void) { }
+#endif
+
+/* Assume gmtime_r works if localtime_r does.
+ A replacement localtime_r is defined below if needed. */
+#if ! HAVE_LOCALTIME_R
+
+# undef gmtime_r
+# define gmtime_r zdump_gmtime_r
+
+static struct tm *
+gmtime_r(time_t *tp, struct tm *tmp)
+{
+ struct tm *r = gmtime(tp);
+ if (r) {
+ *tmp = *r;
+ r = tmp;
+ }
+ return r;
+}
+
+#endif
+
+/* Platforms with TM_ZONE don't need tzname, so they can use the
+ faster localtime_rz or localtime_r if available. */
+
+#if defined TM_ZONE && HAVE_LOCALTIME_RZ
+# define USE_LOCALTIME_RZ true
+#else
+# define USE_LOCALTIME_RZ false
+#endif
+
+#if ! USE_LOCALTIME_RZ
+
+# if !defined TM_ZONE || ! HAVE_LOCALTIME_R || ! HAVE_TZSET
+# undef localtime_r
+# define localtime_r zdump_localtime_r
+static struct tm *
+localtime_r(time_t *tp, struct tm *tmp)
+{
+ struct tm *r = localtime(tp);
+ if (r) {
+ *tmp = *r;
+ r = tmp;
+ }
+ return r;
+}
+# endif
+
+# undef localtime_rz
+# define localtime_rz zdump_localtime_rz
+static struct tm *
+localtime_rz(timezone_t rz __unused, time_t *tp, struct tm *tmp)
+{
+ return localtime_r(tp, tmp);
+}
+
+# ifdef TYPECHECK
+# undef mktime_z
+# define mktime_z zdump_mktime_z
+static time_t
+mktime_z(timezone_t tz, struct tm *tmp)
+{
+ return mktime(tmp);
+}
+# endif
+
+# undef tzalloc
+# undef tzfree
+# define tzalloc zdump_tzalloc
+# define tzfree zdump_tzfree
+
+static timezone_t
+tzalloc(char const *val)
+{
+# if HAVE_SETENV
+ if (setenv("TZ", val, 1) != 0) {
+ perror("setenv");
+ exit(EXIT_FAILURE);
+ }
+ tzset();
+ return &optarg; /* Any valid non-null char ** will do. */
+# else
+ enum { TZeqlen = 3 };
+ static char const TZeq[TZeqlen] = "TZ=";
+ static char **fakeenv;
+ static ptrdiff_t fakeenv0size;
+ void *freeable = NULL;
+ char **env = fakeenv, **initial_environ;
+ size_t valsize = strlen(val) + 1;
+ if (fakeenv0size < valsize) {
+ char **e = environ, **to;
+ ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */
+
+ while (*e++) {
+# ifdef ckd_add
+ if (ckd_add(&initial_nenvptrs, initial_envptrs, 1)
+ || SIZE_MAX < initial_envptrs)
+ size_overflow();
+# else
+ if (initial_nenvptrs == min(PTRDIFF_MAX, SIZE_MAX) / sizeof *environ)
+ size_overflow();
+ initial_nenvptrs++;
+# endif
+ }
+ fakeenv0size = sumsize(valsize, valsize);
+ fakeenv0size = max(fakeenv0size, 64);
+ freeable = env;
+ fakeenv = env =
+ xmalloc(sumsize(sumsize(sizeof *environ,
+ initial_nenvptrs * sizeof *environ),
+ sumsize(TZeqlen, fakeenv0size)));
+ to = env + 1;
+ for (e = environ; (*to = *e); e++)
+ to += strncmp(*e, TZeq, TZeqlen) != 0;
+ env[0] = memcpy(to + 1, TZeq, TZeqlen);
+ }
+ memcpy(env[0] + TZeqlen, val, valsize);
+ initial_environ = environ;
+ environ = env;
+ tzset();
+ free(freeable);
+ return initial_environ;
+# endif
+}
+
+static void
+tzfree(timezone_t initial_environ)
+{
+# if !HAVE_SETENV
+ environ = initial_environ;
+ tzset();
+# else
+ (void)initial_environ;
+# endif
+}
+#endif /* ! USE_LOCALTIME_RZ */
+
+/* A UT time zone, and its initializer. */
+static timezone_t gmtz;
+static void
+gmtzinit(void)
+{
+ if (USE_LOCALTIME_RZ) {
+ /* Try "GMT" first to find out whether this is one of the rare
+ platforms where time_t counts leap seconds; this works due to
+ the "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT"
+ fails, fall back on "GMT0" which might be similar due to the
+ "Link GMT GMT0" line in the "backward" file, and which
+ should work on all POSIX platforms. The rest of zdump does not
+ use the "GMT" abbreviation that comes from this setting, so it
+ is OK to use "GMT" here rather than the more-modern "UTC" which
+ would not work on platforms that omit the "backward" file. */
+ gmtz = tzalloc("GMT");
+ if (!gmtz) {
+ static char const gmt0[] = "GMT0";
+ gmtz = tzalloc(gmt0);
+ if (!gmtz) {
+ perror(gmt0);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+}
+
+/* Convert *TP to UT, storing the broken-down time into *TMP.
+ Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP),
+ except typically faster if USE_LOCALTIME_RZ. */
+static struct tm *
+my_gmtime_r(time_t *tp, struct tm *tmp)
+{
+ return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
+}
+
+#ifndef TYPECHECK
+# define my_localtime_rz localtime_rz
+#else /* !defined TYPECHECK */
+
+static struct tm *
+my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
+{
+ tmp = localtime_rz(tz, tp, tmp);
+ if (tmp) {
+ struct tm tm;
+ register time_t t;
+
+ tm = *tmp;
+ t = mktime_z(tz, &tm);
+ if (t != *tp) {
+ fflush(stdout);
+ fprintf(stderr, "\n%s: ", progname);
+ fprintf(stderr, tformat(), *tp);
+ fprintf(stderr, " ->");
+ fprintf(stderr, " year=%d", tmp->tm_year);
+ fprintf(stderr, " mon=%d", tmp->tm_mon);
+ fprintf(stderr, " mday=%d", tmp->tm_mday);
+ fprintf(stderr, " hour=%d", tmp->tm_hour);
+ fprintf(stderr, " min=%d", tmp->tm_min);
+ fprintf(stderr, " sec=%d", tmp->tm_sec);
+ fprintf(stderr, " isdst=%d", tmp->tm_isdst);
+ fprintf(stderr, " -> ");
+ fprintf(stderr, tformat(), t);
+ fprintf(stderr, "\n");
+ errout = true;
+ }
+ }
+ return tmp;
+}
+#endif /* !defined TYPECHECK */
+
+static void
+abbrok(const char *const abbrp, const char *const zone)
+{
+ register const char * cp;
+ register const char * wp;
+
+ if (warned)
+ return;
+ cp = abbrp;
+ while (is_alpha(*cp) || is_digit(*cp) || *cp == '-' || *cp == '+')
+ ++cp;
+ if (*cp)
+ wp = _("has characters other than ASCII alphanumerics, '-' or '+'");
+ else if (cp - abbrp < 3)
+ wp = _("has fewer than 3 characters");
+ else if (cp - abbrp > 6)
+ wp = _("has more than 6 characters");
+ else
+ return;
+ fflush(stdout);
+ fprintf(stderr,
+ _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
+ progname, zone, abbrp, wp);
+ warned = errout = true;
+}
+
+/* Return a time zone abbreviation. If the abbreviation needs to be
+ saved, use *BUF (of size *BUFALLOC) to save it, and return the
+ abbreviation in the possibly-reallocated *BUF. Otherwise, just
+ return the abbreviation. Get the abbreviation from TMP.
+ Exit on memory allocation failure. */
+static char const *
+saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp)
+{
+ char const *ab = abbr(tmp);
+ if (HAVE_LOCALTIME_RZ)
+ return ab;
+ else {
+ size_t ablen = strlen(ab);
+ if ((size_t)*bufalloc <= ablen) {
+ free(*buf);
+
+ /* Make the new buffer at least twice as long as the old,
+ to avoid O(N**2) behavior on repeated calls. */
+ *bufalloc = sumsize(*bufalloc, ablen + 1);
+
+ *buf = xmalloc(*bufalloc);
+ }
+ return strcpy(*buf, ab);
+ }
+}
+
+static void
+close_file(FILE *stream)
+{
+ char const *e = (ferror(stream) ? _("I/O error")
+ : fclose(stream) != 0 ? strerror(errno) : NULL);
+ if (e) {
+ fprintf(stderr, "%s: %s\n", progname, e);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+usage(FILE * const stream, const int status)
+{
+ fprintf(stream,
+_("%s: usage: %s OPTIONS TIMEZONE ...\n"
+ "Options include:\n"
+ " -c [L,]U Start at year L (default -500), end before year U (default 2500)\n"
+ " -t [L,]U Start at time L, end before time U (in seconds since 1970)\n"
+ " -i List transitions briefly (format is experimental)\n" \
+ " -v List transitions verbosely\n"
+ " -V List transitions a bit less verbosely\n"
+ " --help Output this help\n"
+ " --version Output version info\n"
+ "\n"
+ "Report bugs to %s.\n"),
+ progname, progname, REPORT_BUGS_TO);
+ if (status == EXIT_SUCCESS)
+ close_file(stream);
+ exit(status);
+}
+
+int
+main(int argc, char *argv[])
+{
+ /* These are static so that they're initially zero. */
+ static char * abbrev;
+ static ptrdiff_t abbrevsize;
+
+ register int i;
+ register bool vflag;
+ register bool Vflag;
+ register char * cutarg;
+ register char * cuttimes;
+ register time_t cutlotime;
+ register time_t cuthitime;
+ time_t now;
+ bool iflag = false;
+
+ cutlotime = absolute_min_time;
+ cuthitime = absolute_max_time;
+#if HAVE_GETTEXT
+ setlocale(LC_ALL, "");
+# ifdef TZ_DOMAINDIR
+ bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+# endif /* defined TEXTDOMAINDIR */
+ textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT */
+ progname = argv[0] ? argv[0] : "zdump";
+ for (i = 1; i < argc; ++i)
+ if (strcmp(argv[i], "--version") == 0) {
+ printf("zdump %s%s\n", PKGVERSION, TZVERSION);
+ return EXIT_SUCCESS;
+ } else if (strcmp(argv[i], "--help") == 0) {
+ usage(stdout, EXIT_SUCCESS);
+ }
+ vflag = Vflag = false;
+ cutarg = cuttimes = NULL;
+ for (;;)
+ switch (getopt(argc, argv, "c:it:vV")) {
+ case 'c': cutarg = optarg; break;
+ case 't': cuttimes = optarg; break;
+ case 'i': iflag = true; break;
+ case 'v': vflag = true; break;
+ case 'V': Vflag = true; break;
+ case -1:
+ if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
+ goto arg_processing_done;
+ ATTRIBUTE_FALLTHROUGH;
+ default:
+ usage(stderr, EXIT_FAILURE);
+ }
+ arg_processing_done:;
+
+ if (iflag | vflag | Vflag) {
+ intmax_t lo;
+ intmax_t hi;
+ char *loend, *hiend;
+ register intmax_t cutloyear = ZDUMP_LO_YEAR;
+ register intmax_t cuthiyear = ZDUMP_HI_YEAR;
+ if (cutarg != NULL) {
+ lo = strtoimax(cutarg, &loend, 10);
+ if (cutarg != loend && !*loend) {
+ hi = lo;
+ cuthiyear = hi;
+ } else if (cutarg != loend && *loend == ','
+ && (hi = strtoimax(loend + 1, &hiend, 10),
+ loend + 1 != hiend && !*hiend)) {
+ cutloyear = lo;
+ cuthiyear = hi;
+ } else {
+ fprintf(stderr, _("%s: wild -c argument %s\n"),
+ progname, cutarg);
+ return EXIT_FAILURE;
+ }
+ }
+ if (cutarg != NULL || cuttimes == NULL) {
+ cutlotime = yeartot(cutloyear);
+ cuthitime = yeartot(cuthiyear);
+ }
+ if (cuttimes != NULL) {
+ lo = strtoimax(cuttimes, &loend, 10);
+ if (cuttimes != loend && !*loend) {
+ hi = lo;
+ if (hi < cuthitime) {
+ if (hi < absolute_min_time + 1)
+ hi = absolute_min_time + 1;
+ cuthitime = hi;
+ }
+ } else if (cuttimes != loend && *loend == ','
+ && (hi = strtoimax(loend + 1, &hiend, 10),
+ loend + 1 != hiend && !*hiend)) {
+ if (cutlotime < lo) {
+ if (absolute_max_time < lo)
+ lo = absolute_max_time;
+ cutlotime = lo;
+ }
+ if (hi < cuthitime) {
+ if (hi < absolute_min_time + 1)
+ hi = absolute_min_time + 1;
+ cuthitime = hi;
+ }
+ } else {
+ fprintf(stderr,
+ _("%s: wild -t argument %s\n"),
+ progname, cuttimes);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ gmtzinit();
+ if (iflag | vflag | Vflag)
+ now = 0;
+ else {
+ now = time(NULL);
+ now |= !now;
+ }
+ longest = 0;
+ for (i = optind; i < argc; i++) {
+ size_t arglen = strlen(argv[i]);
+ if (longest < arglen)
+ longest = min(arglen, INT_MAX);
+ }
+
+ for (i = optind; i < argc; ++i) {
+ timezone_t tz = tzalloc(argv[i]);
+ char const *ab;
+ time_t t;
+ struct tm tm, newtm;
+ bool tm_ok;
+ if (!tz) {
+ perror(argv[i]);
+ return EXIT_FAILURE;
+ }
+ if (now) {
+ show(tz, argv[i], now, false);
+ tzfree(tz);
+ continue;
+ }
+ warned = false;
+ t = absolute_min_time;
+ if (! (iflag | Vflag)) {
+ show(tz, argv[i], t, true);
+ if (my_localtime_rz(tz, &t, &tm) == NULL
+ && t < cutlotime) {
+ time_t newt = cutlotime;
+ if (my_localtime_rz(tz, &newt, &newtm) != NULL)
+ showextrema(tz, argv[i], t, NULL, newt);
+ }
+ }
+ if (t + 1 < cutlotime)
+ t = cutlotime - 1;
+ tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
+ if (tm_ok) {
+ ab = saveabbr(&abbrev, &abbrevsize, &tm);
+ if (iflag) {
+ showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
+ showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
+ }
+ } else
+ ab = NULL;
+ while (t < cuthitime - 1) {
+ time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
+ && t + SECSPERDAY / 2 < cuthitime - 1)
+ ? t + SECSPERDAY / 2
+ : cuthitime - 1);
+ struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
+ bool newtm_ok = newtmp != NULL;
+ if (tm_ok != newtm_ok
+ || (ab && (delta(&newtm, &tm) != newt - t
+ || newtm.tm_isdst != tm.tm_isdst
+ || strcmp(abbr(&newtm), ab) != 0))) {
+ newt = hunt(tz, t, newt, false);
+ newtmp = localtime_rz(tz, &newt, &newtm);
+ newtm_ok = newtmp != NULL;
+ if (iflag)
+ showtrans("%Y-%m-%d\t%L\t%Q", newtmp, newt,
+ newtm_ok ? abbr(&newtm) : NULL, argv[i]);
+ else {
+ show(tz, argv[i], newt - 1, true);
+ show(tz, argv[i], newt, true);
+ }
+ }
+ t = newt;
+ tm_ok = newtm_ok;
+ if (newtm_ok) {
+ ab = saveabbr(&abbrev, &abbrevsize, &newtm);
+ tm = newtm;
+ }
+ }
+ if (! (iflag | Vflag)) {
+ time_t newt = absolute_max_time;
+ t = cuthitime;
+ if (t < newt) {
+ struct tm *tmp = my_localtime_rz(tz, &t, &tm);
+ if (tmp != NULL
+ && my_localtime_rz(tz, &newt, &newtm) == NULL)
+ showextrema(tz, argv[i], t, tmp, newt);
+ }
+ show(tz, argv[i], absolute_max_time, true);
+ }
+ tzfree(tz);
+ }
+ close_file(stdout);
+ if (errout && (ferror(stderr) || fclose(stderr) != 0))
+ return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+}
+
+static time_t
+yeartot(intmax_t y)
+{
+ register intmax_t myy, seconds, years;
+ register time_t t;
+
+ myy = EPOCH_YEAR;
+ t = 0;
+ while (myy < y) {
+ if (SECSPER400YEARS_FITS && 400 <= y - myy) {
+ intmax_t diff400 = (y - myy) / 400;
+ if (INTMAX_MAX / SECSPER400YEARS < diff400)
+ return absolute_max_time;
+ seconds = diff400 * SECSPER400YEARS;
+ years = diff400 * 400;
+ } else {
+ seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
+ years = 1;
+ }
+ myy += years;
+ if (t > absolute_max_time - seconds)
+ return absolute_max_time;
+ t += seconds;
+ }
+ while (y < myy) {
+ if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) {
+ intmax_t diff400 = (myy - y) / 400;
+ if (INTMAX_MAX / SECSPER400YEARS < diff400)
+ return absolute_min_time;
+ seconds = diff400 * SECSPER400YEARS;
+ years = diff400 * 400;
+ } else {
+ seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR;
+ years = 1;
+ }
+ myy -= years;
+ if (t < absolute_min_time + seconds)
+ return absolute_min_time;
+ t -= seconds;
+ }
+ return t;
+}
+
+/* Search for a discontinuity in timezone TZ, in the
+ timestamps ranging from LOT through HIT. LOT and HIT disagree
+ about some aspect of timezone. If ONLY_OK, search only for
+ definedness changes, i.e., localtime succeeds on one side of the
+ transition but fails on the other side. Return the timestamp just
+ before the transition from LOT's settings. */
+
+static time_t
+hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok)
+{
+ static char * loab;
+ static ptrdiff_t loabsize;
+ struct tm lotm;
+ struct tm tm;
+
+ /* Convert LOT into a broken-down time here, even though our
+ caller already did that. On platforms without TM_ZONE,
+ tzname may have been altered since our caller broke down
+ LOT, and tzname needs to be changed back. */
+ bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
+ bool tm_ok;
+ char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL;
+
+ for ( ; ; ) {
+ /* T = average of LOT and HIT, rounding down.
+ Avoid overflow, even on oddball C89 platforms
+ where / rounds down and TIME_T_MIN == -TIME_T_MAX
+ so lot / 2 + hit / 2 might overflow. */
+ time_t t = (lot / 2
+ - ((lot % 2 + hit % 2) < 0)
+ + ((lot % 2 + hit % 2) == 2)
+ + hit / 2);
+ if (t == lot)
+ break;
+ tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
+ if (lotm_ok == tm_ok
+ && (only_ok
+ || (ab && tm.tm_isdst == lotm.tm_isdst
+ && delta(&tm, &lotm) == t - lot
+ && strcmp(abbr(&tm), ab) == 0))) {
+ lot = t;
+ if (tm_ok)
+ lotm = tm;
+ } else hit = t;
+ }
+ return hit;
+}
+
+/*
+** Thanks to Paul Eggert for logic used in delta_nonneg.
+*/
+
+static intmax_t
+delta_nonneg(struct tm *newp, struct tm *oldp)
+{
+ intmax_t oldy = oldp->tm_year;
+ int cycles = (newp->tm_year - oldy) / YEARSPERREPEAT;
+ intmax_t sec = SECSPERREPEAT, result = cycles * sec;
+ int tmy = oldp->tm_year + cycles * YEARSPERREPEAT;
+ for ( ; tmy < newp->tm_year; ++tmy)
+ result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
+ result += newp->tm_yday - oldp->tm_yday;
+ result *= HOURSPERDAY;
+ result += newp->tm_hour - oldp->tm_hour;
+ result *= MINSPERHOUR;
+ result += newp->tm_min - oldp->tm_min;
+ result *= SECSPERMIN;
+ result += newp->tm_sec - oldp->tm_sec;
+ return result;
+}
+
+static intmax_t
+delta(struct tm *newp, struct tm *oldp)
+{
+ return (newp->tm_year < oldp->tm_year
+ ? -delta_nonneg(oldp, newp)
+ : delta_nonneg(newp, oldp));
+}
+
+#ifndef TM_GMTOFF
+/* Return A->tm_yday, adjusted to compare it fairly to B->tm_yday.
+ Assume A and B differ by at most one year. */
+static int
+adjusted_yday(struct tm const *a, struct tm const *b)
+{
+ int yday = a->tm_yday;
+ if (b->tm_year < a->tm_year)
+ yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE);
+ return yday;
+}
+#endif
+
+/* If A is the broken-down local time and B the broken-down UT for
+ the same instant, return A's UT offset in seconds, where positive
+ offsets are east of Greenwich. On failure, return LONG_MIN.
+
+ If T is nonnull, *T is the timestamp that corresponds to A; call
+ my_gmtime_r and use its result instead of B. Otherwise, B is the
+ possibly nonnull result of an earlier call to my_gmtime_r. */
+static long
+gmtoff(struct tm const *a, ATTRIBUTE_MAYBE_UNUSED time_t *t,
+ ATTRIBUTE_MAYBE_UNUSED struct tm const *b)
+{
+#ifdef TM_GMTOFF
+ return a->TM_GMTOFF;
+#else
+ struct tm tm;
+ if (t)
+ b = my_gmtime_r(t, &tm);
+ if (! b)
+ return LONG_MIN;
+ else {
+ int ayday = adjusted_yday(a, b);
+ int byday = adjusted_yday(b, a);
+ int days = ayday - byday;
+ long hours = a->tm_hour - b->tm_hour + 24 * days;
+ long minutes = a->tm_min - b->tm_min + 60 * hours;
+ long seconds = a->tm_sec - b->tm_sec + 60 * minutes;
+ return seconds;
+ }
+#endif
+}
+
+static void
+show(timezone_t tz, char *zone, time_t t, bool v)
+{
+ register struct tm * tmp;
+ register struct tm * gmtmp;
+ struct tm tm, gmtm;
+
+ printf("%-*s ", (int)longest, zone);
+ if (v) {
+ gmtmp = my_gmtime_r(&t, &gmtm);
+ if (gmtmp == NULL) {
+ printf(tformat(), t);
+ printf(_(" (gmtime failed)"));
+ } else {
+ dumptime(gmtmp);
+ printf(" UT");
+ }
+ printf(" = ");
+ }
+ tmp = my_localtime_rz(tz, &t, &tm);
+ if (tmp == NULL) {
+ printf(tformat(), t);
+ printf(_(" (localtime failed)"));
+ } else {
+ dumptime(tmp);
+ if (*abbr(tmp) != '\0')
+ printf(" %s", abbr(tmp));
+ if (v) {
+ long off = gmtoff(tmp, NULL, gmtmp);
+ printf(" isdst=%d", tmp->tm_isdst);
+ if (off != LONG_MIN)
+ printf(" gmtoff=%ld", off);
+ }
+ }
+ printf("\n");
+ if (tmp != NULL && *abbr(tmp) != '\0')
+ abbrok(abbr(tmp), zone);
+}
+
+/* Show timestamps just before and just after a transition between
+ defined and undefined (or vice versa) in either localtime or
+ gmtime. These transitions are for timezone TZ with name ZONE, in
+ the range from LO (with broken-down time LOTMP if that is nonnull)
+ through HI. LO and HI disagree on definedness. */
+
+static void
+showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
+{
+ struct tm localtm[2], gmtm[2];
+ time_t t, boundary = hunt(tz, lo, hi, true);
+ bool old = false;
+ hi = (SECSPERDAY < hi - boundary
+ ? boundary + SECSPERDAY
+ : hi + (hi < TIME_T_MAX));
+ if (SECSPERDAY < boundary - lo) {
+ lo = boundary - SECSPERDAY;
+ lotmp = my_localtime_rz(tz, &lo, &localtm[old]);
+ }
+ if (lotmp)
+ localtm[old] = *lotmp;
+ else
+ localtm[old].tm_sec = -1;
+ if (! my_gmtime_r(&lo, &gmtm[old]))
+ gmtm[old].tm_sec = -1;
+
+ /* Search sequentially for definedness transitions. Although this
+ could be sped up by refining 'hunt' to search for either
+ localtime or gmtime definedness transitions, it hardly seems
+ worth the trouble. */
+ for (t = lo + 1; t < hi; t++) {
+ bool new = !old;
+ if (! my_localtime_rz(tz, &t, &localtm[new]))
+ localtm[new].tm_sec = -1;
+ if (! my_gmtime_r(&t, &gmtm[new]))
+ gmtm[new].tm_sec = -1;
+ if (((localtm[old].tm_sec < 0) != (localtm[new].tm_sec < 0))
+ | ((gmtm[old].tm_sec < 0) != (gmtm[new].tm_sec < 0))) {
+ show(tz, zone, t - 1, true);
+ show(tz, zone, t, true);
+ }
+ old = new;
+ }
+}
+
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+
+/* A substitute for snprintf that is good enough for zdump. */
+static int ATTRIBUTE_FORMAT((printf, 3, 4))
+my_snprintf(char *s, size_t size, char const *format, ...)
+{
+ int n;
+ va_list args;
+ char const *arg;
+ size_t arglen, slen;
+ char buf[1024];
+ va_start(args, format);
+ if (strcmp(format, "%s") == 0) {
+ arg = va_arg(args, char const *);
+ arglen = strlen(arg);
+ } else {
+ n = vsprintf(buf, format, args);
+ if (n < 0) {
+ va_end(args);
+ return n;
+ }
+ arg = buf;
+ arglen = n;
+ }
+ slen = arglen < size ? arglen : size - 1;
+ memcpy(s, arg, slen);
+ s[slen] = '\0';
+ n = arglen <= INT_MAX ? arglen : -1;
+ va_end(args);
+ return n;
+}
+#endif
+
+/* Store into BUF, of size SIZE, a formatted local time taken from *TM.
+ Use ISO 8601 format +HH:MM:SS. Omit :SS if SS is zero, and omit
+ :MM too if MM is also zero.
+
+ Return the length of the resulting string. If the string does not
+ fit, return the length that the string would have been if it had
+ fit; do not overrun the output buffer. */
+static int
+format_local_time(char *buf, ptrdiff_t size, struct tm const *tm)
+{
+ int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
+ return (ss
+ ? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
+ : mm
+ ? my_snprintf(buf, size, "%02d:%02d", hh, mm)
+ : my_snprintf(buf, size, "%02d", hh));
+}
+
+/* Store into BUF, of size SIZE, a formatted UT offset for the
+ localtime *TM corresponding to time T. Use ISO 8601 format
+ +HHMMSS, or -HHMMSS for timestamps west of Greenwich; use the
+ format -00 for unknown UT offsets. If the hour needs more than
+ two digits to represent, extend the length of HH as needed.
+ Otherwise, omit SS if SS is zero, and omit MM too if MM is also
+ zero.
+
+ Return the length of the resulting string, or -1 if the result is
+ not representable as a string. If the string does not fit, return
+ the length that the string would have been if it had fit; do not
+ overrun the output buffer. */
+static int
+format_utc_offset(char *buf, ptrdiff_t size, struct tm const *tm, time_t t)
+{
+ long off = gmtoff(tm, &t, NULL);
+ char sign = ((off < 0
+ || (off == 0
+ && (*abbr(tm) == '-' || strcmp(abbr(tm), "zzz") == 0)))
+ ? '-' : '+');
+ long hh;
+ int mm, ss;
+ if (off < 0)
+ {
+ if (off == LONG_MIN)
+ return -1;
+ off = -off;
+ }
+ ss = off % 60;
+ mm = off / 60 % 60;
+ hh = off / 60 / 60;
+ return (ss || 100 <= hh
+ ? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
+ : mm
+ ? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
+ : my_snprintf(buf, size, "%c%02ld", sign, hh));
+}
+
+/* Store into BUF (of size SIZE) a quoted string representation of P.
+ If the representation's length is less than SIZE, return the
+ length; the representation is not null terminated. Otherwise
+ return SIZE, to indicate that BUF is too small. */
+static ptrdiff_t
+format_quoted_string(char *buf, ptrdiff_t size, char const *p)
+{
+ char *b = buf;
+ ptrdiff_t s = size;
+ if (!s)
+ return size;
+ *b++ = '"', s--;
+ for (;;) {
+ char c = *p++;
+ if (s <= 1)
+ return size;
+ switch (c) {
+ default: *b++ = c, s--; continue;
+ case '\0': *b++ = '"', s--; return size - s;
+ case '"': case '\\': break;
+ case ' ': c = 's'; break;
+ case '\f': c = 'f'; break;
+ case '\n': c = 'n'; break;
+ case '\r': c = 'r'; break;
+ case '\t': c = 't'; break;
+ case '\v': c = 'v'; break;
+ }
+ *b++ = '\\', *b++ = c, s -= 2;
+ }
+}
+
+/* Store into BUF (of size SIZE) a timestamp formatted by TIME_FMT.
+ TM is the broken-down time, T the seconds count, AB the time zone
+ abbreviation, and ZONE_NAME the zone name. Return true if
+ successful, false if the output would require more than SIZE bytes.
+ TIME_FMT uses the same format that strftime uses, with these
+ additions:
+
+ %f zone name
+ %L local time as per format_local_time
+ %Q like "U\t%Z\tD" where U is the UT offset as for format_utc_offset
+ and D is the isdst flag; except omit D if it is zero, omit %Z if
+ it equals U, quote and escape %Z if it contains nonalphabetics,
+ and omit any trailing tabs. */
+
+static bool
+istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
+ struct tm const *tm, time_t t, char const *ab, char const *zone_name)
+{
+ char *b = buf;
+ ptrdiff_t s = size;
+ char const *f = time_fmt, *p;
+
+ for (p = f; ; p++)
+ if (*p == '%' && p[1] == '%')
+ p++;
+ else if (!*p
+ || (*p == '%'
+ && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
+ ptrdiff_t formatted_len;
+ ptrdiff_t f_prefix_len = p - f;
+ ptrdiff_t f_prefix_copy_size = sumsize(f_prefix_len, 2);
+ char fbuf[100];
+ bool oversized = sizeof fbuf <= (size_t)f_prefix_copy_size;
+ char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
+ memcpy(f_prefix_copy, f, f_prefix_len);
+ strcpy(f_prefix_copy + f_prefix_len, "X");
+ formatted_len = strftime(b, s, f_prefix_copy, tm);
+ if (oversized)
+ free(f_prefix_copy);
+ if (formatted_len == 0)
+ return false;
+ formatted_len--;
+ b += formatted_len, s -= formatted_len;
+ if (!*p++)
+ break;
+ switch (*p) {
+ case 'f':
+ formatted_len = format_quoted_string(b, s, zone_name);
+ break;
+ case 'L':
+ formatted_len = format_local_time(b, s, tm);
+ break;
+ case 'Q':
+ {
+ bool show_abbr;
+ int offlen = format_utc_offset(b, s, tm, t);
+ if (! (0 <= offlen && offlen < s))
+ return false;
+ show_abbr = strcmp(b, ab) != 0;
+ b += offlen, s -= offlen;
+ if (show_abbr) {
+ char const *abp;
+ ptrdiff_t len;
+ if (s <= 1)
+ return false;
+ *b++ = '\t', s--;
+ for (abp = ab; is_alpha(*abp); abp++)
+ continue;
+ len = (!*abp && *ab
+ ? my_snprintf(b, s, "%s", ab)
+ : format_quoted_string(b, s, ab));
+ if (s <= len)
+ return false;
+ b += len, s -= len;
+ }
+ formatted_len
+ = (tm->tm_isdst
+ ? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
+ : 0);
+ }
+ break;
+ }
+ if (s <= formatted_len)
+ return false;
+ b += formatted_len, s -= formatted_len;
+ f = p + 1;
+ }
+ *b = '\0';
+ return true;
+}
+
+/* Show a time transition. */
+static void
+showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
+ char const *zone_name)
+{
+ if (!tm) {
+ printf(tformat(), t);
+ putchar('\n');
+ } else {
+ char stackbuf[1000];
+ ptrdiff_t size = sizeof stackbuf;
+ char *buf = stackbuf;
+ char *bufalloc = NULL;
+ while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
+ size = sumsize(size, size);
+ free(bufalloc);
+ buf = bufalloc = xmalloc(size);
+ }
+ puts(buf);
+ free(bufalloc);
+ }
+}
+
+static char const *
+abbr(struct tm const *tmp)
+{
+#ifdef TM_ZONE
+ return tmp->TM_ZONE;
+#else
+# if HAVE_TZNAME
+ if (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst])
+ return tzname[0 < tmp->tm_isdst];
+# endif
+ return "";
+#endif
+}
+
+/*
+** The code below can fail on certain theoretical systems;
+** it works on all known real-world systems as of 2022-01-25.
+*/
+
+static const char *
+tformat(void)
+{
+#if HAVE_GENERIC
+ /* C11-style _Generic is more likely to return the correct
+ format when distinct types have the same size. */
+ char const *fmt =
+ _Generic(+ (time_t) 0,
+ int: "%d", long: "%ld", long long: "%lld",
+ unsigned: "%u", unsigned long: "%lu",
+ unsigned long long: "%llu",
+ default: NULL);
+ if (fmt)
+ return fmt;
+ fmt = _Generic((time_t) 0,
+ intmax_t: "%"PRIdMAX, uintmax_t: "%"PRIuMAX,
+ default: NULL);
+ if (fmt)
+ return fmt;
+#endif
+ if (0 > (time_t) -1) { /* signed */
+ if (sizeof(time_t) == sizeof(intmax_t))
+ return "%"PRIdMAX;
+ if (sizeof(time_t) > sizeof(long))
+ return "%lld";
+ if (sizeof(time_t) > sizeof(int))
+ return "%ld";
+ return "%d";
+ }
+#ifdef PRIuMAX
+ if (sizeof(time_t) == sizeof(uintmax_t))
+ return "%"PRIuMAX;
+#endif
+ if (sizeof(time_t) > sizeof(unsigned long))
+ return "%llu";
+ if (sizeof(time_t) > sizeof(unsigned int))
+ return "%lu";
+ return "%u";
+}
+
+static void
+dumptime(register const struct tm *timeptr)
+{
+ static const char wday_name[][4] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+ static const char mon_name[][4] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ register int lead;
+ register int trail;
+ int DIVISOR = 10;
+
+ /*
+ ** The packaged localtime_rz and gmtime_r never put out-of-range
+ ** values in tm_wday or tm_mon, but since this code might be compiled
+ ** with other (perhaps experimental) versions, paranoia is in order.
+ */
+ printf("%s %s%3d %.2d:%.2d:%.2d ",
+ ((0 <= timeptr->tm_wday
+ && timeptr->tm_wday < (int)(sizeof wday_name / sizeof wday_name[0]))
+ ? wday_name[timeptr->tm_wday] : "???"),
+ ((0 <= timeptr->tm_mon
+ && timeptr->tm_mon < (int)(sizeof mon_name / sizeof mon_name[0]))
+ ? mon_name[timeptr->tm_mon] : "???"),
+ timeptr->tm_mday, timeptr->tm_hour,
+ timeptr->tm_min, timeptr->tm_sec);
+ trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
+ lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
+ trail / DIVISOR;
+ trail %= DIVISOR;
+ if (trail < 0 && lead > 0) {
+ trail += DIVISOR;
+ --lead;
+ } else if (lead < 0 && trail > 0) {
+ trail -= DIVISOR;
+ ++lead;
+ }
+ if (lead == 0)
+ printf("%d", trail);
+ else printf("%d%d", lead, ((trail < 0) ? -trail : trail));
+}
diff --git a/contrib/tzcode/zic.8 b/contrib/tzcode/zic.8
new file mode 100644
index 000000000000..79624da4e9e2
--- /dev/null
+++ b/contrib/tzcode/zic.8
@@ -0,0 +1,856 @@
+.\" This file is in the public domain, so clarified as of
+.\" 2009-05-17 by Arthur David Olson.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 15, 2022
+.Dt ZIC 8
+.Os
+.Sh NAME
+.Nm zic
+.Nd timezone compiler
+.Sh SYNOPSIS
+.Nm
+.Op Fl -help
+.Op Fl -version
+.Op Fl Dsv
+.Op Fl b Ar slim | fat
+.Op Fl d Ar directory
+.Op Fl g Ar gid
+.Op Fl l Ar localtime
+.Op Fl L Ar leapseconds
+.Op Fl m Ar mode
+.Op Fl p Ar posixrules
+.Oo
+.Fl r
+.Op @ Ns Ar lo Ns
+.Op /@ Ns Ar hi
+.Oc
+.Op Fl R @ Ns Ar hi
+.Op Fl t Ar localtime-link
+.Op Fl u Ar uid
+.Op Ar filename ...
+.Sh DESCRIPTION
+The
+.Nm
+program reads text from the file(s) named on the command line
+and creates the timezone information format (TZif) files
+specified in this input.
+If a
+.Ar filename
+is
+.Dq "-" ,
+standard input is read.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl -version
+Output version information and exit.
+.It Fl -help
+Output short usage message and exit.
+.It Fl b Ar bloat
+Output backward-compatibility data as specified by
+.Ar bloat .
+If
+.Ar bloat
+is
+.Cm fat ,
+generate additional data entries that work around potential bugs or
+incompatibilities in older software, such as software that mishandles
+the 64-bit generated data.
+If
+.Ar bloat
+is
+.Cm slim ,
+keep the output files small; this can help check for the bugs
+and incompatibilities.
+The default is
+.Cm slim ,
+as software that mishandles 64-bit data typically
+mishandles timestamps after the year 2038 anyway.
+Also see the
+.Fl r
+option for another way to alter output size.
+.It Fl d Ar directory
+Create time conversion information files in the named directory rather than
+in the standard directory named below.
+.It Fl l Ar timezone
+Use
+.Ar timezone
+as local time.
+The
+.Nm
+utility
+will act as if the input contained a link line of the form
+.Bd -literal -offset indent
+Link timezone localtime
+.Ed
+.Pp
+If
+.Ar timezone
+is
+.Ql - ,
+any already-existing link is removed.
+.It Fl L Ar filename
+Read leap second information from the file with the given name.
+If this option is not used,
+no leap second information appears in output files.
+.It Fl p Ar timezone
+Use
+.Ar timezone 's
+rules when handling nonstandard
+TZ strings like
+.Dq "EET\-2EEST"
+that lack transition rules.
+The
+.Nm
+utility
+will act as if the input contained a link line of the form
+.Bd -literal -offset indent
+Link \fItimezone\fP posixrules
+.Ed
+.Pp
+This feature is obsolete and poorly supported.
+Among other things it should not be used for timestamps after the year 2037,
+and it should not be combined with
+.Fl b Cm slim
+if
+.Ar timezone 's
+transitions are at standard time or Universal Time (UT) instead of local time.
+.Pp
+If
+.Ar timezone
+is
+.Ql - ,
+any already-existing link is removed.
+.It Fl r Oo @ Ns Ar lo Oc Ns Oo /@ Ns Ar hi Oc
+Limit the applicability of output files
+to timestamps in the range from
+.Ar lo
+(inclusive) to
+.Ar hi
+(exclusive), where
+.Ar lo
+and
+.Ar hi
+are possibly-signed decimal counts of seconds since the Epoch
+(1970-01-01 00:00:00 UTC).
+Omitted counts default to extreme values.
+The output files use UT offset 0 and abbreviation
+.Dq "\-00"
+in place of the omitted timestamp data.
+For example,
+.Fl r @0
+omits data intended for negative timestamps (i.e., before the Epoch), and
+.Fl r @0/@2147483648
+outputs data intended only for nonnegative timestamps that fit into
+31-bit signed integers.
+Although this option typically reduces the output file's size,
+the size can increase due to the need to represent the timestamp range
+boundaries, particularly if
+.Ar hi
+causes a TZif file to contain explicit entries for
+.Em pre-
+.Ar hi
+transitions rather than concisely representing them
+with an extended POSIX TZ string.
+Also see the
+.Fl b Cm slim
+option for another way to shrink output size.
+.It Fl R @ Ns Ar hi
+Generate redundant trailing explicit transitions for timestamps
+that occur less than
+.Ar hi
+seconds since the Epoch, even though the transitions could be
+more concisely represented via the extended POSIX TZ string.
+This option does not affect the represented timestamps.
+Although it accommodates nonstandard TZif readers
+that ignore the extended POSIX TZ string,
+it increases the size of the altered output files.
+.It Fl t Ar file
+When creating local time information, put the configuration link in
+the named file rather than in the standard location.
+.It Fl v
+Be more verbose, and complain about the following situations:
+.Bl -bullet
+.It
+The input specifies a link to a link,
+something not supported by some older parsers, including
+.Nm
+itself through release 2022e.
+.It
+A year that appears in a data file is outside the range
+of representable years.
+.It
+A time of 24:00 or more appears in the input.
+Pre-1998 versions of
+.Nm
+prohibit 24:00, and pre-2007 versions prohibit times greater than 24:00.
+.It
+A rule goes past the start or end of the month.
+Pre-2004 versions of
+.Nm
+prohibit this.
+.It
+A time zone abbreviation uses a
+.Ql %z
+format.
+Pre-2015 versions of
+.Nm
+do not support this.
+.It
+A timestamp contains fractional seconds.
+Pre-2018 versions of
+.Nm
+do not support this.
+.It
+The input contains abbreviations that are mishandled by pre-2018 versions of
+.Nm
+due to a longstanding coding bug.
+These abbreviations include
+.Dq L
+for
+.Dq Link ,
+.Dq mi
+for
+.Dq min ,
+.Dq Sa
+for
+.Dq Sat ,
+and
+.Dq Su
+for
+.Dq Sun .
+.It
+The output file does not contain all the information about the
+long-term future of a timezone, because the future cannot be summarized as
+an extended POSIX TZ string.
+For example, as of 2019 this problem
+occurs for Iran's daylight-saving rules for the predicted future, as
+these rules are based on the Iranian calendar, which cannot be
+represented.
+.It
+The output contains data that may not be handled properly by client
+code designed for older
+.Nm
+output formats.
+These compatibility issues affect only timestamps
+before 1970 or after the start of 2038.
+.It
+The output contains a truncated leap second table,
+which can cause some older TZif readers to misbehave.
+This can occur if the
+.Fl L
+option is used, and either an Expires line is present or
+the
+.Fl r
+option is also used.
+.It
+The output file contains more than 1200 transitions,
+which may be mishandled by some clients.
+The current reference client supports at most 2000 transitions;
+pre-2014 versions of the reference client support at most 1200
+transitions.
+.It
+A time zone abbreviation has fewer than 3 or more than 6 characters.
+POSIX requires at least 3, and requires implementations to support
+at least 6.
+.It
+An output file name contains a byte that is not an ASCII letter,
+.Dq "\-" ,
+.Dq "/" ,
+or
+.Dq "_" ;
+or it contains a file name component that contains more than 14 bytes
+or that starts with
+.Dq "\-" .
+.El
+.El
+.RE
+.Sh FILES
+Input files use the format described in this section; output files use
+.Xr tzfile 5
+format.
+.Pp
+Input files should be text files, that is, they should be a series of
+zero or more lines, each ending in a newline byte and containing at
+most 2048 bytes counting the newline, and without any NUL bytes.
+The input text's encoding
+is typically UTF-8 or ASCII; it should have a unibyte representation
+for the POSIX Portable Character Set (PPCS)
+\*<https://pubs\*:.opengroup\*:.org/\*:onlinepubs/\*:9699919799/\*:basedefs/\*:V1_chap06\*:.html\*>
+and the encoding's non-unibyte characters should consist entirely of
+non-PPCS bytes.
+Non-PPCS characters typically occur only in comments:
+although output file names and time zone abbreviations can contain
+nearly any character, other software will work better if these are
+limited to the restricted syntax described under the
+.Fl v
+option.
+.Pp
+Input lines are made up of fields.
+Fields are separated from one another by one or more white space characters.
+The white space characters are space, form feed, carriage return, newline,
+tab, and vertical tab.
+Leading and trailing white space on input lines is ignored.
+An unquoted sharp character (\(sh) in the input introduces a comment which extends
+to the end of the line the sharp character appears on.
+White space characters and sharp characters may be enclosed in double quotes
+(\(dq) if they're to be used as part of a field.
+Any line that is blank (after comment stripping) is ignored.
+Nonblank lines are expected to be of one of three types:
+rule lines, zone lines, and link lines.
+.Pp
+Names must be in English and are case insensitive.
+They appear in several contexts, and include month and weekday names
+and keywords such as
+.Dq "maximum" ,
+.Dq "only" ,
+.Dq "Rolling" ,
+and
+.Dq "Zone" .
+A name can be abbreviated by omitting all but an initial prefix; any
+abbreviation must be unambiguous in context.
+.Pp
+A rule line has the form
+.Bd -literal -offset indent
+Rule NAME FROM TO \- IN ON AT SAVE LETTER/S
+.Ed
+.Pp
+For example:
+.Bd -literal -offset indent
+Rule US 1967 1973 \- Apr lastSun 2:00w 1:00d D
+.Ed
+.Pp
+The fields that make up a rule line are:
+.Bl -tag -width "LETTER/S"
+.It NAME
+Gives the name of the rule set that contains this line.
+The name must start with a character that is neither
+an ASCII digit nor
+.Dq \-
+nor
+.Dq + .
+To allow for future extensions,
+an unquoted name should not contain characters from the set
+.Dq Ql "!$%&'()*,/:;<=>?@[\]^`{|}~" .
+.It FROM
+Gives the first year in which the rule applies.
+Any signed integer year can be supplied; the proleptic Gregorian calendar
+is assumed, with year 0 preceding year 1.
+The word
+.Cm minimum
+(or an abbreviation) means the indefinite past.
+The word
+.Cm maximum
+(or an abbreviation) means the indefinite future.
+Rules can describe times that are not representable as time values,
+with the unrepresentable times ignored; this allows rules to be portable
+among hosts with differing time value types.
+.It TO
+Gives the final year in which the rule applies.
+In addition to
+.Cm minimum
+and
+.Cm maximum
+(as above),
+the word
+.Cm only
+(or an abbreviation)
+may be used to repeat the value of the
+.Ar FROM
+field.
+.It \-
+Is a reserved field and should always contain
+.Ql \-
+for compatibility with older versions of
+.Nm .
+It was previously known as the
+.Ar TYPE
+field, which could contain values to allow a
+separate script to further restrict in which
+.Dq types
+of years the rule would apply.
+.It IN
+Names the month in which the rule takes effect.
+Month names may be abbreviated.
+.It ON
+Gives the day on which the rule takes effect.
+Recognized forms include:
+.Bl -tag -compact -width "Sun<=25"
+.It 5
+the fifth of the month
+.It lastSun
+the last Sunday in the month
+.It lastMon
+the last Monday in the month
+.It Sun>=8
+first Sunday on or after the eighth
+.It Sun<=25
+last Sunday on or before the 25th
+.El
+.Pp
+A weekday name (e.g.,
+.Ql "Sunday" )
+or a weekday name preceded by
+.Dq "last"
+(e.g.,
+.Ql "lastSunday" )
+may be abbreviated or spelled out in full.
+There must be no white space characters within the
+.Ar ON
+field.
+The
+.Dq <=
+and
+.Dq >=
+constructs can result in a day in the neighboring month;
+for example, the IN-ON combination
+.Dq "Oct Sun>=31"
+stands for the first Sunday on or after October 31,
+even if that Sunday occurs in November.
+.It AT
+Gives the time of day at which the rule takes effect,
+relative to 00:00, the start of a calendar day.
+Recognized forms include:
+.Bl -tag -compact -width "00:19:32.13"
+.It 2
+time in hours
+.It 2:00
+time in hours and minutes
+.It 01:28:14
+time in hours, minutes, and seconds
+.It 00:19:32.13
+time with fractional seconds
+.It 12:00
+midday, 12 hours after 00:00
+.It 15:00
+3 PM, 15 hours after 00:00
+.It 24:00
+end of day, 24 hours after 00:00
+.It 260:00
+260 hours after 00:00
+.It \-2:30
+2.5 hours before 00:00
+.It \-
+equivalent to 0
+.El
+.Pp
+Although
+.Nm
+rounds times to the nearest integer second
+(breaking ties to the even integer), the fractions may be useful
+to other applications requiring greater precision.
+The source format does not specify any maximum precision.
+Any of these forms may be followed by the letter
+.Ql w
+if the given time is local or
+.Dq "wall clock"
+time,
+.Ql s
+if the given time is standard time without any adjustment for daylight saving,
+or
+.Ql u
+(or
+.Ql g
+or
+.Ql z )
+if the given time is universal time;
+in the absence of an indicator,
+local (wall clock) time is assumed.
+These forms ignore leap seconds; for example,
+if a leap second occurs at 00:59:60 local time,
+.Ql "1:00"
+stands for 3601 seconds after local midnight instead of the usual 3600 seconds.
+The intent is that a rule line describes the instants when a
+clock/calendar set to the type of time specified in the
+.Ar AT
+field would show the specified date and time of day.
+.It SAVE
+Gives the amount of time to be added to local standard time when the rule is in
+effect, and whether the resulting time is standard or daylight saving.
+This field has the same format as the
+.Ar AT
+field
+except with a different set of suffix letters:
+.Ql s
+for standard time and
+.Ql d
+for daylight saving time.
+The suffix letter is typically omitted, and defaults to
+.Ql s
+if the offset is zero and to
+.Ql d
+otherwise.
+Negative offsets are allowed; in Ireland, for example, daylight saving
+time is observed in winter and has a negative offset relative to
+Irish Standard Time.
+The offset is merely added to standard time; for example,
+.Nm
+does not distinguish a 10:30 standard time plus an 0:30
+.Ar SAVE
+from a 10:00 standard time plus a 1:00
+.Ar SAVE .
+.It LETTER/S
+Gives the
+.Dq "variable part"
+(for example, the
+.Dq "S"
+or
+.Dq "D"
+in
+.Dq "EST"
+or
+.Dq "EDT" )
+of time zone abbreviations to be used when this rule is in effect.
+If this field is
+.Ql \- ,
+the variable part is null.
+.El
+.Pp
+A zone line has the form
+.Bd -literal -offset indent
+Zone NAME STDOFF RULES FORMAT [UNTIL]
+.Ed
+.Pp
+For example:
+.Bd -literal -offset indent
+Zone Asia/Amman 2:00 Jordan EE%sT 2017 Oct 27 01:00
+.Ed
+.Pp
+The fields that make up a zone line are:
+.Bl -tag -width "STDOFF"
+.It NAME
+The name of the timezone.
+This is the name used in creating the time conversion information file for the
+timezone.
+It should not contain a file name component
+.Dq ".\&"
+or
+.Dq ".." ;
+a file name component is a maximal substring that does not contain
+.Dq "/" .
+.It STDOFF
+The amount of time to add to UT to get standard time,
+without any adjustment for daylight saving.
+This field has the same format as the
+.Ar AT
+and
+.Ar SAVE
+fields of rule lines, except without suffix letters;
+begin the field with a minus sign if time must be subtracted from UT.
+.It RULES
+The name of the rules that apply in the timezone or,
+alternatively, a field in the same format as a rule-line SAVE column,
+giving the amount of time to be added to local standard time
+and whether the resulting time is standard or daylight saving.
+If this field is
+.Ql \-
+then standard time always applies.
+When an amount of time is given, only the sum of standard time and
+this amount matters.
+.It FORMAT
+The format for time zone abbreviations.
+The pair of characters
+.Ql %s
+is used to show where the
+.Dq "variable part"
+of the time zone abbreviation goes.
+Alternatively, a format can use the pair of characters
+.Ql %z
+to stand for the UT offset in the form
+.Ar \(+- hh ,
+.Ar \(+- hhmm ,
+or
+.Ar \(+- hhmmss ,
+using the shortest form that does not lose information, where
+.Ar hh ,
+.Ar mm ,
+and
+.Ar ss
+are the hours, minutes, and seconds east (+) or west (\-) of UT.
+Alternatively,
+a slash (/)
+separates standard and daylight abbreviations.
+To conform to POSIX, a time zone abbreviation should contain only
+alphanumeric ASCII characters,
+.Ql "+"
+and
+.Ql "\-".
+By convention, the time zone abbreviation
+.Ql "\-00"
+is a placeholder that means local time is unspecified.
+.It UNTIL
+The time at which the UT offset or the rule(s) change for a location.
+It takes the form of one to four fields
+.Ar YEAR Op Ar MONTH Op Ar DAY Op Ar TIME .
+If this is specified,
+the time zone information is generated from the given UT offset
+and rule change until the time specified, which is interpreted using
+the rules in effect just before the transition.
+The month, day, and time of day have the same format as the
+.Ar IN ,
+.Ar ON ,
+and
+.Ar AT
+fields of a rule; trailing fields can be omitted, and default to the
+earliest possible value for the missing fields.
+.IP
+The next line must be a
+.Dq "continuation"
+line; this has the same form as a zone line except that the
+string
+.Dq "Zone"
+and the name are omitted, as the continuation line will
+place information starting at the time specified as the
+.Dq "until"
+information in the previous line in the file used by the previous line.
+Continuation lines may contain
+.Dq "until"
+information, just as zone lines do, indicating that the next line is a further
+continuation.
+.El
+.Pp
+If a zone changes at the same instant that a rule would otherwise take
+effect in the earlier zone or continuation line, the rule is ignored.
+A zone or continuation line
+.Ar L
+with a named rule set starts with standard time by default:
+that is, any of
+.Ar L 's
+timestamps preceding
+.Ar L 's
+earliest rule use the rule in effect after
+.Ar L 's
+first transition into standard time.
+In a single zone it is an error if two rules take effect at the same
+instant, or if two zone changes take effect at the same instant.
+.Pp
+If a continuation line subtracts
+.Ar N
+seconds from the UT offset after a transition that would be
+interpreted to be later if using the continuation line's UT offset and
+rules, the
+.Dq "until"
+time of the previous zone or continuation line is interpreted
+according to the continuation line's UT offset and rules, and any rule
+that would otherwise take effect in the next
+.Ar N
+seconds is instead assumed to take effect simultaneously.
+For example:
+.Bd -literal -offset indent
+# Rule NAME FROM TO \*- IN ON AT SAVE LETTER/S
+Rule US 1967 2006 - Oct lastSun 2:00 0 S
+Rule US 1967 1973 - Apr lastSun 2:00 1:00 D
+
+# Zone\0\0NAME STDOFF RULES FORMAT [UNTIL]
+Zone\0\0America/Menominee \*-5:00 \*- EST 1973 Apr 29 2:00
+ \*-6:00 US C%sT
+.Ed
+Here, an incorrect reading would be there were two clock changes on 1973-04-29,
+the first from 02:00 EST (\-05) to 01:00 CST (\-06),
+and the second an hour later from 02:00 CST (\-06) to 03:00 CDT (\-05).
+However,
+.Nm
+interprets this more sensibly as a single transition from 02:00 CST (\-05) to
+02:00 CDT (\-05).
+.Pp
+A link line has the form
+.Bd -literal -offset indent
+Link TARGET LINK-NAME
+.Ed
+.Pp
+For example:
+.Bd -literal -offset indent
+Link Europe/Istanbul Asia/Istanbul
+.Ed
+.Pp
+The
+.Ar TARGET
+field should appear as the
+.Ar NAME
+field in some zone line or as the
+.Ar LINK-NAME
+field in some link line.
+The
+.Ar LINK-NAME
+field is used as an alternative name for that zone;
+it has the same syntax as a zone line's
+.Ar NAME
+field.
+Links can chain together, although the behavior is unspecified if a
+chain of one or more links does not terminate in a Zone name.
+A link line can appear before the line that defines the link target.
+For example:
+.Bd -literal -offset indent
+Link Greenwich G_M_T
+Link Etc/GMT Greenwich
+Zone Etc/GMT\0\00\0\0\-\0\0GMT
+.Ed
+.Pp
+The two links are chained together, and G_M_T, Greenwich, and Etc/GMT
+all name the same zone.
+.Pp
+Except for continuation lines,
+lines may appear in any order in the input.
+However, the behavior is unspecified if multiple zone or link lines
+define the same name.
+.Pp
+The file that describes leap seconds can have leap lines and an
+expiration line.
+Leap lines have the following form:
+.Bd -literal -offset indent
+Leap YEAR MONTH DAY HH:MM:SS CORR R/S
+.Ed
+.Pp
+For example:
+.Bd -literal -offset indent
+Leap 2016 Dec 31 23:59:60 + S
+.Ed
+.Pp
+The
+.Ar YEAR ,
+.Ar MONTH ,
+.Ar DAY ,
+and
+.Ar HH:MM:SS
+fields tell when the leap second happened.
+The
+.Ar CORR
+field
+should be
+.Ql "+"
+if a second was added
+or
+.Ql "\-"
+if a second was skipped.
+The
+.Ar R/S
+field
+should be (an abbreviation of)
+.Dq "Stationary"
+if the leap second time given by the other fields should be interpreted as UTC
+or
+(an abbreviation of)
+.Dq "Rolling"
+if the leap second time given by the other fields should be interpreted as
+local (wall clock) time.
+.Pp
+Rolling leap seconds were implemented back when it was not
+clear whether common practice was rolling or stationary,
+with concerns that one would see
+Times Square ball drops where there'd be a
+.Dq "3... 2... 1... leap... Happy New Year"
+countdown, placing the leap second at
+midnight New York time rather than midnight UTC.
+However, this countdown style does not seem to have caught on,
+which means rolling leap seconds are not used in practice;
+also, they are not supported if the
+.Fl r
+option is used.
+.Pp
+The expiration line, if present, has the form:
+.Bd -literal -offset indent
+Expires YEAR MONTH DAY HH:MM:SS
+.Ed
+.Pp
+For example:
+.Bd -literal -offset indent
+Expires 2020 Dec 28 00:00:00
+.Ed
+.Pp
+The
+.Ar YEAR ,
+.Ar MONTH ,
+.Ar DAY ,
+and
+.Ar HH:MM:SS
+fields give the expiration timestamp in UTC for the leap second table.
+.Sh "EXTENDED EXAMPLE"
+Here is an extended example of
+.Nm
+input, intended to illustrate many of its features.
+.Bd -literal -offset indent
+# Rule NAME FROM TO \- IN ON AT SAVE LETTER/S
+Rule Swiss 1941 1942 \- May Mon>=1 1:00 1:00 S
+Rule Swiss 1941 1942 \- Oct Mon>=1 2:00 0 \-
+
+Rule EU 1977 1980 \- Apr Sun>=1 1:00u 1:00 S
+Rule EU 1977 only \- Sep lastSun 1:00u 0 \-
+Rule EU 1978 only \- Oct 1 1:00u 0 \-
+Rule EU 1979 1995 \- Sep lastSun 1:00u 0 \-
+Rule EU 1981 max \- Mar lastSun 1:00u 1:00 S
+Rule EU 1996 max \- Oct lastSun 1:00u 0 \-
+
+# Zone NAME STDOFF RULES FORMAT [UNTIL]
+Zone Europe/Zurich 0:34:08 \- LMT 1853 Jul 16
+ 0:29:45.50 \- BMT 1894 Jun
+ 1:00 Swiss CE%sT 1981
+ 1:00 EU CE%sT
+
+Link Europe/Zurich Europe/Vaduz
+.Ed
+.Pp
+In this example, the EU rules are for the European Union
+and for its predecessor organization, the European Communities.
+The timezone is named Europe/Zurich and it has the alias Europe/Vaduz.
+This example says that Zurich was 34 minutes and 8
+seconds east of UT until 1853-07-16 at 00:00, when the legal offset
+was changed to
+7\(de26\(fm22.50\(sd,
+which works out to 0:29:45.50;
+.Nm
+treats this by rounding it to 0:29:46.
+After 1894-06-01 at 00:00 the UT offset became one hour
+and Swiss daylight saving rules (defined with lines beginning with
+.Dq "Rule Swiss")
+apply.
+From 1981 to the present, EU daylight saving rules have
+applied, and the UTC offset has remained at one hour.
+.Pp
+In 1941 and 1942, daylight saving time applied from the first Monday
+in May at 01:00 to the first Monday in October at 02:00.
+The pre-1981 EU daylight-saving rules have no effect
+here, but are included for completeness.
+Since 1981, daylight
+saving has begun on the last Sunday in March at 01:00 UTC.
+Until 1995 it ended the last Sunday in September at 01:00 UTC,
+but this changed to the last Sunday in October starting in 1996.
+.Pp
+For purposes of display,
+.Dq "LMT"
+and
+.Dq "BMT"
+were initially used, respectively.
+Since
+Swiss rules and later EU rules were applied, the time zone abbreviation
+has been CET for standard time and CEST for daylight saving
+time.
+.Sh FILES
+.Bl -tag -width "/usr/share/zoneinfo"
+.It Pa /etc/localtime
+Default local timezone file.
+.It Pa /usr/share/zoneinfo
+Default timezone information directory.
+.El
+.Sh NOTES
+For areas with more than two types of local time,
+you may need to use local standard time in the
+.Ar AT
+field of the earliest transition time's rule to ensure that
+the earliest transition time recorded in the compiled file is correct.
+.Pp
+If,
+for a particular timezone,
+a clock advance caused by the start of daylight saving
+coincides with and is equal to
+a clock retreat caused by a change in UT offset,
+.Nm
+produces a single transition to daylight saving at the new UT offset
+without any change in local (wall clock) time.
+To get separate transitions
+use multiple zone continuation lines
+specifying transition instants using universal time.
+.Sh SEE ALSO
+.Xr tzfile 5 ,
+.Xr zdump 8
diff --git a/contrib/tzcode/zic.c b/contrib/tzcode/zic.c
new file mode 100644
index 000000000000..3be46c8563d7
--- /dev/null
+++ b/contrib/tzcode/zic.c
@@ -0,0 +1,4051 @@
+/* Compile .zi time zone data into TZif binary files. */
+
+/*
+** This file is in the public domain, so clarified as of
+** 2006-07-17 by Arthur David Olson.
+*/
+
+/* Use the system 'time' function, instead of any private replacement.
+ This avoids creating an unnecessary dependency on localtime.c. */
+#undef EPOCH_LOCAL
+#undef EPOCH_OFFSET
+#undef RESERVE_STD_EXT_IDS
+#undef time_tz
+
+#include "version.h"
+#include "private.h"
+#include "tzfile.h"
+
+#include <fcntl.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+typedef int_fast64_t zic_t;
+static zic_t const
+ ZIC_MIN = INT_FAST64_MIN,
+ ZIC_MAX = INT_FAST64_MAX,
+ ZIC32_MIN = -1 - (zic_t) 0x7fffffff,
+ ZIC32_MAX = 0x7fffffff;
+#define SCNdZIC SCNdFAST64
+
+#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
+# define ZIC_MAX_ABBR_LEN_WO_WARN 6
+#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
+
+/* An upper bound on how much a format might grow due to concatenation. */
+enum { FORMAT_LEN_GROWTH_BOUND = 5 };
+
+#ifdef HAVE_DIRECT_H
+# include <direct.h>
+# include <io.h>
+# undef mkdir
+# define mkdir(name, mode) _mkdir(name)
+#endif
+
+#ifndef HAVE_GETRANDOM
+# ifdef __has_include
+# if __has_include(<sys/random.h>)
+# include <sys/random.h>
+# endif
+# elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__)
+# include <sys/random.h>
+# endif
+# define HAVE_GETRANDOM GRND_RANDOM
+#elif HAVE_GETRANDOM
+# include <sys/random.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef S_IRUSR
+# define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+#else
+# define MKDIR_UMASK 0755
+#endif
+
+/* The minimum alignment of a type, for pre-C23 platforms. */
+#if __STDC_VERSION__ < 201112
+# define alignof(type) offsetof(struct { char a; type b; }, b)
+#elif __STDC_VERSION__ < 202311
+# include <stdalign.h>
+#endif
+
+/* The maximum length of a text line, including the trailing newline. */
+#ifndef _POSIX2_LINE_MAX
+# define _POSIX2_LINE_MAX 2048
+#endif
+
+/* The type for line numbers. Use PRIdMAX to format them; formerly
+ there was also "#define PRIdLINENO PRIdMAX" and formats used
+ PRIdLINENO, but xgettext cannot grok that. */
+typedef intmax_t lineno;
+
+struct rule {
+ int r_filenum;
+ lineno r_linenum;
+ const char * r_name;
+
+ zic_t r_loyear; /* for example, 1986 */
+ zic_t r_hiyear; /* for example, 1986 */
+ bool r_lowasnum;
+ bool r_hiwasnum;
+
+ int r_month; /* 0..11 */
+
+ int r_dycode; /* see below */
+ int r_dayofmonth;
+ int r_wday;
+
+ zic_t r_tod; /* time from midnight */
+ bool r_todisstd; /* is r_tod standard time? */
+ bool r_todisut; /* is r_tod UT? */
+ bool r_isdst; /* is this daylight saving time? */
+ zic_t r_save; /* offset from standard time */
+ const char * r_abbrvar; /* variable part of abbreviation */
+
+ bool r_todo; /* a rule to do (used in outzone) */
+ zic_t r_temp; /* used in outzone */
+};
+
+/*
+** r_dycode r_dayofmonth r_wday
+*/
+enum {
+ DC_DOM, /* 1..31 */ /* unused */
+ DC_DOWGEQ, /* 1..31 */ /* 0..6 (Sun..Sat) */
+ DC_DOWLEQ /* 1..31 */ /* 0..6 (Sun..Sat) */
+};
+
+struct zone {
+ int z_filenum;
+ lineno z_linenum;
+
+ const char * z_name;
+ zic_t z_stdoff;
+ char * z_rule;
+ const char * z_format;
+ char z_format_specifier;
+
+ bool z_isdst;
+ zic_t z_save;
+
+ struct rule * z_rules;
+ ptrdiff_t z_nrules;
+
+ struct rule z_untilrule;
+ zic_t z_untiltime;
+};
+
+#if !HAVE_POSIX_DECLS
+extern int getopt(int argc, char * const argv[],
+ const char * options);
+extern int link(const char * target, const char * linkname);
+extern char * optarg;
+extern int optind;
+#endif
+
+#if ! HAVE_SYMLINK
+static ssize_t
+readlink(char const *restrict file, char *restrict buf, size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+static int
+symlink(char const *target, char const *linkname)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+#endif
+#ifndef AT_SYMLINK_FOLLOW
+# if HAVE_LINK
+# define linkat(targetdir, target, linknamedir, linkname, flag) \
+ (itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname))
+# else
+# define linkat(targetdir, target, linknamedir, linkname, flag) \
+ (errno = ENOTSUP, -1)
+# endif
+#endif
+
+static void addtt(zic_t starttime, int type);
+static int addtype(zic_t, char const *, bool, bool, bool);
+static void leapadd(zic_t, int, int);
+static void adjleap(void);
+static void associate(void);
+static void dolink(const char *, const char *, bool);
+static int getfields(char *, char **, int);
+static zic_t gethms(const char * string, const char * errstring);
+static zic_t getsave(char *, bool *);
+static void inexpires(char **, int);
+static void infile(int, char const *);
+static void inleap(char ** fields, int nfields);
+static void inlink(char ** fields, int nfields);
+static void inrule(char ** fields, int nfields);
+static bool inzcont(char ** fields, int nfields);
+static bool inzone(char ** fields, int nfields);
+static bool inzsub(char **, int, bool);
+static bool itssymlink(char const *);
+static bool is_alpha(char a);
+static char lowerit(char);
+static void mkdirs(char const *, bool);
+static void newabbr(const char * abbr);
+static zic_t oadd(zic_t t1, zic_t t2);
+static void outzone(const struct zone * zp, ptrdiff_t ntzones);
+static zic_t rpytime(const struct rule * rp, zic_t wantedy);
+static bool rulesub(struct rule * rp,
+ const char * loyearp, const char * hiyearp,
+ const char * typep, const char * monthp,
+ const char * dayp, const char * timep);
+static void setgroup(gid_t *flag, const char *name);
+static void setuser(uid_t *flag, const char *name);
+static zic_t tadd(zic_t t1, zic_t t2);
+
+/* Bound on length of what %z can expand to. */
+enum { PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1 };
+
+static int charcnt;
+static bool errors;
+static bool warnings;
+static int filenum;
+static int leapcnt;
+static bool leapseen;
+static zic_t leapminyear;
+static zic_t leapmaxyear;
+static lineno linenum;
+static size_t max_abbrvar_len = PERCENT_Z_LEN_BOUND;
+static int max_format_len;
+static zic_t max_year;
+static zic_t min_year;
+static bool noise;
+static int rfilenum;
+static lineno rlinenum;
+static const char * progname;
+static char const * leapsec;
+static char *const * main_argv;
+static ptrdiff_t timecnt;
+static ptrdiff_t timecnt_alloc;
+static int typecnt;
+static int unspecifiedtype;
+
+/*
+** Line codes.
+*/
+
+enum {
+ LC_RULE,
+ LC_ZONE,
+ LC_LINK,
+ LC_LEAP,
+ LC_EXPIRES
+};
+
+/*
+** Which fields are which on a Zone line.
+*/
+
+enum {
+ ZF_NAME = 1,
+ ZF_STDOFF,
+ ZF_RULE,
+ ZF_FORMAT,
+ ZF_TILYEAR,
+ ZF_TILMONTH,
+ ZF_TILDAY,
+ ZF_TILTIME,
+ ZONE_MAXFIELDS,
+ ZONE_MINFIELDS = ZF_TILYEAR
+};
+
+/*
+** Which fields are which on a Zone continuation line.
+*/
+
+enum {
+ ZFC_STDOFF,
+ ZFC_RULE,
+ ZFC_FORMAT,
+ ZFC_TILYEAR,
+ ZFC_TILMONTH,
+ ZFC_TILDAY,
+ ZFC_TILTIME,
+ ZONEC_MAXFIELDS,
+ ZONEC_MINFIELDS = ZFC_TILYEAR
+};
+
+/*
+** Which files are which on a Rule line.
+*/
+
+enum {
+ RF_NAME = 1,
+ RF_LOYEAR,
+ RF_HIYEAR,
+ RF_COMMAND,
+ RF_MONTH,
+ RF_DAY,
+ RF_TOD,
+ RF_SAVE,
+ RF_ABBRVAR,
+ RULE_FIELDS
+};
+
+/*
+** Which fields are which on a Link line.
+*/
+
+enum {
+ LF_TARGET = 1,
+ LF_LINKNAME,
+ LINK_FIELDS
+};
+
+/*
+** Which fields are which on a Leap line.
+*/
+
+enum {
+ LP_YEAR = 1,
+ LP_MONTH,
+ LP_DAY,
+ LP_TIME,
+ LP_CORR,
+ LP_ROLL,
+ LEAP_FIELDS,
+
+ /* Expires lines are like Leap lines, except without CORR and ROLL fields. */
+ EXPIRES_FIELDS = LP_TIME + 1
+};
+
+/* The maximum number of fields on any of the above lines.
+ (The "+"s pacify gcc -Wenum-compare.) */
+enum {
+ MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
+ max(+LEAP_FIELDS, +EXPIRES_FIELDS))
+};
+
+/*
+** Year synonyms.
+*/
+
+enum {
+ YR_MINIMUM,
+ YR_MAXIMUM,
+ YR_ONLY
+};
+
+static struct rule * rules;
+static ptrdiff_t nrules; /* number of rules */
+static ptrdiff_t nrules_alloc;
+
+static struct zone * zones;
+static ptrdiff_t nzones; /* number of zones */
+static ptrdiff_t nzones_alloc;
+
+struct link {
+ int l_filenum;
+ lineno l_linenum;
+ const char * l_target;
+ const char * l_linkname;
+};
+
+static struct link * links;
+static ptrdiff_t nlinks;
+static ptrdiff_t nlinks_alloc;
+
+struct lookup {
+ const char * l_word;
+ const int l_value;
+};
+
+static struct lookup const * byword(const char * string,
+ const struct lookup * lp);
+
+static struct lookup const zi_line_codes[] = {
+ { "Rule", LC_RULE },
+ { "Zone", LC_ZONE },
+ { "Link", LC_LINK },
+ { NULL, 0 }
+};
+static struct lookup const leap_line_codes[] = {
+ { "Leap", LC_LEAP },
+ { "Expires", LC_EXPIRES },
+ { NULL, 0}
+};
+
+static struct lookup const mon_names[] = {
+ { "January", TM_JANUARY },
+ { "February", TM_FEBRUARY },
+ { "March", TM_MARCH },
+ { "April", TM_APRIL },
+ { "May", TM_MAY },
+ { "June", TM_JUNE },
+ { "July", TM_JULY },
+ { "August", TM_AUGUST },
+ { "September", TM_SEPTEMBER },
+ { "October", TM_OCTOBER },
+ { "November", TM_NOVEMBER },
+ { "December", TM_DECEMBER },
+ { NULL, 0 }
+};
+
+static struct lookup const wday_names[] = {
+ { "Sunday", TM_SUNDAY },
+ { "Monday", TM_MONDAY },
+ { "Tuesday", TM_TUESDAY },
+ { "Wednesday", TM_WEDNESDAY },
+ { "Thursday", TM_THURSDAY },
+ { "Friday", TM_FRIDAY },
+ { "Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const lasts[] = {
+ { "last-Sunday", TM_SUNDAY },
+ { "last-Monday", TM_MONDAY },
+ { "last-Tuesday", TM_TUESDAY },
+ { "last-Wednesday", TM_WEDNESDAY },
+ { "last-Thursday", TM_THURSDAY },
+ { "last-Friday", TM_FRIDAY },
+ { "last-Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const begin_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { NULL, 0 }
+};
+
+static struct lookup const end_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { "only", YR_ONLY },
+ { NULL, 0 }
+};
+
+static struct lookup const leap_types[] = {
+ { "Rolling", true },
+ { "Stationary", false },
+ { NULL, 0 }
+};
+
+static const int len_months[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 len_years[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+static struct attype {
+ zic_t at;
+ bool dontmerge;
+ unsigned char type;
+} * attypes;
+static zic_t utoffs[TZ_MAX_TYPES];
+static char isdsts[TZ_MAX_TYPES];
+static unsigned char desigidx[TZ_MAX_TYPES];
+static bool ttisstds[TZ_MAX_TYPES];
+static bool ttisuts[TZ_MAX_TYPES];
+static char chars[TZ_MAX_CHARS];
+static zic_t trans[TZ_MAX_LEAPS];
+static zic_t corr[TZ_MAX_LEAPS];
+static char roll[TZ_MAX_LEAPS];
+
+/*
+** Memory allocation.
+*/
+
+static ATTRIBUTE_NORETURN void
+memory_exhausted(const char *msg)
+{
+ fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
+ exit(EXIT_FAILURE);
+}
+
+static ATTRIBUTE_NORETURN void
+size_overflow(void)
+{
+ memory_exhausted(_("size overflow"));
+}
+
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+size_sum(size_t a, size_t b)
+{
+#ifdef ckd_add
+ ptrdiff_t sum;
+ if (!ckd_add(&sum, a, b) /* && sum <= SIZE_MAX */)
+ return sum;
+#else
+ ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX);
+ if (a <= sum_max && b <= sum_max - a)
+ return a + b;
+#endif
+ size_overflow();
+}
+
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+size_product(ptrdiff_t nitems, ptrdiff_t itemsize)
+{
+#ifdef ckd_mul
+ ptrdiff_t product;
+ if (!ckd_mul(&product, nitems, itemsize) /* && product <= SIZE_MAX */)
+ return product;
+#else
+ ptrdiff_t nitems_max = min(PTRDIFF_MAX, SIZE_MAX) / itemsize;
+ if (nitems <= nitems_max)
+ return nitems * itemsize;
+#endif
+ size_overflow();
+}
+
+static ATTRIBUTE_REPRODUCIBLE ptrdiff_t
+align_to(ptrdiff_t size, ptrdiff_t alignment)
+{
+ ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits);
+ return sum & ~lo_bits;
+}
+
+#if !HAVE_STRDUP
+static char *
+strdup(char const *str)
+{
+ char *result = malloc(strlen(str) + 1);
+ return result ? strcpy(result, str) : result;
+}
+#endif
+
+static void *
+memcheck(void *ptr)
+{
+ if (ptr == NULL)
+ memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM));
+ return ptr;
+}
+
+static void * ATTRIBUTE_MALLOC
+emalloc(size_t size)
+{
+ return memcheck(malloc(size));
+}
+
+static void *
+erealloc(void *ptr, size_t size)
+{
+ return memcheck(realloc(ptr, size));
+}
+
+static char * ATTRIBUTE_MALLOC
+estrdup(char const *str)
+{
+ return memcheck(strdup(str));
+}
+
+static ptrdiff_t
+grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize)
+{
+ ptrdiff_t addend = (*nitems_alloc >> 1) + 1;
+#if defined ckd_add && defined ckd_mul
+ ptrdiff_t product;
+ if (!ckd_add(nitems_alloc, *nitems_alloc, addend)
+ && !ckd_mul(&product, *nitems_alloc, itemsize) /* && product <= SIZE_MAX */)
+ return product;
+#else
+ ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX);
+ if (*nitems_alloc <= ((amax - 1) / 3 * 2) / itemsize) {
+ *nitems_alloc += addend;
+ return *nitems_alloc * itemsize;
+ }
+#endif
+ memory_exhausted(_("integer overflow"));
+}
+
+static void *
+growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
+ ptrdiff_t *nitems_alloc)
+{
+ return (nitems < *nitems_alloc
+ ? ptr
+ : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
+}
+
+/*
+** Error handling.
+*/
+
+/* In most of the code, an input file name is represented by its index
+ into the main argument vector, except that LEAPSEC_FILENUM stands
+ for leapsec and COMMAND_LINE_FILENUM stands for the command line. */
+enum { LEAPSEC_FILENUM = -2, COMMAND_LINE_FILENUM = -1 };
+
+/* Return the name of the Ith input file, for diagnostics. */
+static char const *
+filename(int i)
+{
+ if (i == COMMAND_LINE_FILENUM)
+ return _("command line");
+ else {
+ char const *fname = i == LEAPSEC_FILENUM ? leapsec : main_argv[i];
+ return strcmp(fname, "-") == 0 ? _("standard input") : fname;
+ }
+}
+
+static void
+eats(int fnum, lineno num, int rfnum, lineno rnum)
+{
+ filenum = fnum;
+ linenum = num;
+ rfilenum = rfnum;
+ rlinenum = rnum;
+}
+
+static void
+eat(int fnum, lineno num)
+{
+ eats(fnum, num, 0, -1);
+}
+
+static void ATTRIBUTE_FORMAT((printf, 1, 0))
+verror(const char *const string, va_list args)
+{
+ /*
+ ** Match the format of "cc" to allow sh users to
+ ** zic ... 2>&1 | error -t "*" -v
+ ** on BSD systems.
+ */
+ if (filenum)
+ fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
+ filename(filenum), linenum);
+ vfprintf(stderr, string, args);
+ if (rfilenum)
+ fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
+ filename(rfilenum), rlinenum);
+ fprintf(stderr, "\n");
+}
+
+static void ATTRIBUTE_FORMAT((printf, 1, 2))
+error(const char *const string, ...)
+{
+ va_list args;
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+ errors = true;
+}
+
+static void ATTRIBUTE_FORMAT((printf, 1, 2))
+warning(const char *const string, ...)
+{
+ va_list args;
+ fprintf(stderr, _("warning: "));
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+ warnings = true;
+}
+
+/* Close STREAM. If it had an I/O error, report it against DIR/NAME,
+ remove TEMPNAME if nonnull, and then exit. */
+static void
+close_file(FILE *stream, char const *dir, char const *name,
+ char const *tempname)
+{
+ char const *e = (ferror(stream) ? _("I/O error")
+ : fclose(stream) != 0 ? strerror(errno) : NULL);
+ if (e) {
+ fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
+ dir ? dir : "", dir ? "/" : "",
+ name ? name : "", name ? ": " : "",
+ e);
+ if (tempname)
+ remove(tempname);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static ATTRIBUTE_NORETURN void
+usage(FILE *stream, int status)
+{
+ fprintf(stream,
+ _("%s: usage is %s [ --version ] [ --help ] [ -v ] \\\n"
+ "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
+ " [ -L leapseconds ] \\\n"
+ "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -R '@hi' ] \\\n"
+ "\t[ -t localtime-link ] [ -D ] [ -g gid ] [ -u uid ] \\\n"
+ "\t[ filename ... ]\n\n"
+ "Report bugs to %s.\n"),
+ progname, progname, REPORT_BUGS_TO);
+ if (status == EXIT_SUCCESS)
+ close_file(stream, NULL, NULL, NULL);
+ exit(status);
+}
+
+/* Change the working directory to DIR, possibly creating DIR and its
+ ancestors. After this is done, all files are accessed with names
+ relative to DIR. */
+static void
+change_directory(char const *dir)
+{
+ if (chdir(dir) != 0) {
+ int chdir_errno = errno;
+ if (chdir_errno == ENOENT) {
+ mkdirs(dir, false);
+ chdir_errno = chdir(dir) == 0 ? 0 : errno;
+ }
+ if (chdir_errno != 0) {
+ fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
+ progname, dir, strerror(chdir_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+/* Compare the two links A and B, for a stable sort by link name. */
+static int
+qsort_linkcmp(void const *a, void const *b)
+{
+ struct link const *l = a;
+ struct link const *m = b;
+ int cmp = strcmp(l->l_linkname, m->l_linkname);
+ if (cmp)
+ return cmp;
+
+ /* The link names are the same. Make the sort stable by comparing
+ file numbers (where subtraction cannot overflow) and possibly
+ line numbers (where it can). */
+ cmp = l->l_filenum - m->l_filenum;
+ if (cmp)
+ return cmp;
+ return (l->l_linenum > m->l_linenum) - (l->l_linenum < m->l_linenum);
+}
+
+/* Compare the string KEY to the link B, for bsearch. */
+static int
+bsearch_linkcmp(void const *key, void const *b)
+{
+ struct link const *m = b;
+ return strcmp(key, m->l_linkname);
+}
+
+/* Make the links specified by the Link lines. */
+static void
+make_links(void)
+{
+ ptrdiff_t i, j, nalinks, pass_size;
+ if (1 < nlinks)
+ qsort(links, nlinks, sizeof *links, qsort_linkcmp);
+
+ /* Ignore each link superseded by a later link with the same name. */
+ j = 0;
+ for (i = 0; i < nlinks; i++) {
+ while (i + 1 < nlinks
+ && strcmp(links[i].l_linkname, links[i + 1].l_linkname) == 0)
+ i++;
+ links[j++] = links[i];
+ }
+ nlinks = pass_size = j;
+
+ /* Walk through the link array making links. However,
+ if a link's target has not been made yet, append a copy to the
+ end of the array. The end of the array will gradually fill
+ up with a small sorted subsequence of not-yet-made links.
+ nalinks counts all the links in the array, including copies.
+ When we reach the copied subsequence, it may still contain
+ a link to a not-yet-made link, so the process repeats.
+ At any given point in time, the link array consists of the
+ following subregions, where 0 <= i <= j <= nalinks and
+ 0 <= nlinks <= nalinks:
+
+ 0 .. (i - 1):
+ links that either have been made, or have been copied to a
+ later point point in the array (this later point can be in
+ any of the three subregions)
+ i .. (j - 1):
+ not-yet-made links for this pass
+ j .. (nalinks - 1):
+ not-yet-made links that this pass has skipped because
+ they were links to not-yet-made links
+
+ The first subregion might not be sorted if nlinks < i;
+ the other two subregions are sorted. This algorithm does
+ not alter entries 0 .. (nlinks - 1), which remain sorted.
+
+ If there are L links, this algorithm is O(C*L*log(L)) where
+ C is the length of the longest link chain. Usually C is
+ short (e.g., 3) though its worst-case value is L. */
+
+ j = nalinks = nlinks;
+
+ for (i = 0; i < nalinks; i++) {
+ struct link *l;
+
+ eat(links[i].l_filenum, links[i].l_linenum);
+
+ /* If this pass examined all its links, start the next pass. */
+ if (i == j) {
+ if (nalinks - i == pass_size) {
+ error(_("\"Link %s %s\" is part of a link cycle"),
+ links[i].l_target, links[i].l_linkname);
+ break;
+ }
+ j = nalinks;
+ pass_size = nalinks - i;
+ }
+
+ /* Diagnose self links, which the cycle detection algorithm would not
+ otherwise catch. */
+ if (strcmp(links[i].l_target, links[i].l_linkname) == 0) {
+ error(_("link %s targets itself"), links[i].l_target);
+ continue;
+ }
+
+ /* Make this link unless its target has not been made yet. */
+ l = bsearch(links[i].l_target, &links[i + 1], j - (i + 1),
+ sizeof *links, bsearch_linkcmp);
+ if (!l)
+ l = bsearch(links[i].l_target, &links[j], nalinks - j,
+ sizeof *links, bsearch_linkcmp);
+ if (!l)
+ dolink(links[i].l_target, links[i].l_linkname, false);
+ else {
+ /* The link target has not been made yet; copy the link to the end. */
+ links = growalloc(links, sizeof *links, nalinks, &nlinks_alloc);
+ links[nalinks++] = links[i];
+ }
+
+ if (noise && i < nlinks) {
+ if (l)
+ warning(_("link %s targeting link %s mishandled by pre-2023 zic"),
+ links[i].l_linkname, links[i].l_target);
+ else if (bsearch(links[i].l_target, links, nlinks, sizeof *links,
+ bsearch_linkcmp))
+ warning(_("link %s targeting link %s"),
+ links[i].l_linkname, links[i].l_target);
+ }
+ }
+}
+
+/* Simple signal handling: just set a flag that is checked
+ periodically outside critical sections. To set up the handler,
+ prefer sigaction if available to close a signal race. */
+
+static sig_atomic_t got_signal;
+
+static void
+signal_handler(int sig)
+{
+#ifndef SA_SIGINFO
+ signal(sig, signal_handler);
+#endif
+ got_signal = sig;
+}
+
+/* Arrange for SIGINT etc. to be caught by the handler. */
+static void
+catch_signals(void)
+{
+ static int const signals[] = {
+#ifdef SIGHUP
+ SIGHUP,
+#endif
+ SIGINT,
+#ifdef SIGPIPE
+ SIGPIPE,
+#endif
+ SIGTERM
+ };
+ size_t i;
+ for (i = 0; i < sizeof signals / sizeof signals[0]; i++) {
+#ifdef SA_SIGINFO
+ struct sigaction act0, act;
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ if (sigaction(signals[i], &act, &act0) == 0
+ && ! (act0.sa_flags & SA_SIGINFO) && act0.sa_handler == SIG_IGN) {
+ sigaction(signals[i], &act0, NULL);
+ got_signal = 0;
+ }
+#else
+ if (signal(signals[i], signal_handler) == SIG_IGN) {
+ signal(signals[i], SIG_IGN);
+ got_signal = 0;
+ }
+#endif
+ }
+}
+
+/* If a signal has arrived, terminate zic with appropriate status. */
+static void
+check_for_signal(void)
+{
+ int sig = got_signal;
+ if (sig) {
+ signal(sig, SIG_DFL);
+ raise(sig);
+ abort(); /* A bug in 'raise'. */
+ }
+}
+
+enum { TIME_T_BITS_IN_FILE = 64 };
+
+/* The minimum and maximum values representable in a TZif file. */
+static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The minimum, and one less than the maximum, values specified by
+ the -r option. These default to MIN_TIME and MAX_TIME. */
+static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The time specified by the -R option, defaulting to MIN_TIME. */
+static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The time specified by an Expires line, or negative if no such line. */
+static zic_t leapexpires = -1;
+
+/* Set the time range of the output to TIMERANGE.
+ Return true if successful. */
+static bool
+timerange_option(char *timerange)
+{
+ intmax_t lo = min_time, hi = max_time;
+ char *lo_end = timerange, *hi_end;
+ if (*timerange == '@') {
+ errno = 0;
+ lo = strtoimax(timerange + 1, &lo_end, 10);
+ if (lo_end == timerange + 1 || (lo == INTMAX_MAX && errno == ERANGE))
+ return false;
+ }
+ hi_end = lo_end;
+ if (lo_end[0] == '/' && lo_end[1] == '@') {
+ errno = 0;
+ hi = strtoimax(lo_end + 2, &hi_end, 10);
+ if (hi_end == lo_end + 2 || hi == INTMAX_MIN)
+ return false;
+ hi -= ! (hi == INTMAX_MAX && errno == ERANGE);
+ }
+ if (*hi_end || hi < lo || max_time < lo || hi < min_time)
+ return false;
+ lo_time = max(lo, min_time);
+ hi_time = min(hi, max_time);
+ return true;
+}
+
+/* Generate redundant time stamps up to OPT. Return true if successful. */
+static bool
+redundant_time_option(char *opt)
+{
+ if (*opt == '@') {
+ intmax_t redundant;
+ char *opt_end;
+ redundant = strtoimax(opt + 1, &opt_end, 10);
+ if (opt_end != opt + 1 && !*opt_end) {
+ redundant_time = max(redundant_time, redundant);
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char * psxrules;
+static const char * lcltime;
+static const char * directory;
+static const char * leapsec;
+static int Dflag;
+static uid_t uflag = (uid_t)-1;
+static gid_t gflag = (gid_t)-1;
+static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH
+ | S_IWUSR);
+static const char * tzdefault;
+
+/* -1 if the TZif output file should be slim, 0 if default, 1 if the
+ output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT
+ determines the default. */
+static int bloat;
+
+static bool
+want_bloat(void)
+{
+ return 0 <= bloat;
+}
+
+#ifndef ZIC_BLOAT_DEFAULT
+# define ZIC_BLOAT_DEFAULT "slim"
+#endif
+
+int
+main(int argc, char **argv)
+{
+ register int c, k;
+ register ptrdiff_t i, j;
+ bool timerange_given = false;
+
+#ifdef S_IWGRP
+ umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
+#endif
+#if HAVE_GETTEXT
+ setlocale(LC_ALL, "");
+# ifdef TZ_DOMAINDIR
+ bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+# endif /* defined TEXTDOMAINDIR */
+ textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT */
+ main_argv = argv;
+ progname = /* argv[0] ? argv[0] : */ "zic";
+ if (TYPE_BIT(zic_t) < 64) {
+ fprintf(stderr, "%s: %s\n", progname,
+ _("wild compilation-time specification of zic_t"));
+ return EXIT_FAILURE;
+ }
+ for (k = 1; k < argc; k++)
+ if (strcmp(argv[k], "--version") == 0) {
+ printf("zic %s%s\n", PKGVERSION, TZVERSION);
+ close_file(stdout, NULL, NULL, NULL);
+ return EXIT_SUCCESS;
+ } else if (strcmp(argv[k], "--help") == 0) {
+ usage(stdout, EXIT_SUCCESS);
+ }
+ while ((c = getopt(argc, argv, "Db:d:g:l:L:m:p:r:R:st:u:vy:")) != EOF
+ && c != -1)
+ switch (c) {
+ default:
+ usage(stderr, EXIT_FAILURE);
+ case 'D':
+ Dflag = 1;
+ break;
+ case 'b':
+ if (strcmp(optarg, "slim") == 0) {
+ if (0 < bloat)
+ error(_("incompatible -b options"));
+ bloat = -1;
+ } else if (strcmp(optarg, "fat") == 0) {
+ if (bloat < 0)
+ error(_("incompatible -b options"));
+ bloat = 1;
+ } else
+ error(_("invalid option: -b '%s'"), optarg);
+ break;
+ case 'd':
+ if (directory == NULL)
+ directory = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -d option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'g':
+ setgroup(&gflag, optarg);
+ break;
+ case 'l':
+ if (lcltime == NULL)
+ lcltime = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -l option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'm':
+ {
+ void *set = setmode(optarg);
+ if (set == NULL) {
+ fprintf(stderr,
+_("invalid file mode"));
+ return EXIT_FAILURE;
+ }
+ mflag = getmode(set, mflag);
+ free(set);
+ break;
+ }
+ case 'p':
+ if (psxrules == NULL)
+ psxrules = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -p option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 't':
+ if (tzdefault != NULL) {
+ fprintf(stderr,
+ _("%s: More than one -t option"
+ " specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ tzdefault = optarg;
+ break;
+ case 'u':
+ setuser(&uflag, optarg);
+ break;
+ case 'y':
+ warning(_("-y ignored"));
+ break;
+ case 'L':
+ if (leapsec == NULL)
+ leapsec = optarg;
+ else {
+ fprintf(stderr,
+_("%s: More than one -L option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'v':
+ noise = true;
+ break;
+ case 'r':
+ if (timerange_given) {
+ fprintf(stderr,
+_("%s: More than one -r option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ if (! timerange_option(optarg)) {
+ fprintf(stderr,
+_("%s: invalid time range: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ timerange_given = true;
+ break;
+ case 'R':
+ if (! redundant_time_option(optarg)) {
+ fprintf(stderr, _("%s: invalid time: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 's':
+ warning(_("-s ignored"));
+ break;
+ }
+ if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
+ usage(stderr, EXIT_FAILURE); /* usage message by request */
+ if (hi_time + (hi_time < ZIC_MAX) < redundant_time) {
+ fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname);
+ return EXIT_FAILURE;
+ }
+ if (bloat == 0) {
+ static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
+ if (strcmp(bloat_default, "slim") == 0)
+ bloat = -1;
+ else if (strcmp(bloat_default, "fat") == 0)
+ bloat = 1;
+ else
+ abort(); /* Configuration error. */
+ }
+ if (directory == NULL)
+ directory = TZDIR;
+ if (tzdefault == NULL)
+ tzdefault = TZDEFAULT;
+
+ if (optind < argc && leapsec != NULL) {
+ infile(LEAPSEC_FILENUM, leapsec);
+ adjleap();
+ }
+
+ for (k = optind; k < argc; k++)
+ infile(k, argv[k]);
+ if (errors)
+ return EXIT_FAILURE;
+ associate();
+ change_directory(directory);
+ catch_signals();
+ for (i = 0; i < nzones; i = j) {
+ /*
+ ** Find the next non-continuation zone entry.
+ */
+ for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
+ continue;
+ outzone(&zones[i], j - i);
+ }
+ make_links();
+ if (lcltime != NULL) {
+ eat(COMMAND_LINE_FILENUM, 1);
+ dolink(lcltime, tzdefault, true);
+ }
+ if (psxrules != NULL) {
+ eat(COMMAND_LINE_FILENUM, 1);
+ dolink(psxrules, TZDEFRULES, true);
+ }
+ if (warnings && (ferror(stderr) || fclose(stderr) != 0))
+ return EXIT_FAILURE;
+ return errors ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static bool
+componentcheck(char const *name, char const *component,
+ char const *component_end)
+{
+ enum { component_len_max = 14 };
+ ptrdiff_t component_len = component_end - component;
+ if (component_len == 0) {
+ if (!*name)
+ error(_("empty file name"));
+ else
+ error(_(component == name
+ ? "file name '%s' begins with '/'"
+ : *component_end
+ ? "file name '%s' contains '//'"
+ : "file name '%s' ends with '/'"),
+ name);
+ return false;
+ }
+ if (0 < component_len && component_len <= 2
+ && component[0] == '.' && component_end[-1] == '.') {
+ int len = component_len;
+ error(_("file name '%s' contains '%.*s' component"),
+ name, len, component);
+ return false;
+ }
+ if (noise) {
+ if (0 < component_len && component[0] == '-')
+ warning(_("file name '%s' component contains leading '-'"),
+ name);
+ if (component_len_max < component_len)
+ warning(_("file name '%s' contains overlength component"
+ " '%.*s...'"),
+ name, component_len_max, component);
+ }
+ return true;
+}
+
+static bool
+namecheck(const char *name)
+{
+ register char const *cp;
+
+ /* Benign characters in a portable file name. */
+ static char const benign[] =
+ "-/_"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /* Non-control chars in the POSIX portable character set,
+ excluding the benign characters. */
+ static char const printable_and_not_benign[] =
+ " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
+
+ register char const *component = name;
+ for (cp = name; *cp; cp++) {
+ unsigned char c = *cp;
+ if (noise && !strchr(benign, c)) {
+ warning((strchr(printable_and_not_benign, c)
+ ? _("file name '%s' contains byte '%c'")
+ : _("file name '%s' contains byte '\\%o'")),
+ name, c);
+ }
+ if (c == '/') {
+ if (!componentcheck(name, component, cp))
+ return false;
+ component = cp + 1;
+ }
+ }
+ return componentcheck(name, component, cp);
+}
+
+/* Return a random uint_fast64_t. */
+static uint_fast64_t
+get_rand_u64(void)
+{
+#if HAVE_GETRANDOM
+ static uint_fast64_t entropy_buffer[max(1, 256 / sizeof(uint_fast64_t))];
+ static int nwords;
+ if (!nwords) {
+ ssize_t s;
+ do
+ s = getrandom(entropy_buffer, sizeof entropy_buffer, 0);
+ while (s < 0 && errno == EINTR);
+
+ nwords = s < 0 ? -1 : s / sizeof *entropy_buffer;
+ }
+ if (0 < nwords)
+ return entropy_buffer[--nwords];
+#endif
+
+ /* getrandom didn't work, so fall back on portable code that is
+ not the best because the seed isn't cryptographically random and
+ 'rand' might not be cryptographically secure. */
+ {
+ static bool initialized;
+ if (!initialized) {
+ srand(time(NULL));
+ initialized = true;
+ }
+ }
+
+ /* Return a random number if rand() yields a random number and in
+ the typical case where RAND_MAX is one less than a power of two.
+ In other cases this code yields a sort-of-random number. */
+ {
+ uint_fast64_t rand_max = RAND_MAX,
+ nrand = rand_max < UINT_FAST64_MAX ? rand_max + 1 : 0,
+ rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1,
+ r = 0, rmax = 0;
+
+ do {
+ uint_fast64_t rmax1 = rmax;
+ if (rmod) {
+ /* Avoid signed integer overflow on theoretical platforms
+ where uint_fast64_t promotes to int. */
+ rmax1 %= rmod;
+ r %= rmod;
+ }
+ rmax1 = nrand * rmax1 + rand_max;
+ r = nrand * r + rand();
+ rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX;
+ } while (rmax < UINT_FAST64_MAX);
+
+ return r;
+ }
+}
+
+/* Generate a randomish name in the same directory as *NAME. If
+ *NAMEALLOC, put the name into *NAMEALLOC which is assumed to be
+ that returned by a previous call and is thus already almost set up
+ and equal to *NAME; otherwise, allocate a new name and put its
+ address into both *NAMEALLOC and *NAME. */
+static void
+random_dirent(char const **name, char **namealloc)
+{
+ char const *src = *name;
+ char *dst = *namealloc;
+ static char const prefix[] = ".zic";
+ static char const alphabet[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ enum { prefixlen = sizeof prefix - 1, alphabetlen = sizeof alphabet - 1 };
+ int suffixlen = 6;
+ char const *lastslash = strrchr(src, '/');
+ ptrdiff_t dirlen = lastslash ? lastslash + 1 - src : 0;
+ int i;
+ uint_fast64_t r;
+ uint_fast64_t base = alphabetlen;
+
+ /* BASE**6 */
+ uint_fast64_t base__6 = base * base * base * base * base * base;
+
+ /* The largest uintmax_t that is a multiple of BASE**6. Any random
+ uintmax_t value that is this value or greater, yields a biased
+ remainder when divided by BASE**6. UNFAIR_MIN equals the
+ mathematical value of ((UINTMAX_MAX + 1) - (UINTMAX_MAX + 1) % BASE**6)
+ computed without overflow. */
+ uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
+
+ if (!dst) {
+ dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
+ memcpy(dst, src, dirlen);
+ memcpy(dst + dirlen, prefix, prefixlen);
+ dst[dirlen + prefixlen + suffixlen] = '\0';
+ *name = *namealloc = dst;
+ }
+
+ do
+ r = get_rand_u64();
+ while (unfair_min <= r);
+
+ for (i = 0; i < suffixlen; i++) {
+ dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
+ r /= alphabetlen;
+ }
+}
+
+/* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
+ name of the temporary file that will eventually be renamed to
+ *OUTNAME. Assign the temporary file's name to both *OUTNAME and
+ *TEMPNAME. If *TEMPNAME is null, allocate the name of any such
+ temporary file; otherwise, reuse *TEMPNAME's storage, which is
+ already set up and only needs its trailing suffix updated. */
+static FILE *
+open_outfile(char const **outname, char **tempname)
+{
+#if __STDC_VERSION__ < 201112
+ static char const fopen_mode[] = "wb";
+#else
+ static char const fopen_mode[] = "wbx";
+#endif
+
+ FILE *fp;
+ bool dirs_made = false;
+ if (!*tempname)
+ random_dirent(outname, tempname);
+
+ /*
+ * Remove old file, if any, to snap links.
+ */
+ if (remove(*outname) != 0 && errno != ENOENT && errno != EISDIR) {
+ fprintf(stderr, _("can't remove %s"), *outname);
+ exit(EXIT_FAILURE);
+ }
+
+ while (! (fp = fopen(*outname, fopen_mode))) {
+ int fopen_errno = errno;
+ if (fopen_errno == ENOENT && !dirs_made) {
+ mkdirs(*outname, true);
+ dirs_made = true;
+ } else if (fopen_errno == EEXIST)
+ random_dirent(outname, tempname);
+ else {
+ fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
+ progname, directory, *outname, strerror(fopen_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return fp;
+}
+
+/* If TEMPNAME, the result is in the temporary file TEMPNAME even
+ though the user wanted it in NAME, so rename TEMPNAME to NAME.
+ Report an error and exit if there is trouble. Also, free TEMPNAME. */
+static void
+rename_dest(char *tempname, char const *name)
+{
+ if (tempname) {
+ if (rename(tempname, name) != 0) {
+ int rename_errno = errno;
+ remove(tempname);
+ fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
+ progname, directory, name, strerror(rename_errno));
+ exit(EXIT_FAILURE);
+ }
+ free(tempname);
+ }
+}
+
+/* Create symlink contents suitable for symlinking FROM to TO, as a
+ freshly allocated string. FROM should be a relative file name, and
+ is relative to the global variable DIRECTORY. TO can be either
+ relative or absolute. */
+static char *
+relname(char const *target, char const *linkname)
+{
+ size_t i, taillen, dir_len = 0, dotdots = 0;
+ ptrdiff_t dotdotetcsize, linksize = min(PTRDIFF_MAX, SIZE_MAX);
+ char const *f = target;
+ char *result = NULL;
+ if (*linkname == '/') {
+ /* Make F absolute too. */
+ size_t len = strlen(directory);
+ size_t lenslash = len + (len && directory[len - 1] != '/');
+ size_t targetsize = strlen(target) + 1;
+ linksize = size_sum(lenslash, targetsize);
+ f = result = emalloc(linksize);
+ memcpy(result, directory, len);
+ result[len] = '/';
+ memcpy(result + lenslash, target, targetsize);
+ }
+ for (i = 0; f[i] && f[i] == linkname[i]; i++)
+ if (f[i] == '/')
+ dir_len = i + 1;
+ for (; linkname[i]; i++)
+ dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
+ taillen = strlen(f + dir_len);
+ dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
+ if (dotdotetcsize <= linksize) {
+ if (!result)
+ result = emalloc(dotdotetcsize);
+ for (i = 0; i < dotdots; i++)
+ memcpy(result + 3 * i, "../", 3);
+ memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
+ }
+ return result;
+}
+
+static void
+dolink(char const *target, char const *linkname, bool staysymlink)
+{
+ bool linkdirs_made = false;
+ int link_errno;
+ char *tempname = NULL;
+ char const *outname = linkname;
+
+ check_for_signal();
+
+ if (strcmp(target, "-") == 0) {
+ if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR)
+ return;
+ else {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
+ progname, directory, linkname, e);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ while (true) {
+ if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW)
+ == 0) {
+ link_errno = 0;
+ break;
+ }
+ link_errno = errno;
+ if (link_errno == EXDEV || link_errno == ENOTSUP)
+ break;
+
+ if (link_errno == EEXIST) {
+ staysymlink &= !tempname;
+ random_dirent(&outname, &tempname);
+ if (staysymlink && itssymlink(linkname))
+ break;
+ } else if (link_errno == ENOENT && !linkdirs_made) {
+ mkdirs(linkname, true);
+ linkdirs_made = true;
+ } else {
+ fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
+ progname, directory, target, directory, outname,
+ strerror(link_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (link_errno != 0) {
+ bool absolute = *target == '/';
+ char *linkalloc = absolute ? NULL : relname(target, linkname);
+ char const *contents = absolute ? target : linkalloc;
+ int symlink_errno;
+
+ while (true) {
+ if (symlink(contents, outname) == 0) {
+ symlink_errno = 0;
+ break;
+ }
+ symlink_errno = errno;
+ if (symlink_errno == EEXIST)
+ random_dirent(&outname, &tempname);
+ else if (symlink_errno == ENOENT && !linkdirs_made) {
+ mkdirs(linkname, true);
+ linkdirs_made = true;
+ } else
+ break;
+ }
+ free(linkalloc);
+ if (symlink_errno == 0) {
+ if (link_errno != ENOTSUP && link_errno != EEXIST)
+ warning(_("symbolic link used because hard link failed: %s"),
+ strerror(link_errno));
+ } else {
+ FILE *fp, *tp;
+ int c;
+ fp = fopen(target, "rb");
+ if (!fp) {
+ char const *e = strerror(errno);
+ fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
+ progname, directory, target, e);
+ exit(EXIT_FAILURE);
+ }
+ tp = open_outfile(&outname, &tempname);
+ while ((c = getc(fp)) != EOF)
+ putc(c, tp);
+ close_file(tp, directory, linkname, tempname);
+ close_file(fp, directory, target, NULL);
+ if (link_errno != ENOTSUP)
+ warning(_("copy used because hard link failed: %s"),
+ strerror(link_errno));
+ else if (symlink_errno != ENOTSUP)
+ warning(_("copy used because symbolic link failed: %s"),
+ strerror(symlink_errno));
+ }
+ }
+ rename_dest(tempname, linkname);
+}
+
+/* Return true if NAME is a symbolic link. */
+static bool
+itssymlink(char const *name)
+{
+ char c;
+ return 0 <= readlink(name, &c, 1);
+}
+
+/*
+** Associate sets of rules with zones.
+*/
+
+/*
+** Sort by rule name.
+*/
+
+static int
+rcomp(const void *cp1, const void *cp2)
+{
+ struct rule const *r1 = cp1, *r2 = cp2;
+ return strcmp(r1->r_name, r2->r_name);
+}
+
+static void
+associate(void)
+{
+ register struct zone * zp;
+ register struct rule * rp;
+ register ptrdiff_t i, j, base, out;
+
+ if (1 < nrules) {
+ qsort(rules, nrules, sizeof *rules, rcomp);
+ for (i = 0; i < nrules - 1; ++i) {
+ if (strcmp(rules[i].r_name,
+ rules[i + 1].r_name) != 0)
+ continue;
+ if (rules[i].r_filenum == rules[i + 1].r_filenum)
+ continue;
+ eat(rules[i].r_filenum, rules[i].r_linenum);
+ warning(_("same rule name in multiple files"));
+ eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum);
+ warning(_("same rule name in multiple files"));
+ for (j = i + 2; j < nrules; ++j) {
+ if (strcmp(rules[i].r_name,
+ rules[j].r_name) != 0)
+ break;
+ if (rules[i].r_filenum == rules[j].r_filenum)
+ continue;
+ if (rules[i + 1].r_filenum
+ == rules[j].r_filenum)
+ continue;
+ break;
+ }
+ i = j - 1;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ zp->z_rules = NULL;
+ zp->z_nrules = 0;
+ }
+ for (base = 0; base < nrules; base = out) {
+ rp = &rules[base];
+ for (out = base + 1; out < nrules; ++out)
+ if (strcmp(rp->r_name, rules[out].r_name) != 0)
+ break;
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (strcmp(zp->z_rule, rp->r_name) != 0)
+ continue;
+ zp->z_rules = rp;
+ zp->z_nrules = out - base;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (zp->z_nrules == 0) {
+ /*
+ ** Maybe we have a local standard time offset.
+ */
+ eat(zp->z_filenum, zp->z_linenum);
+ zp->z_save = getsave(zp->z_rule, &zp->z_isdst);
+ /*
+ ** Note, though, that if there's no rule,
+ ** a '%s' in the format is a bad thing.
+ */
+ if (zp->z_format_specifier == 's')
+ error("%s", _("%s in ruleless zone"));
+ }
+ }
+ if (errors)
+ exit(EXIT_FAILURE);
+}
+
+/* Read a text line from FP into BUF, which is of size BUFSIZE.
+ Terminate it with a NUL byte instead of a newline.
+ Return true if successful, false if EOF.
+ On error, report the error and exit. */
+static bool
+inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
+{
+ ptrdiff_t linelen = 0, ch;
+ while ((ch = getc(fp)) != '\n') {
+ if (ch < 0) {
+ if (ferror(fp)) {
+ error(_("input error"));
+ exit(EXIT_FAILURE);
+ }
+ if (linelen == 0)
+ return false;
+ error(_("unterminated line"));
+ exit(EXIT_FAILURE);
+ }
+ if (!ch) {
+ error(_("NUL input byte"));
+ exit(EXIT_FAILURE);
+ }
+ buf[linelen++] = ch;
+ if (linelen == bufsize) {
+ error(_("line too long"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ buf[linelen] = '\0';
+ return true;
+}
+
+static void
+infile(int fnum, char const *name)
+{
+ register FILE * fp;
+ register const struct lookup * lp;
+ register bool wantcont;
+ register lineno num;
+
+ if (strcmp(name, "-") == 0) {
+ fp = stdin;
+ } else if ((fp = fopen(name, "r")) == NULL) {
+ const char *e = strerror(errno);
+
+ fprintf(stderr, _("%s: Can't open %s: %s\n"),
+ progname, name, e);
+ exit(EXIT_FAILURE);
+ }
+ wantcont = false;
+ for (num = 1; ; ++num) {
+ enum { bufsize_bound
+ = (min(INT_MAX, min(PTRDIFF_MAX, SIZE_MAX))
+ / FORMAT_LEN_GROWTH_BOUND) };
+ char buf[min(_POSIX2_LINE_MAX, bufsize_bound)];
+ int nfields;
+ char *fields[MAX_FIELDS];
+ eat(fnum, num);
+ if (!inputline(fp, buf, sizeof buf))
+ break;
+ nfields = getfields(buf, fields,
+ sizeof fields / sizeof *fields);
+ if (nfields == 0) {
+ /* nothing to do */
+ } else if (wantcont) {
+ wantcont = inzcont(fields, nfields);
+ } else {
+ struct lookup const *line_codes
+ = fnum < 0 ? leap_line_codes : zi_line_codes;
+ lp = byword(fields[0], line_codes);
+ if (lp == NULL)
+ error(_("input line of unknown type"));
+ else switch (lp->l_value) {
+ case LC_RULE:
+ inrule(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_ZONE:
+ wantcont = inzone(fields, nfields);
+ break;
+ case LC_LINK:
+ inlink(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_LEAP:
+ inleap(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_EXPIRES:
+ inexpires(fields, nfields);
+ wantcont = false;
+ break;
+ default: unreachable();
+ }
+ }
+ }
+ close_file(fp, NULL, filename(fnum), NULL);
+ if (wantcont)
+ error(_("expected continuation line not found"));
+}
+
+/*
+** Convert a string of one of the forms
+** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
+** into a number of seconds.
+** A null string maps to zero.
+** Call error with errstring and return zero on errors.
+*/
+
+static zic_t
+gethms(char const *string, char const *errstring)
+{
+ zic_t hh;
+ int sign, mm = 0, ss = 0;
+ char hhx, mmx, ssx, xr = '0', xs;
+ int tenths = 0;
+ bool ok = true;
+
+ if (string == NULL || *string == '\0')
+ return 0;
+ if (*string == '-') {
+ sign = -1;
+ ++string;
+ } else sign = 1;
+ switch (sscanf(string,
+ "%"SCNdZIC"%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+ &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs)) {
+ default: ok = false; break;
+ case 8:
+ ok = '0' <= xr && xr <= '9';
+ ATTRIBUTE_FALLTHROUGH;
+ case 7:
+ ok &= ssx == '.';
+ if (ok && noise)
+ warning(_("fractional seconds rejected by"
+ " pre-2018 versions of zic"));
+ ATTRIBUTE_FALLTHROUGH;
+ case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH;
+ case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH;
+ case 1: break;
+ }
+ if (!ok) {
+ error("%s", errstring);
+ return 0;
+ }
+ if (hh < 0 ||
+ mm < 0 || mm >= MINSPERHOUR ||
+ ss < 0 || ss > SECSPERMIN) {
+ error("%s", errstring);
+ return 0;
+ }
+ if (ZIC_MAX / SECSPERHOUR < hh) {
+ error(_("time overflow"));
+ return 0;
+ }
+ ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
+ if (noise && (hh > HOURSPERDAY ||
+ (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
+warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
+ return oadd(sign * hh * SECSPERHOUR,
+ sign * (mm * SECSPERMIN + ss));
+}
+
+static zic_t
+getsave(char *field, bool *isdst)
+{
+ int dst = -1;
+ zic_t save;
+ ptrdiff_t fieldlen = strlen(field);
+ if (fieldlen != 0) {
+ char *ep = field + fieldlen - 1;
+ switch (*ep) {
+ case 'd': dst = 1; *ep = '\0'; break;
+ case 's': dst = 0; *ep = '\0'; break;
+ }
+ }
+ save = gethms(field, _("invalid saved time"));
+ *isdst = dst < 0 ? save != 0 : dst;
+ return save;
+}
+
+static void
+inrule(char **fields, int nfields)
+{
+ struct rule r;
+
+ if (nfields != RULE_FIELDS) {
+ error(_("wrong number of fields on Rule line"));
+ return;
+ }
+ switch (*fields[RF_NAME]) {
+ case '\0':
+ case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
+ case '+': case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
+ return;
+ }
+ r.r_filenum = filenum;
+ r.r_linenum = linenum;
+ r.r_save = getsave(fields[RF_SAVE], &r.r_isdst);
+ if (!rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR],
+ fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
+ fields[RF_TOD]))
+ return;
+ r.r_name = estrdup(fields[RF_NAME]);
+ r.r_abbrvar = estrdup(fields[RF_ABBRVAR]);
+ if (max_abbrvar_len < strlen(r.r_abbrvar))
+ max_abbrvar_len = strlen(r.r_abbrvar);
+ rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
+ rules[nrules++] = r;
+}
+
+static bool
+inzone(char **fields, int nfields)
+{
+ register ptrdiff_t i;
+
+ if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
+ error(_("wrong number of fields on Zone line"));
+ return false;
+ }
+ if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
+ error(
+_("\"Zone %s\" line and -l option are mutually exclusive"),
+ tzdefault);
+ return false;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
+ error(
+_("\"Zone %s\" line and -p option are mutually exclusive"),
+ TZDEFRULES);
+ return false;
+ }
+ for (i = 0; i < nzones; ++i)
+ if (zones[i].z_name != NULL &&
+ strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
+ error(_("duplicate zone name %s"
+ " (file \"%s\", line %"PRIdMAX")"),
+ fields[ZF_NAME],
+ filename(zones[i].z_filenum),
+ zones[i].z_linenum);
+ return false;
+ }
+ return inzsub(fields, nfields, false);
+}
+
+static bool
+inzcont(char **fields, int nfields)
+{
+ if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
+ error(_("wrong number of fields on Zone continuation line"));
+ return false;
+ }
+ return inzsub(fields, nfields, true);
+}
+
+static bool
+inzsub(char **fields, int nfields, bool iscont)
+{
+ register char * cp;
+ char * cp1;
+ struct zone z;
+ int format_len;
+ register int i_stdoff, i_rule, i_format;
+ register int i_untilyear, i_untilmonth;
+ register int i_untilday, i_untiltime;
+ register bool hasuntil;
+
+ if (iscont) {
+ i_stdoff = ZFC_STDOFF;
+ i_rule = ZFC_RULE;
+ i_format = ZFC_FORMAT;
+ i_untilyear = ZFC_TILYEAR;
+ i_untilmonth = ZFC_TILMONTH;
+ i_untilday = ZFC_TILDAY;
+ i_untiltime = ZFC_TILTIME;
+ } else if (!namecheck(fields[ZF_NAME]))
+ return false;
+ else {
+ i_stdoff = ZF_STDOFF;
+ i_rule = ZF_RULE;
+ i_format = ZF_FORMAT;
+ i_untilyear = ZF_TILYEAR;
+ i_untilmonth = ZF_TILMONTH;
+ i_untilday = ZF_TILDAY;
+ i_untiltime = ZF_TILTIME;
+ }
+ z.z_filenum = filenum;
+ z.z_linenum = linenum;
+ z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
+ if ((cp = strchr(fields[i_format], '%')) != 0) {
+ if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
+ || strchr(fields[i_format], '/')) {
+ error(_("invalid abbreviation format"));
+ return false;
+ }
+ }
+ z.z_format_specifier = cp ? *cp : '\0';
+ format_len = strlen(fields[i_format]);
+ if (max_format_len < format_len)
+ max_format_len = format_len;
+ hasuntil = nfields > i_untilyear;
+ if (hasuntil) {
+ z.z_untilrule.r_filenum = filenum;
+ z.z_untilrule.r_linenum = linenum;
+ if (!rulesub(
+ &z.z_untilrule,
+ fields[i_untilyear],
+ "only",
+ "",
+ (nfields > i_untilmonth) ?
+ fields[i_untilmonth] : "Jan",
+ (nfields > i_untilday) ? fields[i_untilday] : "1",
+ (nfields > i_untiltime) ? fields[i_untiltime] : "0"))
+ return false;
+ z.z_untiltime = rpytime(&z.z_untilrule,
+ z.z_untilrule.r_loyear);
+ if (iscont && nzones > 0 &&
+ z.z_untiltime > min_time &&
+ z.z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime > min_time &&
+ zones[nzones - 1].z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime >= z.z_untiltime) {
+ error(_(
+"Zone continuation line end time is not after end time of previous line"
+ ));
+ return false;
+ }
+ }
+ z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]);
+ z.z_rule = estrdup(fields[i_rule]);
+ z.z_format = cp1 = estrdup(fields[i_format]);
+ if (z.z_format_specifier == 'z') {
+ cp1[cp - fields[i_format]] = 's';
+ if (noise)
+ warning(_("format '%s' not handled by pre-2015 versions of zic"),
+ fields[i_format]);
+ }
+ zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
+ zones[nzones++] = z;
+ /*
+ ** If there was an UNTIL field on this line,
+ ** there's more information about the zone on the next line.
+ */
+ return hasuntil;
+}
+
+static zic_t
+getleapdatetime(char **fields, bool expire_line)
+{
+ register const char * cp;
+ register const struct lookup * lp;
+ register zic_t i, j;
+ zic_t year;
+ int month, day;
+ zic_t dayoff, tod;
+ zic_t t;
+ char xs;
+
+ dayoff = 0;
+ cp = fields[LP_YEAR];
+ if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) {
+ /*
+ ** Leapin' Lizards!
+ */
+ error(_("invalid leaping year"));
+ return -1;
+ }
+ if (!expire_line) {
+ if (!leapseen || leapmaxyear < year)
+ leapmaxyear = year;
+ if (!leapseen || leapminyear > year)
+ leapminyear = year;
+ leapseen = true;
+ }
+ j = EPOCH_YEAR;
+ while (j != year) {
+ if (year > j) {
+ i = len_years[isleap(j)];
+ ++j;
+ } else {
+ --j;
+ i = -len_years[isleap(j)];
+ }
+ dayoff = oadd(dayoff, i);
+ }
+ if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return -1;
+ }
+ month = lp->l_value;
+ j = TM_JANUARY;
+ while (j != month) {
+ i = len_months[isleap(year)][j];
+ dayoff = oadd(dayoff, i);
+ ++j;
+ }
+ cp = fields[LP_DAY];
+ if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
+ day <= 0 || day > len_months[isleap(year)][month]) {
+ error(_("invalid day of month"));
+ return -1;
+ }
+ dayoff = oadd(dayoff, day - 1);
+ if (dayoff < min_time / SECSPERDAY) {
+ error(_("time too small"));
+ return -1;
+ }
+ if (dayoff > max_time / SECSPERDAY) {
+ error(_("time too large"));
+ return -1;
+ }
+ t = dayoff * SECSPERDAY;
+ tod = gethms(fields[LP_TIME], _("invalid time of day"));
+ t = tadd(t, tod);
+ if (t < 0)
+ error(_("leap second precedes Epoch"));
+ return t;
+}
+
+static void
+inleap(char **fields, int nfields)
+{
+ if (nfields != LEAP_FIELDS)
+ error(_("wrong number of fields on Leap line"));
+ else {
+ zic_t t = getleapdatetime(fields, false);
+ if (0 <= t) {
+ struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
+ if (!lp)
+ error(_("invalid Rolling/Stationary field on Leap line"));
+ else {
+ int correction = 0;
+ if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
+ correction = -1;
+ else if (strcmp(fields[LP_CORR], "+") == 0)
+ correction = 1;
+ else
+ error(_("invalid CORRECTION field on Leap line"));
+ if (correction)
+ leapadd(t, correction, lp->l_value);
+ }
+ }
+ }
+}
+
+static void
+inexpires(char **fields, int nfields)
+{
+ if (nfields != EXPIRES_FIELDS)
+ error(_("wrong number of fields on Expires line"));
+ else if (0 <= leapexpires)
+ error(_("multiple Expires lines"));
+ else
+ leapexpires = getleapdatetime(fields, true);
+}
+
+static void
+inlink(char **fields, int nfields)
+{
+ struct link l;
+
+ if (nfields != LINK_FIELDS) {
+ error(_("wrong number of fields on Link line"));
+ return;
+ }
+ if (*fields[LF_TARGET] == '\0') {
+ error(_("blank TARGET field on Link line"));
+ return;
+ }
+ if (! namecheck(fields[LF_LINKNAME]))
+ return;
+ l.l_filenum = filenum;
+ l.l_linenum = linenum;
+ l.l_target = estrdup(fields[LF_TARGET]);
+ l.l_linkname = estrdup(fields[LF_LINKNAME]);
+ links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
+ links[nlinks++] = l;
+}
+
+static bool
+rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
+ const char *typep, const char *monthp, const char *dayp,
+ const char *timep)
+{
+ register const struct lookup * lp;
+ register const char * cp;
+ register char * dp;
+ register char * ep;
+ char xs;
+
+ if ((lp = byword(monthp, mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return false;
+ }
+ rp->r_month = lp->l_value;
+ rp->r_todisstd = false;
+ rp->r_todisut = false;
+ dp = estrdup(timep);
+ if (*dp != '\0') {
+ ep = dp + strlen(dp) - 1;
+ switch (lowerit(*ep)) {
+ case 's': /* Standard */
+ rp->r_todisstd = true;
+ rp->r_todisut = false;
+ *ep = '\0';
+ break;
+ case 'w': /* Wall */
+ rp->r_todisstd = false;
+ rp->r_todisut = false;
+ *ep = '\0';
+ break;
+ case 'g': /* Greenwich */
+ case 'u': /* Universal */
+ case 'z': /* Zulu */
+ rp->r_todisstd = true;
+ rp->r_todisut = true;
+ *ep = '\0';
+ break;
+ }
+ }
+ rp->r_tod = gethms(dp, _("invalid time of day"));
+ free(dp);
+ /*
+ ** Year work.
+ */
+ cp = loyearp;
+ lp = byword(cp, begin_years);
+ rp->r_lowasnum = lp == NULL;
+ if (!rp->r_lowasnum) switch (lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_loyear = ZIC_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_loyear = ZIC_MAX;
+ break;
+ default: unreachable();
+ } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
+ error(_("invalid starting year"));
+ return false;
+ }
+ cp = hiyearp;
+ lp = byword(cp, end_years);
+ rp->r_hiwasnum = lp == NULL;
+ if (!rp->r_hiwasnum) switch (lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_hiyear = ZIC_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_hiyear = ZIC_MAX;
+ break;
+ case YR_ONLY:
+ rp->r_hiyear = rp->r_loyear;
+ break;
+ default: unreachable();
+ } else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
+ error(_("invalid ending year"));
+ return false;
+ }
+ if (rp->r_loyear > rp->r_hiyear) {
+ error(_("starting year greater than ending year"));
+ return false;
+ }
+ if (*typep != '\0') {
+ error(_("year type \"%s\" is unsupported; use \"-\" instead"),
+ typep);
+ return false;
+ }
+ /*
+ ** Day work.
+ ** Accept things such as:
+ ** 1
+ ** lastSunday
+ ** last-Sunday (undocumented; warn about this)
+ ** Sun<=20
+ ** Sun>=7
+ */
+ dp = estrdup(dayp);
+ if ((lp = byword(dp, lasts)) != NULL) {
+ rp->r_dycode = DC_DOWLEQ;
+ rp->r_wday = lp->l_value;
+ rp->r_dayofmonth = len_months[1][rp->r_month];
+ } else {
+ if ((ep = strchr(dp, '<')) != 0)
+ rp->r_dycode = DC_DOWLEQ;
+ else if ((ep = strchr(dp, '>')) != 0)
+ rp->r_dycode = DC_DOWGEQ;
+ else {
+ ep = dp;
+ rp->r_dycode = DC_DOM;
+ }
+ if (rp->r_dycode != DC_DOM) {
+ *ep++ = 0;
+ if (*ep++ != '=') {
+ error(_("invalid day of month"));
+ free(dp);
+ return false;
+ }
+ if ((lp = byword(dp, wday_names)) == NULL) {
+ error(_("invalid weekday name"));
+ free(dp);
+ return false;
+ }
+ rp->r_wday = lp->l_value;
+ }
+ if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
+ rp->r_dayofmonth <= 0 ||
+ (rp->r_dayofmonth > len_months[1][rp->r_month])) {
+ error(_("invalid day of month"));
+ free(dp);
+ return false;
+ }
+ }
+ free(dp);
+ return true;
+}
+
+static void
+convert(uint_fast32_t val, char *buf)
+{
+ register int i;
+ register int shift;
+ unsigned char *const b = (unsigned char *) buf;
+
+ for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
+ b[i] = (val >> shift) & 0xff;
+}
+
+static void
+convert64(uint_fast64_t val, char *buf)
+{
+ register int i;
+ register int shift;
+ unsigned char *const b = (unsigned char *) buf;
+
+ for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
+ b[i] = (val >> shift) & 0xff;
+}
+
+static void
+puttzcode(zic_t val, FILE *fp)
+{
+ char buf[4];
+
+ convert(val, buf);
+ fwrite(buf, sizeof buf, 1, fp);
+}
+
+static void
+puttzcodepass(zic_t val, FILE *fp, int pass)
+{
+ if (pass == 1)
+ puttzcode(val, fp);
+ else {
+ char buf[8];
+
+ convert64(val, buf);
+ fwrite(buf, sizeof buf, 1, fp);
+ }
+}
+
+static int
+atcomp(const void *avp, const void *bvp)
+{
+ struct attype const *ap = avp, *bp = bvp;
+ zic_t a = ap->at, b = bp->at;
+ return a < b ? -1 : a > b;
+}
+
+struct timerange {
+ int defaulttype;
+ ptrdiff_t base, count;
+ int leapbase, leapcount;
+ bool leapexpiry;
+};
+
+static struct timerange
+limitrange(struct timerange r, zic_t lo, zic_t hi,
+ zic_t const *ats, unsigned char const *types)
+{
+ /* Omit ordinary transitions < LO. */
+ while (0 < r.count && ats[r.base] < lo) {
+ r.defaulttype = types[r.base];
+ r.count--;
+ r.base++;
+ }
+
+ /* Omit as many initial leap seconds as possible, such that the
+ first leap second in the truncated list is <= LO, and is a
+ positive leap second if and only if it has a positive correction.
+ This supports common TZif readers that assume that the first leap
+ second is positive if and only if its correction is positive. */
+ while (1 < r.leapcount && trans[r.leapbase + 1] <= lo) {
+ r.leapcount--;
+ r.leapbase++;
+ }
+ while (0 < r.leapbase
+ && ((corr[r.leapbase - 1] < corr[r.leapbase])
+ != (0 < corr[r.leapbase]))) {
+ r.leapcount++;
+ r.leapbase--;
+ }
+
+
+ /* Omit ordinary and leap second transitions greater than HI + 1. */
+ if (hi < max_time) {
+ while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
+ r.count--;
+ while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
+ r.leapcount--;
+ }
+
+ /* Determine whether to append an expiration to the leap second table. */
+ r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
+
+ return r;
+}
+
+static void
+writezone(const char *const name, const char *const string, char version,
+ int defaulttype)
+{
+ register FILE * fp;
+ register ptrdiff_t i, j;
+ register size_t u;
+ register int pass;
+ char *tempname = NULL;
+ char const *outname = name;
+
+ /* Allocate the ATS and TYPES arrays via a single malloc,
+ as this is a bit faster. Do not malloc(0) if !timecnt,
+ as that might return NULL even on success. */
+ zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt,
+ sizeof *ats + 1),
+ alignof(zic_t)));
+ void *typesptr = ats + timecnt;
+ unsigned char *types = typesptr;
+ struct timerange rangeall = {0}, range32, range64;
+
+ /*
+ ** Sort.
+ */
+ if (timecnt > 1)
+ qsort(attypes, timecnt, sizeof *attypes, atcomp);
+ /*
+ ** Optimize.
+ */
+ {
+ ptrdiff_t fromi, toi;
+
+ toi = 0;
+ fromi = 0;
+ for ( ; fromi < timecnt; ++fromi) {
+ if (toi != 0
+ && ((attypes[fromi].at
+ + utoffs[attypes[toi - 1].type])
+ <= (attypes[toi - 1].at
+ + utoffs[toi == 1 ? 0
+ : attypes[toi - 2].type]))) {
+ attypes[toi - 1].type =
+ attypes[fromi].type;
+ continue;
+ }
+ if (toi == 0
+ || attypes[fromi].dontmerge
+ || (utoffs[attypes[toi - 1].type]
+ != utoffs[attypes[fromi].type])
+ || (isdsts[attypes[toi - 1].type]
+ != isdsts[attypes[fromi].type])
+ || (desigidx[attypes[toi - 1].type]
+ != desigidx[attypes[fromi].type]))
+ attypes[toi++] = attypes[fromi];
+ }
+ timecnt = toi;
+ }
+
+ if (noise && timecnt > 1200) {
+ if (timecnt > TZ_MAX_TIMES)
+ warning(_("reference clients mishandle"
+ " more than %d transition times"),
+ TZ_MAX_TIMES);
+ else
+ warning(_("pre-2014 clients may mishandle"
+ " more than 1200 transition times"));
+ }
+ /*
+ ** Transfer.
+ */
+ for (i = 0; i < timecnt; ++i) {
+ ats[i] = attypes[i].at;
+ types[i] = attypes[i].type;
+ }
+
+ /*
+ ** Correct for leap seconds.
+ */
+ for (i = 0; i < timecnt; ++i) {
+ j = leapcnt;
+ while (--j >= 0)
+ if (ats[i] > trans[j] - corr[j]) {
+ ats[i] = tadd(ats[i], corr[j]);
+ break;
+ }
+ }
+
+ rangeall.defaulttype = defaulttype;
+ rangeall.count = timecnt;
+ rangeall.leapcount = leapcnt;
+ range64 = limitrange(rangeall, lo_time,
+ max(hi_time,
+ redundant_time - (ZIC_MIN < redundant_time)),
+ ats, types);
+ range32 = limitrange(range64, ZIC32_MIN, ZIC32_MAX, ats, types);
+
+ /* TZif version 4 is needed if a no-op transition is appended to
+ indicate the expiration of the leap second table, or if the first
+ leap second transition is not to a +1 or -1 correction. */
+ for (pass = 1; pass <= 2; pass++) {
+ struct timerange const *r = pass == 1 ? &range32 : &range64;
+ if (pass == 1 && !want_bloat())
+ continue;
+ if (r->leapexpiry) {
+ if (noise)
+ warning(_("%s: pre-2021b clients may mishandle"
+ " leap second expiry"),
+ name);
+ version = '4';
+ }
+ if (0 < r->leapcount
+ && corr[r->leapbase] != 1 && corr[r->leapbase] != -1) {
+ if (noise)
+ warning(_("%s: pre-2021b clients may mishandle"
+ " leap second table truncation"),
+ name);
+ version = '4';
+ }
+ if (version == '4')
+ break;
+ }
+
+ fp = open_outfile(&outname, &tempname);
+
+ for (pass = 1; pass <= 2; ++pass) {
+ register ptrdiff_t thistimei, thistimecnt, thistimelim;
+ register int thisleapi, thisleapcnt, thisleaplim;
+ struct tzhead tzh;
+ int pretranstype = -1, thisdefaulttype;
+ bool locut, hicut, thisleapexpiry;
+ zic_t lo, thismin, thismax;
+ int old0;
+ char omittype[TZ_MAX_TYPES];
+ int typemap[TZ_MAX_TYPES];
+ int thistypecnt, stdcnt, utcnt;
+ char thischars[TZ_MAX_CHARS];
+ int thischarcnt;
+ bool toomanytimes;
+ int indmap[TZ_MAX_CHARS];
+
+ if (pass == 1) {
+ thisdefaulttype = range32.defaulttype;
+ thistimei = range32.base;
+ thistimecnt = range32.count;
+ toomanytimes = thistimecnt >> 31 >> 1 != 0;
+ thisleapi = range32.leapbase;
+ thisleapcnt = range32.leapcount;
+ thisleapexpiry = range32.leapexpiry;
+ thismin = ZIC32_MIN;
+ thismax = ZIC32_MAX;
+ } else {
+ thisdefaulttype = range64.defaulttype;
+ thistimei = range64.base;
+ thistimecnt = range64.count;
+ toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
+ thisleapi = range64.leapbase;
+ thisleapcnt = range64.leapcount;
+ thisleapexpiry = range64.leapexpiry;
+ thismin = min_time;
+ thismax = max_time;
+ }
+ if (toomanytimes)
+ error(_("too many transition times"));
+
+ locut = thismin < lo_time && lo_time <= thismax;
+ hicut = thismin <= hi_time && hi_time < thismax;
+ thistimelim = thistimei + thistimecnt;
+ memset(omittype, true, typecnt);
+
+ /* Determine whether to output a transition before the first
+ transition in range. This is needed when the output is
+ truncated at the start, and is also useful when catering to
+ buggy 32-bit clients that do not use time type 0 for
+ timestamps before the first transition. */
+ if ((locut || (pass == 1 && thistimei))
+ && ! (thistimecnt && ats[thistimei] == lo_time)) {
+ pretranstype = thisdefaulttype;
+ omittype[pretranstype] = false;
+ }
+
+ /* Arguably the default time type in the 32-bit data
+ should be range32.defaulttype, which is suited for
+ timestamps just before ZIC32_MIN. However, zic
+ traditionally used the time type of the indefinite
+ past instead. Internet RFC 8532 says readers should
+ ignore 32-bit data, so this discrepancy matters only
+ to obsolete readers where the traditional type might
+ be more appropriate even if it's "wrong". So, use
+ the historical zic value, unless -r specifies a low
+ cutoff that excludes some 32-bit timestamps. */
+ if (pass == 1 && lo_time <= thismin)
+ thisdefaulttype = range64.defaulttype;
+
+ if (locut)
+ thisdefaulttype = unspecifiedtype;
+ omittype[thisdefaulttype] = false;
+ for (i = thistimei; i < thistimelim; i++)
+ omittype[types[i]] = false;
+ if (hicut)
+ omittype[unspecifiedtype] = false;
+
+ /* Reorder types to make THISDEFAULTTYPE type 0.
+ Use TYPEMAP to swap OLD0 and THISDEFAULTTYPE so that
+ THISDEFAULTTYPE appears as type 0 in the output instead
+ of OLD0. TYPEMAP also omits unused types. */
+ old0 = strlen(omittype);
+
+#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
+ /*
+ ** For some pre-2011 systems: if the last-to-be-written
+ ** standard (or daylight) type has an offset different from the
+ ** most recently used offset,
+ ** append an (unused) copy of the most recently used type
+ ** (to help get global "altzone" and "timezone" variables
+ ** set correctly).
+ */
+ if (want_bloat()) {
+ register int mrudst, mrustd, hidst, histd, type;
+
+ hidst = histd = mrudst = mrustd = -1;
+ if (0 <= pretranstype) {
+ if (isdsts[pretranstype])
+ mrudst = pretranstype;
+ else
+ mrustd = pretranstype;
+ }
+ for (i = thistimei; i < thistimelim; i++)
+ if (isdsts[types[i]])
+ mrudst = types[i];
+ else mrustd = types[i];
+ for (i = old0; i < typecnt; i++) {
+ int h = (i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i);
+ if (!omittype[h]) {
+ if (isdsts[h])
+ hidst = i;
+ else
+ histd = i;
+ }
+ }
+ if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
+ utoffs[hidst] != utoffs[mrudst]) {
+ isdsts[mrudst] = -1;
+ type = addtype(utoffs[mrudst],
+ &chars[desigidx[mrudst]],
+ true,
+ ttisstds[mrudst],
+ ttisuts[mrudst]);
+ isdsts[mrudst] = 1;
+ omittype[type] = false;
+ }
+ if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
+ utoffs[histd] != utoffs[mrustd]) {
+ isdsts[mrustd] = -1;
+ type = addtype(utoffs[mrustd],
+ &chars[desigidx[mrustd]],
+ false,
+ ttisstds[mrustd],
+ ttisuts[mrustd]);
+ isdsts[mrustd] = 0;
+ omittype[type] = false;
+ }
+ }
+#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
+ thistypecnt = 0;
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ typemap[i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i]
+ = thistypecnt++;
+
+ for (u = 0; u < sizeof indmap / sizeof indmap[0]; ++u)
+ indmap[u] = -1;
+ thischarcnt = stdcnt = utcnt = 0;
+ for (i = old0; i < typecnt; i++) {
+ register char * thisabbr;
+
+ if (omittype[i])
+ continue;
+ if (ttisstds[i])
+ stdcnt = thistypecnt;
+ if (ttisuts[i])
+ utcnt = thistypecnt;
+ if (indmap[desigidx[i]] >= 0)
+ continue;
+ thisabbr = &chars[desigidx[i]];
+ for (j = 0; j < thischarcnt; ++j)
+ if (strcmp(&thischars[j], thisabbr) == 0)
+ break;
+ if (j == thischarcnt) {
+ strcpy(&thischars[thischarcnt], thisabbr);
+ thischarcnt += strlen(thisabbr) + 1;
+ }
+ indmap[desigidx[i]] = j;
+ }
+ if (pass == 1 && !want_bloat()) {
+ hicut = thisleapexpiry = false;
+ pretranstype = -1;
+ thistimecnt = thisleapcnt = 0;
+ thistypecnt = thischarcnt = 1;
+ }
+#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
+ memset(&tzh, 0, sizeof tzh);
+ memcpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+ tzh.tzh_version[0] = version;
+ convert(utcnt, tzh.tzh_ttisutcnt);
+ convert(stdcnt, tzh.tzh_ttisstdcnt);
+ convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
+ convert((0 <= pretranstype) + thistimecnt + hicut,
+ tzh.tzh_timecnt);
+ convert(thistypecnt, tzh.tzh_typecnt);
+ convert(thischarcnt, tzh.tzh_charcnt);
+ DO(tzh_magic);
+ DO(tzh_version);
+ DO(tzh_reserved);
+ DO(tzh_ttisutcnt);
+ DO(tzh_ttisstdcnt);
+ DO(tzh_leapcnt);
+ DO(tzh_timecnt);
+ DO(tzh_typecnt);
+ DO(tzh_charcnt);
+#undef DO
+ if (pass == 1 && !want_bloat()) {
+ /* Output a minimal data block with just one time type. */
+ puttzcode(0, fp); /* utoff */
+ putc(0, fp); /* dst */
+ putc(0, fp); /* index of abbreviation */
+ putc(0, fp); /* empty-string abbreviation */
+ continue;
+ }
+
+ /* Output a LO_TIME transition if needed; see limitrange.
+ But do not go below the minimum representable value
+ for this pass. */
+ lo = pass == 1 && lo_time < ZIC32_MIN ? ZIC32_MIN : lo_time;
+
+ if (0 <= pretranstype)
+ puttzcodepass(lo, fp, pass);
+ for (i = thistimei; i < thistimelim; ++i) {
+ puttzcodepass(ats[i], fp, pass);
+ }
+ if (hicut)
+ puttzcodepass(hi_time + 1, fp, pass);
+ if (0 <= pretranstype)
+ putc(typemap[pretranstype], fp);
+ for (i = thistimei; i < thistimelim; i++)
+ putc(typemap[types[i]], fp);
+ if (hicut)
+ putc(typemap[unspecifiedtype], fp);
+
+ for (i = old0; i < typecnt; i++) {
+ int h = (i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i);
+ if (!omittype[h]) {
+ puttzcode(utoffs[h], fp);
+ putc(isdsts[h], fp);
+ putc(indmap[desigidx[h]], fp);
+ }
+ }
+ if (thischarcnt != 0)
+ fwrite(thischars, sizeof thischars[0],
+ thischarcnt, fp);
+ thisleaplim = thisleapi + thisleapcnt;
+ for (i = thisleapi; i < thisleaplim; ++i) {
+ register zic_t todo;
+
+ if (roll[i]) {
+ if (timecnt == 0 || trans[i] < ats[0]) {
+ j = 0;
+ while (isdsts[j])
+ if (++j >= typecnt) {
+ j = 0;
+ break;
+ }
+ } else {
+ j = 1;
+ while (j < timecnt &&
+ trans[i] >= ats[j])
+ ++j;
+ j = types[j - 1];
+ }
+ todo = tadd(trans[i], -utoffs[j]);
+ } else todo = trans[i];
+ puttzcodepass(todo, fp, pass);
+ puttzcode(corr[i], fp);
+ }
+ if (thisleapexpiry) {
+ /* Append a no-op leap correction indicating when the leap
+ second table expires. Although this does not conform to
+ Internet RFC 8536, most clients seem to accept this and
+ the plan is to amend the RFC to allow this in version 4
+ TZif files. */
+ puttzcodepass(leapexpires, fp, pass);
+ puttzcode(thisleaplim ? corr[thisleaplim - 1] : 0, fp);
+ }
+ if (stdcnt != 0)
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ putc(ttisstds[i], fp);
+ if (utcnt != 0)
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ putc(ttisuts[i], fp);
+ }
+ fprintf(fp, "\n%s\n", string);
+ close_file(fp, directory, name, tempname);
+ if (chmod(tempname, mflag) < 0) {
+ fprintf(stderr, _("cannot change mode of %s to %03o"),
+ tempname, (unsigned)mflag);
+ exit(EXIT_FAILURE);
+ }
+ if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
+ && chown(tempname, uflag, gflag) < 0) {
+ fprintf(stderr, _("cannot change ownership of %s"),
+ tempname);
+ exit(EXIT_FAILURE);
+ }
+ rename_dest(tempname, name);
+ free(ats);
+}
+
+static char const *
+abbroffset(char *buf, zic_t offset)
+{
+ char sign = '+';
+ int seconds, minutes;
+
+ if (offset < 0) {
+ offset = -offset;
+ sign = '-';
+ }
+
+ seconds = offset % SECSPERMIN;
+ offset /= SECSPERMIN;
+ minutes = offset % MINSPERHOUR;
+ offset /= MINSPERHOUR;
+ if (100 <= offset) {
+ error(_("%%z UT offset magnitude exceeds 99:59:59"));
+ return "%z";
+ } else {
+ char *p = buf;
+ *p++ = sign;
+ *p++ = '0' + offset / 10;
+ *p++ = '0' + offset % 10;
+ if (minutes | seconds) {
+ *p++ = '0' + minutes / 10;
+ *p++ = '0' + minutes % 10;
+ if (seconds) {
+ *p++ = '0' + seconds / 10;
+ *p++ = '0' + seconds % 10;
+ }
+ }
+ *p = '\0';
+ return buf;
+ }
+}
+
+static char const disable_percent_s[] = "";
+
+static ptrdiff_t
+doabbr(char *abbr, struct zone const *zp, char const *letters,
+ bool isdst, zic_t save, bool doquotes)
+{
+ register char * cp;
+ register char * slashp;
+ ptrdiff_t len;
+ char const *format = zp->z_format;
+
+ slashp = strchr(format, '/');
+ if (slashp == NULL) {
+ char letterbuf[PERCENT_Z_LEN_BOUND + 1];
+ if (zp->z_format_specifier == 'z')
+ letters = abbroffset(letterbuf, zp->z_stdoff + save);
+ else if (!letters)
+ letters = "%s";
+ else if (letters == disable_percent_s)
+ return 0;
+ sprintf(abbr, format, letters);
+ } else if (isdst) {
+ strcpy(abbr, slashp + 1);
+ } else {
+ memcpy(abbr, format, slashp - format);
+ abbr[slashp - format] = '\0';
+ }
+ len = strlen(abbr);
+ if (!doquotes)
+ return len;
+ for (cp = abbr; is_alpha(*cp); cp++)
+ continue;
+ if (len > 0 && *cp == '\0')
+ return len;
+ abbr[len + 2] = '\0';
+ abbr[len + 1] = '>';
+ memmove(abbr + 1, abbr, len);
+ abbr[0] = '<';
+ return len + 2;
+}
+
+static void
+updateminmax(const zic_t x)
+{
+ if (min_year > x)
+ min_year = x;
+ if (max_year < x)
+ max_year = x;
+}
+
+static int
+stringoffset(char *result, zic_t offset)
+{
+ register int hours;
+ register int minutes;
+ register int seconds;
+ bool negative = offset < 0;
+ int len = negative;
+
+ if (negative) {
+ offset = -offset;
+ result[0] = '-';
+ }
+ seconds = offset % SECSPERMIN;
+ offset /= SECSPERMIN;
+ minutes = offset % MINSPERHOUR;
+ offset /= MINSPERHOUR;
+ hours = offset;
+ if (hours >= HOURSPERDAY * DAYSPERWEEK) {
+ result[0] = '\0';
+ return 0;
+ }
+ len += sprintf(result + len, "%d", hours);
+ if (minutes != 0 || seconds != 0) {
+ len += sprintf(result + len, ":%02d", minutes);
+ if (seconds != 0)
+ len += sprintf(result + len, ":%02d", seconds);
+ }
+ return len;
+}
+
+static int
+stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff)
+{
+ register zic_t tod = rp->r_tod;
+ register int compat = 0;
+
+ if (rp->r_dycode == DC_DOM) {
+ register int month, total;
+
+ if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
+ return -1;
+ total = 0;
+ for (month = 0; month < rp->r_month; ++month)
+ total += len_months[0][month];
+ /* Omit the "J" in Jan and Feb, as that's shorter. */
+ if (rp->r_month <= 1)
+ result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
+ else
+ result += sprintf(result, "J%d", total + rp->r_dayofmonth);
+ } else {
+ register int week;
+ register int wday = rp->r_wday;
+ register int wdayoff;
+
+ if (rp->r_dycode == DC_DOWGEQ) {
+ wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
+ wday -= wdayoff;
+ tod += wdayoff * SECSPERDAY;
+ week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
+ } else if (rp->r_dycode == DC_DOWLEQ) {
+ if (rp->r_dayofmonth == len_months[1][rp->r_month])
+ week = 5;
+ else {
+ wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
+ wday -= wdayoff;
+ tod += wdayoff * SECSPERDAY;
+ week = rp->r_dayofmonth / DAYSPERWEEK;
+ }
+ } else return -1; /* "cannot happen" */
+ if (wday < 0)
+ wday += DAYSPERWEEK;
+ result += sprintf(result, "M%d.%d.%d",
+ rp->r_month + 1, week, wday);
+ }
+ if (rp->r_todisut)
+ tod += stdoff;
+ if (rp->r_todisstd && !rp->r_isdst)
+ tod += save;
+ if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
+ *result++ = '/';
+ if (! stringoffset(result, tod))
+ return -1;
+ if (tod < 0) {
+ if (compat < 2013)
+ compat = 2013;
+ } else if (SECSPERDAY <= tod) {
+ if (compat < 1994)
+ compat = 1994;
+ }
+ }
+ return compat;
+}
+
+static int
+rule_cmp(struct rule const *a, struct rule const *b)
+{
+ if (!a)
+ return -!!b;
+ if (!b)
+ return 1;
+ if (a->r_hiyear != b->r_hiyear)
+ return a->r_hiyear < b->r_hiyear ? -1 : 1;
+ if (a->r_hiyear == ZIC_MAX)
+ return 0;
+ if (a->r_month - b->r_month != 0)
+ return a->r_month - b->r_month;
+ return a->r_dayofmonth - b->r_dayofmonth;
+}
+
+static int
+stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
+{
+ register const struct zone * zp;
+ register struct rule * rp;
+ register struct rule * stdrp;
+ register struct rule * dstrp;
+ register ptrdiff_t i;
+ register int compat = 0;
+ register int c;
+ int offsetlen;
+ struct rule stdr, dstr;
+ ptrdiff_t len;
+ int dstcmp;
+ struct rule *lastrp[2] = { NULL, NULL };
+ struct zone zstr[2];
+ struct zone const *stdzp;
+ struct zone const *dstzp;
+
+ result[0] = '\0';
+
+ /* Internet RFC 8536 section 5.1 says to use an empty TZ string if
+ future timestamps are truncated. */
+ if (hi_time < max_time)
+ return -1;
+
+ zp = zpfirst + zonecount - 1;
+ for (i = 0; i < zp->z_nrules; ++i) {
+ struct rule **last;
+ int cmp;
+ rp = &zp->z_rules[i];
+ last = &lastrp[rp->r_isdst];
+ cmp = rule_cmp(*last, rp);
+ if (cmp < 0)
+ *last = rp;
+ else if (cmp == 0)
+ return -1;
+ }
+ stdrp = lastrp[false];
+ dstrp = lastrp[true];
+ dstcmp = zp->z_nrules ? rule_cmp(dstrp, stdrp) : zp->z_isdst ? 1 : -1;
+ stdzp = dstzp = zp;
+
+ if (dstcmp < 0) {
+ /* Standard time all year. */
+ dstrp = NULL;
+ } else if (0 < dstcmp) {
+ /* DST all year. Use an abbreviation like
+ "XXX3EDT4,0/0,J365/23" for EDT (-04) all year. */
+ zic_t save = dstrp ? dstrp->r_save : zp->z_save;
+ if (0 <= save)
+ {
+ /* Positive DST, the typical case for all-year DST.
+ Fake a timezone with negative DST. */
+ stdzp = &zstr[0];
+ dstzp = &zstr[1];
+ zstr[0].z_stdoff = zp->z_stdoff + 2 * save;
+ zstr[0].z_format = "XXX"; /* Any 3 letters will do. */
+ zstr[0].z_format_specifier = 0;
+ zstr[1].z_stdoff = zstr[0].z_stdoff;
+ zstr[1].z_format = zp->z_format;
+ zstr[1].z_format_specifier = zp->z_format_specifier;
+ }
+ dstr.r_month = TM_JANUARY;
+ dstr.r_dycode = DC_DOM;
+ dstr.r_dayofmonth = 1;
+ dstr.r_tod = 0;
+ dstr.r_todisstd = dstr.r_todisut = false;
+ dstr.r_isdst = true;
+ dstr.r_save = save < 0 ? save : -save;
+ dstr.r_abbrvar = dstrp ? dstrp->r_abbrvar : NULL;
+ stdr.r_month = TM_DECEMBER;
+ stdr.r_dycode = DC_DOM;
+ stdr.r_dayofmonth = 31;
+ stdr.r_tod = SECSPERDAY + dstr.r_save;
+ stdr.r_todisstd = stdr.r_todisut = false;
+ stdr.r_isdst = false;
+ stdr.r_save = 0;
+ stdr.r_abbrvar = save < 0 && stdrp ? stdrp->r_abbrvar : NULL;
+ dstrp = &dstr;
+ stdrp = &stdr;
+ }
+ len = doabbr(result, stdzp, stdrp ? stdrp->r_abbrvar : NULL,
+ false, 0, true);
+ offsetlen = stringoffset(result + len, - stdzp->z_stdoff);
+ if (! offsetlen) {
+ result[0] = '\0';
+ return -1;
+ }
+ len += offsetlen;
+ if (dstrp == NULL)
+ return compat;
+ len += doabbr(result + len, dstzp, dstrp->r_abbrvar,
+ dstrp->r_isdst, dstrp->r_save, true);
+ if (dstrp->r_save != SECSPERMIN * MINSPERHOUR) {
+ offsetlen = stringoffset(result + len,
+ - (dstzp->z_stdoff + dstrp->r_save));
+ if (! offsetlen) {
+ result[0] = '\0';
+ return -1;
+ }
+ len += offsetlen;
+ }
+ result[len++] = ',';
+ c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff);
+ if (c < 0) {
+ result[0] = '\0';
+ return -1;
+ }
+ if (compat < c)
+ compat = c;
+ len += strlen(result + len);
+ result[len++] = ',';
+ c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff);
+ if (c < 0) {
+ result[0] = '\0';
+ return -1;
+ }
+ if (compat < c)
+ compat = c;
+ return compat;
+}
+
+static void
+outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
+{
+ register ptrdiff_t i, j;
+ register zic_t starttime, untiltime;
+ register bool startttisstd;
+ register bool startttisut;
+ register char * startbuf;
+ register char * ab;
+ register char * envvar;
+ register int max_abbr_len;
+ register int max_envvar_len;
+ register bool prodstic; /* all rules are min to max */
+ register int compat;
+ register bool do_extend;
+ register char version;
+ ptrdiff_t lastatmax = -1;
+ zic_t max_year0;
+ int defaulttype = -1;
+
+ check_for_signal();
+
+ /* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND. */
+ max_abbr_len = 2 + max_format_len + max_abbrvar_len;
+ max_envvar_len = 2 * max_abbr_len + 5 * 9;
+
+ startbuf = emalloc(max_abbr_len + 1);
+ ab = emalloc(max_abbr_len + 1);
+ envvar = emalloc(max_envvar_len + 1);
+ INITIALIZE(untiltime);
+ INITIALIZE(starttime);
+ /*
+ ** Now. . .finally. . .generate some useful data!
+ */
+ timecnt = 0;
+ typecnt = 0;
+ charcnt = 0;
+ prodstic = zonecount == 1;
+ /*
+ ** Thanks to Earl Chew
+ ** for noting the need to unconditionally initialize startttisstd.
+ */
+ startttisstd = false;
+ startttisut = false;
+ min_year = max_year = EPOCH_YEAR;
+ if (leapseen) {
+ updateminmax(leapminyear);
+ updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
+ }
+ for (i = 0; i < zonecount; ++i) {
+ struct zone const *zp = &zpfirst[i];
+ if (i < zonecount - 1)
+ updateminmax(zp->z_untilrule.r_loyear);
+ for (j = 0; j < zp->z_nrules; ++j) {
+ struct rule *rp = &zp->z_rules[j];
+ if (rp->r_lowasnum)
+ updateminmax(rp->r_loyear);
+ if (rp->r_hiwasnum)
+ updateminmax(rp->r_hiyear);
+ if (rp->r_lowasnum || rp->r_hiwasnum)
+ prodstic = false;
+ }
+ }
+ /*
+ ** Generate lots of data if a rule can't cover all future times.
+ */
+ compat = stringzone(envvar, zpfirst, zonecount);
+ version = compat < 2013 ? '2' : '3';
+ do_extend = compat < 0;
+ if (noise) {
+ if (!*envvar)
+ warning("%s %s",
+ _("no POSIX environment variable for zone"),
+ zpfirst->z_name);
+ else if (compat != 0) {
+ /* Circa-COMPAT clients, and earlier clients, might
+ not work for this zone when given dates before
+ 1970 or after 2038. */
+ warning(_("%s: pre-%d clients may mishandle"
+ " distant timestamps"),
+ zpfirst->z_name, compat);
+ }
+ }
+ if (do_extend) {
+ /*
+ ** Search through a couple of extra years past the obvious
+ ** 400, to avoid edge cases. For example, suppose a non-POSIX
+ ** rule applies from 2012 onwards and has transitions in March
+ ** and September, plus some one-off transitions in November
+ ** 2013. If zic looked only at the last 400 years, it would
+ ** set max_year=2413, with the intent that the 400 years 2014
+ ** through 2413 will be repeated. The last transition listed
+ ** in the tzfile would be in 2413-09, less than 400 years
+ ** after the last one-off transition in 2013-11. Two years
+ ** might be overkill, but with the kind of edge cases
+ ** available we're not sure that one year would suffice.
+ */
+ enum { years_of_observations = YEARSPERREPEAT + 2 };
+
+ if (min_year >= ZIC_MIN + years_of_observations)
+ min_year -= years_of_observations;
+ else min_year = ZIC_MIN;
+ if (max_year <= ZIC_MAX - years_of_observations)
+ max_year += years_of_observations;
+ else max_year = ZIC_MAX;
+ /*
+ ** Regardless of any of the above,
+ ** for a "proDSTic" zone which specifies that its rules
+ ** always have and always will be in effect,
+ ** we only need one cycle to define the zone.
+ */
+ if (prodstic) {
+ min_year = 1900;
+ max_year = min_year + years_of_observations;
+ }
+ }
+ max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
+ + EPOCH_YEAR + 1));
+ max_year0 = max_year;
+ if (want_bloat()) {
+ /* For the benefit of older systems,
+ generate data from 1900 through 2038. */
+ if (min_year > 1900)
+ min_year = 1900;
+ if (max_year < 2038)
+ max_year = 2038;
+ }
+
+ if (min_time < lo_time || hi_time < max_time)
+ unspecifiedtype = addtype(0, "-00", false, false, false);
+
+ for (i = 0; i < zonecount; ++i) {
+ struct rule *prevrp = NULL;
+ /*
+ ** A guess that may well be corrected later.
+ */
+ zic_t save = 0;
+ struct zone const *zp = &zpfirst[i];
+ bool usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ bool useuntil = i < (zonecount - 1);
+ zic_t stdoff = zp->z_stdoff;
+ zic_t startoff = stdoff;
+ zic_t prevktime;
+ INITIALIZE(prevktime);
+ if (useuntil && zp->z_untiltime <= min_time)
+ continue;
+ eat(zp->z_filenum, zp->z_linenum);
+ *startbuf = '\0';
+ if (zp->z_nrules == 0) {
+ int type;
+ save = zp->z_save;
+ doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
+ type = addtype(oadd(zp->z_stdoff, save),
+ startbuf, zp->z_isdst, startttisstd,
+ startttisut);
+ if (usestart) {
+ addtt(starttime, type);
+ usestart = false;
+ } else
+ defaulttype = type;
+ } else {
+ zic_t year;
+ for (year = min_year; year <= max_year; ++year) {
+ if (useuntil && year > zp->z_untilrule.r_hiyear)
+ break;
+ /*
+ ** Mark which rules to do in the current year.
+ ** For those to do, calculate rpytime(rp, year);
+ ** The former TYPE field was also considered here.
+ */
+ for (j = 0; j < zp->z_nrules; ++j) {
+ zic_t one = 1;
+ zic_t y2038_boundary = one << 31;
+ struct rule *rp = &zp->z_rules[j];
+ eats(zp->z_filenum, zp->z_linenum,
+ rp->r_filenum, rp->r_linenum);
+ rp->r_todo = year >= rp->r_loyear &&
+ year <= rp->r_hiyear;
+ if (rp->r_todo) {
+ rp->r_temp = rpytime(rp, year);
+ rp->r_todo
+ = (rp->r_temp < y2038_boundary
+ || year <= max_year0);
+ }
+ }
+ for ( ; ; ) {
+ register ptrdiff_t k;
+ register zic_t jtime, ktime;
+ register zic_t offset;
+ struct rule *rp;
+ int type;
+
+ INITIALIZE(ktime);
+ if (useuntil) {
+ /*
+ ** Turn untiltime into UT
+ ** assuming the current stdoff and
+ ** save values.
+ */
+ untiltime = zp->z_untiltime;
+ if (!zp->z_untilrule.r_todisut)
+ untiltime = tadd(untiltime,
+ -stdoff);
+ if (!zp->z_untilrule.r_todisstd)
+ untiltime = tadd(untiltime,
+ -save);
+ }
+ /*
+ ** Find the rule (of those to do, if any)
+ ** that takes effect earliest in the year.
+ */
+ k = -1;
+ for (j = 0; j < zp->z_nrules; ++j) {
+ struct rule *r = &zp->z_rules[j];
+ if (!r->r_todo)
+ continue;
+ eats(zp->z_filenum, zp->z_linenum,
+ r->r_filenum, r->r_linenum);
+ offset = r->r_todisut ? 0 : stdoff;
+ if (!r->r_todisstd)
+ offset = oadd(offset, save);
+ jtime = r->r_temp;
+ if (jtime == min_time ||
+ jtime == max_time)
+ continue;
+ jtime = tadd(jtime, -offset);
+ if (k < 0 || jtime < ktime) {
+ k = j;
+ ktime = jtime;
+ } else if (jtime == ktime) {
+ char const *dup_rules_msg =
+ _("two rules for same instant");
+ eats(zp->z_filenum, zp->z_linenum,
+ r->r_filenum, r->r_linenum);
+ warning("%s", dup_rules_msg);
+ r = &zp->z_rules[k];
+ eats(zp->z_filenum, zp->z_linenum,
+ r->r_filenum, r->r_linenum);
+ error("%s", dup_rules_msg);
+ }
+ }
+ if (k < 0)
+ break; /* go on to next year */
+ rp = &zp->z_rules[k];
+ rp->r_todo = false;
+ if (useuntil && ktime >= untiltime) {
+ if (!*startbuf
+ && (oadd(zp->z_stdoff, rp->r_save)
+ == startoff))
+ doabbr(startbuf, zp, rp->r_abbrvar,
+ rp->r_isdst, rp->r_save,
+ false);
+ break;
+ }
+ save = rp->r_save;
+ if (usestart && ktime == starttime)
+ usestart = false;
+ if (usestart) {
+ if (ktime < starttime) {
+ startoff = oadd(zp->z_stdoff,
+ save);
+ doabbr(startbuf, zp,
+ rp->r_abbrvar,
+ rp->r_isdst,
+ rp->r_save,
+ false);
+ continue;
+ }
+ if (*startbuf == '\0'
+ && startoff == oadd(zp->z_stdoff,
+ save)) {
+ doabbr(startbuf,
+ zp,
+ rp->r_abbrvar,
+ rp->r_isdst,
+ rp->r_save,
+ false);
+ }
+ }
+ eats(zp->z_filenum, zp->z_linenum,
+ rp->r_filenum, rp->r_linenum);
+ doabbr(ab, zp, rp->r_abbrvar,
+ rp->r_isdst, rp->r_save, false);
+ offset = oadd(zp->z_stdoff, rp->r_save);
+ if (!want_bloat() && !useuntil && !do_extend
+ && prevrp && lo_time <= prevktime
+ && redundant_time <= ktime
+ && rp->r_hiyear == ZIC_MAX
+ && prevrp->r_hiyear == ZIC_MAX)
+ break;
+ type = addtype(offset, ab, rp->r_isdst,
+ rp->r_todisstd, rp->r_todisut);
+ if (defaulttype < 0 && !rp->r_isdst)
+ defaulttype = type;
+ if (rp->r_hiyear == ZIC_MAX
+ && ! (0 <= lastatmax
+ && ktime < attypes[lastatmax].at))
+ lastatmax = timecnt;
+ addtt(ktime, type);
+ prevrp = rp;
+ prevktime = ktime;
+ }
+ }
+ }
+ if (usestart) {
+ bool isdst = startoff != zp->z_stdoff;
+ if (*startbuf == '\0' && zp->z_format)
+ doabbr(startbuf, zp, disable_percent_s,
+ isdst, save, false);
+ eat(zp->z_filenum, zp->z_linenum);
+ if (*startbuf == '\0')
+error(_("can't determine time zone abbreviation to use just after until time"));
+ else {
+ int type = addtype(startoff, startbuf, isdst,
+ startttisstd, startttisut);
+ if (defaulttype < 0 && !isdst)
+ defaulttype = type;
+ addtt(starttime, type);
+ }
+ }
+ /*
+ ** Now we may get to set starttime for the next zone line.
+ */
+ if (useuntil) {
+ startttisstd = zp->z_untilrule.r_todisstd;
+ startttisut = zp->z_untilrule.r_todisut;
+ starttime = zp->z_untiltime;
+ if (!startttisstd)
+ starttime = tadd(starttime, -save);
+ if (!startttisut)
+ starttime = tadd(starttime, -stdoff);
+ }
+ }
+ if (defaulttype < 0)
+ defaulttype = 0;
+ if (0 <= lastatmax)
+ attypes[lastatmax].dontmerge = true;
+ if (do_extend) {
+ /*
+ ** If we're extending the explicitly listed observations
+ ** for 400 years because we can't fill the POSIX-TZ field,
+ ** check whether we actually ended up explicitly listing
+ ** observations through that period. If there aren't any
+ ** near the end of the 400-year period, add a redundant
+ ** one at the end of the final year, to make it clear
+ ** that we are claiming to have definite knowledge of
+ ** the lack of transitions up to that point.
+ */
+ struct rule xr;
+ struct attype *lastat;
+ xr.r_month = TM_JANUARY;
+ xr.r_dycode = DC_DOM;
+ xr.r_dayofmonth = 1;
+ xr.r_tod = 0;
+ for (lastat = attypes, i = 1; i < timecnt; i++)
+ if (attypes[i].at > lastat->at)
+ lastat = &attypes[i];
+ if (!lastat || lastat->at < rpytime(&xr, max_year - 1)) {
+ addtt(rpytime(&xr, max_year + 1),
+ lastat ? lastat->type : defaulttype);
+ attypes[timecnt - 1].dontmerge = true;
+ }
+ }
+ writezone(zpfirst->z_name, envvar, version, defaulttype);
+ free(startbuf);
+ free(ab);
+ free(envvar);
+}
+
+static void
+addtt(zic_t starttime, int type)
+{
+ attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
+ attypes[timecnt].at = starttime;
+ attypes[timecnt].dontmerge = false;
+ attypes[timecnt].type = type;
+ ++timecnt;
+}
+
+static int
+addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
+{
+ register int i, j;
+
+ if (! (-1L - 2147483647L <= utoff && utoff <= 2147483647L)) {
+ error(_("UT offset out of range"));
+ exit(EXIT_FAILURE);
+ }
+ if (!want_bloat())
+ ttisstd = ttisut = false;
+
+ for (j = 0; j < charcnt; ++j)
+ if (strcmp(&chars[j], abbr) == 0)
+ break;
+ if (j == charcnt)
+ newabbr(abbr);
+ else {
+ /* If there's already an entry, return its index. */
+ for (i = 0; i < typecnt; i++)
+ if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
+ && ttisstd == ttisstds[i] && ttisut == ttisuts[i])
+ return i;
+ }
+ /*
+ ** There isn't one; add a new one, unless there are already too
+ ** many.
+ */
+ if (typecnt >= TZ_MAX_TYPES) {
+ error(_("too many local time types"));
+ exit(EXIT_FAILURE);
+ }
+ i = typecnt++;
+ utoffs[i] = utoff;
+ isdsts[i] = isdst;
+ ttisstds[i] = ttisstd;
+ ttisuts[i] = ttisut;
+ desigidx[i] = j;
+ return i;
+}
+
+static void
+leapadd(zic_t t, int correction, int rolling)
+{
+ register int i;
+
+ if (TZ_MAX_LEAPS <= leapcnt) {
+ error(_("too many leap seconds"));
+ exit(EXIT_FAILURE);
+ }
+ if (rolling && (lo_time != min_time || hi_time != max_time)) {
+ error(_("Rolling leap seconds not supported with -r"));
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < leapcnt; ++i)
+ if (t <= trans[i])
+ break;
+ memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
+ memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
+ memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
+ trans[i] = t;
+ corr[i] = correction;
+ roll[i] = rolling;
+ ++leapcnt;
+}
+
+static void
+adjleap(void)
+{
+ register int i;
+ register zic_t last = 0;
+ register zic_t prevtrans = 0;
+
+ /*
+ ** propagate leap seconds forward
+ */
+ for (i = 0; i < leapcnt; ++i) {
+ if (trans[i] - prevtrans < 28 * SECSPERDAY) {
+ error(_("Leap seconds too close together"));
+ exit(EXIT_FAILURE);
+ }
+ prevtrans = trans[i];
+ trans[i] = tadd(trans[i], last);
+ last = corr[i] += last;
+ }
+
+ if (0 <= leapexpires) {
+ leapexpires = oadd(leapexpires, last);
+ if (! (leapcnt == 0 || (trans[leapcnt - 1] < leapexpires))) {
+ error(_("last Leap time does not precede Expires time"));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+/* Is A a space character in the C locale? */
+static bool
+is_space(char a)
+{
+ switch (a) {
+ default:
+ return false;
+ case ' ': case '\f': case '\n': case '\r': case '\t': case '\v':
+ return true;
+ }
+}
+
+/* Is A an alphabetic character in the C locale? */
+static bool
+is_alpha(char a)
+{
+ switch (a) {
+ default:
+ return false;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+ case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ return true;
+ }
+}
+
+/* If A is an uppercase character in the C locale, return its lowercase
+ counterpart. Otherwise, return A. */
+static char
+lowerit(char a)
+{
+ switch (a) {
+ default: return a;
+ case 'A': return 'a'; case 'B': return 'b'; case 'C': return 'c';
+ case 'D': return 'd'; case 'E': return 'e'; case 'F': return 'f';
+ case 'G': return 'g'; case 'H': return 'h'; case 'I': return 'i';
+ case 'J': return 'j'; case 'K': return 'k'; case 'L': return 'l';
+ case 'M': return 'm'; case 'N': return 'n'; case 'O': return 'o';
+ case 'P': return 'p'; case 'Q': return 'q'; case 'R': return 'r';
+ case 'S': return 's'; case 'T': return 't'; case 'U': return 'u';
+ case 'V': return 'v'; case 'W': return 'w'; case 'X': return 'x';
+ case 'Y': return 'y'; case 'Z': return 'z';
+ }
+}
+
+/* case-insensitive equality */
+static ATTRIBUTE_REPRODUCIBLE bool
+ciequal(register const char *ap, register const char *bp)
+{
+ while (lowerit(*ap) == lowerit(*bp++))
+ if (*ap++ == '\0')
+ return true;
+ return false;
+}
+
+static ATTRIBUTE_REPRODUCIBLE bool
+itsabbr(register const char *abbr, register const char *word)
+{
+ if (lowerit(*abbr) != lowerit(*word))
+ return false;
+ ++word;
+ while (*++abbr != '\0')
+ do {
+ if (*word == '\0')
+ return false;
+ } while (lowerit(*word++) != lowerit(*abbr));
+ return true;
+}
+
+/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
+
+static ATTRIBUTE_REPRODUCIBLE bool
+ciprefix(char const *abbr, char const *word)
+{
+ do
+ if (!*abbr)
+ return true;
+ while (lowerit(*abbr++) == lowerit(*word++));
+
+ return false;
+}
+
+static const struct lookup *
+byword(const char *word, const struct lookup *table)
+{
+ register const struct lookup * foundlp;
+ register const struct lookup * lp;
+
+ if (word == NULL || table == NULL)
+ return NULL;
+
+ /* If TABLE is LASTS and the word starts with "last" followed
+ by a non-'-', skip the "last" and look in WDAY_NAMES instead.
+ Warn about any usage of the undocumented prefix "last-". */
+ if (table == lasts && ciprefix("last", word) && word[4]) {
+ if (word[4] == '-')
+ warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
+ word, word + 5);
+ else {
+ word += 4;
+ table = wday_names;
+ }
+ }
+
+ /*
+ ** Look for exact match.
+ */
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciequal(word, lp->l_word))
+ return lp;
+ /*
+ ** Look for inexact match.
+ */
+ foundlp = NULL;
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciprefix(word, lp->l_word)) {
+ if (foundlp == NULL)
+ foundlp = lp;
+ else return NULL; /* multiple inexact matches */
+ }
+
+ if (foundlp && noise) {
+ /* Warn about any backward-compatibility issue with pre-2017c zic. */
+ bool pre_2017c_match = false;
+ for (lp = table; lp->l_word; lp++)
+ if (itsabbr(word, lp->l_word)) {
+ if (pre_2017c_match) {
+ warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
+ break;
+ }
+ pre_2017c_match = true;
+ }
+ }
+
+ return foundlp;
+}
+
+static int
+getfields(char *cp, char **array, int arrayelts)
+{
+ register char * dp;
+ register int nsubs;
+
+ nsubs = 0;
+ for ( ; ; ) {
+ char *dstart;
+ while (is_space(*cp))
+ ++cp;
+ if (*cp == '\0' || *cp == '#')
+ break;
+ dstart = dp = cp;
+ do {
+ if ((*dp = *cp++) != '"')
+ ++dp;
+ else while ((*dp = *cp++) != '"')
+ if (*dp != '\0')
+ ++dp;
+ else {
+ error(_("Odd number of quotation marks"));
+ exit(EXIT_FAILURE);
+ }
+ } while (*cp && *cp != '#' && !is_space(*cp));
+ if (is_space(*cp))
+ ++cp;
+ *dp = '\0';
+ if (nsubs == arrayelts) {
+ error(_("Too many input fields"));
+ exit(EXIT_FAILURE);
+ }
+ array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
+ }
+ return nsubs;
+}
+
+static ATTRIBUTE_NORETURN void
+time_overflow(void)
+{
+ error(_("time overflow"));
+ exit(EXIT_FAILURE);
+}
+
+static ATTRIBUTE_REPRODUCIBLE zic_t
+oadd(zic_t t1, zic_t t2)
+{
+#ifdef ckd_add
+ zic_t sum;
+ if (!ckd_add(&sum, t1, t2))
+ return sum;
+#else
+ if (t1 < 0 ? ZIC_MIN - t1 <= t2 : t2 <= ZIC_MAX - t1)
+ return t1 + t2;
+#endif
+ time_overflow();
+}
+
+static ATTRIBUTE_REPRODUCIBLE zic_t
+tadd(zic_t t1, zic_t t2)
+{
+#ifdef ckd_add
+ zic_t sum;
+ if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time)
+ return sum;
+#else
+ if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1)
+ return t1 + t2;
+#endif
+ if (t1 == min_time || t1 == max_time)
+ return t1;
+ time_overflow();
+}
+
+/*
+** Given a rule, and a year, compute the date (in seconds since January 1,
+** 1970, 00:00 LOCAL time) in that year that the rule refers to.
+*/
+
+static zic_t
+rpytime(const struct rule *rp, zic_t wantedy)
+{
+ register int m, i;
+ register zic_t dayoff; /* with a nod to Margaret O. */
+ register zic_t t, y;
+ int yrem;
+
+ if (wantedy == ZIC_MIN)
+ return min_time;
+ if (wantedy == ZIC_MAX)
+ return max_time;
+ m = TM_JANUARY;
+ y = EPOCH_YEAR;
+
+ /* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
+ sans overflow. */
+ yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
+ dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
+ + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
+ * DAYSPERREPEAT);
+ /* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow. */
+ wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
+
+ while (wantedy != y) {
+ i = len_years[isleap(y)];
+ dayoff = oadd(dayoff, i);
+ y++;
+ }
+ while (m != rp->r_month) {
+ i = len_months[isleap(y)][m];
+ dayoff = oadd(dayoff, i);
+ ++m;
+ }
+ i = rp->r_dayofmonth;
+ if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
+ if (rp->r_dycode == DC_DOWLEQ)
+ --i;
+ else {
+ error(_("use of 2/29 in non leap-year"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ --i;
+ dayoff = oadd(dayoff, i);
+ if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
+ /*
+ ** Don't trust mod of negative numbers.
+ */
+ zic_t wday = ((EPOCH_WDAY + dayoff % DAYSPERWEEK + DAYSPERWEEK)
+ % DAYSPERWEEK);
+ while (wday != rp->r_wday)
+ if (rp->r_dycode == DC_DOWGEQ) {
+ dayoff = oadd(dayoff, 1);
+ if (++wday >= DAYSPERWEEK)
+ wday = 0;
+ ++i;
+ } else {
+ dayoff = oadd(dayoff, -1);
+ if (--wday < 0)
+ wday = DAYSPERWEEK - 1;
+ --i;
+ }
+ if (i < 0 || i >= len_months[isleap(y)][m]) {
+ if (noise)
+ warning(_("rule goes past start/end of month; \
+will not work with pre-2004 versions of zic"));
+ }
+ }
+ if (dayoff < min_time / SECSPERDAY)
+ return min_time;
+ if (dayoff > max_time / SECSPERDAY)
+ return max_time;
+ t = (zic_t) dayoff * SECSPERDAY;
+ return tadd(t, rp->r_tod);
+}
+
+static void
+newabbr(const char *string)
+{
+ register int i;
+
+ if (strcmp(string, GRANDPARENTED) != 0) {
+ register const char * cp;
+ const char * mp;
+
+ cp = string;
+ mp = NULL;
+ while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
+ || *cp == '-' || *cp == '+')
+ ++cp;
+ if (noise && cp - string < 3)
+ mp = _("time zone abbreviation has fewer than 3 characters");
+ if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
+ mp = _("time zone abbreviation has too many characters");
+ if (*cp != '\0')
+mp = _("time zone abbreviation differs from POSIX standard");
+ if (mp != NULL)
+ warning("%s (%s)", mp, string);
+ }
+ i = strlen(string) + 1;
+ if (charcnt + i > TZ_MAX_CHARS) {
+ error(_("too many, or too long, time zone abbreviations"));
+ exit(EXIT_FAILURE);
+ }
+ strcpy(&chars[charcnt], string);
+ charcnt += i;
+}
+
+/* Ensure that the directories of ARGNAME exist, by making any missing
+ ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
+ do it for ARGNAME too. Exit with failure if there is trouble.
+ Do not consider an existing file to be trouble. */
+static void
+mkdirs(char const *argname, bool ancestors)
+{
+ char *name = estrdup(argname);
+ char *cp = name;
+
+ /* On MS-Windows systems, do not worry about drive letters or
+ backslashes, as this should suffice in practice. Time zone
+ names do not use drive letters and backslashes. If the -d
+ option of zic does not name an already-existing directory,
+ it can use slashes to separate the already-existing
+ ancestor prefix from the to-be-created subdirectories. */
+
+ /*
+ * If -D was specified, do not create directories. A subsequent
+ * file operation will fail and produce an appropriate error
+ * message.
+ */
+ if (Dflag)
+ return;
+ /* Do not mkdir a root directory, as it must exist. */
+ while (*cp == '/')
+ cp++;
+
+ while (cp && ((cp = strchr(cp, '/')) || !ancestors)) {
+ if (cp)
+ *cp = '\0';
+ /*
+ ** Try to create it. It's OK if creation fails because
+ ** the directory already exists, perhaps because some
+ ** other process just created it. For simplicity do
+ ** not check first whether it already exists, as that
+ ** is checked anyway if the mkdir fails.
+ */
+ if (mkdir(name, MKDIR_UMASK) != 0) {
+ /* Do not report an error if err == EEXIST, because
+ some other process might have made the directory
+ in the meantime. Likewise for ENOSYS, because
+ Solaris 10 mkdir fails with ENOSYS if the
+ directory is an automounted mount point.
+ Likewise for EACCES, since mkdir can fail
+ with EACCES merely because the parent directory
+ is unwritable. Likewise for most other error
+ numbers. */
+ int err = errno;
+ if (err == ELOOP || err == ENAMETOOLONG
+ || err == ENOENT || err == ENOTDIR) {
+ error(_("%s: Can't create directory %s: %s"),
+ progname, name, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (cp)
+ *cp++ = '/';
+ }
+ free(name);
+}
+
+#include <grp.h>
+#include <pwd.h>
+
+static void
+setgroup(flag, name)
+ gid_t *flag;
+ const char *name;
+{
+ struct group *gr;
+
+ if (*flag != (gid_t)-1) {
+ fprintf(stderr, _("multiple -g flags specified"));
+ exit(EXIT_FAILURE);
+ }
+
+ gr = getgrnam(name);
+ if (gr == 0) {
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(name, &ep, 10);
+ if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
+ *flag = ul;
+ return;
+ }
+ fprintf(stderr, _("group `%s' not found"), name);
+ exit(EXIT_FAILURE);
+ }
+ *flag = gr->gr_gid;
+}
+
+static void
+setuser(flag, name)
+ uid_t *flag;
+ const char *name;
+{
+ struct passwd *pw;
+
+ if (*flag != (gid_t)-1) {
+ fprintf(stderr, _("multiple -u flags specified"));
+ exit(EXIT_FAILURE);
+ }
+
+ pw = getpwnam(name);
+ if (pw == 0) {
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(name, &ep, 10);
+ if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
+ *flag = ul;
+ return;
+ }
+ fprintf(stderr, _("user `%s' not found"), name);
+ exit(EXIT_FAILURE);
+ }
+ *flag = pw->pw_uid;
+}
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
diff --git a/contrib/tzcode/zic/README b/contrib/tzcode/zic/README
deleted file mode 100644
index 5cb701eca276..000000000000
--- a/contrib/tzcode/zic/README
+++ /dev/null
@@ -1,88 +0,0 @@
-@(#)README 8.3
-This file is in the public domain, so clarified as of
-2009-05-17 by Arthur David Olson.
-
-$FreeBSD$
-
-"What time is it?" -- Richard Deacon as The King
-"Any time you want it to be." -- Frank Baxter as The Scientist
- (from the Bell System film "About Time")
-
-The 1989 update of the time zone package featured
-
-* POSIXization (including interpretation of POSIX-style TZ environment
- variables, provided by Guy Harris),
-* ANSIfication (including versions of "mktime" and "difftime"),
-* SVIDulation (an "altzone" variable)
-* MACHination (the "gtime" function)
-* corrections to some time zone data (including corrections to the rules
- for Great Britain and New Zealand)
-* reference data from the United States Naval Observatory for folks who
- want to do additional time zones
-* and the 1989 data for Saudi Arabia.
-
-(Since this code will be treated as "part of the implementation" in some places
-and as "part of the application" in others, there's no good way to name
-functions, such as timegm, that are not part of the proposed ANSI C standard;
-such functions have kept their old, underscore-free names in this update.)
-
-And the "dysize" function has disappeared; it was present to allow compilation
-of the "date" command on old BSD systems, and a version of "date" is now
-provided in the package. The "date" command is not created when you "make all"
-since it may lack options provided by the version distributed with your
-operating system, or may not interact with the system in the same way the
-native version does.
-
-Since POSIX frowns on correct leap second handling, the default behavior of
-the "zic" command (in the absence of a "-L" option) has been changed to omit
-leap second information from its output files.
-
-Here is a recipe for acquiring, building, installing, and testing the
-tz distribution on a GNU/Linux or similar host.
-
- mkdir tz
- cd tz
- wget 'ftp://elsie.nci.nih.gov/pub/tz*.tar.gz'
- gzip -dc tzcode*.tar.gz | tar -xf -
- gzip -dc tzdata*.tar.gz | tar -xf -
-
-Be sure to read the comments in "Makefile" and make any changes needed
-to make things right for your system, especially if you are using some
-platform other than GNU/Linux. Then run the following commands,
-substituting your desired installation directory for "$HOME/tzdir":
-
- make TOPDIR=$HOME/tzdir install
- $HOME/tzdir/etc/zdump -v America/Los_Angeles
-
-To use the new functions, use a "-ltz" option when compiling or linking.
-
-Historical local time information has been included here to:
-
-* provide a compendium of data about the history of civil time
- that is useful even if the data are not 100% accurate;
-
-* give an idea of the variety of local time rules that have
- existed in the past and thus an idea of the variety that may be
- expected in the future;
-
-* provide a test of the generality of the local time rule description
- system.
-
-The information in the time zone data files is by no means authoritative;
-the files currently do not even attempt to cover all time stamps before
-1970, and there are undoubtedly errors even for time stamps since 1970.
-If you know that the rules are different from those in a file, by all means
-feel free to change file (and please send the changed version to
-tz@elsie.nci.nih.gov for use in the future). Europeans take note!
-
-Thanks to these Timezone Caballeros who've made major contributions to the
-time conversion package: Keith Bostic; Bob Devine; Paul Eggert; Robert Elz;
-Guy Harris; Mark Horton; John Mackin; and Bradley White. Thanks also to
-Michael Bloom, Art Neilson, Stephen Prince, John Sovereign, and Frank Wales
-for testing work, and to Gwillim Law for checking local mean time data.
-None of them are responsible for remaining errors.
-
-Look in the ~ftp/pub directory of elsie.nci.nih.gov
-for updated versions of these files.
-
-Please send comments or information to tz@elsie.nci.nih.gov.
diff --git a/contrib/tzcode/zic/Theory b/contrib/tzcode/zic/Theory
deleted file mode 100644
index 752125c95a56..000000000000
--- a/contrib/tzcode/zic/Theory
+++ /dev/null
@@ -1,570 +0,0 @@
-@(#)Theory 8.4
-This file is in the public domain, so clarified as of
-2009-05-17 by Arthur David Olson.
-$FreeBSD$
-
------ Outline -----
-
- Time and date functions
- Names of time zone regions
- Time zone abbreviations
- Calendrical issues
- Time and time zones on Mars
-
------ Time and date functions -----
-
-These time and date functions are upwards compatible with POSIX,
-an international standard for UNIX-like systems.
-As of this writing, the current edition of POSIX is:
-
- Standard for Information technology
- -- Portable Operating System Interface (POSIX (R))
- -- System Interfaces
- IEEE Std 1003.1, 2004 Edition
- <http://www.opengroup.org/online-pubs?DOC=7999959899>
- <http://www.opengroup.org/pubs/catalog/t041.htm>
-
-POSIX has the following properties and limitations.
-
-* In POSIX, time display in a process is controlled by the
- environment variable TZ. Unfortunately, the POSIX TZ string takes
- a form that is hard to describe and is error-prone in practice.
- Also, POSIX TZ strings can't deal with other (for example, Israeli)
- daylight saving time rules, or situations where more than two
- time zone abbreviations are used in an area.
-
- The POSIX TZ string takes the following form:
-
- stdoffset[dst[offset],date[/time],date[/time]]
-
- where:
-
- std and dst
- are 3 or more characters specifying the standard
- and daylight saving time (DST) zone names.
- Starting with POSIX.1-2001, std and dst may also be
- in a quoted form like "<UTC+10>"; this allows
- "+" and "-" in the names.
- offset
- is of the form `[-]hh:[mm[:ss]]' and specifies the
- offset west of UTC. The default DST offset is one hour
- ahead of standard time.
- date[/time],date[/time]
- specifies the beginning and end of DST. If this is absent,
- the system supplies its own rules for DST, and these can
- differ from year to year; typically US DST rules are used.
- time
- takes the form `hh:[mm[:ss]]' and defaults to 02:00.
- date
- takes one of the following forms:
- Jn (1<=n<=365)
- origin-1 day number not counting February 29
- n (0<=n<=365)
- origin-0 day number counting February 29 if present
- Mm.n.d (0[Sunday]<=d<=6[Saturday], 1<=n<=5, 1<=m<=12)
- for the dth day of week n of month m of the year,
- where week 1 is the first week in which day d appears,
- and `5' stands for the last week in which day d appears
- (which may be either the 4th or 5th week).
-
- Here is an example POSIX TZ string, for US Pacific time using rules
- appropriate from 1987 through 2006:
-
- TZ='PST8PDT,M4.1.0/02:00,M10.5.0/02:00'
-
- This POSIX TZ string is hard to remember, and mishandles time stamps
- before 1987 and after 2006. With this package you can use this
- instead:
-
- TZ='America/Los_Angeles'
-
-* POSIX does not define the exact meaning of TZ values like "EST5EDT".
- Typically the current US DST rules are used to interpret such values,
- but this means that the US DST rules are compiled into each program
- that does time conversion. This means that when US time conversion
- rules change (as in the United States in 1987), all programs that
- do time conversion must be recompiled to ensure proper results.
-
-* In POSIX, there's no tamper-proof way for a process to learn the
- system's best idea of local wall clock. (This is important for
- applications that an administrator wants used only at certain times--
- without regard to whether the user has fiddled the "TZ" environment
- variable. While an administrator can "do everything in UTC" to get
- around the problem, doing so is inconvenient and precludes handling
- daylight saving time shifts--as might be required to limit phone
- calls to off-peak hours.)
-
-* POSIX requires that systems ignore leap seconds.
-
-These are the extensions that have been made to the POSIX functions:
-
-* The "TZ" environment variable is used in generating the name of a file
- from which time zone information is read (or is interpreted a la
- POSIX); "TZ" is no longer constrained to be a three-letter time zone
- name followed by a number of hours and an optional three-letter
- daylight time zone name. The daylight saving time rules to be used
- for a particular time zone are encoded in the time zone file;
- the format of the file allows U.S., Australian, and other rules to be
- encoded, and allows for situations where more than two time zone
- abbreviations are used.
-
- It was recognized that allowing the "TZ" environment variable to
- take on values such as "America/New_York" might cause "old" programs
- (that expect "TZ" to have a certain form) to operate incorrectly;
- consideration was given to using some other environment variable
- (for example, "TIMEZONE") to hold the string used to generate the
- time zone information file name. In the end, however, it was decided
- to continue using "TZ": it is widely used for time zone purposes;
- separately maintaining both "TZ" and "TIMEZONE" seemed a nuisance;
- and systems where "new" forms of "TZ" might cause problems can simply
- use TZ values such as "EST5EDT" which can be used both by
- "new" programs (a la POSIX) and "old" programs (as zone names and
- offsets).
-
-* To handle places where more than two time zone abbreviations are used,
- the functions "localtime" and "gmtime" set tzname[tmp->tm_isdst]
- (where "tmp" is the value the function returns) to the time zone
- abbreviation to be used. This differs from POSIX, where the elements
- of tzname are only changed as a result of calls to tzset.
-
-* Since the "TZ" environment variable can now be used to control time
- conversion, the "daylight" and "timezone" variables are no longer
- needed. (These variables are defined and set by "tzset"; however, their
- values will not be used by "localtime.")
-
-* The "localtime" function has been set up to deliver correct results
- for near-minimum or near-maximum time_t values. (A comment in the
- source code tells how to get compatibly wrong results).
-
-* A function "tzsetwall" has been added to arrange for the system's
- best approximation to local wall clock time to be delivered by
- subsequent calls to "localtime." Source code for portable
- applications that "must" run on local wall clock time should call
- "tzsetwall();" if such code is moved to "old" systems that don't
- provide tzsetwall, you won't be able to generate an executable program.
- (These time zone functions also arrange for local wall clock time to be
- used if tzset is called--directly or indirectly--and there's no "TZ"
- environment variable; portable applications should not, however, rely
- on this behavior since it's not the way SVR2 systems behave.)
-
-* These functions can account for leap seconds, thanks to Bradley White.
-
-Points of interest to folks with other systems:
-
-* This package is already part of many POSIX-compliant hosts,
- including BSD, HP, Linux, Network Appliance, SCO, SGI, and Sun.
- On such hosts, the primary use of this package
- is to update obsolete time zone rule tables.
- To do this, you may need to compile the time zone compiler
- `zic' supplied with this package instead of using the system `zic',
- since the format of zic's input changed slightly in late 1994,
- and many vendors still do not support the new input format.
-
-* The UNIX Version 7 "timezone" function is not present in this package;
- it's impossible to reliably map timezone's arguments (a "minutes west
- of GMT" value and a "daylight saving time in effect" flag) to a
- time zone abbreviation, and we refuse to guess.
- Programs that in the past used the timezone function may now examine
- tzname[localtime(&clock)->tm_isdst] to learn the correct time
- zone abbreviation to use. Alternatively, use
- localtime(&clock)->tm_zone if this has been enabled.
-
-* The 4.2BSD gettimeofday function is not used in this package.
- This formerly let users obtain the current UTC offset and DST flag,
- but this functionality was removed in later versions of BSD.
-
-* In SVR2, time conversion fails for near-minimum or near-maximum
- time_t values when doing conversions for places that don't use UTC.
- This package takes care to do these conversions correctly.
-
-The functions that are conditionally compiled if STD_INSPIRED is defined
-should, at this point, be looked on primarily as food for thought. They are
-not in any sense "standard compatible"--some are not, in fact, specified in
-*any* standard. They do, however, represent responses of various authors to
-standardization proposals.
-
-Other time conversion proposals, in particular the one developed by folks at
-Hewlett Packard, offer a wider selection of functions that provide capabilities
-beyond those provided here. The absence of such functions from this package
-is not meant to discourage the development, standardization, or use of such
-functions. Rather, their absence reflects the decision to make this package
-contain valid extensions to POSIX, to ensure its broad acceptability. If
-more powerful time conversion functions can be standardized, so much the
-better.
-
-
------ Names of time zone rule files -----
-
-The time zone rule file naming conventions attempt to strike a balance
-among the following goals:
-
- * Uniquely identify every national region where clocks have all
- agreed since 1970. This is essential for the intended use: static
- clocks keeping local civil time.
-
- * Indicate to humans as to where that region is. This simplifes use.
-
- * Be robust in the presence of political changes. This reduces the
- number of updates and backward-compatibility hacks. For example,
- names of countries are ordinarily not used, to avoid
- incompatibilities when countries change their name
- (e.g. Zaire->Congo) or when locations change countries
- (e.g. Hong Kong from UK colony to China).
-
- * Be portable to a wide variety of implementations.
- This promotes use of the technology.
-
- * Use a consistent naming convention over the entire world.
- This simplifies both use and maintenance.
-
-This naming convention is not intended for use by inexperienced users
-to select TZ values by themselves (though they can of course examine
-and reuse existing settings). Distributors should provide
-documentation and/or a simple selection interface that explains the
-names; see the 'tzselect' program supplied with this distribution for
-one example.
-
-Names normally have the form AREA/LOCATION, where AREA is the name
-of a continent or ocean, and LOCATION is the name of a specific
-location within that region. North and South America share the same
-area, `America'. Typical names are `Africa/Cairo', `America/New_York',
-and `Pacific/Honolulu'.
-
-Here are the general rules used for choosing location names,
-in decreasing order of importance:
-
- Use only valid POSIX file name components (i.e., the parts of
- names other than `/'). Within a file name component,
- use only ASCII letters, `.', `-' and `_'. Do not use
- digits, as that might create an ambiguity with POSIX
- TZ strings. A file name component must not exceed 14
- characters or start with `-'. E.g., prefer `Brunei'
- to `Bandar_Seri_Begawan'.
- Include at least one location per time zone rule set per country.
- One such location is enough. Use ISO 3166 (see the file
- iso3166.tab) to help decide whether something is a country.
- However, uninhabited ISO 3166 regions like Bouvet Island
- do not need locations, since local time is not defined there.
- If all the clocks in a country's region have agreed since 1970,
- don't bother to include more than one location
- even if subregions' clocks disagreed before 1970.
- Otherwise these tables would become annoyingly large.
- If a name is ambiguous, use a less ambiguous alternative;
- e.g. many cities are named San Jose and Georgetown, so
- prefer `Costa_Rica' to `San_Jose' and `Guyana' to `Georgetown'.
- Keep locations compact. Use cities or small islands, not countries
- or regions, so that any future time zone changes do not split
- locations into different time zones. E.g. prefer `Paris'
- to `France', since France has had multiple time zones.
- Use mainstream English spelling, e.g. prefer `Rome' to `Roma', and
- prefer `Athens' to the true name (which uses Greek letters).
- The POSIX file name restrictions encourage this rule.
- Use the most populous among locations in a country's time zone,
- e.g. prefer `Shanghai' to `Beijing'. Among locations with
- similar populations, pick the best-known location,
- e.g. prefer `Rome' to `Milan'.
- Use the singular form, e.g. prefer `Canary' to `Canaries'.
- Omit common suffixes like `_Islands' and `_City', unless that
- would lead to ambiguity. E.g. prefer `Cayman' to
- `Cayman_Islands' and `Guatemala' to `Guatemala_City',
- but prefer `Mexico_City' to `Mexico' because the country
- of Mexico has several time zones.
- Use `_' to represent a space.
- Omit `.' from abbreviations in names, e.g. prefer `St_Helena'
- to `St._Helena'.
- Do not change established names if they only marginally
- violate the above rules. For example, don't change
- the existing name `Rome' to `Milan' merely because
- Milan's population has grown to be somewhat greater
- than Rome's.
- If a name is changed, put its old spelling in the `backward' file.
-
-The file `zone.tab' lists the geographical locations used to name
-time zone rule files. It is intended to be an exhaustive list
-of canonical names for geographic regions.
-
-Older versions of this package used a different naming scheme,
-and these older names are still supported.
-See the file `backward' for most of these older names
-(e.g. `US/Eastern' instead of `America/New_York').
-The other old-fashioned names still supported are
-`WET', `CET', `MET', `EET' (see the file `europe'),
-and `Factory' (see the file `factory').
-
-
------ Time zone abbreviations -----
-
-When this package is installed, it generates time zone abbreviations
-like `EST' to be compatible with human tradition and POSIX.
-Here are the general rules used for choosing time zone abbreviations,
-in decreasing order of importance:
-
- Use abbreviations that consist of three or more ASCII letters.
- Previous editions of this database also used characters like
- ' ' and '?', but these characters have a special meaning to
- the shell and cause commands like
- set `date`
- to have unexpected effects.
- Previous editions of this rule required upper-case letters,
- but the Congressman who introduced Chamorro Standard Time
- preferred "ChST", so the rule has been relaxed.
-
- This rule guarantees that all abbreviations could have
- been specified by a POSIX TZ string. POSIX
- requires at least three characters for an
- abbreviation. POSIX through 2000 says that an abbreviation
- cannot start with ':', and cannot contain ',', '-',
- '+', NUL, or a digit. POSIX from 2001 on changes this
- rule to say that an abbreviation can contain only '-', '+',
- and alphanumeric characters from the portable character set
- in the current locale. To be portable to both sets of
- rules, an abbreviation must therefore use only ASCII
- letters.
-
- Use abbreviations that are in common use among English-speakers,
- e.g. `EST' for Eastern Standard Time in North America.
- We assume that applications translate them to other languages
- as part of the normal localization process; for example,
- a French application might translate `EST' to `HNE'.
-
- For zones whose times are taken from a city's longitude, use the
- traditional xMT notation, e.g. `PMT' for Paris Mean Time.
- The only name like this in current use is `GMT'.
-
- If there is no common English abbreviation, abbreviate the English
- translation of the usual phrase used by native speakers.
- If this is not available or is a phrase mentioning the country
- (e.g. ``Cape Verde Time''), then:
-
- When a country has a single or principal time zone region,
- append `T' to the country's ISO code, e.g. `CVT' for
- Cape Verde Time. For summer time append `ST';
- for double summer time append `DST'; etc.
- When a country has multiple time zones, take the first three
- letters of an English place name identifying each zone
- and then append `T', `ST', etc. as before;
- e.g. `VLAST' for VLAdivostok Summer Time.
-
- Use UTC (with time zone abbreviation "zzz") for locations while
- uninhabited. The "zzz" mnemonic is that these locations are,
- in some sense, asleep.
-
-Application writers should note that these abbreviations are ambiguous
-in practice: e.g. `EST' has a different meaning in Australia than
-it does in the United States. In new applications, it's often better
-to use numeric UTC offsets like `-0500' instead of time zone
-abbreviations like `EST'; this avoids the ambiguity.
-
-
------ Calendrical issues -----
-
-Calendrical issues are a bit out of scope for a time zone database,
-but they indicate the sort of problems that we would run into if we
-extended the time zone database further into the past. An excellent
-resource in this area is Nachum Dershowitz and Edward M. Reingold,
-<a href="http://emr.cs.iit.edu/home/reingold/calendar-book/third-edition/">
-Calendrical Calculations: Third Edition
-</a>, Cambridge University Press (2008). Other information and
-sources are given below. They sometimes disagree.
-
-
-France
-
-Gregorian calendar adopted 1582-12-20.
-French Revolutionary calendar used 1793-11-24 through 1805-12-31,
-and (in Paris only) 1871-05-06 through 1871-05-23.
-
-
-Russia
-
-From Chris Carrier (1996-12-02):
-On 1929-10-01 the Soviet Union instituted an ``Eternal Calendar''
-with 30-day months plus 5 holidays, with a 5-day week.
-On 1931-12-01 it changed to a 6-day week; in 1934 it reverted to the
-Gregorian calendar while retaining the 6-day week; on 1940-06-27 it
-reverted to the 7-day week. With the 6-day week the usual days
-off were the 6th, 12th, 18th, 24th and 30th of the month.
-(Source: Evitiar Zerubavel, _The Seven Day Circle_)
-
-
-Mark Brader reported a similar story in "The Book of Calendars", edited
-by Frank Parise (1982, Facts on File, ISBN 0-8719-6467-8), page 377. But:
-
-From: Petteri Sulonen (via Usenet)
-Date: 14 Jan 1999 00:00:00 GMT
-...
-
-If your source is correct, how come documents between 1929 -- 1940 were
-still dated using the conventional, Gregorian calendar?
-
-I can post a scan of a document dated December 1, 1934, signed by
-Yenukidze, the secretary, on behalf of Kalinin, the President of the
-Executive Committee of the Supreme Soviet, if you like.
-
-
-
-Sweden (and Finland)
-
-From: Mark Brader
-<a href="news:1996Jul6.012937.29190@sq.com">
-Subject: Re: Gregorian reform -- a part of locale?
-</a>
-Date: 1996-07-06
-
-In 1700, Denmark made the transition from Julian to Gregorian. Sweden
-decided to *start* a transition in 1700 as well, but rather than have one of
-those unsightly calendar gaps :-), they simply decreed that the next leap
-year after 1696 would be in 1744 -- putting the whole country on a calendar
-different from both Julian and Gregorian for a period of 40 years.
-
-However, in 1704 something went wrong and the plan was not carried through;
-they did, after all, have a leap year that year. And one in 1708. In 1712
-they gave it up and went back to Julian, putting 30 days in February that
-year!...
-
-Then in 1753, Sweden made the transition to Gregorian in the usual manner,
-getting there only 13 years behind the original schedule.
-
-(A previous posting of this story was challenged, and Swedish readers
-produced the following references to support it: "Tiderakning och historia"
-by Natanael Beckman (1924) and "Tid, en bok om tiderakning och
-kalendervasen" by Lars-Olof Lode'n (no date was given).)
-
-
-Grotefend's data
-
-From: "Michael Palmer" [with one obvious typo fixed]
-Subject: Re: Gregorian Calendar (was Re: Another FHC related question
-Newsgroups: soc.genealogy.german
-Date: Tue, 9 Feb 1999 02:32:48 -800
-...
-
-The following is a(n incomplete) listing, arranged chronologically, of
-European states, with the date they converted from the Julian to the
-Gregorian calendar:
-
-04/15 Oct 1582 - Italy (with exceptions), Spain, Portugal, Poland (Roman
- Catholics and Danzig only)
-09/20 Dec 1582 - France, Lorraine
-
-21 Dec 1582/
- 01 Jan 1583 - Holland, Brabant, Flanders, Hennegau
-10/21 Feb 1583 - bishopric of Liege (L"uttich)
-13/24 Feb 1583 - bishopric of Augsburg
-04/15 Oct 1583 - electorate of Trier
-05/16 Oct 1583 - Bavaria, bishoprics of Freising, Eichstedt, Regensburg,
- Salzburg, Brixen
-13/24 Oct 1583 - Austrian Oberelsass and Breisgau
-20/31 Oct 1583 - bishopric of Basel
-02/13 Nov 1583 - duchy of J"ulich-Berg
-02/13 Nov 1583 - electorate and city of K"oln
-04/15 Nov 1583 - bishopric of W"urzburg
-11/22 Nov 1583 - electorate of Mainz
-16/27 Nov 1583 - bishopric of Strassburg and the margraviate of Baden
-17/28 Nov 1583 - bishopric of M"unster and duchy of Cleve
-14/25 Dec 1583 - Steiermark
-
-06/17 Jan 1584 - Austria and Bohemia
-11/22 Jan 1584 - Luzern, Uri, Schwyz, Zug, Freiburg, Solothurn
-12/23 Jan 1584 - Silesia and the Lausitz
-22 Jan/
- 02 Feb 1584 - Hungary (legally on 21 Oct 1587)
- Jun 1584 - Unterwalden
-01/12 Jul 1584 - duchy of Westfalen
-
-16/27 Jun 1585 - bishopric of Paderborn
-
-14/25 Dec 1590 - Transylvania
-
-22 Aug/
- 02 Sep 1612 - duchy of Prussia
-
-13/24 Dec 1614 - Pfalz-Neuburg
-
- 1617 - duchy of Kurland (reverted to the Julian calendar in
- 1796)
-
- 1624 - bishopric of Osnabr"uck
-
- 1630 - bishopric of Minden
-
-15/26 Mar 1631 - bishopric of Hildesheim
-
- 1655 - Kanton Wallis
-
-05/16 Feb 1682 - city of Strassburg
-
-18 Feb/
- 01 Mar 1700 - Protestant Germany (including Swedish possessions in
- Germany), Denmark, Norway
-30 Jun/
- 12 Jul 1700 - Gelderland, Zutphen
-10 Nov/
- 12 Dec 1700 - Utrecht, Overijssel
-
-31 Dec 1700/
- 12 Jan 1701 - Friesland, Groningen, Z"urich, Bern, Basel, Geneva,
- Turgau, and Schaffhausen
-
- 1724 - Glarus, Appenzell, and the city of St. Gallen
-
-01 Jan 1750 - Pisa and Florence
-
-02/14 Sep 1752 - Great Britain
-
-17 Feb/
- 01 Mar 1753 - Sweden
-
-1760-1812 - Graub"unden
-
-The Russian empire (including Finland and the Baltic states) did not
-convert to the Gregorian calendar until the Soviet revolution of 1917.
-
-Source: H. Grotefend, _Taschenbuch der Zeitrechnung des deutschen
-Mittelalters und der Neuzeit_, herausgegeben von Dr. O. Grotefend
-(Hannover: Hahnsche Buchhandlung, 1941), pp. 26-28.
-
-
------ Time and time zones on Mars -----
-
-Some people have adjusted their work schedules to fit Mars time.
-Dozens of special Mars watches were built for Jet Propulsion
-Laboratory workers who kept Mars time during the Mars Exploration
-Rovers mission (2004). These timepieces look like normal Seikos and
-Citizens but use Mars seconds rather than terrestrial seconds.
-
-A Mars solar day is called a "sol" and has a mean period equal to
-about 24 hours 39 minutes 35.244 seconds in terrestrial time. It is
-divided into a conventional 24-hour clock, so each Mars second equals
-about 1.02749125 terrestrial seconds.
-
-The prime meridian of Mars goes through the center of the crater
-Airy-0, named in honor of the British astronomer who built the
-Greenwich telescope that defines Earth's prime meridian. Mean solar
-time on the Mars prime meridian is called Mars Coordinated Time (MTC).
-
-Each landed mission on Mars has adopted a different reference for
-solar time keeping, so there is no real standard for Mars time zones.
-For example, the Mars Exploration Rover project (2004) defined two
-time zones "Local Solar Time A" and "Local Solar Time B" for its two
-missions, each zone designed so that its time equals local true solar
-time at approximately the middle of the nominal mission. Such a "time
-zone" is not particularly suited for any application other than the
-mission itself.
-
-Many calendars have been proposed for Mars, but none have achieved
-wide acceptance. Astronomers often use Mars Sol Date (MSD) which is a
-sequential count of Mars solar days elapsed since about 1873-12-29
-12:00 GMT.
-
-The tz database does not currently support Mars time, but it is
-documented here in the hopes that support will be added eventually.
-
-Sources:
-
-Michael Allison and Robert Schmunk,
-"Technical Notes on Mars Solar Time as Adopted by the Mars24 Sunclock"
-<http://www.giss.nasa.gov/tools/mars24/help/notes.html> (2004-07-30).
-
-Jia-Rui Chong, "Workdays Fit for a Martian", Los Angeles Times
-(2004-01-14), pp A1, A20-A21.
diff --git a/contrib/tzcode/zic/ialloc.c b/contrib/tzcode/zic/ialloc.c
deleted file mode 100644
index 1694c2905c74..000000000000
--- a/contrib/tzcode/zic/ialloc.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 2006-07-17 by Arthur David Olson.
-*/
-
-#ifndef lint
-#ifndef NOID
-static const char elsieid[] = "@(#)ialloc.c 8.30";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
-
-/*LINTLIBRARY*/
-
-#include "private.h"
-
-#define nonzero(n) (((n) == 0) ? 1 : (n))
-
-char *
-imalloc(n)
-const int n;
-{
- return malloc((size_t) nonzero(n));
-}
-
-char *
-icalloc(nelem, elsize)
-int nelem;
-int elsize;
-{
- if (nelem == 0 || elsize == 0)
- nelem = elsize = 1;
- return calloc((size_t) nelem, (size_t) elsize);
-}
-
-void *
-irealloc(pointer, size)
-void * const pointer;
-const int size;
-{
- if (pointer == NULL)
- return imalloc(size);
- return realloc((void *) pointer, (size_t) nonzero(size));
-}
-
-char *
-icatalloc(old, new)
-char * const old;
-const char * const new;
-{
- register char * result;
- register int oldsize, newsize;
-
- newsize = (new == NULL) ? 0 : strlen(new);
- if (old == NULL)
- oldsize = 0;
- else if (newsize == 0)
- return old;
- else oldsize = strlen(old);
- if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
- if (new != NULL)
- (void) strcpy(result + oldsize, new);
- return result;
-}
-
-char *
-icpyalloc(string)
-const char * const string;
-{
- return icatalloc((char *) NULL, string);
-}
-
-void
-ifree(p)
-char * const p;
-{
- if (p != NULL)
- (void) free(p);
-}
-
-void
-icfree(p)
-char * const p;
-{
- if (p != NULL)
- (void) free(p);
-}
diff --git a/contrib/tzcode/zic/private.h b/contrib/tzcode/zic/private.h
deleted file mode 100644
index d00bf9bae140..000000000000
--- a/contrib/tzcode/zic/private.h
+++ /dev/null
@@ -1,272 +0,0 @@
-#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 modifications: separate libc's privates from zic's.
- * This makes it easier when we need to update one but not the other.
- * I have removed all of the ifdef spaghetti which is not relevant to
- * zic from this file.
- *
- * $FreeBSD$
- */
-
-/*
-** 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 const char privatehid[] = "@(#)private.h 8.6";
-#endif /* !defined NOID */
-#endif /* !defined lint */
-
-#define GRANDPARENTED "Local time zone must be set--use tzsetup"
-
-/*
-** Defaults for preprocessor symbols.
-** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
-*/
-
-#ifndef HAVE_GETTEXT
-#define HAVE_GETTEXT 0
-#endif /* !defined HAVE_GETTEXT */
-
-#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 */
-
-/*
-** 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 */
-
-#if HAVE_UNISTD_H
-#include "unistd.h" /* for F_OK and R_OK, and other POSIX goodness */
-#endif /* 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 */
-
-/* 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 */
-
-#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 */
-
-/*
-** UNIX was a registered trademark of The Open Group in 2003.
-*/
-
-#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/zic/scheck.c b/contrib/tzcode/zic/scheck.c
deleted file mode 100644
index abdb4ba5c79b..000000000000
--- a/contrib/tzcode/zic/scheck.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 2006-07-17 by Arthur David Olson.
-*/
-
-#ifndef lint
-#ifndef NOID
-static const char elsieid[] = "@(#)scheck.c 8.19";
-#endif /* !defined lint */
-#endif /* !defined NOID */
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
-
-/*LINTLIBRARY*/
-
-#include "private.h"
-
-const char *
-scheck(string, format)
-const char * const string;
-const char * const format;
-{
- register char * fbuf;
- register const char * fp;
- register char * tp;
- register int c;
- register const char * result;
- char dummy;
-
- result = "";
- if (string == NULL || format == NULL)
- return result;
- fbuf = imalloc((int) (2 * strlen(format) + 4));
- if (fbuf == NULL)
- return result;
- fp = format;
- tp = fbuf;
- while ((*tp++ = c = *fp++) != '\0') {
- if (c != '%')
- continue;
- if (*fp == '%') {
- *tp++ = *fp++;
- continue;
- }
- *tp++ = '*';
- if (*fp == '*')
- ++fp;
- while (is_digit(*fp))
- *tp++ = *fp++;
- if (*fp == 'l' || *fp == 'h')
- *tp++ = *fp++;
- else if (*fp == '[')
- do *tp++ = *fp++;
- while (*fp != '\0' && *fp != ']');
- if ((*tp++ = *fp++) == '\0')
- break;
- }
- *(tp - 1) = '%';
- *tp++ = 'c';
- *tp = '\0';
- if (sscanf(string, fbuf, &dummy) != 1)
- result = (char *) format;
- ifree(fbuf);
- return result;
-}
diff --git a/contrib/tzcode/zic/zdump.8 b/contrib/tzcode/zic/zdump.8
deleted file mode 100644
index 9ac0a03230dd..000000000000
--- a/contrib/tzcode/zic/zdump.8
+++ /dev/null
@@ -1,63 +0,0 @@
-.\"
-.\" @(#)zdump.8 8.2
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
-.\" $FreeBSD$
-.\"
-.Dd June 20, 2004
-.Dt ZDUMP 8
-.Os
-.Sh NAME
-.Nm zdump
-.Nd timezone dumper
-.Sh SYNOPSIS
-.Nm
-.Op Fl -version
-.Op Fl v
-.Op Fl c Ar [loyear,]hiyear
-.Op Ar zonename ...
-.Sh DESCRIPTION
-The
-.Nm
-utility prints the current time in each
-.Ar zonename
-named on the command line.
-.Pp
-The following options are available:
-.Bl -tag -width indent
-.It Fl -version
-Output version information and exit.
-.It Fl v
-For each
-.Ar zonename
-on the command line,
-print the time at the lowest possible time value,
-the time one day after the lowest possible time value,
-the times both one second before and exactly at
-each detected time discontinuity,
-the time at one day less than the highest possible time value,
-and the time at the highest possible time value,
-Each line ends with
-.Em isdst=1
-if the given time is Daylight Saving Time or
-.Em isdst=0
-otherwise.
-.It Fl c Ar loyear,hiyear
-Cut off verbose output near the start of the given year(s).
-By default,
-the program cuts off verbose output near the starts of the years -500 and 2500.
-.El
-.Sh LIMITATIONS
-The
-.Fl v
-option may not be used on systems with floating-point time_t values
-that are neither float nor double.
-.Pp
-Time discontinuities are found by sampling the results returned by localtime
-at twelve-hour intervals.
-This works in all real-world cases;
-one can construct artificial time zones for which this fails.
-.Sh "SEE ALSO"
-.Xr ctime 3 ,
-.Xr tzfile 5 ,
-.Xr zic 8
diff --git a/contrib/tzcode/zic/zdump.c b/contrib/tzcode/zic/zdump.c
deleted file mode 100644
index 01111fec2c99..000000000000
--- a/contrib/tzcode/zic/zdump.c
+++ /dev/null
@@ -1,668 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 2009-05-17 by Arthur David Olson.
-*/
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-static char elsieid[] = "@(#)zdump.c 8.10";
-#endif /* not lint */
-
-/*
-** This code has been made independent of the rest of the time
-** conversion package to increase confidence in the verification it provides.
-** You can use this code to help in verifying other implementations.
-*/
-
-#include <err.h>
-#include <stdio.h> /* for stdout, stderr */
-#include <stdlib.h> /* for exit, malloc, atoi */
-#include <string.h> /* for strcpy */
-#include <sys/types.h> /* for time_t */
-#include <time.h> /* for struct tm */
-#include <unistd.h>
-#include <float.h> /* for FLT_MAX and DBL_MAX */
-#include <ctype.h> /* for isalpha et al. */
-#ifndef isascii
-#define isascii(x) 1
-#endif /* !defined isascii */
-
-#ifndef ZDUMP_LO_YEAR
-#define ZDUMP_LO_YEAR (-500)
-#endif /* !defined ZDUMP_LO_YEAR */
-
-#ifndef ZDUMP_HI_YEAR
-#define ZDUMP_HI_YEAR 2500
-#endif /* !defined ZDUMP_HI_YEAR */
-
-#ifndef MAX_STRING_LENGTH
-#define MAX_STRING_LENGTH 1024
-#endif /* !defined MAX_STRING_LENGTH */
-
-#ifndef TRUE
-#define TRUE 1
-#endif /* !defined TRUE */
-
-#ifndef FALSE
-#define FALSE 0
-#endif /* !defined FALSE */
-
-#ifndef EXIT_SUCCESS
-#define EXIT_SUCCESS 0
-#endif /* !defined EXIT_SUCCESS */
-
-#ifndef EXIT_FAILURE
-#define EXIT_FAILURE 1
-#endif /* !defined EXIT_FAILURE */
-
-#ifndef SECSPERMIN
-#define SECSPERMIN 60
-#endif /* !defined SECSPERMIN */
-
-#ifndef MINSPERHOUR
-#define MINSPERHOUR 60
-#endif /* !defined MINSPERHOUR */
-
-#ifndef SECSPERHOUR
-#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
-#endif /* !defined SECSPERHOUR */
-
-#ifndef HOURSPERDAY
-#define HOURSPERDAY 24
-#endif /* !defined HOURSPERDAY */
-
-#ifndef EPOCH_YEAR
-#define EPOCH_YEAR 1970
-#endif /* !defined EPOCH_YEAR */
-
-#ifndef TM_YEAR_BASE
-#define TM_YEAR_BASE 1900
-#endif /* !defined TM_YEAR_BASE */
-
-#ifndef DAYSPERNYEAR
-#define DAYSPERNYEAR 365
-#endif /* !defined DAYSPERNYEAR */
-
-#ifndef isleap
-#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
-#endif /* !defined isleap */
-
-#ifndef isleap_sum
-/*
-** See tzfile.h for details on isleap_sum.
-*/
-#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
-#endif /* !defined isleap_sum */
-
-#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
-#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
-#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
-
-#ifndef HAVE_GETTEXT
-#define HAVE_GETTEXT 0
-#endif
-#if HAVE_GETTEXT
-#include "locale.h" /* for setlocale */
-#include "libintl.h"
-#endif /* HAVE_GETTEXT */
-
-#ifndef GNUC_or_lint
-#ifdef lint
-#define GNUC_or_lint
-#else /* !defined 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)
-#else /* !defined 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 */
-
-extern char ** environ;
-extern char * tzname[2];
-
-static time_t absolute_min_time;
-static time_t absolute_max_time;
-static size_t longest;
-static char * progname;
-static int warned;
-
-static void usage(FILE *stream, int status);
-static char * abbr(struct tm * tmp);
-static void abbrok(const char * abbrp, const char * zone);
-static long delta(struct tm * newp, struct tm * oldp);
-static void dumptime(const struct tm * tmp);
-static time_t hunt(char * name, time_t lot, time_t hit);
-static void setabsolutes(void);
-static void show(char * zone, time_t t, int v);
-static const char * tformat(void);
-static time_t yeartot(long y);
-
-#ifndef TYPECHECK
-#define my_localtime localtime
-#else /* !defined TYPECHECK */
-static struct tm *
-my_localtime(tp)
-time_t * tp;
-{
- register struct tm * tmp;
-
- tmp = localtime(tp);
- if (tp != NULL && tmp != NULL) {
- struct tm tm;
- register time_t t;
-
- tm = *tmp;
- t = mktime(&tm);
- if (t - *tp >= 1 || *tp - t >= 1) {
- (void) fflush(stdout);
- (void) fprintf(stderr, "\n%s: ", progname);
- (void) fprintf(stderr, tformat(), *tp);
- (void) fprintf(stderr, " ->");
- (void) fprintf(stderr, " year=%d", tmp->tm_year);
- (void) fprintf(stderr, " mon=%d", tmp->tm_mon);
- (void) fprintf(stderr, " mday=%d", tmp->tm_mday);
- (void) fprintf(stderr, " hour=%d", tmp->tm_hour);
- (void) fprintf(stderr, " min=%d", tmp->tm_min);
- (void) fprintf(stderr, " sec=%d", tmp->tm_sec);
- (void) fprintf(stderr, " isdst=%d", tmp->tm_isdst);
- (void) fprintf(stderr, " -> ");
- (void) fprintf(stderr, tformat(), t);
- (void) fprintf(stderr, "\n");
- }
- }
- return tmp;
-}
-#endif /* !defined TYPECHECK */
-
-static void
-abbrok(abbrp, zone)
-const char * const abbrp;
-const char * const zone;
-{
- register const char * cp;
- register char * wp;
-
- if (warned)
- return;
- cp = abbrp;
- wp = NULL;
- while (isascii((unsigned char) *cp) &&
- (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
- ++cp;
- if (cp - abbrp < 3)
- wp = _("has fewer than 3 characters");
- else if (cp - abbrp > 6)
- wp = _("has more than 6 characters");
- else if (*cp)
- wp = "has characters other than ASCII alphanumerics, '-' or '+'";
- else
- return;
- (void) fflush(stdout);
- (void) fprintf(stderr,
- _("%s: warning: zone \"%s\" abbreviation \"%s\" %s\n"),
- progname, zone, abbrp, wp);
- warned = TRUE;
-}
-
-int
-main(argc, argv)
-int argc;
-char * argv[];
-{
- register int i;
- register int c;
- register int vflag;
- register char * cutarg;
- register long cutloyear = ZDUMP_LO_YEAR;
- register long cuthiyear = ZDUMP_HI_YEAR;
- register time_t cutlotime;
- register time_t cuthitime;
- register char ** fakeenv;
- time_t now;
- time_t t;
- time_t newt;
- struct tm tm;
- struct tm newtm;
- register struct tm * tmp;
- register struct tm * newtmp;
-
- progname=argv[0];
- INITIALIZE(cutlotime);
- INITIALIZE(cuthitime);
-#if HAVE_GETTEXT
- (void) setlocale(LC_MESSAGES, "");
-#ifdef TZ_DOMAINDIR
- (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* TEXTDOMAINDIR */
- (void) textdomain(TZ_DOMAIN);
-#endif /* HAVE_GETTEXT */
- for (i = 1; i < argc; ++i)
- if (strcmp(argv[i], "--version") == 0) {
- errx(EXIT_SUCCESS, "%s", elsieid);
- } else if (strcmp(argv[i], "--help") == 0) {
- usage(stdout, EXIT_SUCCESS);
- }
- vflag = 0;
- cutarg = NULL;
- while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
- if (c == 'v')
- vflag = 1;
- else cutarg = optarg;
- if ((c != -1) ||
- (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
- usage(stderr, EXIT_FAILURE);
- }
- if (vflag) {
- if (cutarg != NULL) {
- long lo;
- long hi;
- char dummy;
-
- if (sscanf(cutarg, "%ld%c", &hi, &dummy) == 1) {
- cuthiyear = hi;
- } else if (sscanf(cutarg, "%ld,%ld%c",
- &lo, &hi, &dummy) == 2) {
- cutloyear = lo;
- cuthiyear = hi;
- } else {
-(void) fprintf(stderr, _("%s: wild -c argument %s\n"),
- progname, cutarg);
- exit(EXIT_FAILURE);
- }
- }
- setabsolutes();
- cutlotime = yeartot(cutloyear);
- cuthitime = yeartot(cuthiyear);
- }
- (void) time(&now);
- longest = 0;
- for (i = optind; i < argc; ++i)
- if (strlen(argv[i]) > longest)
- longest = strlen(argv[i]);
- {
- register int from;
- register int to;
-
- for (i = 0; environ[i] != NULL; ++i)
- continue;
- fakeenv = (char **) malloc((size_t) ((i + 2) *
- sizeof *fakeenv));
- if (fakeenv == NULL ||
- (fakeenv[0] = (char *) malloc((size_t) (longest +
- 4))) == NULL)
- errx(EXIT_FAILURE,
- _("malloc() failed"));
- to = 0;
- (void) strcpy(fakeenv[to++], "TZ=");
- for (from = 0; environ[from] != NULL; ++from)
- if (strncmp(environ[from], "TZ=", 3) != 0)
- fakeenv[to++] = environ[from];
- fakeenv[to] = NULL;
- environ = fakeenv;
- }
- for (i = optind; i < argc; ++i) {
- static char buf[MAX_STRING_LENGTH];
-
- (void) strcpy(&fakeenv[0][3], argv[i]);
- if (!vflag) {
- show(argv[i], now, FALSE);
- continue;
- }
- warned = FALSE;
- t = absolute_min_time;
- show(argv[i], t, TRUE);
- t += SECSPERHOUR * HOURSPERDAY;
- show(argv[i], t, TRUE);
- if (t < cutlotime)
- t = cutlotime;
- tmp = my_localtime(&t);
- if (tmp != NULL) {
- tm = *tmp;
- (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
- }
- for ( ; ; ) {
- if (t >= cuthitime || t >= cuthitime - SECSPERHOUR * 12)
- break;
- newt = t + SECSPERHOUR * 12;
- newtmp = localtime(&newt);
- if (newtmp != NULL)
- newtm = *newtmp;
- if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) :
- (delta(&newtm, &tm) != (newt - t) ||
- newtm.tm_isdst != tm.tm_isdst ||
- strcmp(abbr(&newtm), buf) != 0)) {
- newt = hunt(argv[i], t, newt);
- newtmp = localtime(&newt);
- if (newtmp != NULL) {
- newtm = *newtmp;
- (void) strncpy(buf,
- abbr(&newtm),
- (sizeof buf) - 1);
- }
- }
- t = newt;
- tm = newtm;
- tmp = newtmp;
- }
- t = absolute_max_time;
- t -= SECSPERHOUR * HOURSPERDAY;
- show(argv[i], t, TRUE);
- t += SECSPERHOUR * HOURSPERDAY;
- show(argv[i], t, TRUE);
- }
- if (fflush(stdout) || ferror(stdout))
- errx(EXIT_FAILURE, _("error writing standard output"));
- exit(EXIT_SUCCESS);
- /* If exit fails to exit... */
- return(EXIT_FAILURE);
-}
-
-static void
-setabsolutes(void)
-{
- if (0.5 == (time_t) 0.5) {
- /*
- ** time_t is floating.
- */
- if (sizeof (time_t) == sizeof (float)) {
- absolute_min_time = (time_t) -FLT_MAX;
- absolute_max_time = (time_t) FLT_MAX;
- } else if (sizeof (time_t) == sizeof (double)) {
- absolute_min_time = (time_t) -DBL_MAX;
- absolute_max_time = (time_t) DBL_MAX;
- } else {
- (void) fprintf(stderr,
-_("%s: use of -v on system with floating time_t other than float or double\n"),
- progname);
- exit(EXIT_FAILURE);
- }
- } else if (0 > (time_t) -1) {
- /*
- ** time_t is signed. Assume overflow wraps around.
- */
- time_t t = 0;
- time_t t1 = 1;
-
- while (t < t1) {
- t = t1;
- t1 = 2 * t1 + 1;
- }
-
- absolute_max_time = t;
- t = -t;
- absolute_min_time = t - 1;
- if (t < absolute_min_time)
- absolute_min_time = t;
- } else {
- /*
- ** time_t is unsigned.
- */
- absolute_min_time = 0;
- absolute_max_time = absolute_min_time - 1;
- }
-}
-
-static time_t
-yeartot(y)
-const long y;
-{
- register long myy;
- register long seconds;
- register time_t t;
-
- myy = EPOCH_YEAR;
- t = 0;
- while (myy != y) {
- if (myy < y) {
- seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
- ++myy;
- if (t > absolute_max_time - seconds) {
- t = absolute_max_time;
- break;
- }
- t += seconds;
- } else {
- --myy;
- seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
- if (t < absolute_min_time + seconds) {
- t = absolute_min_time;
- break;
- }
- t -= seconds;
- }
- }
- return t;
-}
-
-static void
-usage(FILE *stream, int status)
-{
- fprintf(stream,
-_("usage: %s [--version] [-v] [--help] [-c [loyear,]hiyear] zonename ...\n\
-\n\
-Report bugs to tz@elsie.nci.nih.gov.\n"), progname);
- exit(status);
-}
-
-static time_t
-hunt(char *name, time_t lot, time_t hit)
-{
- time_t t;
- long diff;
- struct tm lotm;
- register struct tm * lotmp;
- struct tm tm;
- register struct tm * tmp;
- char loab[MAX_STRING_LENGTH];
-
- lotmp = my_localtime(&lot);
- if (lotmp != NULL) {
- lotm = *lotmp;
- (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
- }
- for ( ; ; ) {
- diff = (long) (hit - lot);
- if (diff < 2)
- break;
- t = lot;
- t += diff / 2;
- if (t <= lot)
- ++t;
- else if (t >= hit)
- --t;
- tmp = my_localtime(&t);
- if (tmp != NULL)
- tm = *tmp;
- if ((lotmp == NULL || tmp == NULL) ? (lotmp == tmp) :
- (delta(&tm, &lotm) == (t - lot) &&
- tm.tm_isdst == lotm.tm_isdst &&
- strcmp(abbr(&tm), loab) == 0)) {
- lot = t;
- lotm = tm;
- lotmp = tmp;
- } else hit = t;
- }
- show(name, lot, TRUE);
- show(name, hit, TRUE);
- return hit;
-}
-
-/*
-** Thanks to Paul Eggert for logic used in delta.
-*/
-
-static long
-delta(newp, oldp)
-struct tm * newp;
-struct tm * oldp;
-{
- register long result;
- register int tmy;
-
- if (newp->tm_year < oldp->tm_year)
- return -delta(oldp, newp);
- result = 0;
- for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
- result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
- result += newp->tm_yday - oldp->tm_yday;
- result *= HOURSPERDAY;
- result += newp->tm_hour - oldp->tm_hour;
- result *= MINSPERHOUR;
- result += newp->tm_min - oldp->tm_min;
- result *= SECSPERMIN;
- result += newp->tm_sec - oldp->tm_sec;
- return result;
-}
-
-static void
-show(char *zone, time_t t, int v)
-{
- register struct tm * tmp;
-
- (void) printf("%-*s ", (int) longest, zone);
- if (v) {
- tmp = gmtime(&t);
- if (tmp == NULL) {
- (void) printf(tformat(), t);
- } else {
- dumptime(tmp);
- (void) printf(" UTC");
- }
- (void) printf(" = ");
- }
- tmp = my_localtime(&t);
- dumptime(tmp);
- if (tmp != NULL) {
- if (*abbr(tmp) != '\0')
- (void) printf(" %s", abbr(tmp));
- if (v) {
- (void) printf(" isdst=%d", tmp->tm_isdst);
-#ifdef TM_GMTOFF
- (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
-#endif /* defined TM_GMTOFF */
- }
- }
- (void) printf("\n");
- if (tmp != NULL && *abbr(tmp) != '\0')
- abbrok(abbr(tmp), zone);
-}
-
-static char *
-abbr(tmp)
-struct tm * tmp;
-{
- register char * result;
- static char nada;
-
- if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
- return &nada;
- result = tzname[tmp->tm_isdst];
- return (result == NULL) ? &nada : result;
-}
-
-/*
-** The code below can fail on certain theoretical systems;
-** it works on all known real-world systems as of 2004-12-30.
-*/
-
-static const char *
-tformat(void)
-{
- if (0.5 == (time_t) 0.5) { /* floating */
- if (sizeof (time_t) > sizeof (double))
- return "%Lg";
- return "%g";
- }
- if (0 > (time_t) -1) { /* signed */
- if (sizeof (time_t) > sizeof (long))
- return "%lld";
- if (sizeof (time_t) > sizeof (int))
- return "%ld";
- return "%d";
- }
- if (sizeof (time_t) > sizeof (unsigned long))
- return "%llu";
- if (sizeof (time_t) > sizeof (unsigned int))
- return "%lu";
- return "%u";
-}
-
-static void
-dumptime(timeptr)
-register const struct tm * timeptr;
-{
- static const char wday_name[][3] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
- };
- static const char mon_name[][3] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
- register const char * wn;
- register const char * mn;
- register int lead;
- register int trail;
-
- if (timeptr == NULL) {
- (void) printf("NULL");
- return;
- }
- /*
- ** The packaged versions of localtime and gmtime never put out-of-range
- ** values in tm_wday or tm_mon, but since this code might be compiled
- ** with other (perhaps experimental) versions, paranoia is in order.
- */
- if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
- (int) (sizeof wday_name / sizeof wday_name[0]))
- wn = "???";
- else wn = wday_name[timeptr->tm_wday];
- if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
- (int) (sizeof mon_name / sizeof mon_name[0]))
- mn = "???";
- else mn = mon_name[timeptr->tm_mon];
- (void) printf("%.3s %.3s%3d %.2d:%.2d:%.2d ",
- wn, mn,
- timeptr->tm_mday, timeptr->tm_hour,
- timeptr->tm_min, timeptr->tm_sec);
-#define DIVISOR 10
- trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
- lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
- trail / DIVISOR;
- trail %= DIVISOR;
- if (trail < 0 && lead > 0) {
- trail += DIVISOR;
- --lead;
- } else if (lead < 0 && trail > 0) {
- trail -= DIVISOR;
- ++lead;
- }
- if (lead == 0)
- (void) printf("%d", trail);
- else (void) printf("%d%d", lead, ((trail < 0) ? -trail : trail));
-}
diff --git a/contrib/tzcode/zic/zdump/Makefile b/contrib/tzcode/zic/zdump/Makefile
deleted file mode 100644
index 7ee8db324927..000000000000
--- a/contrib/tzcode/zic/zdump/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${.CURDIR}/..
-
-PROG= zdump
-MAN= zdump.8
-SRCS= zdump.c ialloc.c scheck.c
-
-CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
-CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
-CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
-
-WARNS?= 2
-
-.include <bsd.prog.mk>
diff --git a/contrib/tzcode/zic/zic.8 b/contrib/tzcode/zic/zic.8
deleted file mode 100644
index aa191314bfb6..000000000000
--- a/contrib/tzcode/zic/zic.8
+++ /dev/null
@@ -1,468 +0,0 @@
-.\" $FreeBSD$
-.Dd June 20, 2004
-.Dt ZIC 8
-.Os
-.Sh NAME
-.Nm zic
-.Nd timezone compiler
-.Sh SYNOPSIS
-.Nm
-.Op Fl -version
-.Op Fl Dsv
-.Op Fl d Ar directory
-.Op Fl g Ar group
-.Op Fl L Ar leapsecondfilename
-.Op Fl l Ar localtime
-.Op Fl m Ar mode
-.Op Fl p Ar posixrules
-.Op Fl u Ar user
-.Op Fl y Ar command
-.Op Ar filename ...
-.Sh DESCRIPTION
-The
-.Nm
-utility reads text from the file(s) named on the command line
-and creates the time conversion information files specified in this input.
-If a
-.Ar filename
-is
-.Em - ,
-the standard input is read.
-.Pp
-The following options are available:
-.Bl -tag -width indent
-.It Fl -version
-Output version information and exit.
-.It Fl D
-Do not automatically create directories.
-If the input file(s) specify
-an output file in a directory which does not already exist, the
-default behavior is to attempt to create the directory.
-If
-.Fl D
-is specified,
-.Nm
-will instead error out immediately.
-.It Fl d Ar directory
-Create time conversion information files in the named directory rather than
-in the standard directory named below.
-.It Fl g Ar group
-After creating each output file, change its group ownership to the
-specified
-.Ar group
-(which can be either a name or a numeric group ID).
-.It Fl L Ar leapsecondfilename
-Read leap second information from the file with the given name.
-If this option is not used,
-no leap second information appears in output files.
-.It Fl l Ar timezone
-Use the given
-.Ar time zone
-as local time.
-The
-.Nm
-utility will act as if the input contained a link line of the form
-.Pp
-.D1 No "Link timezone localtime"
-.Pp
-(Note that this action has no effect on
-.Fx ,
-since the local time zone is specified in
-.Pa /etc/localtime
-and not
-.Pa /usr/share/zoneinfo/localtime . )
-.It Fl m Ar mode
-After creating each output file, change its access mode to
-.Ar mode .
-Both numeric and alphabetic modes are accepted
-(see
-.Xr chmod 1 ) .
-.It Fl p Ar timezone
-Use the given
-.Ar "time zone" Ns 's
-rules when handling POSIX-format
-time zone environment variables.
-The
-.Nm
-utility will act as if the input contained a link line of the form
-.Pp
-.D1 No "Link timezone posixrules"
-.It Fl u Ar user
-After creating each output file, change its owner to
-.Ar user
-(which can be either a name or a numeric user ID).
-.It Fl v
-Complain if a year that appears in a data file is outside the range
-of years representable by
-.Xr time 3
-values.
-.It Fl s
-Limit time values stored in output files to values that are the same
-whether they are taken to be signed or unsigned.
-You can use this option to generate SVVS-compatible files.
-.It Fl y Ar command
-Use the given
-.Ar command
-rather than
-.Em yearistype
-when checking year types (see below).
-.El
-.Pp
-Input lines are made up of fields.
-Fields are separated from one another by any number of white space characters.
-Leading and trailing white space on input lines is ignored.
-An unquoted sharp character (#) in the input introduces a comment which extends
-to the end of the line the sharp character appears on.
-White space characters and sharp characters may be enclosed in double quotes
-(") if they are to be used as part of a field.
-Any line that is blank (after comment stripping) is ignored.
-Non-blank lines are expected to be of one of three types:
-rule lines, zone lines, and link lines.
-.Pp
-Names (such as month names) must be in English and are case insensitive.
-Abbreviations, if used, must be unambiguous in context.
-.Pp
-A rule line has the form:
-.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S"
-For example:
-.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D"
-.Pp
-The fields that make up a rule line are:
-.Bl -tag -width "LETTER/S" -offset indent
-.It NAME
-Give the (arbitrary) name of the set of rules this rule is part of.
-.It FROM
-Give the first year in which the rule applies.
-Any integer year can be supplied; the Gregorian calendar is assumed.
-The word
-.Em minimum
-(or an abbreviation) means the minimum year representable as an integer.
-The word
-.Em maximum
-(or an abbreviation) means the maximum year representable as an integer.
-Rules can describe times that are not representable as time values,
-with the unrepresentable times ignored; this allows rules to be portable
-among hosts with differing time value types.
-.It TO
-Give the final year in which the rule applies.
-In addition to
-.Em minimum
-and
-.Em maximum
-(as above),
-the word
-.Em only
-(or an abbreviation)
-may be used to repeat the value of the
-.Em FROM
-field.
-.It TYPE
-Give the type of year in which the rule applies.
-If
-.Em TYPE
-is
-.Em \-
-then the rule applies in all years between
-.Em FROM
-and
-.Em TO
-inclusive.
-If
-.Em TYPE
-is something else, then
-.Nm
-executes the command
-.Li yearistype Ar year Ar type
-to check the type of a year:
-an exit status of zero is taken to mean that the year is of the given type;
-an exit status of one is taken to mean that the year is not of the given type.
-.It IN
-Name the month in which the rule takes effect.
-Month names may be abbreviated.
-.It ON
-Give the day on which the rule takes effect.
-Recognized forms include:
-.Pp
-.Bl -tag -width lastSun -compact -offset indent
-.It \&5
-the fifth of the month
-.It lastSun
-the last Sunday in the month
-.It lastMon
-the last Monday in the month
-.It Sun>=8
-first Sunday on or after the eighth
-.It Sun<=25
-last Sunday on or before the 25th
-.El
-.Pp
-Names of days of the week may be abbreviated or spelled out in full.
-Note that there must be no spaces within the
-.Em ON
-field.
-.It AT
-Give the time of day at which the rule takes effect.
-Recognized forms include:
-.Pp
-.Bl -tag -width "\&1:28:14" -offset indent -compact
-.It 2
-time in hours
-.It 2:00
-time in hours and minutes
-.It 15:00
-24-hour format time (for times after noon)
-.It 1:28:14
-time in hours, minutes, and seconds
-.El
-.Pp
-where hour 0 is midnight at the start of the day,
-and hour 24 is midnight at the end of the day.
-Any of these forms may be followed by the letter
-.Sq Li w
-if the given time is local
-.Dq "wall clock"
-time,
-.Sq Li s
-if the given time is local
-.Dq standard
-time, or
-.Sq Li u
-(or
-.Sq Li g
-or
-.Sq Li z )
-if the given time is universal time;
-in the absence of an indicator,
-wall clock time is assumed.
-.It SAVE
-Give the amount of time to be added to local standard time when the rule is in
-effect.
-This field has the same format as the
-.Em AT
-field
-(although, of course, the
-.Sq Li w
-and
-.Sq Li s
-suffixes are not used).
-.It LETTER/S
-Give the
-.Dq "variable part"
-(for example, the
-.Dq S
-or
-.Dq D
-in
-.Dq EST
-or
-.Dq EDT )
-of time zone abbreviations to be used when this rule is in effect.
-If this field is
-.Em \- ,
-the variable part is null.
-.El
-.Pp
-A zone line has the form:
-.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTILYEAR [MONTH [DAY [TIME]]]]"
-For example:
-.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00"
-The fields that make up a zone line are:
-.Bl -tag -width indent
-.It NAME
-The name of the time zone.
-This is the name used in creating the time conversion information file for the
-zone.
-.It GMTOFF
-The amount of time to add to UTC to get standard time in this zone.
-This field has the same format as the
-.Em AT
-and
-.Em SAVE
-fields of rule lines;
-begin the field with a minus sign if time must be subtracted from UTC.
-.It RULES/SAVE
-The name of the rule(s) that apply in the time zone or,
-alternately, an amount of time to add to local standard time.
-If this field is
-.Em \-
-then standard time always applies in the time zone.
-.It FORMAT
-The format for time zone abbreviations in this time zone.
-The pair of characters
-.Em %s
-is used to show where the
-.Dq "variable part"
-of the time zone abbreviation goes.
-Alternately,
-a slash (/)
-separates standard and daylight abbreviations.
-.It UNTILYEAR [MONTH [DAY [TIME]]]
-The time at which the UTC offset or the rule(s) change for a location.
-It is specified as a year, a month, a day, and a time of day.
-If this is specified,
-the time zone information is generated from the given UTC offset
-and rule change until the time specified.
-The month, day, and time of day have the same format as the IN, ON, and AT
-fields of a rule; trailing fields can be omitted, and default to the
-earliest possible value for the missing fields.
-.Pp
-The next line must be a
-.Dq continuation
-line; this has the same form as a zone line except that the
-string
-.Dq Zone
-and the name are omitted, as the continuation line will
-place information starting at the time specified as the
-.Em until
-information in the previous line in the file used by the previous line.
-Continuation lines may contain
-.Em until
-information, just as zone lines do, indicating that the next line is a further
-continuation.
-.El
-.Pp
-A link line has the form
-.Dl "Link LINK-FROM LINK-TO"
-For example:
-.Dl "Link Europe/Istanbul Asia/Istanbul"
-The
-.Em LINK-FROM
-field should appear as the
-.Em NAME
-field in some zone line;
-the
-.Em LINK-TO
-field is used as an alternate name for that zone.
-.Pp
-Except for continuation lines,
-lines may appear in any order in the input.
-.Pp
-Lines in the file that describes leap seconds have the following form:
-.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S"
-For example:
-.Dl "Leap 1974 Dec 31 23:59:60 + S"
-The
-.Em YEAR ,
-.Em MONTH ,
-.Em DAY ,
-and
-.Em HH:MM:SS
-fields tell when the leap second happened.
-The
-.Em CORR
-field
-should be
-.Dq +
-if a second was added
-or
-.Dq -
-if a second was skipped.
-.\" There's no need to document the following, since it's impossible for more
-.\" than one leap second to be inserted or deleted at a time.
-.\" The C Standard is in error in suggesting the possibility.
-.\" See Terry J Quinn, The BIPM and the accurate measure of time,
-.\" Proc IEEE 79, 7 (July 1991), 894-905.
-.\" or
-.\" .q ++
-.\" if two seconds were added
-.\" or
-.\" .q --
-.\" if two seconds were skipped.
-The
-.Em R/S
-field
-should be (an abbreviation of)
-.Dq Stationary
-if the leap second time given by the other fields should be interpreted as UTC
-or
-(an abbreviation of)
-.Dq Rolling
-if the leap second time given by the other fields should be interpreted as
-local wall clock time.
-.Sh "EXTENDED EXAMPLE"
-Here is an extended example of
-.Nm
-input, intended to illustrate many of its features.
-.br
-.ne 22
-.nf
-.in +2m
-.ta \w'# Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
-.sp
-# Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
-Rule Swiss 1940 only - Nov 2 0:00 1:00 S
-Rule Swiss 1940 only - Dec 31 0:00 0 -
-Rule Swiss 1941 1942 - May Sun>=1 2:00 1:00 S
-Rule Swiss 1941 1942 - Oct Sun>=1 0:00 0
-.sp .5
-Rule EU 1977 1980 - Apr Sun>=1 1:00u 1:00 S
-Rule EU 1977 only - Sep lastSun 1:00u 0 -
-Rule EU 1978 only - Oct 1 1:00u 0 -
-Rule EU 1979 1995 - Sep lastSun 1:00u 0 -
-Rule EU 1981 max - Mar lastSun 1:00u 1:00 S
-Rule EU 1996 max - Oct lastSun 1:00u 0 -
-.sp
-.ta \w'# Zone\0\0'u +\w'Europe/Zurich\0\0'u +\w'0:34:08\0\0'u +\w'RULES/SAVE\0\0'u +\w'FORMAT\0\0'u
-# Zone NAME GMTOFF RULES FORMAT UNTIL
-Zone Europe/Zurich 0:34:08 - LMT 1848 Sep 12
- 0:29:44 - BMT 1894 Jun
- 1:00 Swiss CE%sT 1981
- 1:00 EU CE%sT
-.sp
-Link Europe/Zurich Switzerland
-.sp
-.in
-.fi
-In this example, the zone is named Europe/Zurich but it has an alias
-as Switzerland.
-Zurich was 34 minutes and 8 seconds west of GMT until 1848-09-12
-at 00:00, when the offset changed to 29 minutes and 44 seconds.
-After 1894-06-01 at 00:00 Swiss daylight saving rules (defined with
-lines beginning with "Rule Swiss") apply, and the GMT offset became
-one hour.
-From 1981 to the present, EU daylight saving rules have applied,
-and the UTC offset has remained at one hour.
-.Pp
-In 1940, daylight saving time applied from November 2 at 00:00 to
-December 31 at 00:00.
-In 1941 and 1942, daylight saving time applied from the first Sunday
-in May at 02:00 to the first Sunday in October at 00:00.
-The pre-1981 EU daylight-saving rules have no effect here, but are
-included for completeness.
-Since 1981, daylight saving has begun on the last Sunday in March
-at 01:00 UTC.
-Until 1995 it ended the last Sunday in September at 01:00 UTC, but
-this changed to the last Sunday in October starting in 1996.
-.Pp
-For purposes of display, "LMT" and "BMT" were initially used,
-respectively.
-Since Swiss rules and later EU rules were applied, the display name
-for the timezone has been CET for standard time and CEST for daylight
-saving time.
-.Sh NOTES
-For areas with more than two types of local time,
-you may need to use local standard time in the
-.Em AT
-field of the earliest transition time's rule to ensure that
-the earliest transition time recorded in the compiled file is correct.
-.Pp
-If, for a particular zone, a clock advance caused by the start of
-daylight saving coincides with and is equal to a clock retreat
-caused by a change in UTC offset,
-.Nm
-produces a single transition to daylight saving at the new UTC offset
-(without any change in wall clock time).
-To get separate transitions use multiple zone continuation lines
-specifying transition instants using universal time.
-.Sh FILES
-.Bl -tag -width /usr/share/zoneinfo -compact
-.It /usr/share/zoneinfo
-standard directory used for created files
-.El
-.Sh "SEE ALSO"
-.Xr ctime 3 ,
-.Xr tzfile 5 ,
-.Xr zdump 8
-.\" @(#)zic.8 8.6
-.\" This file is in the public domain, so clarified as of
-.\" 2009-05-17 by Arthur David Olson.
diff --git a/contrib/tzcode/zic/zic.c b/contrib/tzcode/zic/zic.c
deleted file mode 100644
index 00043b7940e7..000000000000
--- a/contrib/tzcode/zic/zic.c
+++ /dev/null
@@ -1,2756 +0,0 @@
-/*
-** This file is in the public domain, so clarified as of
-** 2006-07-17 by Arthur David Olson.
-*/
-
-static const char elsieid[] = "@(#)zic.c 8.22";
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
-
-#include "private.h"
-#include "tzfile.h"
-#include <err.h>
-#include <locale.h>
-#include <sys/stat.h> /* for umask manifest constants */
-#include <sys/types.h>
-#include <unistd.h>
-
-#define ZIC_VERSION '2'
-
-typedef int_fast64_t zic_t;
-
-#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
-#define ZIC_MAX_ABBR_LEN_WO_WARN 6
-#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
-
-#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
-
-/*
-** On some ancient hosts, predicates like `isspace(C)' are defined
-** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
-** which says they are defined only if C == ((unsigned char) C) || C == EOF.
-** Neither the C Standard nor POSIX require that `isascii' exist.
-** For portability, we check both ancient and modern requirements.
-** If isascii is not defined, the isascii check succeeds trivially.
-*/
-#include "ctype.h"
-#ifndef isascii
-#define isascii(x) 1
-#endif
-
-#define OFFSET_STRLEN_MAXIMUM (7 + INT_STRLEN_MAXIMUM(long))
-#define RULE_STRLEN_MAXIMUM 8 /* "Mdd.dd.d" */
-
-#define end(cp) (strchr((cp), '\0'))
-
-struct rule {
- const char * r_filename;
- int r_linenum;
- const char * r_name;
-
- int r_loyear; /* for example, 1986 */
- int r_hiyear; /* for example, 1986 */
- const char * r_yrtype;
- int r_lowasnum;
- int r_hiwasnum;
-
- int r_month; /* 0..11 */
-
- int r_dycode; /* see below */
- int r_dayofmonth;
- int r_wday;
-
- long r_tod; /* time from midnight */
- int r_todisstd; /* above is standard time if TRUE */
- /* or wall clock time if FALSE */
- int r_todisgmt; /* above is GMT if TRUE */
- /* or local time if FALSE */
- long r_stdoff; /* offset from standard time */
- const char * r_abbrvar; /* variable part of abbreviation */
-
- int r_todo; /* a rule to do (used in outzone) */
- zic_t r_temp; /* used in outzone */
-};
-
-/*
-** r_dycode r_dayofmonth r_wday
-*/
-
-#define DC_DOM 0 /* 1..31 */ /* unused */
-#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
-#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
-
-struct zone {
- const char * z_filename;
- int z_linenum;
-
- const char * z_name;
- long z_gmtoff;
- const char * z_rule;
- const char * z_format;
-
- long z_stdoff;
-
- struct rule * z_rules;
- int z_nrules;
-
- struct rule z_untilrule;
- zic_t z_untiltime;
-};
-
-static void addtt(zic_t starttime, int type);
-static int addtype(long gmtoff, const char * abbr, int isdst,
- int ttisstd, int ttisgmt);
-static void leapadd(zic_t t, int positive, int rolling, int count);
-static void adjleap(void);
-static void associate(void);
-static int ciequal(const char * ap, const char * bp);
-static void convert(long val, char * buf);
-static void convert64(zic_t val, char * buf);
-static void dolink(const char * fromfield, const char * tofield);
-static void doabbr(char * abbr, const char * format,
- const char * letters, int isdst, int doquotes);
-static void eat(const char * name, int num);
-static void eats(const char * name, int num,
- const char * rname, int rnum);
-static long eitol(int i);
-static void error(const char * message);
-static char ** getfields(char * buf);
-static long gethms(const char * string, const char * errstrng,
- int signable);
-static void infile(const char * filename);
-static void inleap(char ** fields, int nfields);
-static void inlink(char ** fields, int nfields);
-static void inrule(char ** fields, int nfields);
-static int inzcont(char ** fields, int nfields);
-static int inzone(char ** fields, int nfields);
-static int inzsub(char ** fields, int nfields, int iscont);
-static int is32(zic_t x);
-static int itsabbr(const char * abbr, const char * word);
-static int itsdir(const char * name);
-static int lowerit(int c);
-static char * memcheck(char * tocheck);
-static int mkdirs(char * filename);
-static void newabbr(const char * abbr);
-static long oadd(long t1, long t2);
-static void outzone(const struct zone * zp, int ntzones);
-static void puttzcode(long code, FILE * fp);
-static void puttzcode64(zic_t code, FILE * fp);
-static int rcomp(const void * leftp, const void * rightp);
-static zic_t rpytime(const struct rule * rp, int wantedy);
-static void rulesub(struct rule * rp,
- const char * loyearp, const char * hiyearp,
- const char * typep, const char * monthp,
- const char * dayp, const char * timep);
-static int stringoffset(char * result, long offset);
-static int stringrule(char * result, const struct rule * rp,
- long dstoff, long gmtoff);
-static void stringzone(char * result,
- const struct zone * zp, int ntzones);
-static void setboundaries(void);
-static void setgroup(gid_t *flag, const char *name);
-static void setuser(uid_t *flag, const char *name);
-static zic_t tadd(zic_t t1, long t2);
-static void usage(FILE *stream, int status);
-static void writezone(const char * name, const char * string);
-static int yearistype(int year, const char * type);
-
-static int charcnt;
-static int errors;
-static const char * filename;
-static int leapcnt;
-static int leapseen;
-static int leapminyear;
-static int leapmaxyear;
-static int linenum;
-static int max_abbrvar_len;
-static int max_format_len;
-static zic_t max_time;
-static int max_year;
-static zic_t min_time;
-static int min_year;
-static zic_t min_time;
-static int noise;
-static const char * rfilename;
-static int rlinenum;
-static int timecnt;
-static int typecnt;
-
-/*
-** Line codes.
-*/
-
-#define LC_RULE 0
-#define LC_ZONE 1
-#define LC_LINK 2
-#define LC_LEAP 3
-
-/*
-** Which fields are which on a Zone line.
-*/
-
-#define ZF_NAME 1
-#define ZF_GMTOFF 2
-#define ZF_RULE 3
-#define ZF_FORMAT 4
-#define ZF_TILYEAR 5
-#define ZF_TILMONTH 6
-#define ZF_TILDAY 7
-#define ZF_TILTIME 8
-#define ZONE_MINFIELDS 5
-#define ZONE_MAXFIELDS 9
-
-/*
-** Which fields are which on a Zone continuation line.
-*/
-
-#define ZFC_GMTOFF 0
-#define ZFC_RULE 1
-#define ZFC_FORMAT 2
-#define ZFC_TILYEAR 3
-#define ZFC_TILMONTH 4
-#define ZFC_TILDAY 5
-#define ZFC_TILTIME 6
-#define ZONEC_MINFIELDS 3
-#define ZONEC_MAXFIELDS 7
-
-/*
-** Which files are which on a Rule line.
-*/
-
-#define RF_NAME 1
-#define RF_LOYEAR 2
-#define RF_HIYEAR 3
-#define RF_COMMAND 4
-#define RF_MONTH 5
-#define RF_DAY 6
-#define RF_TOD 7
-#define RF_STDOFF 8
-#define RF_ABBRVAR 9
-#define RULE_FIELDS 10
-
-/*
-** Which fields are which on a Link line.
-*/
-
-#define LF_FROM 1
-#define LF_TO 2
-#define LINK_FIELDS 3
-
-/*
-** Which fields are which on a Leap line.
-*/
-
-#define LP_YEAR 1
-#define LP_MONTH 2
-#define LP_DAY 3
-#define LP_TIME 4
-#define LP_CORR 5
-#define LP_ROLL 6
-#define LEAP_FIELDS 7
-
-/*
-** Year synonyms.
-*/
-
-#define YR_MINIMUM 0
-#define YR_MAXIMUM 1
-#define YR_ONLY 2
-
-static struct rule * rules;
-static int nrules; /* number of rules */
-
-static struct zone * zones;
-static int nzones; /* number of zones */
-
-struct link {
- const char * l_filename;
- int l_linenum;
- const char * l_from;
- const char * l_to;
-};
-
-static struct link * links;
-static int nlinks;
-
-struct lookup {
- const char * l_word;
- const int l_value;
-};
-
-static struct lookup const * byword(const char * string,
- const struct lookup * lp);
-
-static struct lookup const line_codes[] = {
- { "Rule", LC_RULE },
- { "Zone", LC_ZONE },
- { "Link", LC_LINK },
- { "Leap", LC_LEAP },
- { NULL, 0}
-};
-
-static struct lookup const mon_names[] = {
- { "January", TM_JANUARY },
- { "February", TM_FEBRUARY },
- { "March", TM_MARCH },
- { "April", TM_APRIL },
- { "May", TM_MAY },
- { "June", TM_JUNE },
- { "July", TM_JULY },
- { "August", TM_AUGUST },
- { "September", TM_SEPTEMBER },
- { "October", TM_OCTOBER },
- { "November", TM_NOVEMBER },
- { "December", TM_DECEMBER },
- { NULL, 0 }
-};
-
-static struct lookup const wday_names[] = {
- { "Sunday", TM_SUNDAY },
- { "Monday", TM_MONDAY },
- { "Tuesday", TM_TUESDAY },
- { "Wednesday", TM_WEDNESDAY },
- { "Thursday", TM_THURSDAY },
- { "Friday", TM_FRIDAY },
- { "Saturday", TM_SATURDAY },
- { NULL, 0 }
-};
-
-static struct lookup const lasts[] = {
- { "last-Sunday", TM_SUNDAY },
- { "last-Monday", TM_MONDAY },
- { "last-Tuesday", TM_TUESDAY },
- { "last-Wednesday", TM_WEDNESDAY },
- { "last-Thursday", TM_THURSDAY },
- { "last-Friday", TM_FRIDAY },
- { "last-Saturday", TM_SATURDAY },
- { NULL, 0 }
-};
-
-static struct lookup const begin_years[] = {
- { "minimum", YR_MINIMUM },
- { "maximum", YR_MAXIMUM },
- { NULL, 0 }
-};
-
-static struct lookup const end_years[] = {
- { "minimum", YR_MINIMUM },
- { "maximum", YR_MAXIMUM },
- { "only", YR_ONLY },
- { NULL, 0 }
-};
-
-static struct lookup const leap_types[] = {
- { "Rolling", TRUE },
- { "Stationary", FALSE },
- { NULL, 0 }
-};
-
-static const int len_months[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 len_years[2] = {
- DAYSPERNYEAR, DAYSPERLYEAR
-};
-
-static struct attype {
- zic_t at;
- unsigned char type;
-} attypes[TZ_MAX_TIMES];
-static long gmtoffs[TZ_MAX_TYPES];
-static char isdsts[TZ_MAX_TYPES];
-static unsigned char abbrinds[TZ_MAX_TYPES];
-static char ttisstds[TZ_MAX_TYPES];
-static char ttisgmts[TZ_MAX_TYPES];
-static char chars[TZ_MAX_CHARS];
-static zic_t trans[TZ_MAX_LEAPS];
-static long corr[TZ_MAX_LEAPS];
-static char roll[TZ_MAX_LEAPS];
-
-/*
-** Memory allocation.
-*/
-
-static char *
-memcheck(ptr)
-char * const ptr;
-{
- if (ptr == NULL)
- errx(EXIT_FAILURE, _("memory exhausted"));
- return ptr;
-}
-
-#define emalloc(size) memcheck(imalloc(size))
-#define erealloc(ptr, size) memcheck(irealloc((ptr), (size)))
-#define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
-#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp)))
-
-/*
-** Error handling.
-*/
-
-static void
-eats(name, num, rname, rnum)
-const char * const name;
-const int num;
-const char * const rname;
-const int rnum;
-{
- filename = name;
- linenum = num;
- rfilename = rname;
- rlinenum = rnum;
-}
-
-static void
-eat(name, num)
-const char * const name;
-const int num;
-{
- eats(name, num, (char *) NULL, -1);
-}
-
-static void
-error(string)
-const char * const string;
-{
- /*
- ** Match the format of "cc" to allow sh users to
- ** zic ... 2>&1 | error -t "*" -v
- ** on BSD systems.
- */
- (void) fprintf(stderr, _("\"%s\", line %d: %s"),
- filename, linenum, string);
- if (rfilename != NULL)
- (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
- rfilename, rlinenum);
- (void) fprintf(stderr, "\n");
- ++errors;
-}
-
-static void
-warning(string)
-const char * const string;
-{
- char * cp;
-
- cp = ecpyalloc(_("warning: "));
- cp = ecatalloc(cp, string);
- error(cp);
- ifree(cp);
- --errors;
-}
-
-static void
-usage(FILE *stream, int status)
- {
- (void) fprintf(stream, _("usage is zic \
-[ --version ] [--help] [ -v ] [ -l localtime ] [ -p posixrules ] \\\n\
-\t[ -d directory ] [ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n\
-\n\
-Report bugs to tz@elsie.nci.nih.gov.\n"));
- exit(status);
-}
-
-static const char * psxrules;
-static const char * lcltime;
-static const char * directory;
-static const char * leapsec;
-static const char * yitcommand;
-static int Dflag;
-static uid_t uflag = (uid_t)-1;
-static gid_t gflag = (gid_t)-1;
-static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH
- | S_IWUSR);
-
-int
-main(argc, argv)
-int argc;
-char * argv[];
-{
- register int i;
- register int j;
- register int c;
-
-#ifdef unix
- (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
-#endif /* defined unix */
-#if HAVE_GETTEXT
- (void) setlocale(LC_ALL, "");
-#ifdef TZ_DOMAINDIR
- (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
-#endif /* defined TEXTDOMAINDIR */
- (void) textdomain(TZ_DOMAIN);
-#endif /* HAVE_GETTEXT */
- if (TYPE_BIT(zic_t) < 64) {
- (void) fprintf(stderr, "zic: %s\n",
- _("wild compilation-time specification of zic_t"));
- exit(EXIT_FAILURE);
- }
- for (i = 1; i < argc; ++i)
- if (strcmp(argv[i], "--version") == 0) {
- errx(EXIT_SUCCESS, "%s", elsieid);
- } else if (strcmp(argv[i], "--help") == 0) {
- usage(stdout, EXIT_SUCCESS);
- }
- while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
- switch (c) {
- default:
- usage(stderr, EXIT_FAILURE);
- case 'D':
- Dflag = 1;
- break;
- case 'd':
- if (directory == NULL)
- directory = optarg;
- else
- errx(EXIT_FAILURE,
-_("more than one -d option specified"));
- break;
- case 'g':
- setgroup(&gflag, optarg);
- break;
- case 'l':
- if (lcltime == NULL)
- lcltime = optarg;
- else
- errx(EXIT_FAILURE,
-_("more than one -l option specified"));
- break;
- case 'm':
- {
- void *set = setmode(optarg);
- if (set == NULL)
- errx(EXIT_FAILURE,
-_("invalid file mode"));
- mflag = getmode(set, mflag);
- free(set);
- break;
- }
- case 'p':
- if (psxrules == NULL)
- psxrules = optarg;
- else
- errx(EXIT_FAILURE,
-_("more than one -p option specified"));
- break;
- case 'u':
- setuser(&uflag, optarg);
- break;
- case 'y':
- if (yitcommand == NULL)
- yitcommand = optarg;
- else
- errx(EXIT_FAILURE,
-_("more than one -y option specified"));
- break;
- case 'L':
- if (leapsec == NULL)
- leapsec = optarg;
- else
- errx(EXIT_FAILURE,
-_("more than one -L option specified"));
- break;
- case 'v':
- noise = TRUE;
- break;
- case 's':
- (void) printf("zic: -s ignored\n");
- break;
- }
- if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
- usage(stderr, EXIT_FAILURE); /* usage message by request */
- if (directory == NULL)
- directory = TZDIR;
- if (yitcommand == NULL)
- yitcommand = "yearistype";
-
- setboundaries();
-
- if (optind < argc && leapsec != NULL) {
- infile(leapsec);
- adjleap();
- }
-
- for (i = optind; i < argc; ++i)
- infile(argv[i]);
- if (errors)
- exit(EXIT_FAILURE);
- associate();
- for (i = 0; i < nzones; i = j) {
- /*
- ** Find the next non-continuation zone entry.
- */
- for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
- continue;
- outzone(&zones[i], j - i);
- }
- /*
- ** Make links.
- */
- for (i = 0; i < nlinks; ++i) {
- eat(links[i].l_filename, links[i].l_linenum);
- dolink(links[i].l_from, links[i].l_to);
- if (noise)
- for (j = 0; j < nlinks; ++j)
- if (strcmp(links[i].l_to,
- links[j].l_from) == 0)
- warning(_("link to link"));
- }
- if (lcltime != NULL) {
- eat("command line", 1);
- dolink(lcltime, TZDEFAULT);
- }
- if (psxrules != NULL) {
- eat("command line", 1);
- dolink(psxrules, TZDEFRULES);
- }
- return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
-
-static void
-dolink(fromfield, tofield)
-const char * const fromfield;
-const char * const tofield;
-{
- register char * fromname;
- register char * toname;
-
- if (fromfield[0] == '/')
- fromname = ecpyalloc(fromfield);
- else {
- fromname = ecpyalloc(directory);
- fromname = ecatalloc(fromname, "/");
- fromname = ecatalloc(fromname, fromfield);
- }
- if (tofield[0] == '/')
- toname = ecpyalloc(tofield);
- else {
- toname = ecpyalloc(directory);
- toname = ecatalloc(toname, "/");
- toname = ecatalloc(toname, tofield);
- }
- /*
- ** We get to be careful here since
- ** there's a fair chance of root running us.
- */
- if (!itsdir(toname))
- (void) remove(toname);
- if (link(fromname, toname) != 0) {
- int result;
-
- if (mkdirs(toname) != 0)
- exit(EXIT_FAILURE);
-
- result = link(fromname, toname);
-#if HAVE_SYMLINK
- if (result != 0 &&
- access(fromname, F_OK) == 0 &&
- !itsdir(fromname)) {
- const char *s = tofield;
- register char * symlinkcontents = NULL;
- while ((s = strchr(s+1, '/')) != NULL)
- symlinkcontents =
- ecatalloc(symlinkcontents,
- "../");
- symlinkcontents =
- ecatalloc(symlinkcontents,
- fromname);
- result =
- symlink(symlinkcontents,
- toname);
- if (result == 0)
-warning(_("hard link failed, symbolic link used"));
- ifree(symlinkcontents);
- }
-#endif /* HAVE_SYMLINK */
- if (result != 0) {
- err(EXIT_FAILURE, _("can't link from %s to %s"),
- fromname, toname);
- }
- }
- ifree(fromname);
- ifree(toname);
-}
-
-#define TIME_T_BITS_IN_FILE 64
-
-static void
-setboundaries (void)
-{
- register int i;
-
- min_time = -1;
- for (i = 0; i < TIME_T_BITS_IN_FILE - 1; ++i)
- min_time *= 2;
- max_time = -(min_time + 1);
-}
-
-static int
-itsdir(name)
-const char * const name;
-{
- register char * myname;
- register int accres;
-
- myname = ecpyalloc(name);
- myname = ecatalloc(myname, "/.");
- accres = access(myname, F_OK);
- ifree(myname);
- return accres == 0;
-}
-
-/*
-** Associate sets of rules with zones.
-*/
-
-/*
-** Sort by rule name.
-*/
-
-static int
-rcomp(cp1, cp2)
-const void * cp1;
-const void * cp2;
-{
- return strcmp(((const struct rule *) cp1)->r_name,
- ((const struct rule *) cp2)->r_name);
-}
-
-static void
-associate(void)
-{
- register struct zone * zp;
- register struct rule * rp;
- register int base, out;
- register int i, j;
-
- if (nrules != 0) {
- (void) qsort((void *) rules, (size_t) nrules,
- (size_t) sizeof *rules, rcomp);
- for (i = 0; i < nrules - 1; ++i) {
- if (strcmp(rules[i].r_name,
- rules[i + 1].r_name) != 0)
- continue;
- if (strcmp(rules[i].r_filename,
- rules[i + 1].r_filename) == 0)
- continue;
- eat(rules[i].r_filename, rules[i].r_linenum);
- warning(_("same rule name in multiple files"));
- eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
- warning(_("same rule name in multiple files"));
- for (j = i + 2; j < nrules; ++j) {
- if (strcmp(rules[i].r_name,
- rules[j].r_name) != 0)
- break;
- if (strcmp(rules[i].r_filename,
- rules[j].r_filename) == 0)
- continue;
- if (strcmp(rules[i + 1].r_filename,
- rules[j].r_filename) == 0)
- continue;
- break;
- }
- i = j - 1;
- }
- }
- for (i = 0; i < nzones; ++i) {
- zp = &zones[i];
- zp->z_rules = NULL;
- zp->z_nrules = 0;
- }
- for (base = 0; base < nrules; base = out) {
- rp = &rules[base];
- for (out = base + 1; out < nrules; ++out)
- if (strcmp(rp->r_name, rules[out].r_name) != 0)
- break;
- for (i = 0; i < nzones; ++i) {
- zp = &zones[i];
- if (strcmp(zp->z_rule, rp->r_name) != 0)
- continue;
- zp->z_rules = rp;
- zp->z_nrules = out - base;
- }
- }
- for (i = 0; i < nzones; ++i) {
- zp = &zones[i];
- if (zp->z_nrules == 0) {
- /*
- ** Maybe we have a local standard time offset.
- */
- eat(zp->z_filename, zp->z_linenum);
- zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
- TRUE);
- /*
- ** Note, though, that if there's no rule,
- ** a '%s' in the format is a bad thing.
- */
- if (strchr(zp->z_format, '%') != 0)
- error(_("%s in ruleless zone"));
- }
- }
- if (errors)
- exit(EXIT_FAILURE);
-}
-
-static void
-infile(name)
-const char * name;
-{
- register FILE * fp;
- register char ** fields;
- register char * cp;
- register const struct lookup * lp;
- register int nfields;
- register int wantcont;
- register int num;
- char buf[BUFSIZ];
-
- if (strcmp(name, "-") == 0) {
- name = _("standard input");
- fp = stdin;
- } else if ((fp = fopen(name, "r")) == NULL)
- err(EXIT_FAILURE, _("can't open %s"), name);
- wantcont = FALSE;
- for (num = 1; ; ++num) {
- eat(name, num);
- if (fgets(buf, (int) sizeof buf, fp) != buf)
- break;
- cp = strchr(buf, '\n');
- if (cp == NULL) {
- error(_("line too long"));
- exit(EXIT_FAILURE);
- }
- *cp = '\0';
- fields = getfields(buf);
- nfields = 0;
- while (fields[nfields] != NULL) {
- static char nada;
-
- if (strcmp(fields[nfields], "-") == 0)
- fields[nfields] = &nada;
- ++nfields;
- }
- if (nfields == 0) {
- /* nothing to do */
- } else if (wantcont) {
- wantcont = inzcont(fields, nfields);
- } else {
- lp = byword(fields[0], line_codes);
- if (lp == NULL)
- error(_("input line of unknown type"));
- else switch ((int) (lp->l_value)) {
- case LC_RULE:
- inrule(fields, nfields);
- wantcont = FALSE;
- break;
- case LC_ZONE:
- wantcont = inzone(fields, nfields);
- break;
- case LC_LINK:
- inlink(fields, nfields);
- wantcont = FALSE;
- break;
- case LC_LEAP:
- if (name != leapsec)
- warnx(
-_("leap line in non leap seconds file %s"), name);
- else inleap(fields, nfields);
- wantcont = FALSE;
- break;
- default: /* "cannot happen" */
- errx(EXIT_FAILURE,
-_("panic: invalid l_value %d"), lp->l_value);
- }
- }
- ifree((char *) fields);
- }
- if (ferror(fp))
- errx(EXIT_FAILURE, _("error reading %s"), filename);
- if (fp != stdin && fclose(fp))
- err(EXIT_FAILURE, _("error closing %s"), filename);
- if (wantcont)
- error(_("expected continuation line not found"));
-}
-
-/*
-** Convert a string of one of the forms
-** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
-** into a number of seconds.
-** A null string maps to zero.
-** Call error with errstring and return zero on errors.
-*/
-
-static long
-gethms(string, errstring, signable)
-const char * string;
-const char * const errstring;
-const int signable;
-{
- long hh;
- int mm, ss, sign;
-
- if (string == NULL || *string == '\0')
- return 0;
- if (!signable)
- sign = 1;
- else if (*string == '-') {
- sign = -1;
- ++string;
- } else sign = 1;
- if (sscanf(string, scheck(string, "%ld"), &hh) == 1)
- mm = ss = 0;
- else if (sscanf(string, scheck(string, "%ld:%d"), &hh, &mm) == 2)
- ss = 0;
- else if (sscanf(string, scheck(string, "%ld:%d:%d"),
- &hh, &mm, &ss) != 3) {
- error(errstring);
- return 0;
- }
- if (hh < 0 ||
- mm < 0 || mm >= MINSPERHOUR ||
- ss < 0 || ss > SECSPERMIN) {
- error(errstring);
- return 0;
- }
- if (LONG_MAX / SECSPERHOUR < hh) {
- error(_("time overflow"));
- return 0;
- }
- if (noise && hh == HOURSPERDAY && mm == 0 && ss == 0)
- warning(_("24:00 not handled by pre-1998 versions of zic"));
- if (noise && (hh > HOURSPERDAY ||
- (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
-warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
- return oadd(eitol(sign) * hh * eitol(SECSPERHOUR),
- eitol(sign) * (eitol(mm) * eitol(SECSPERMIN) + eitol(ss)));
-}
-
-static void
-inrule(fields, nfields)
-register char ** const fields;
-const int nfields;
-{
- static struct rule r;
-
- if (nfields != RULE_FIELDS) {
- error(_("wrong number of fields on Rule line"));
- return;
- }
- if (*fields[RF_NAME] == '\0') {
- error(_("nameless rule"));
- return;
- }
- r.r_filename = filename;
- r.r_linenum = linenum;
- r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
- rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
- fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
- r.r_name = ecpyalloc(fields[RF_NAME]);
- r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
- if (max_abbrvar_len < strlen(r.r_abbrvar))
- max_abbrvar_len = strlen(r.r_abbrvar);
- rules = (struct rule *) (void *) erealloc((char *) rules,
- (int) ((nrules + 1) * sizeof *rules));
- rules[nrules++] = r;
-}
-
-static int
-inzone(fields, nfields)
-register char ** const fields;
-const int nfields;
-{
- register int i;
- static char * buf;
-
- if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
- error(_("wrong number of fields on Zone line"));
- return FALSE;
- }
- if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
- buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
- (void) sprintf(buf,
-_("\"Zone %s\" line and -l option are mutually exclusive"),
- TZDEFAULT);
- error(buf);
- return FALSE;
- }
- if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
- buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
- (void) sprintf(buf,
-_("\"Zone %s\" line and -p option are mutually exclusive"),
- TZDEFRULES);
- error(buf);
- return FALSE;
- }
- for (i = 0; i < nzones; ++i)
- if (zones[i].z_name != NULL &&
- strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
- buf = erealloc(buf, (int) (132 +
- strlen(fields[ZF_NAME]) +
- strlen(zones[i].z_filename)));
- (void) sprintf(buf,
-_("duplicate zone name %s (file \"%s\", line %d)"),
- fields[ZF_NAME],
- zones[i].z_filename,
- zones[i].z_linenum);
- error(buf);
- return FALSE;
- }
- return inzsub(fields, nfields, FALSE);
-}
-
-static int
-inzcont(fields, nfields)
-register char ** const fields;
-const int nfields;
-{
- if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
- error(_("wrong number of fields on Zone continuation line"));
- return FALSE;
- }
- return inzsub(fields, nfields, TRUE);
-}
-
-static int
-inzsub(fields, nfields, iscont)
-register char ** const fields;
-const int nfields;
-const int iscont;
-{
- register char * cp;
- static struct zone z;
- register int i_gmtoff, i_rule, i_format;
- register int i_untilyear, i_untilmonth;
- register int i_untilday, i_untiltime;
- register int hasuntil;
-
- if (iscont) {
- i_gmtoff = ZFC_GMTOFF;
- i_rule = ZFC_RULE;
- i_format = ZFC_FORMAT;
- i_untilyear = ZFC_TILYEAR;
- i_untilmonth = ZFC_TILMONTH;
- i_untilday = ZFC_TILDAY;
- i_untiltime = ZFC_TILTIME;
- z.z_name = NULL;
- } else {
- i_gmtoff = ZF_GMTOFF;
- i_rule = ZF_RULE;
- i_format = ZF_FORMAT;
- i_untilyear = ZF_TILYEAR;
- i_untilmonth = ZF_TILMONTH;
- i_untilday = ZF_TILDAY;
- i_untiltime = ZF_TILTIME;
- z.z_name = ecpyalloc(fields[ZF_NAME]);
- }
- z.z_filename = filename;
- z.z_linenum = linenum;
- z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
- if ((cp = strchr(fields[i_format], '%')) != 0) {
- if (*++cp != 's' || strchr(cp, '%') != 0) {
- error(_("invalid abbreviation format"));
- return FALSE;
- }
- }
- z.z_rule = ecpyalloc(fields[i_rule]);
- z.z_format = ecpyalloc(fields[i_format]);
- if (max_format_len < strlen(z.z_format))
- max_format_len = strlen(z.z_format);
- hasuntil = nfields > i_untilyear;
- if (hasuntil) {
- z.z_untilrule.r_filename = filename;
- z.z_untilrule.r_linenum = linenum;
- rulesub(&z.z_untilrule,
- fields[i_untilyear],
- "only",
- "",
- (nfields > i_untilmonth) ?
- fields[i_untilmonth] : "Jan",
- (nfields > i_untilday) ? fields[i_untilday] : "1",
- (nfields > i_untiltime) ? fields[i_untiltime] : "0");
- z.z_untiltime = rpytime(&z.z_untilrule,
- z.z_untilrule.r_loyear);
- if (iscont && nzones > 0 &&
- z.z_untiltime > min_time &&
- z.z_untiltime < max_time &&
- zones[nzones - 1].z_untiltime > min_time &&
- zones[nzones - 1].z_untiltime < max_time &&
- zones[nzones - 1].z_untiltime >= z.z_untiltime) {
- error(_(
-"Zone continuation line end time is not after end time of previous line"
- ));
- return FALSE;
- }
- }
- zones = (struct zone *) (void *) erealloc((char *) zones,
- (int) ((nzones + 1) * sizeof *zones));
- zones[nzones++] = z;
- /*
- ** If there was an UNTIL field on this line,
- ** there's more information about the zone on the next line.
- */
- return hasuntil;
-}
-
-static void
-inleap(fields, nfields)
-register char ** const fields;
-const int nfields;
-{
- register const char * cp;
- register const struct lookup * lp;
- register int i, j;
- int year, month, day;
- long dayoff, tod;
- zic_t t;
-
- if (nfields != LEAP_FIELDS) {
- error(_("wrong number of fields on Leap line"));
- return;
- }
- dayoff = 0;
- cp = fields[LP_YEAR];
- if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
- /*
- ** Leapin' Lizards!
- */
- error(_("invalid leaping year"));
- return;
- }
- if (!leapseen || leapmaxyear < year)
- leapmaxyear = year;
- if (!leapseen || leapminyear > year)
- leapminyear = year;
- leapseen = TRUE;
- j = EPOCH_YEAR;
- while (j != year) {
- if (year > j) {
- i = len_years[isleap(j)];
- ++j;
- } else {
- --j;
- i = -len_years[isleap(j)];
- }
- dayoff = oadd(dayoff, eitol(i));
- }
- if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
- error(_("invalid month name"));
- return;
- }
- month = lp->l_value;
- j = TM_JANUARY;
- while (j != month) {
- i = len_months[isleap(year)][j];
- dayoff = oadd(dayoff, eitol(i));
- ++j;
- }
- cp = fields[LP_DAY];
- if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
- day <= 0 || day > len_months[isleap(year)][month]) {
- error(_("invalid day of month"));
- return;
- }
- dayoff = oadd(dayoff, eitol(day - 1));
- if (dayoff < 0 && !TYPE_SIGNED(zic_t)) {
- error(_("time before zero"));
- return;
- }
- if (dayoff < min_time / SECSPERDAY) {
- error(_("time too small"));
- return;
- }
- if (dayoff > max_time / SECSPERDAY) {
- error(_("time too large"));
- return;
- }
- t = (zic_t) dayoff * SECSPERDAY;
- tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
- cp = fields[LP_CORR];
- {
- register int positive;
- int count;
-
- if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
- positive = FALSE;
- count = 1;
- } else if (strcmp(cp, "--") == 0) {
- positive = FALSE;
- count = 2;
- } else if (strcmp(cp, "+") == 0) {
- positive = TRUE;
- count = 1;
- } else if (strcmp(cp, "++") == 0) {
- positive = TRUE;
- count = 2;
- } else {
- error(_("illegal CORRECTION field on Leap line"));
- return;
- }
- if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
- error(_(
- "illegal Rolling/Stationary field on Leap line"
- ));
- return;
- }
- leapadd(tadd(t, tod), positive, lp->l_value, count);
- }
-}
-
-static void
-inlink(fields, nfields)
-register char ** const fields;
-const int nfields;
-{
- struct link l;
-
- if (nfields != LINK_FIELDS) {
- error(_("wrong number of fields on Link line"));
- return;
- }
- if (*fields[LF_FROM] == '\0') {
- error(_("blank FROM field on Link line"));
- return;
- }
- if (*fields[LF_TO] == '\0') {
- error(_("blank TO field on Link line"));
- return;
- }
- l.l_filename = filename;
- l.l_linenum = linenum;
- l.l_from = ecpyalloc(fields[LF_FROM]);
- l.l_to = ecpyalloc(fields[LF_TO]);
- links = (struct link *) (void *) erealloc((char *) links,
- (int) ((nlinks + 1) * sizeof *links));
- links[nlinks++] = l;
-}
-
-static void
-rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
-register struct rule * const rp;
-const char * const loyearp;
-const char * const hiyearp;
-const char * const typep;
-const char * const monthp;
-const char * const dayp;
-const char * const timep;
-{
- register const struct lookup * lp;
- register const char * cp;
- register char * dp;
- register char * ep;
-
- if ((lp = byword(monthp, mon_names)) == NULL) {
- error(_("invalid month name"));
- return;
- }
- rp->r_month = lp->l_value;
- rp->r_todisstd = FALSE;
- rp->r_todisgmt = FALSE;
- dp = ecpyalloc(timep);
- if (*dp != '\0') {
- ep = dp + strlen(dp) - 1;
- switch (lowerit(*ep)) {
- case 's': /* Standard */
- rp->r_todisstd = TRUE;
- rp->r_todisgmt = FALSE;
- *ep = '\0';
- break;
- case 'w': /* Wall */
- rp->r_todisstd = FALSE;
- rp->r_todisgmt = FALSE;
- *ep = '\0';
- break;
- case 'g': /* Greenwich */
- case 'u': /* Universal */
- case 'z': /* Zulu */
- rp->r_todisstd = TRUE;
- rp->r_todisgmt = TRUE;
- *ep = '\0';
- break;
- }
- }
- rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
- ifree(dp);
- /*
- ** Year work.
- */
- cp = loyearp;
- lp = byword(cp, begin_years);
- rp->r_lowasnum = lp == NULL;
- if (!rp->r_lowasnum) switch ((int) lp->l_value) {
- case YR_MINIMUM:
- rp->r_loyear = INT_MIN;
- break;
- case YR_MAXIMUM:
- rp->r_loyear = INT_MAX;
- break;
- default: /* "cannot happen" */
- errx(EXIT_FAILURE,
- _("panic: invalid l_value %d"), lp->l_value);
- } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
- error(_("invalid starting year"));
- return;
- }
- cp = hiyearp;
- lp = byword(cp, end_years);
- rp->r_hiwasnum = lp == NULL;
- if (!rp->r_hiwasnum) switch ((int) lp->l_value) {
- case YR_MINIMUM:
- rp->r_hiyear = INT_MIN;
- break;
- case YR_MAXIMUM:
- rp->r_hiyear = INT_MAX;
- break;
- case YR_ONLY:
- rp->r_hiyear = rp->r_loyear;
- break;
- default: /* "cannot happen" */
- errx(EXIT_FAILURE,
- _("panic: invalid l_value %d"), lp->l_value);
- } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
- error(_("invalid ending year"));
- return;
- }
- if (rp->r_loyear > rp->r_hiyear) {
- error(_("starting year greater than ending year"));
- return;
- }
- if (*typep == '\0')
- rp->r_yrtype = NULL;
- else {
- if (rp->r_loyear == rp->r_hiyear) {
- error(_("typed single year"));
- return;
- }
- rp->r_yrtype = ecpyalloc(typep);
- }
- /*
- ** Day work.
- ** Accept things such as:
- ** 1
- ** last-Sunday
- ** Sun<=20
- ** Sun>=7
- */
- dp = ecpyalloc(dayp);
- if ((lp = byword(dp, lasts)) != NULL) {
- rp->r_dycode = DC_DOWLEQ;
- rp->r_wday = lp->l_value;
- rp->r_dayofmonth = len_months[1][rp->r_month];
- } else {
- if ((ep = strchr(dp, '<')) != 0)
- rp->r_dycode = DC_DOWLEQ;
- else if ((ep = strchr(dp, '>')) != 0)
- rp->r_dycode = DC_DOWGEQ;
- else {
- ep = dp;
- rp->r_dycode = DC_DOM;
- }
- if (rp->r_dycode != DC_DOM) {
- *ep++ = 0;
- if (*ep++ != '=') {
- error(_("invalid day of month"));
- ifree(dp);
- return;
- }
- if ((lp = byword(dp, wday_names)) == NULL) {
- error(_("invalid weekday name"));
- ifree(dp);
- return;
- }
- rp->r_wday = lp->l_value;
- }
- if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
- rp->r_dayofmonth <= 0 ||
- (rp->r_dayofmonth > len_months[1][rp->r_month])) {
- error(_("invalid day of month"));
- ifree(dp);
- return;
- }
- }
- ifree(dp);
-}
-
-static void
-convert(val, buf)
-const long val;
-char * const buf;
-{
- register int i;
- register int shift;
-
- for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
- buf[i] = val >> shift;
-}
-
-static void
-convert64(val, buf)
-const zic_t val;
-char * const buf;
-{
- register int i;
- register int shift;
-
- for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
- buf[i] = val >> shift;
-}
-
-static void
-puttzcode(val, fp)
-const long val;
-FILE * const fp;
-{
- char buf[4];
-
- convert(val, buf);
- (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
-}
-
-static void
-puttzcode64(val, fp)
-const zic_t val;
-FILE * const fp;
-{
- char buf[8];
-
- convert64(val, buf);
- (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
-}
-
-static int
-atcomp(avp, bvp)
-const void * avp;
-const void * bvp;
-{
- const zic_t a = ((const struct attype *) avp)->at;
- const zic_t b = ((const struct attype *) bvp)->at;
-
- return (a < b) ? -1 : (a > b);
-}
-
-static int
-is32(x)
-const zic_t x;
-{
- return INT32_MIN <= x && x <= INT32_MAX;
-}
-
-static void
-writezone(name, string)
-const char * const name;
-const char * const string;
-{
- register FILE * fp;
- register int i, j;
- register int leapcnt32, leapi32;
- register int timecnt32, timei32;
- register int pass;
- static char * fullname;
- static const struct tzhead tzh0;
- static struct tzhead tzh;
- zic_t ats[TZ_MAX_TIMES];
- unsigned char types[TZ_MAX_TIMES];
-
- /*
- ** Sort.
- */
- if (timecnt > 1)
- (void) qsort((void *) attypes, (size_t) timecnt,
- (size_t) sizeof *attypes, atcomp);
- /*
- ** Optimize.
- */
- {
- int fromi;
- int toi;
-
- toi = 0;
- fromi = 0;
- while (fromi < timecnt && attypes[fromi].at < min_time)
- ++fromi;
- if (isdsts[0] == 0)
- while (fromi < timecnt && attypes[fromi].type == 0)
- ++fromi; /* handled by default rule */
- for ( ; fromi < timecnt; ++fromi) {
- if (toi != 0 && ((attypes[fromi].at +
- gmtoffs[attypes[toi - 1].type]) <=
- (attypes[toi - 1].at + gmtoffs[toi == 1 ? 0
- : attypes[toi - 2].type]))) {
- attypes[toi - 1].type =
- attypes[fromi].type;
- continue;
- }
- if (toi == 0 ||
- attypes[toi - 1].type != attypes[fromi].type)
- attypes[toi++] = attypes[fromi];
- }
- timecnt = toi;
- }
- /*
- ** Transfer.
- */
- for (i = 0; i < timecnt; ++i) {
- ats[i] = attypes[i].at;
- types[i] = attypes[i].type;
- }
- /*
- ** Correct for leap seconds.
- */
- for (i = 0; i < timecnt; ++i) {
- j = leapcnt;
- while (--j >= 0)
- if (ats[i] > trans[j] - corr[j]) {
- ats[i] = tadd(ats[i], corr[j]);
- break;
- }
- }
- /*
- ** Figure out 32-bit-limited starts and counts.
- */
- timecnt32 = timecnt;
- timei32 = 0;
- leapcnt32 = leapcnt;
- leapi32 = 0;
- while (timecnt32 > 0 && !is32(ats[timecnt32 - 1]))
- --timecnt32;
- while (timecnt32 > 0 && !is32(ats[timei32])) {
- --timecnt32;
- ++timei32;
- }
- while (leapcnt32 > 0 && !is32(trans[leapcnt32 - 1]))
- --leapcnt32;
- while (leapcnt32 > 0 && !is32(trans[leapi32])) {
- --leapcnt32;
- ++leapi32;
- }
- fullname = erealloc(fullname,
- (int) (strlen(directory) + 1 + strlen(name) + 1));
- (void) sprintf(fullname, "%s/%s", directory, name);
-
- /*
- * Remove old file, if any, to snap links.
- */
- if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
- err(EXIT_FAILURE, _("can't remove %s"), fullname);
-
- if ((fp = fopen(fullname, "wb")) == NULL) {
- if (mkdirs(fullname) != 0)
- exit(EXIT_FAILURE);
- if ((fp = fopen(fullname, "wb")) == NULL)
- err(EXIT_FAILURE, _("can't create %s"), fullname);
- }
- for (pass = 1; pass <= 2; ++pass) {
- register int thistimei, thistimecnt;
- register int thisleapi, thisleapcnt;
- register int thistimelim, thisleaplim;
- int writetype[TZ_MAX_TIMES];
- int typemap[TZ_MAX_TYPES];
- register int thistypecnt;
- char thischars[TZ_MAX_CHARS];
- char thischarcnt;
- int indmap[TZ_MAX_CHARS];
-
- if (pass == 1) {
- thistimei = timei32;
- thistimecnt = timecnt32;
- thisleapi = leapi32;
- thisleapcnt = leapcnt32;
- } else {
- thistimei = 0;
- thistimecnt = timecnt;
- thisleapi = 0;
- thisleapcnt = leapcnt;
- }
- thistimelim = thistimei + thistimecnt;
- thisleaplim = thisleapi + thisleapcnt;
- for (i = 0; i < typecnt; ++i)
- writetype[i] = thistimecnt == timecnt;
- if (thistimecnt == 0) {
- /*
- ** No transition times fall in the current
- ** (32- or 64-bit) window.
- */
- if (typecnt != 0)
- writetype[typecnt - 1] = TRUE;
- } else {
- for (i = thistimei - 1; i < thistimelim; ++i)
- if (i >= 0)
- writetype[types[i]] = TRUE;
- /*
- ** For America/Godthab and Antarctica/Palmer
- */
- if (thistimei == 0)
- writetype[0] = TRUE;
- }
-#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
- /*
- ** For some pre-2011 systems: if the last-to-be-written
- ** standard (or daylight) type has an offset different from the
- ** most recently used offset,
- ** append an (unused) copy of the most recently used type
- ** (to help get global "altzone" and "timezone" variables
- ** set correctly).
- */
- {
- register int mrudst, mrustd, hidst, histd, type;
-
- hidst = histd = mrudst = mrustd = -1;
- for (i = thistimei; i < thistimelim; ++i)
- if (isdsts[types[i]])
- mrudst = types[i];
- else mrustd = types[i];
- for (i = 0; i < typecnt; ++i)
- if (writetype[i]) {
- if (isdsts[i])
- hidst = i;
- else histd = i;
- }
- if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
- gmtoffs[hidst] != gmtoffs[mrudst]) {
- isdsts[mrudst] = -1;
- type = addtype(gmtoffs[mrudst],
- &chars[abbrinds[mrudst]],
- TRUE,
- ttisstds[mrudst],
- ttisgmts[mrudst]);
- isdsts[mrudst] = TRUE;
- writetype[type] = TRUE;
- }
- if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
- gmtoffs[histd] != gmtoffs[mrustd]) {
- isdsts[mrustd] = -1;
- type = addtype(gmtoffs[mrustd],
- &chars[abbrinds[mrustd]],
- FALSE,
- ttisstds[mrustd],
- ttisgmts[mrustd]);
- isdsts[mrustd] = FALSE;
- writetype[type] = TRUE;
- }
- }
-#endif /* !defined LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
- thistypecnt = 0;
- for (i = 0; i < typecnt; ++i)
- typemap[i] = writetype[i] ? thistypecnt++ : -1;
- for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
- indmap[i] = -1;
- thischarcnt = 0;
- for (i = 0; i < typecnt; ++i) {
- register char * thisabbr;
-
- if (!writetype[i])
- continue;
- if (indmap[abbrinds[i]] >= 0)
- continue;
- thisabbr = &chars[abbrinds[i]];
- for (j = 0; j < thischarcnt; ++j)
- if (strcmp(&thischars[j], thisabbr) == 0)
- break;
- if (j == thischarcnt) {
- (void) strcpy(&thischars[(int) thischarcnt],
- thisabbr);
- thischarcnt += strlen(thisabbr) + 1;
- }
- indmap[abbrinds[i]] = j;
- }
-#define DO(field) (void) fwrite((void *) tzh.field, \
- (size_t) sizeof tzh.field, (size_t) 1, fp)
- tzh = tzh0;
- (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
- tzh.tzh_version[0] = ZIC_VERSION;
- convert(eitol(thistypecnt), tzh.tzh_ttisgmtcnt);
- convert(eitol(thistypecnt), tzh.tzh_ttisstdcnt);
- convert(eitol(thisleapcnt), tzh.tzh_leapcnt);
- convert(eitol(thistimecnt), tzh.tzh_timecnt);
- convert(eitol(thistypecnt), tzh.tzh_typecnt);
- convert(eitol(thischarcnt), tzh.tzh_charcnt);
- DO(tzh_magic);
- DO(tzh_version);
- DO(tzh_reserved);
- DO(tzh_ttisgmtcnt);
- DO(tzh_ttisstdcnt);
- DO(tzh_leapcnt);
- DO(tzh_timecnt);
- DO(tzh_typecnt);
- DO(tzh_charcnt);
-#undef DO
- for (i = thistimei; i < thistimelim; ++i)
- if (pass == 1)
- puttzcode((long) ats[i], fp);
- else puttzcode64(ats[i], fp);
- for (i = thistimei; i < thistimelim; ++i) {
- unsigned char uc;
-
- uc = typemap[types[i]];
- (void) fwrite((void *) &uc,
- (size_t) sizeof uc,
- (size_t) 1,
- fp);
- }
- for (i = 0; i < typecnt; ++i)
- if (writetype[i]) {
- puttzcode(gmtoffs[i], fp);
- (void) putc(isdsts[i], fp);
- (void) putc((unsigned char) indmap[abbrinds[i]], fp);
- }
- if (thischarcnt != 0)
- (void) fwrite((void *) thischars,
- (size_t) sizeof thischars[0],
- (size_t) thischarcnt, fp);
- for (i = thisleapi; i < thisleaplim; ++i) {
- register zic_t todo;
-
- if (roll[i]) {
- if (timecnt == 0 || trans[i] < ats[0]) {
- j = 0;
- while (isdsts[j])
- if (++j >= typecnt) {
- j = 0;
- break;
- }
- } else {
- j = 1;
- while (j < timecnt &&
- trans[i] >= ats[j])
- ++j;
- j = types[j - 1];
- }
- todo = tadd(trans[i], -gmtoffs[j]);
- } else todo = trans[i];
- if (pass == 1)
- puttzcode((long) todo, fp);
- else puttzcode64(todo, fp);
- puttzcode(corr[i], fp);
- }
- for (i = 0; i < typecnt; ++i)
- if (writetype[i])
- (void) putc(ttisstds[i], fp);
- for (i = 0; i < typecnt; ++i)
- if (writetype[i])
- (void) putc(ttisgmts[i], fp);
- }
- (void) fprintf(fp, "\n%s\n", string);
- if (ferror(fp) || fclose(fp))
- errx(EXIT_FAILURE, _("error writing %s"), fullname);
- if (chmod(fullname, mflag) < 0)
- err(EXIT_FAILURE, _("cannot change mode of %s to %03o"),
- fullname, (unsigned)mflag);
- if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
- && chown(fullname, uflag, gflag) < 0)
- err(EXIT_FAILURE, _("cannot change ownership of %s"),
- fullname);
-}
-
-static void
-doabbr(abbr, format, letters, isdst, doquotes)
-char * const abbr;
-const char * const format;
-const char * const letters;
-const int isdst;
-const int doquotes;
-{
- register char * cp;
- register char * slashp;
- register int len;
-
- slashp = strchr(format, '/');
- if (slashp == NULL) {
- if (letters == NULL)
- (void) strcpy(abbr, format);
- else (void) sprintf(abbr, format, letters);
- } else if (isdst) {
- (void) strcpy(abbr, slashp + 1);
- } else {
- if (slashp > format)
- (void) strncpy(abbr, format,
- (unsigned) (slashp - format));
- abbr[slashp - format] = '\0';
- }
- if (!doquotes)
- return;
- for (cp = abbr; *cp != '\0'; ++cp)
- if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", *cp) == NULL &&
- strchr("abcdefghijklmnopqrstuvwxyz", *cp) == NULL)
- break;
- len = strlen(abbr);
- if (len > 0 && *cp == '\0')
- return;
- abbr[len + 2] = '\0';
- abbr[len + 1] = '>';
- for ( ; len > 0; --len)
- abbr[len] = abbr[len - 1];
- abbr[0] = '<';
-}
-
-static void
-updateminmax(x)
-const int x;
-{
- if (min_year > x)
- min_year = x;
- if (max_year < x)
- max_year = x;
-}
-
-static int
-stringoffset(result, offset)
-char * result;
-long offset;
-{
- register int hours;
- register int minutes;
- register int seconds;
-
- result[0] = '\0';
- if (offset < 0) {
- (void) strcpy(result, "-");
- offset = -offset;
- }
- seconds = offset % SECSPERMIN;
- offset /= SECSPERMIN;
- minutes = offset % MINSPERHOUR;
- offset /= MINSPERHOUR;
- hours = offset;
- if (hours >= HOURSPERDAY) {
- result[0] = '\0';
- return -1;
- }
- (void) sprintf(end(result), "%d", hours);
- if (minutes != 0 || seconds != 0) {
- (void) sprintf(end(result), ":%02d", minutes);
- if (seconds != 0)
- (void) sprintf(end(result), ":%02d", seconds);
- }
- return 0;
-}
-
-static int
-stringrule(result, rp, dstoff, gmtoff)
-char * result;
-const struct rule * const rp;
-const long dstoff;
-const long gmtoff;
-{
- register long tod;
-
- result = end(result);
- if (rp->r_dycode == DC_DOM) {
- register int month, total;
-
- if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
- return -1;
- total = 0;
- for (month = 0; month < rp->r_month; ++month)
- total += len_months[0][month];
- (void) sprintf(result, "J%d", total + rp->r_dayofmonth);
- } else {
- register int week;
-
- if (rp->r_dycode == DC_DOWGEQ) {
- week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
- if ((week - 1) * DAYSPERWEEK + 1 != rp->r_dayofmonth)
- return -1;
- } else if (rp->r_dycode == DC_DOWLEQ) {
- if (rp->r_dayofmonth == len_months[1][rp->r_month])
- week = 5;
- else {
- week = 1 + rp->r_dayofmonth / DAYSPERWEEK;
- if (week * DAYSPERWEEK - 1 != rp->r_dayofmonth)
- return -1;
- }
- } else return -1; /* "cannot happen" */
- (void) sprintf(result, "M%d.%d.%d",
- rp->r_month + 1, week, rp->r_wday);
- }
- tod = rp->r_tod;
- if (rp->r_todisgmt)
- tod += gmtoff;
- if (rp->r_todisstd && rp->r_stdoff == 0)
- tod += dstoff;
- if (tod < 0) {
- result[0] = '\0';
- return -1;
- }
- if (tod != 2 * SECSPERMIN * MINSPERHOUR) {
- (void) strcat(result, "/");
- if (stringoffset(end(result), tod) != 0)
- return -1;
- }
- return 0;
-}
-
-static void
-stringzone(result, zpfirst, zonecount)
-char * result;
-const struct zone * const zpfirst;
-const int zonecount;
-{
- register const struct zone * zp;
- register struct rule * rp;
- register struct rule * stdrp;
- register struct rule * dstrp;
- register int i;
- register const char * abbrvar;
-
- result[0] = '\0';
- zp = zpfirst + zonecount - 1;
- stdrp = dstrp = NULL;
- for (i = 0; i < zp->z_nrules; ++i) {
- rp = &zp->z_rules[i];
- if (rp->r_hiwasnum || rp->r_hiyear != INT_MAX)
- continue;
- if (rp->r_yrtype != NULL)
- continue;
- if (rp->r_stdoff == 0) {
- if (stdrp == NULL)
- stdrp = rp;
- else return;
- } else {
- if (dstrp == NULL)
- dstrp = rp;
- else return;
- }
- }
- if (stdrp == NULL && dstrp == NULL) {
- /*
- ** There are no rules running through "max".
- ** Let's find the latest rule.
- */
- for (i = 0; i < zp->z_nrules; ++i) {
- rp = &zp->z_rules[i];
- if (stdrp == NULL || rp->r_hiyear > stdrp->r_hiyear ||
- (rp->r_hiyear == stdrp->r_hiyear &&
- rp->r_month > stdrp->r_month))
- stdrp = rp;
- }
- if (stdrp != NULL && stdrp->r_stdoff != 0)
- return; /* We end up in DST (a POSIX no-no). */
- /*
- ** Horrid special case: if year is 2037,
- ** presume this is a zone handled on a year-by-year basis;
- ** do not try to apply a rule to the zone.
- */
- if (stdrp != NULL && stdrp->r_hiyear == 2037)
- return;
- }
- if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_stdoff != 0))
- return;
- abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
- doabbr(result, zp->z_format, abbrvar, FALSE, TRUE);
- if (stringoffset(end(result), -zp->z_gmtoff) != 0) {
- result[0] = '\0';
- return;
- }
- if (dstrp == NULL)
- return;
- doabbr(end(result), zp->z_format, dstrp->r_abbrvar, TRUE, TRUE);
- if (dstrp->r_stdoff != SECSPERMIN * MINSPERHOUR)
- if (stringoffset(end(result),
- -(zp->z_gmtoff + dstrp->r_stdoff)) != 0) {
- result[0] = '\0';
- return;
- }
- (void) strcat(result, ",");
- if (stringrule(result, dstrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
- result[0] = '\0';
- return;
- }
- (void) strcat(result, ",");
- if (stringrule(result, stdrp, dstrp->r_stdoff, zp->z_gmtoff) != 0) {
- result[0] = '\0';
- return;
- }
-}
-
-static void
-outzone(zpfirst, zonecount)
-const struct zone * const zpfirst;
-const int zonecount;
-{
- register const struct zone * zp;
- register struct rule * rp;
- register int i, j;
- register int usestart, useuntil;
- register zic_t starttime, untiltime;
- register long gmtoff;
- register long stdoff;
- register int year;
- register long startoff;
- register int startttisstd;
- register int startttisgmt;
- register int type;
- register char * startbuf;
- register char * ab;
- register char * envvar;
- register int max_abbr_len;
- register int max_envvar_len;
-
- max_abbr_len = 2 + max_format_len + max_abbrvar_len;
- max_envvar_len = 2 * max_abbr_len + 5 * 9;
- startbuf = emalloc(max_abbr_len + 1);
- ab = emalloc(max_abbr_len + 1);
- envvar = emalloc(max_envvar_len + 1);
- INITIALIZE(untiltime);
- INITIALIZE(starttime);
- /*
- ** Now. . .finally. . .generate some useful data!
- */
- timecnt = 0;
- typecnt = 0;
- charcnt = 0;
- /*
- ** Thanks to Earl Chew
- ** for noting the need to unconditionally initialize startttisstd.
- */
- startttisstd = FALSE;
- startttisgmt = FALSE;
- min_year = max_year = EPOCH_YEAR;
- if (leapseen) {
- updateminmax(leapminyear);
- updateminmax(leapmaxyear + (leapmaxyear < INT_MAX));
- }
- for (i = 0; i < zonecount; ++i) {
- zp = &zpfirst[i];
- if (i < zonecount - 1)
- updateminmax(zp->z_untilrule.r_loyear);
- for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
- if (rp->r_lowasnum)
- updateminmax(rp->r_loyear);
- if (rp->r_hiwasnum)
- updateminmax(rp->r_hiyear);
- }
- }
- /*
- ** Generate lots of data if a rule can't cover all future times.
- */
- stringzone(envvar, zpfirst, zonecount);
- if (noise && envvar[0] == '\0') {
- register char * wp;
-
-wp = ecpyalloc(_("no POSIX environment variable for zone"));
- wp = ecatalloc(wp, " ");
- wp = ecatalloc(wp, zpfirst->z_name);
- warning(wp);
- ifree(wp);
- }
- if (envvar[0] == '\0') {
- if (min_year >= INT_MIN + YEARSPERREPEAT)
- min_year -= YEARSPERREPEAT;
- else min_year = INT_MIN;
- if (max_year <= INT_MAX - YEARSPERREPEAT)
- max_year += YEARSPERREPEAT;
- else max_year = INT_MAX;
- }
- /*
- ** For the benefit of older systems,
- ** generate data from 1900 through 2037.
- */
- if (min_year > 1900)
- min_year = 1900;
- if (max_year < 2037)
- max_year = 2037;
- for (i = 0; i < zonecount; ++i) {
- /*
- ** A guess that may well be corrected later.
- */
- stdoff = 0;
- zp = &zpfirst[i];
- usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
- useuntil = i < (zonecount - 1);
- if (useuntil && zp->z_untiltime <= min_time)
- continue;
- gmtoff = zp->z_gmtoff;
- eat(zp->z_filename, zp->z_linenum);
- *startbuf = '\0';
- startoff = zp->z_gmtoff;
- if (zp->z_nrules == 0) {
- stdoff = zp->z_stdoff;
- doabbr(startbuf, zp->z_format,
- (char *) NULL, stdoff != 0, FALSE);
- type = addtype(oadd(zp->z_gmtoff, stdoff),
- startbuf, stdoff != 0, startttisstd,
- startttisgmt);
- if (usestart) {
- addtt(starttime, type);
- usestart = FALSE;
- } else if (stdoff != 0)
- addtt(min_time, type);
- } else for (year = min_year; year <= max_year; ++year) {
- if (useuntil && year > zp->z_untilrule.r_hiyear)
- break;
- /*
- ** Mark which rules to do in the current year.
- ** For those to do, calculate rpytime(rp, year);
- */
- for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
- eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
- rp->r_todo = year >= rp->r_loyear &&
- year <= rp->r_hiyear &&
- yearistype(year, rp->r_yrtype);
- if (rp->r_todo)
- rp->r_temp = rpytime(rp, year);
- }
- for ( ; ; ) {
- register int k;
- register zic_t jtime, ktime;
- register long offset;
-
- INITIALIZE(ktime);
- if (useuntil) {
- /*
- ** Turn untiltime into UTC
- ** assuming the current gmtoff and
- ** stdoff values.
- */
- untiltime = zp->z_untiltime;
- if (!zp->z_untilrule.r_todisgmt)
- untiltime = tadd(untiltime,
- -gmtoff);
- if (!zp->z_untilrule.r_todisstd)
- untiltime = tadd(untiltime,
- -stdoff);
- }
- /*
- ** Find the rule (of those to do, if any)
- ** that takes effect earliest in the year.
- */
- k = -1;
- for (j = 0; j < zp->z_nrules; ++j) {
- rp = &zp->z_rules[j];
- if (!rp->r_todo)
- continue;
- eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
- offset = rp->r_todisgmt ? 0 : gmtoff;
- if (!rp->r_todisstd)
- offset = oadd(offset, stdoff);
- jtime = rp->r_temp;
- if (jtime == min_time ||
- jtime == max_time)
- continue;
- jtime = tadd(jtime, -offset);
- if (k < 0 || jtime < ktime) {
- k = j;
- ktime = jtime;
- }
- }
- if (k < 0)
- break; /* go on to next year */
- rp = &zp->z_rules[k];
- rp->r_todo = FALSE;
- if (useuntil && ktime >= untiltime)
- break;
- stdoff = rp->r_stdoff;
- if (usestart && ktime == starttime)
- usestart = FALSE;
- if (usestart) {
- if (ktime < starttime) {
- startoff = oadd(zp->z_gmtoff,
- stdoff);
- doabbr(startbuf, zp->z_format,
- rp->r_abbrvar,
- rp->r_stdoff != 0,
- FALSE);
- continue;
- }
- if (*startbuf == '\0' &&
- startoff == oadd(zp->z_gmtoff,
- stdoff)) {
- doabbr(startbuf,
- zp->z_format,
- rp->r_abbrvar,
- rp->r_stdoff !=
- 0,
- FALSE);
- }
- }
- eats(zp->z_filename, zp->z_linenum,
- rp->r_filename, rp->r_linenum);
- doabbr(ab, zp->z_format, rp->r_abbrvar,
- rp->r_stdoff != 0, FALSE);
- offset = oadd(zp->z_gmtoff, rp->r_stdoff);
- type = addtype(offset, ab, rp->r_stdoff != 0,
- rp->r_todisstd, rp->r_todisgmt);
- addtt(ktime, type);
- }
- }
- if (usestart) {
- if (*startbuf == '\0' &&
- zp->z_format != NULL &&
- strchr(zp->z_format, '%') == NULL &&
- strchr(zp->z_format, '/') == NULL)
- (void) strcpy(startbuf, zp->z_format);
- eat(zp->z_filename, zp->z_linenum);
- if (*startbuf == '\0')
-error(_("can't determine time zone abbreviation to use just after until time"));
- else addtt(starttime,
- addtype(startoff, startbuf,
- startoff != zp->z_gmtoff,
- startttisstd,
- startttisgmt));
- }
- /*
- ** Now we may get to set starttime for the next zone line.
- */
- if (useuntil) {
- startttisstd = zp->z_untilrule.r_todisstd;
- startttisgmt = zp->z_untilrule.r_todisgmt;
- starttime = zp->z_untiltime;
- if (!startttisstd)
- starttime = tadd(starttime, -stdoff);
- if (!startttisgmt)
- starttime = tadd(starttime, -gmtoff);
- }
- }
- writezone(zpfirst->z_name, envvar);
- ifree(startbuf);
- ifree(ab);
- ifree(envvar);
-}
-
-static void
-addtt(starttime, type)
-const zic_t starttime;
-int type;
-{
- if (starttime <= min_time ||
- (timecnt == 1 && attypes[0].at < min_time)) {
- gmtoffs[0] = gmtoffs[type];
- isdsts[0] = isdsts[type];
- ttisstds[0] = ttisstds[type];
- ttisgmts[0] = ttisgmts[type];
- if (abbrinds[type] != 0)
- (void) strcpy(chars, &chars[abbrinds[type]]);
- abbrinds[0] = 0;
- charcnt = strlen(chars) + 1;
- typecnt = 1;
- timecnt = 0;
- type = 0;
- }
- if (timecnt >= TZ_MAX_TIMES) {
- error(_("too many transitions?!"));
- exit(EXIT_FAILURE);
- }
- attypes[timecnt].at = starttime;
- attypes[timecnt].type = type;
- ++timecnt;
-}
-
-static int
-addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
-const long gmtoff;
-const char * const abbr;
-const int isdst;
-const int ttisstd;
-const int ttisgmt;
-{
- register int i, j;
-
- if (isdst != TRUE && isdst != FALSE) {
- error(_("internal error - addtype called with bad isdst"));
- exit(EXIT_FAILURE);
- }
- if (ttisstd != TRUE && ttisstd != FALSE) {
- error(_("internal error - addtype called with bad ttisstd"));
- exit(EXIT_FAILURE);
- }
- if (ttisgmt != TRUE && ttisgmt != FALSE) {
- error(_("internal error - addtype called with bad ttisgmt"));
- exit(EXIT_FAILURE);
- }
- /*
- ** See if there's already an entry for this zone type.
- ** If so, just return its index.
- */
- for (i = 0; i < typecnt; ++i) {
- if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
- strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
- ttisstd == ttisstds[i] &&
- ttisgmt == ttisgmts[i])
- return i;
- }
- /*
- ** There isn't one; add a new one, unless there are already too
- ** many.
- */
- if (typecnt >= TZ_MAX_TYPES) {
- error(_("too many local time types"));
- exit(EXIT_FAILURE);
- }
- if (! (-1L - 2147483647L <= gmtoff && gmtoff <= 2147483647L)) {
- error(_("UTC offset out of range"));
- exit(EXIT_FAILURE);
- }
- gmtoffs[i] = gmtoff;
- isdsts[i] = isdst;
- ttisstds[i] = ttisstd;
- ttisgmts[i] = ttisgmt;
-
- for (j = 0; j < charcnt; ++j)
- if (strcmp(&chars[j], abbr) == 0)
- break;
- if (j == charcnt)
- newabbr(abbr);
- abbrinds[i] = j;
- ++typecnt;
- return i;
-}
-
-static void
-leapadd(t, positive, rolling, count)
-const zic_t t;
-const int positive;
-const int rolling;
-int count;
-{
- register int i, j;
-
- if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
- error(_("too many leap seconds"));
- exit(EXIT_FAILURE);
- }
- for (i = 0; i < leapcnt; ++i)
- if (t <= trans[i]) {
- if (t == trans[i]) {
- error(_("repeated leap second moment"));
- exit(EXIT_FAILURE);
- }
- break;
- }
- do {
- for (j = leapcnt; j > i; --j) {
- trans[j] = trans[j - 1];
- corr[j] = corr[j - 1];
- roll[j] = roll[j - 1];
- }
- trans[i] = t;
- corr[i] = positive ? 1L : eitol(-count);
- roll[i] = rolling;
- ++leapcnt;
- } while (positive && --count != 0);
-}
-
-static void
-adjleap(void)
-{
- register int i;
- register long last = 0;
-
- /*
- ** propagate leap seconds forward
- */
- for (i = 0; i < leapcnt; ++i) {
- trans[i] = tadd(trans[i], last);
- last = corr[i] += last;
- }
-}
-
-static int
-yearistype(year, type)
-const int year;
-const char * const type;
-{
- static char * buf;
- int result;
-
- if (type == NULL || *type == '\0')
- return TRUE;
- buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
- (void) sprintf(buf, "%s %d %s", yitcommand, year, type);
- result = system(buf);
- if (WIFEXITED(result)) switch (WEXITSTATUS(result)) {
- case 0:
- return TRUE;
- case 1:
- return FALSE;
- }
- error(_("wild result from command execution"));
- warnx(_("command was '%s', result was %d"), buf, result);
- for ( ; ; )
- exit(EXIT_FAILURE);
-}
-
-static int
-lowerit(a)
-int a;
-{
- a = (unsigned char) a;
- return (isascii(a) && isupper(a)) ? tolower(a) : a;
-}
-
-static int
-ciequal(ap, bp) /* case-insensitive equality */
-register const char * ap;
-register const char * bp;
-{
- while (lowerit(*ap) == lowerit(*bp++))
- if (*ap++ == '\0')
- return TRUE;
- return FALSE;
-}
-
-static int
-itsabbr(abbr, word)
-register const char * abbr;
-register const char * word;
-{
- if (lowerit(*abbr) != lowerit(*word))
- return FALSE;
- ++word;
- while (*++abbr != '\0')
- do {
- if (*word == '\0')
- return FALSE;
- } while (lowerit(*word++) != lowerit(*abbr));
- return TRUE;
-}
-
-static const struct lookup *
-byword(word, table)
-register const char * const word;
-register const struct lookup * const table;
-{
- register const struct lookup * foundlp;
- register const struct lookup * lp;
-
- if (word == NULL || table == NULL)
- return NULL;
- /*
- ** Look for exact match.
- */
- for (lp = table; lp->l_word != NULL; ++lp)
- if (ciequal(word, lp->l_word))
- return lp;
- /*
- ** Look for inexact match.
- */
- foundlp = NULL;
- for (lp = table; lp->l_word != NULL; ++lp)
- if (itsabbr(word, lp->l_word)) {
- if (foundlp == NULL)
- foundlp = lp;
- else return NULL; /* multiple inexact matches */
- }
- return foundlp;
-}
-
-static char **
-getfields(cp)
-register char * cp;
-{
- register char * dp;
- register char ** array;
- register int nsubs;
-
- if (cp == NULL)
- return NULL;
- array = (char **) (void *)
- emalloc((int) ((strlen(cp) + 1) * sizeof *array));
- nsubs = 0;
- for ( ; ; ) {
- while (isascii((unsigned char) *cp) &&
- isspace((unsigned char) *cp))
- ++cp;
- if (*cp == '\0' || *cp == '#')
- break;
- array[nsubs++] = dp = cp;
- do {
- if ((*dp = *cp++) != '"')
- ++dp;
- else while ((*dp = *cp++) != '"')
- if (*dp != '\0')
- ++dp;
- else {
- error(_("odd number of quotation marks"));
- exit(EXIT_FAILURE);
- }
- } while (*cp != '\0' && *cp != '#' &&
- (!isascii(*cp) || !isspace((unsigned char) *cp)));
- if (isascii(*cp) && isspace((unsigned char) *cp))
- ++cp;
- *dp = '\0';
- }
- array[nsubs] = NULL;
- return array;
-}
-
-static long
-oadd(t1, t2)
-const long t1;
-const long t2;
-{
- register long t;
-
- t = t1 + t2;
- if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
- error(_("time overflow"));
- exit(EXIT_FAILURE);
- }
- return t;
-}
-
-static zic_t
-tadd(t1, t2)
-const zic_t t1;
-const long t2;
-{
- register zic_t t;
-
- if (t1 == max_time && t2 > 0)
- return max_time;
- if (t1 == min_time && t2 < 0)
- return min_time;
- t = t1 + t2;
- if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
- error(_("time overflow"));
- exit(EXIT_FAILURE);
- }
- return t;
-}
-
-/*
-** Given a rule, and a year, compute the date - in seconds since January 1,
-** 1970, 00:00 LOCAL time - in that year that the rule refers to.
-*/
-
-static zic_t
-rpytime(rp, wantedy)
-register const struct rule * const rp;
-register const int wantedy;
-{
- register int y, m, i;
- register long dayoff; /* with a nod to Margaret O. */
- register zic_t t;
-
- if (wantedy == INT_MIN)
- return min_time;
- if (wantedy == INT_MAX)
- return max_time;
- dayoff = 0;
- m = TM_JANUARY;
- y = EPOCH_YEAR;
- while (wantedy != y) {
- if (wantedy > y) {
- i = len_years[isleap(y)];
- ++y;
- } else {
- --y;
- i = -len_years[isleap(y)];
- }
- dayoff = oadd(dayoff, eitol(i));
- }
- while (m != rp->r_month) {
- i = len_months[isleap(y)][m];
- dayoff = oadd(dayoff, eitol(i));
- ++m;
- }
- i = rp->r_dayofmonth;
- if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
- if (rp->r_dycode == DC_DOWLEQ)
- --i;
- else {
- error(_("use of 2/29 in non leap-year"));
- exit(EXIT_FAILURE);
- }
- }
- --i;
- dayoff = oadd(dayoff, eitol(i));
- if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
- register long wday;
-
-#define LDAYSPERWEEK ((long) DAYSPERWEEK)
- wday = eitol(EPOCH_WDAY);
- /*
- ** Don't trust mod of negative numbers.
- */
- if (dayoff >= 0)
- wday = (wday + dayoff) % LDAYSPERWEEK;
- else {
- wday -= ((-dayoff) % LDAYSPERWEEK);
- if (wday < 0)
- wday += LDAYSPERWEEK;
- }
- while (wday != eitol(rp->r_wday))
- if (rp->r_dycode == DC_DOWGEQ) {
- dayoff = oadd(dayoff, (long) 1);
- if (++wday >= LDAYSPERWEEK)
- wday = 0;
- ++i;
- } else {
- dayoff = oadd(dayoff, (long) -1);
- if (--wday < 0)
- wday = LDAYSPERWEEK - 1;
- --i;
- }
- if (i < 0 || i >= len_months[isleap(y)][m]) {
- if (noise)
- warning(_("rule goes past start/end of month--\
-will not work with pre-2004 versions of zic"));
- }
- }
- if (dayoff < min_time / SECSPERDAY)
- return min_time;
- if (dayoff > max_time / SECSPERDAY)
- return max_time;
- t = (zic_t) dayoff * SECSPERDAY;
- return tadd(t, rp->r_tod);
-}
-
-static void
-newabbr(string)
-const char * const string;
-{
- register int i;
-
- if (strcmp(string, GRANDPARENTED) != 0) {
- register const char * cp;
- register char * wp;
-
- cp = string;
- wp = NULL;
- while (isascii((unsigned char) *cp) &&
- (isalnum((unsigned char)*cp) || *cp == '-' || *cp == '+'))
- ++cp;
- if (noise && cp - string > 3)
-wp = _("time zone abbreviation has more than 3 characters");
- if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
-wp = _("time zone abbreviation has too many characters");
- if (*cp != '\0')
-wp = _("time zone abbreviation differs from POSIX standard");
- if (wp != NULL) {
- wp = ecpyalloc(wp);
- wp = ecatalloc(wp, " (");
- wp = ecatalloc(wp, string);
- wp = ecatalloc(wp, ")");
- warning(wp);
- ifree(wp);
- }
- }
- i = strlen(string) + 1;
- if (charcnt + i > TZ_MAX_CHARS) {
- error(_("too many, or too long, time zone abbreviations"));
- exit(EXIT_FAILURE);
- }
- (void) strcpy(&chars[charcnt], string);
- charcnt += eitol(i);
-}
-
-static int
-mkdirs(argname)
-char * argname;
-{
- register char * name;
- register char * cp;
-
- if (argname == NULL || *argname == '\0' || Dflag)
- return 0;
- cp = name = ecpyalloc(argname);
- while ((cp = strchr(cp + 1, '/')) != 0) {
- *cp = '\0';
-#ifndef unix
- /*
- ** DOS drive specifier?
- */
- if (isalpha((unsigned char) name[0]) &&
- name[1] == ':' && name[2] == '\0') {
- *cp = '/';
- continue;
- }
-#endif /* !defined unix */
- if (!itsdir(name)) {
- /*
- ** It doesn't seem to exist, so we try to create it.
- ** Creation may fail because of the directory being
- ** created by some other multiprocessor, so we get
- ** to do extra checking.
- */
- if (mkdir(name, MKDIR_UMASK) != 0
- && (errno != EEXIST || !itsdir(name))) {
- warn(_("can't create directory %s"), name);
- ifree(name);
- return -1;
- }
- }
- *cp = '/';
- }
- ifree(name);
- return 0;
-}
-
-static long
-eitol(i)
-const int i;
-{
- long l;
-
- l = i;
- if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
- errx(EXIT_FAILURE, _("%d did not sign extend correctly"), i);
- return l;
-}
-
-#include <grp.h>
-#include <pwd.h>
-
-static void
-setgroup(flag, name)
- gid_t *flag;
- const char *name;
-{
- struct group *gr;
-
- if (*flag != (gid_t)-1)
- errx(EXIT_FAILURE, _("multiple -g flags specified"));
-
- gr = getgrnam(name);
- if (gr == 0) {
- char *ep;
- unsigned long ul;
-
- ul = strtoul(name, &ep, 10);
- if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
- *flag = ul;
- return;
- }
- errx(EXIT_FAILURE, _("group `%s' not found"), name);
- }
- *flag = gr->gr_gid;
-}
-
-static void
-setuser(flag, name)
- uid_t *flag;
- const char *name;
-{
- struct passwd *pw;
-
- if (*flag != (gid_t)-1)
- errx(EXIT_FAILURE, _("multiple -u flags specified"));
-
- pw = getpwnam(name);
- if (pw == 0) {
- char *ep;
- unsigned long ul;
-
- ul = strtoul(name, &ep, 10);
- if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
- *flag = ul;
- return;
- }
- errx(EXIT_FAILURE, _("user `%s' not found"), name);
- }
- *flag = pw->pw_uid;
-}
-
-/*
-** UNIX was a registered trademark of The Open Group in 2003.
-*/
diff --git a/contrib/tzcode/zic/zic/Makefile b/contrib/tzcode/zic/zic/Makefile
deleted file mode 100644
index 02dc6e2d710e..000000000000
--- a/contrib/tzcode/zic/zic/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${.CURDIR}/..
-
-PROG= zic
-MAN= zic.8
-SRCS= zic.c ialloc.c scheck.c
-
-CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
-CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
-CFLAGS+= -DHAVE_STRERROR -DHAVE_UNISTD_H
-CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
-
-WARNS?= 2
-
-.include <bsd.prog.mk>
diff --git a/lib/libc/stdtime/Makefile.inc b/lib/libc/stdtime/Makefile.inc
index 3d483469bc97..a7ef64650ba1 100644
--- a/lib/libc/stdtime/Makefile.inc
+++ b/lib/libc/stdtime/Makefile.inc
@@ -1,19 +1,21 @@
# Makefile.inc,v 1.2 1994/09/13 21:26:01 wollman Exp
# $FreeBSD$
-.PATH: ${LIBC_SRCTOP}/stdtime ${SRCTOP}/contrib/tzcode/stdtime
+.PATH: ${LIBC_SRCTOP}/stdtime ${SRCTOP}/contrib/tzcode
SRCS+= asctime.c difftime.c localtime.c strftime.c strptime.c timelocal.c \
time32.c
SYM_MAPS+= ${LIBC_SRCTOP}/stdtime/Symbol.map
-CFLAGS+= -I${SRCTOP}/contrib/tzcode/stdtime -I${LIBC_SRCTOP}/stdtime
+CFLAGS+= -I${SRCTOP}/contrib/tzcode -I${LIBC_SRCTOP}/stdtime
CFLAGS.localtime.c= -fwrapv
-
+CFLAGS.localtime.c+= -DALL_STATE -DTHREAD_SAFE
+CFLAGS.localtime.c+= -DHAVE_TZNAME=2 -DUSG_COMPAT=0 -DALTZONE=0
+CFLAGS.localtime.c+= -DNETBSD_INSPIRED=0 -DSTD_INSPIRED=1
.if ${MK_DETECT_TZ_CHANGES} != "no"
-CFLAGS+= -DDETECT_TZ_CHANGES
+CFLAGS.localtime.c+= -DDETECT_TZ_CHANGES
.endif
MAN+= ctime.3 strftime.3 strptime.3 time2posix.3
diff --git a/contrib/tzcode/stdtime/ctime.3 b/lib/libc/stdtime/ctime.3
index de3a57bab59e..de3a57bab59e 100644
--- a/contrib/tzcode/stdtime/ctime.3
+++ b/lib/libc/stdtime/ctime.3
diff --git a/lib/libc/stdtime/strftime.c b/lib/libc/stdtime/strftime.c
index 50fbc41de24e..722f2a36992b 100644
--- a/lib/libc/stdtime/strftime.c
+++ b/lib/libc/stdtime/strftime.c
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include "tzfile.h"
#include <fcntl.h>
#include <sys/stat.h>
+#include <stdio.h>
#include "un-namespace.h"
#include "timelocal.h"
diff --git a/lib/libc/stdtime/strptime.c b/lib/libc/stdtime/strptime.c
index 5691f61b6d84..d8d8962dcaee 100644
--- a/lib/libc/stdtime/strptime.c
+++ b/lib/libc/stdtime/strptime.c
@@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$");
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
+#include "private.h"
#include "un-namespace.h"
#include "libc_private.h"
#include "timelocal.h"
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 5568691776b8..603733db9664 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -101,6 +101,7 @@ SUBDIR= adduser \
wake \
watch \
watchdogd \
+ zdump \
zic \
zonectl
diff --git a/usr.sbin/zdump/Makefile b/usr.sbin/zdump/Makefile
new file mode 100644
index 000000000000..43e4edaae2b6
--- /dev/null
+++ b/usr.sbin/zdump/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/contrib/tzcode
+
+PROG= zdump
+MAN= zdump.8
+SRCS= zdump.c
+
+CFLAGS+= -DNETBSD_INSPIRED=0 -DSTD_INSPIRED=1
+CFLAGS+= -I${SRCTOP}/contrib/tzcode
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zic/zdump/Makefile.depend b/usr.sbin/zdump/Makefile.depend
index 6cfaab1c3644..6cfaab1c3644 100644
--- a/usr.sbin/zic/zdump/Makefile.depend
+++ b/usr.sbin/zdump/Makefile.depend
diff --git a/usr.sbin/zic/Makefile b/usr.sbin/zic/Makefile
index 9d699a891449..a3eb6f7dadba 100644
--- a/usr.sbin/zic/Makefile
+++ b/usr.sbin/zic/Makefile
@@ -1,7 +1,12 @@
# $FreeBSD$
-# Vendor contact: tz@elsie.nci.nih.gov
+.PATH: ${SRCTOP}/contrib/tzcode
-SUBDIR= zic zdump
+PROG= zic
+MAN= zic.8
+SRCS= zic.c
-.include <bsd.subdir.mk>
+CFLAGS+= -DNETBSD_INSPIRED=0 -DSTD_INSPIRED=1
+CFLAGS+= -I${SRCTOP}/contrib/tzcode
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zic/zic/Makefile.depend b/usr.sbin/zic/Makefile.depend
index 6cfaab1c3644..6cfaab1c3644 100644
--- a/usr.sbin/zic/zic/Makefile.depend
+++ b/usr.sbin/zic/Makefile.depend
diff --git a/usr.sbin/zic/Makefile.inc b/usr.sbin/zic/Makefile.inc
deleted file mode 100644
index 19a7ea422c5b..000000000000
--- a/usr.sbin/zic/Makefile.inc
+++ /dev/null
@@ -1,3 +0,0 @@
-# $FreeBSD$
-
-.include "${SRCTOP}/usr.sbin/Makefile.inc"
diff --git a/usr.sbin/zic/README b/usr.sbin/zic/README
deleted file mode 100644
index 5cb701eca276..000000000000
--- a/usr.sbin/zic/README
+++ /dev/null
@@ -1,88 +0,0 @@
-@(#)README 8.3
-This file is in the public domain, so clarified as of
-2009-05-17 by Arthur David Olson.
-
-$FreeBSD$
-
-"What time is it?" -- Richard Deacon as The King
-"Any time you want it to be." -- Frank Baxter as The Scientist
- (from the Bell System film "About Time")
-
-The 1989 update of the time zone package featured
-
-* POSIXization (including interpretation of POSIX-style TZ environment
- variables, provided by Guy Harris),
-* ANSIfication (including versions of "mktime" and "difftime"),
-* SVIDulation (an "altzone" variable)
-* MACHination (the "gtime" function)
-* corrections to some time zone data (including corrections to the rules
- for Great Britain and New Zealand)
-* reference data from the United States Naval Observatory for folks who
- want to do additional time zones
-* and the 1989 data for Saudi Arabia.
-
-(Since this code will be treated as "part of the implementation" in some places
-and as "part of the application" in others, there's no good way to name
-functions, such as timegm, that are not part of the proposed ANSI C standard;
-such functions have kept their old, underscore-free names in this update.)
-
-And the "dysize" function has disappeared; it was present to allow compilation
-of the "date" command on old BSD systems, and a version of "date" is now
-provided in the package. The "date" command is not created when you "make all"
-since it may lack options provided by the version distributed with your
-operating system, or may not interact with the system in the same way the
-native version does.
-
-Since POSIX frowns on correct leap second handling, the default behavior of
-the "zic" command (in the absence of a "-L" option) has been changed to omit
-leap second information from its output files.
-
-Here is a recipe for acquiring, building, installing, and testing the
-tz distribution on a GNU/Linux or similar host.
-
- mkdir tz
- cd tz
- wget 'ftp://elsie.nci.nih.gov/pub/tz*.tar.gz'
- gzip -dc tzcode*.tar.gz | tar -xf -
- gzip -dc tzdata*.tar.gz | tar -xf -
-
-Be sure to read the comments in "Makefile" and make any changes needed
-to make things right for your system, especially if you are using some
-platform other than GNU/Linux. Then run the following commands,
-substituting your desired installation directory for "$HOME/tzdir":
-
- make TOPDIR=$HOME/tzdir install
- $HOME/tzdir/etc/zdump -v America/Los_Angeles
-
-To use the new functions, use a "-ltz" option when compiling or linking.
-
-Historical local time information has been included here to:
-
-* provide a compendium of data about the history of civil time
- that is useful even if the data are not 100% accurate;
-
-* give an idea of the variety of local time rules that have
- existed in the past and thus an idea of the variety that may be
- expected in the future;
-
-* provide a test of the generality of the local time rule description
- system.
-
-The information in the time zone data files is by no means authoritative;
-the files currently do not even attempt to cover all time stamps before
-1970, and there are undoubtedly errors even for time stamps since 1970.
-If you know that the rules are different from those in a file, by all means
-feel free to change file (and please send the changed version to
-tz@elsie.nci.nih.gov for use in the future). Europeans take note!
-
-Thanks to these Timezone Caballeros who've made major contributions to the
-time conversion package: Keith Bostic; Bob Devine; Paul Eggert; Robert Elz;
-Guy Harris; Mark Horton; John Mackin; and Bradley White. Thanks also to
-Michael Bloom, Art Neilson, Stephen Prince, John Sovereign, and Frank Wales
-for testing work, and to Gwillim Law for checking local mean time data.
-None of them are responsible for remaining errors.
-
-Look in the ~ftp/pub directory of elsie.nci.nih.gov
-for updated versions of these files.
-
-Please send comments or information to tz@elsie.nci.nih.gov.
diff --git a/usr.sbin/zic/zdump/Makefile b/usr.sbin/zic/zdump/Makefile
deleted file mode 100644
index 9671e7bf5298..000000000000
--- a/usr.sbin/zic/zdump/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${SRCTOP}/contrib/tzcode/zic
-
-PROG= zdump
-MAN= zdump.8
-SRCS= zdump.c ialloc.c scheck.c
-
-CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
-CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"${SHAREDIR}/zoneinfo\" -Demkdir=mkdir
-CFLAGS+= -I${.CURDIR:H} -I${SRCTOP}/contrib/tzcode/stdtime
-
-WARNS?= 2
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/zic/zic/Makefile b/usr.sbin/zic/zic/Makefile
deleted file mode 100644
index 17ffb33a57e7..000000000000
--- a/usr.sbin/zic/zic/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-# $FreeBSD$
-
-.include <src.opts.mk>
-
-.PATH: ${SRCTOP}/contrib/tzcode/zic
-
-PROG= zic
-MAN= zic.8
-SRCS= zic.c ialloc.c scheck.c
-
-CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
-CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"${SHAREDIR}/zoneinfo\" -Demkdir=mkdir
-CFLAGS+= -DHAVE_STRERROR -DHAVE_UNISTD_H
-CFLAGS+= -I${.CURDIR:H} -I${SRCTOP}/contrib/tzcode/stdtime
-
-WARNS?= 2
-
-.if ${COMPILER_TYPE} == "gcc"
-CWARNFLAGS+= -Wno-error=strict-overflow
-.endif
-
-.include <bsd.prog.mk>