diff options
author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2023-01-10 15:14:27 +0000 |
---|---|---|
committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2023-01-10 15:14:27 +0000 |
commit | bc42155199b5b0b479311e05b07aee7f6f9c5172 (patch) | |
tree | c18d4dc2fe115961d11dc559b9925732bccfcdad | |
parent | 016e46fd869ebf9891ca4b2cf1d22b337717a8c8 (diff) | |
parent | 85639444f44f168af982f59143b53efbba37669e (diff) | |
download | src-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.
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→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° +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–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–1930, + CMT/BST for Calamarca Mean Time and Bolivian Summer Time + 1890–1932, + DMT/IST for Dublin/Dunsink Mean Time and Irish Summer Time + 1880–1916, + MMT/MST/MDST for Moscow 1880–1919, and + RMT/LST for Riga Mean Time and Latvian Summer time 1880–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–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&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> + −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><-03>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><+09></code>'; + this allows "<code>+</code>" and "<code>-</code>" in the names. + </dd> + <dt><var>offset</var></dt><dd> + is of the form + '<code>[±]<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≤<var>hh</var>≤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≤<var>n</var>≤365)</dt><dd> + origin-1 day number not counting February 29 + </dd> + <dt><var>n</var> (0≤<var>n</var>≤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]≤<var>d</var>≤6[Saturday], 1≤<var>n</var>≤5, + 1≤<var>m</var>≤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 – 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 – 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(&clock)->tm_zone</code> + (if <code>TM_ZONE</code> is defined) or + <code>tzname[localtime(&clock)->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" – 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 – +namely, at calls to <code>localtime</code> and analogous functions – +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–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–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 & Timezones – 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 × 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–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 & 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 – 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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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é +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–3). Its lyrics include "Amanecí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> </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> </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> </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> </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> </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> </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û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 – 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 – like so many myths, such as that +there's a reason for 'Daylight Saving Time' – 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 & 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 “man +page” 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’ll begin by talking about the rules for changing between standard +and daylight saving time since we’ll need that information when we talk +about the zones.</p> + +<p>First, let’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’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 “types” 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’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’s no reason, in principle, why it can’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’s name, or it can just +be a hyphen if there’s no variable part. For example, the abbreviation +used in the central time zone will be either “CST” or +“CDT”. The variable part is ‘S’ or ‘D’; +and, sure enough, that’s just what we find in +the <code>LETTER</code> column +in the <code>Chicago</code> rules. More about this when we talk about +“Zone” lines. + +<p>One important thing to notice is that “Rule” 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 “US” 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>=1 2:00 1:00 D +Rule US 2007 max - Mar Sun>=8 2:00 1:00 D +Rule US 2007 max - Nov Sun>=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 “war time”</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 “war time” to “peace<br>time;” + clocks don’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 ‘s’ (for “standard”) to mean +local standard time, different from local (wall clock) time when observing +daylight saving time; or it can be suffixed with ‘g’, +‘u’, or ‘z’, all three of which mean the +standard time at the +<a href="https://en.wikipedia.org/wiki/Prime_Meridian">prime meridian</a>. +‘g’ stands for “<a +href="https://en.wikipedia.org/wiki/Greenwich_Mean_Time">GMT</a>”; +‘u’ stands for “<a +href="https://en.wikipedia.org/wiki/Universal_Time">UT</a>” or “<a +href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">UTC</a>” +(whichever was official at the time); ‘z’ stands for the +<a href="https://en.wikipedia.org/wiki/Nautical_time">nautical time zone</a> +Z (a.k.a. “Zulu” which, in turn, stands for ‘Z’). +The time can also be suffixed with ‘w’ meaning local (wall +clock) time; but it usually isn’t because that’s the +default.</p> + +<p>Second, the day in the <code>ON</code> column, in addition to +“<code>lastSun</code>” or a particular day of the month, +can have the form, “<code>Sun>=</code><i>x</i>” or +“<code>Sun<=</code><i>x</i>,” where <i>x</i> is a day +of the month. For example, “<code>Sun>=8</code>” means +“the first Sunday on or after the eighth of the month,” in +other words, the second Sunday of the month. Furthermore, although +there are no examples above, the weekday needn’t be +“<code>Sun</code>” 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 “war time” to “peace time” +that happened on August 14, 1945. The “1:00” 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’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>−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">−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>−5:00:00</td> + <td>not observed</td> + <td>EST</td> + <td>1936-11-15</td> +</tr> +<tr> + <td rowspan="4">−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">—</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, +“<code>Zone</code>,” 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’s <code>[UNTIL]</code> +column up to the date and time in the current +line’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’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’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><aside></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’t civil time +wonderful? <code>8-)</code>)<code></aside></code></p> + +<p>The abbreviation, “LMT” stands for “local mean +time”, 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’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 “1:00” +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’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 − +10:30 + 1:00 = GMT − 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, +“<code>+</code>”, or “<code>-</code>”</li> + +<li>the string “%z”, in which case the +“<code>%z</code>” will be replaced by a numeric time zone +abbreviation</li> + +<li>a pair of time zone abbreviations separated by a slash +(‘<code>/</code>’), 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 “<code>%s</code>”, in which case +the “<code>%s</code>” will be replaced by the text in the +appropriate Rule’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’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 “correct” by law. For +example, the last line in +<code>Zone</code> <code>Pacific/Honolulu</code> (shown below) gives +“HST” for “Hawaii standard time” even though the +<a href="https://www.law.cornell.edu/uscode/text/15/263">legal</a> +name for that time zone is “Hawaii-Aleutian standard time.” +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>’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">“C” 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’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>−10:31:26</td> + <td>—</td> + <td>LMT</td> + <td>local mean time</td> + <td>1896-01-13</td> + <td>12:00</td> +</tr> +<tr> + <td>−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>−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>−10:30¹</td> + <td>−1:00¹</td> + <td>HST¹</td> + <td>Hawaii standard time</td> + <td>1942-02-09</td> + <td>02:00</td> +</tr> +<tr> + <td rowspan="2">−9:30</td> + <td>+1:00</td> + <td>HWT</td> + <td>Hawaii war time</td> + <td>1945-08-14</td> + <td>13:30²</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>−10:30</td> + <td>−1:00</td> + <td rowspan="2">HST</td> + <td rowspan="2">Hawaii standard time</td> + <td>1947-06-08</td> +</tr> +<tr> + <td>−10:00³</td> + <td>+0:30³</td> + <td colspan="2">—</td> +</tr> +<tr> + <td colspan="6" class="footnote"> + ¹Switching to US rules…most recent transition (in 1919) was to standard time + </td> +</tr> +<tr> + <td colspan="6" class="footnote"> + ²23:00 <a href="https://en.wikipedia.org/wiki/Universal_Time">UT</a> + + (−9:30) = 13:30 local + </td> +</tr> +<tr> + <td colspan="6" class="footnote"> + ³Since <a href="https://en.wikipedia.org/wiki/ISO_8601">1947–06–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> + − 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 – +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 – 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 – 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 – 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> – +<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 – 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 – +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 → +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–Castañ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 +– 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° apart, except that +<abbr>UT</abbr>−12 and <abbr>UT</abbr>+12 are each 7.5° +wide and are separated by +the 180° 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 – History, rationale, laws & 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–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–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 +– 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 – +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 072 000) and diverge for +timestamps starting with <code>time_t</code> 78 796 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">§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 – Date and time – Representations for information +interchange – Part 1: Basic rules</em></a>.</li> +<li> +<a href="https://www.w3.org/TR/xmlschema/#dateTime"><abbr>XML</abbr> +Schema: Datatypes – 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">§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 & 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 +−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> |