summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Bishop <stuart@stuartbishop.net>2021-09-26 18:24:53 +1000
committerStuart Bishop <stuart@stuartbishop.net>2021-09-26 18:24:53 +1000
commit00f969b51342ae2c4bc881b882271880e1774874 (patch)
treec8e280e0c2987340b146f47011916f6ce9021c6f
parentd42955013d7d43feae1aa628fbdd2827aab2c81a (diff)
parent16e9f021eff80d257f1ba56b9de2d79998eb785a (diff)
downloadpytz-git-00f969b51342ae2c4bc881b882271880e1774874.tar.gz
IANA 2021b
-rw-r--r--tz/CONTRIBUTING15
-rw-r--r--tz/Makefile43
-rw-r--r--tz/NEWS229
-rw-r--r--tz/SECURITY15
-rw-r--r--tz/africa135
-rw-r--r--tz/antarctica29
-rw-r--r--tz/asia34
-rw-r--r--tz/australasia120
-rw-r--r--tz/backward102
-rw-r--r--tz/backzone426
-rw-r--r--tz/checktab.awk50
-rw-r--r--tz/date.c4
-rw-r--r--tz/difftime.c4
-rw-r--r--tz/europe44
-rw-r--r--tz/leap-seconds.list8
-rw-r--r--tz/localtime.c396
-rw-r--r--tz/northamerica244
-rw-r--r--tz/private.h74
-rw-r--r--tz/southamerica87
-rw-r--r--tz/strftime.c12
-rw-r--r--tz/theory.html39
-rw-r--r--tz/tz-art.html18
-rw-r--r--tz/tz-link.html82
-rw-r--r--tz/tzfile.5116
-rw-r--r--tz/tzfile.h2
-rw-r--r--tz/zdump.825
-rw-r--r--tz/zdump.c230
-rw-r--r--tz/zic.845
-rw-r--r--tz/zic.c809
-rw-r--r--tz/ziguard.awk11
-rw-r--r--tz/zone.tab9
-rw-r--r--tz/zone1970.tab29
32 files changed, 2201 insertions, 1285 deletions
diff --git a/tz/CONTRIBUTING b/tz/CONTRIBUTING
index 01336fc..5373354 100644
--- a/tz/CONTRIBUTING
+++ b/tz/CONTRIBUTING
@@ -26,8 +26,11 @@ 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
+file SECURITY.
+
Please submit changes against either the latest release in
-<https://www.iana.org/time-zones> or the master branch of the development
+<https://www.iana.org/time-zones> or the main branch of the development
repository. The latter is preferred. If you use Git the following
workflow may be helpful:
@@ -36,9 +39,9 @@ workflow may be helpful:
git clone https://github.com/eggert/tz.git
cd tz
- * Get current with the master branch.
+ * Get current with the main branch.
- git checkout master
+ git checkout main
git pull
* Switch to a new branch for the changes. Choose a different
@@ -70,17 +73,17 @@ workflow may be helpful:
* Create patch files 0001-*, 0002-*, ...
- git format-patch master
+ git format-patch main
* After reviewing the patch files, send the patches to tz@iana.org
for others to review.
- git send-email master
+ git send-email main
For an archived example of such an email, see
<https://mm.icann.org/pipermail/tz/2018-February/026122.html>.
- * Start anew by getting current with the master branch again
+ * Start anew by getting current with the main branch again
(the second step above).
Please do not create issues or pull requests on GitHub, as the
diff --git a/tz/Makefile b/tz/Makefile
index 1136af9..33f3168 100644
--- a/tz/Makefile
+++ b/tz/Makefile
@@ -45,9 +45,9 @@ LOCALTIME= GMT
#
# 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 in the tzdb implementation for timestamps after 2037.
-# * It is incompatible with 'zic -b slim' if POSIXRULES specifies transitions
-# at standard time or UT rather than at local time.
+# * 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.
#
@@ -122,8 +122,8 @@ 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 = int64_t
-TIME_T_ALTERNATIVES_TAIL = int32_t uint32_t uint64_t
+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.)
@@ -152,8 +152,10 @@ REDO= posix_right
# 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 EXPIRES_LINE defaults to 0 for now so that the leapseconds file
-# can be given to older zic implementations.
+# 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,
@@ -210,6 +212,7 @@ LDLIBS=
# -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_SNPRINTF=0 if your system lacks the snprintf function
@@ -220,7 +223,6 @@ LDLIBS=
# -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 your compiler lacks a <sys/stat.h>
-# -DHAVE_SYS_WAIT_H=0 if your compiler lacks a <sys/wait.h>
# -DHAVE_TZSET=0 if your system lacks a tzset function
# -DHAVE_UNISTD_H=0 if your compiler lacks a <unistd.h>
# -Dlocale_t=XXX if your system uses XXX instead of locale_t
@@ -257,22 +259,26 @@ LDLIBS=
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 -Wcast-align=strict -Wdate-time \
-Wdeclaration-after-statement -Wdouble-promotion \
+ -Wduplicated-branches -Wduplicated-cond \
-Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
- -Winit-self -Wjump-misses-init -Wlogical-op \
+ -Winit-self -Wlogical-op \
-Wmissing-declarations -Wmissing-prototypes -Wnested-externs \
+ -Wnull-dereference \
-Wold-style-definition -Woverlength-strings -Wpointer-arith \
- -Wshadow -Wshift-overflow=2 -Wstrict-prototypes -Wstringop-overflow=4 \
+ -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 \
+ -Wtrampolines -Wundef -Wuninitialized -Wunused-macros \
-Wvariadic-macros -Wvla -Wwrite-strings \
-Wno-address -Wno-format-nonliteral -Wno-sign-compare \
-Wno-type-limits -Wno-unused-parameter
@@ -523,7 +529,7 @@ TZS_YEAR= 2050
TZS_CUTOFF_FLAG= -c $(TZS_YEAR)
TZS= to$(TZS_YEAR).tzs
TZS_NEW= to$(TZS_YEAR)new.tzs
-TZS_DEPS= $(PRIMARY_YDATA) asctime.c localtime.c \
+TZS_DEPS= $(YDATA) asctime.c localtime.c \
private.h tzfile.h zdump.c zic.c
# EIGHT_YARDS is just a yard short of the whole ENCHILADA.
EIGHT_YARDS = $(COMMON) $(DOCS) $(SOURCES) $(DATA) $(MISC) tzdata.zi
@@ -796,9 +802,10 @@ check_links: checklinks.awk $(TDATA_TO_CHECK) tzdata.zi
$(AWK) -f checklinks.awk tzdata.zi
touch $@
-check_tables: checktab.awk $(PRIMARY_YDATA) $(ZONETABLES)
+check_tables: checktab.awk $(YDATA) backward $(ZONETABLES)
for tab in $(ZONETABLES); do \
- $(AWK) -f checktab.awk -v zone_table=$$tab $(PRIMARY_YDATA) \
+ test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \
+ $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \
|| exit; \
done
touch $@
@@ -952,6 +959,12 @@ check_public: $(VERSION_DEPS)
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 -fr public.dir
touch $@
@@ -964,7 +977,7 @@ $(TIME_T_ALTERNATIVES): $(VERSION_DEPS)
mkdir $@.dir
ln $(VERSION_DEPS) $@.dir
case $@ in \
- int32_t) range=-2147483648,2147483648;; \
+ int*32_t) range=-2147483648,2147483648;; \
u*) range=0,4294967296;; \
*) range=-4294967296,4294967296;; \
esac && \
diff --git a/tz/NEWS b/tz/NEWS
index a60847b..d1ccf4e 100644
--- a/tz/NEWS
+++ b/tz/NEWS
@@ -1,5 +1,228 @@
News for the tz database
+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-compatility 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
@@ -31,7 +254,7 @@ Release 2020e - 2020-12-22 15:14:34 -0800
Correct many pre-1986 transitions, fixing entries originally
derived from Shanks. The fixes include:
- Australia: several 1917 through 1971 transitions
- - Bahamas: several 1941 through 1945 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
@@ -825,8 +1048,8 @@ Release 2018d - 2018-03-22 07:05:46 -0700
Institute in Montevideo.
(Thanks to Jeremie Bonjour, Tim Parenti, and Michael Deckers.)
- Enderbury and Kiritimati skipped New Year's Eve 1994, not
- New Year's Day 1995. (Thanks to Kerry Shetline.)
+ 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
diff --git a/tz/SECURITY b/tz/SECURITY
new file mode 100644
index 0000000..40128bc
--- /dev/null
+++ b/tz/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/tz/africa b/tz/africa
index 28168cf..c73f0df 100644
--- a/tz/africa
+++ b/tz/africa
@@ -30,9 +30,6 @@
# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94.
# https://www.jstor.org/stable/1774359
#
-# A reliable and entertaining source about time zones is
-# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997).
-#
# European-style abbreviations are commonly used along the Mediterranean.
# For sub-Saharan Africa abbreviations were less standardized.
# Previous editions of this database used WAT, CAT, SAT, and EAT
@@ -153,15 +150,6 @@ Zone Africa/Ndjamena 1:00:12 - LMT 1912 # N'Djamena
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Africa/Abidjan -0:16:08 - LMT 1912
0:00 - GMT
-Link Africa/Abidjan Africa/Bamako # Mali
-Link Africa/Abidjan Africa/Banjul # Gambia
-Link Africa/Abidjan Africa/Conakry # Guinea
-Link Africa/Abidjan Africa/Dakar # Senegal
-Link Africa/Abidjan Africa/Freetown # Sierra Leone
-Link Africa/Abidjan Africa/Lome # Togo
-Link Africa/Abidjan Africa/Nouakchott # Mauritania
-Link Africa/Abidjan Africa/Ouagadougou # Burkina Faso
-Link Africa/Abidjan Atlantic/St_Helena # St Helena
# Djibouti
# See Africa/Nairobi.
@@ -381,93 +369,8 @@ Zone Africa/Cairo 2:05:09 - LMT 1900 Oct
# Gabon
# See Africa/Lagos.
-# Gambia
-# See Africa/Abidjan.
-
+# The Gambia
# Ghana
-
-# From P Chan (2020-11-20):
-# Interpretation Amendment Ordinance, 1915 (No.24 of 1915) [1915-11-02]
-# Ordinances of the Gold Coast, Ashanti, Northern Territories 1915, p 69-71
-# https://books.google.com/books?id=ErA-AQAAIAAJ&pg=PA70
-# This Ordinance added "'Time' shall mean Greenwich Mean Time" to the
-# Interpretation Ordinance, 1876.
-#
-# Determination of the Time Ordinance, 1919 (No. 18 of 1919) [1919-11-24]
-# Ordinances of the Gold Coast, Ashanti, Northern Territories 1919, p 75-76
-# https://books.google.com/books?id=MbA-AQAAIAAJ&pg=PA75
-# This Ordinance removed the previous definition of time and introduced DST.
-#
-# Time Determination Ordinance (Cap. 214)
-# The Laws of the Gold Coast (including Togoland Under British Mandate)
-# Vol. II (1937), p 2328
-# https://books.google.com/books?id=Z7M-AQAAIAAJ&pg=PA2328
-# Revised edition of the 1919 Ordinance.
-#
-# Time Determination (Amendment) Ordinance, 1940 (No. 9 of 1940) [1940-04-06]
-# Annual Volume of the Laws of the Gold Coast:
-# Containing All Legislation Enacted During Year 1940, p 22
-# https://books.google.com/books?id=1ao-AQAAIAAJ&pg=PA22
-# This Ordinance changed the forward transition from September to May.
-#
-# Defence (Time Determination Ordinance Amendment) Regulations, 1942
-# (Regulations No. 6 of 1942) [1942-01-31, commenced on 1942-02-08]
-# Annual Volume of the Laws of the Gold Coast:
-# Containing All Legislation Enacted During Year 1942, p 48
-# https://books.google.com/books?id=Das-AQAAIAAJ&pg=PA48
-# These regulations advanced the [standard] time by thirty minutes.
-#
-# Defence (Time Determination Ordinance Amendment (No.2)) Regulations,
-# 1942 (Regulations No. 28 of 1942) [1942-04-25]
-# Annual Volume of the Laws of the Gold Coast:
-# Containing All Legislation Enacted During Year 1942, p 87
-# https://books.google.com/books?id=Das-AQAAIAAJ&pg=PA87
-# These regulations abolished DST and changed the time to GMT+0:30.
-#
-# Defence (Revocation) (No.4) Regulations, 1945 (Regulations No. 45 of
-# 1945) [1945-10-24, commenced on 1946-01-06]
-# Annual Volume of the Laws of the Gold Coast:
-# Containing All Legislation Enacted During Year 1945, p 256
-# https://books.google.com/books?id=9as-AQAAIAAJ&pg=PA256
-# These regulations revoked the previous two sets of Regulations.
-#
-# Time Determination (Amendment) Ordinance, 1945 (No. 18 of 1945) [1946-01-06]
-# Annual Volume of the Laws of the Gold Coast:
-# Containing All Legislation Enacted During Year 1945, p 69
-# https://books.google.com/books?id=9as-AQAAIAAJ&pg=PA69
-# This Ordinance abolished DST.
-#
-# Time Determination (Amendment) Ordinance, 1950 (No. 26 of 1950) [1950-07-22]
-# Annual Volume of the Laws of the Gold Coast:
-# Containing All Legislation Enacted During Year 1950, p 35
-# https://books.google.com/books?id=e60-AQAAIAAJ&pg=PA35
-# This Ordinance restored DST but with thirty minutes offset.
-#
-# Time Determination Ordinance (Cap. 264)
-# The Laws of the Gold Coast, Vol. V (1954), p 380
-# https://books.google.com/books?id=Mqc-AQAAIAAJ&pg=PA380
-# Revised edition of the Time Determination Ordinance.
-#
-# Time Determination (Amendment) Ordinance, 1956 (No. 21 of 1956) [1956-08-29]
-# Annual Volume of the Ordinances of the Gold Coast Enacted During the
-# Year 1956, p 83
-# https://books.google.com/books?id=VLE-AQAAIAAJ&pg=PA83
-# This Ordinance abolished DST.
-
-# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
-Rule Ghana 1919 only - Nov 24 0:00 0:20 +0020
-Rule Ghana 1920 1942 - Jan 1 2:00 0 GMT
-Rule Ghana 1920 1939 - Sep 1 2:00 0:20 +0020
-Rule Ghana 1940 1941 - May 1 2:00 0:20 +0020
-Rule Ghana 1950 1955 - Sep 1 2:00 0:30 +0030
-Rule Ghana 1951 1956 - Jan 1 2:00 0 GMT
-
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone Africa/Accra -0:00:52 - LMT 1915 Nov 2
- 0:00 Ghana %s 1942 Feb 8
- 0:30 - +0030 1946 Jan 6
- 0:00 Ghana %s
-
# Guinea
# See Africa/Abidjan.
@@ -533,15 +436,6 @@ Zone Africa/Nairobi 2:27:16 - LMT 1908 May
2:30 - +0230 1936 Dec 31 24:00
2:45 - +0245 1942 Jul 31 24:00
3:00 - EAT
-Link Africa/Nairobi Africa/Addis_Ababa # Ethiopia
-Link Africa/Nairobi Africa/Asmara # Eritrea
-Link Africa/Nairobi Africa/Dar_es_Salaam # Tanzania
-Link Africa/Nairobi Africa/Djibouti
-Link Africa/Nairobi Africa/Kampala # Uganda
-Link Africa/Nairobi Africa/Mogadishu # Somalia
-Link Africa/Nairobi Indian/Antananarivo # Madagascar
-Link Africa/Nairobi Indian/Comoro
-Link Africa/Nairobi Indian/Mayotte
# Lesotho
# See Africa/Johannesburg.
@@ -732,7 +626,7 @@ Zone Indian/Mauritius 3:50:00 - LMT 1907 # Port Louis
# See Africa/Nairobi.
# Morocco
-# See the 'europe' file for Spanish Morocco (Africa/Ceuta).
+# See Africa/Ceuta for Spanish Morocco.
# From Alex Krivenyshev (2008-05-09):
# Here is an article that Morocco plan to introduce Daylight Saving Time between
@@ -1228,13 +1122,6 @@ Zone Africa/El_Aaiun -0:52:48 - LMT 1934 Jan # El Aaiún
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Africa/Maputo 2:10:20 - LMT 1903 Mar
2:00 - CAT
-Link Africa/Maputo Africa/Blantyre # Malawi
-Link Africa/Maputo Africa/Bujumbura # Burundi
-Link Africa/Maputo Africa/Gaborone # Botswana
-Link Africa/Maputo Africa/Harare # Zimbabwe
-Link Africa/Maputo Africa/Kigali # Rwanda
-Link Africa/Maputo Africa/Lubumbashi # E Dem. Rep. of Congo
-Link Africa/Maputo Africa/Lusaka # Zambia
# Namibia
@@ -1382,23 +1269,12 @@ Zone Africa/Lagos 0:13:35 - LMT 1905 Jul 1
0:13:35 - LMT 1914 Jan 1
0:30 - +0030 1919 Sep 1
1:00 - WAT
-Link Africa/Lagos Africa/Bangui # Central African Republic
-Link Africa/Lagos Africa/Brazzaville # Rep. of the Congo
-Link Africa/Lagos Africa/Douala # Cameroon
-Link Africa/Lagos Africa/Kinshasa # Dem. Rep. of the Congo (west)
-Link Africa/Lagos Africa/Libreville # Gabon
-Link Africa/Lagos Africa/Luanda # Angola
-Link Africa/Lagos Africa/Malabo # Equatorial Guinea
-Link Africa/Lagos Africa/Niamey # Niger
-Link Africa/Lagos Africa/Porto-Novo # Benin
# Réunion
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Indian/Reunion 3:41:52 - LMT 1911 Jun # Saint-Denis
4:00 - +04
#
-# Crozet Islands also observes Réunion time; see the 'antarctica' file.
-#
# Scattered Islands (Îles Éparses) administered from Réunion are as follows.
# The following information about them is taken from
# Îles Éparses (<http://www.outre-mer.gouv.fr/domtom/ile.htm>, 1997-07-22,
@@ -1490,8 +1366,6 @@ Rule SA 1943 1944 - Mar Sun>=15 2:00 0 -
Zone Africa/Johannesburg 1:52:00 - LMT 1892 Feb 8
1:30 - SAST 1903 Mar
2:00 SA SAST
-Link Africa/Johannesburg Africa/Maseru # Lesotho
-Link Africa/Johannesburg Africa/Mbabane # Eswatini
#
# Marion and Prince Edward Is
# scientific station since 1947
@@ -1527,12 +1401,13 @@ Zone Africa/Khartoum 2:10:08 - LMT 1931
3:00 - EAT 2017 Nov 1
2:00 - CAT
+# South Sudan
+
# From Steffen Thorsen (2021-01-18):
# "South Sudan will change its time zone by setting the clock back 1
# hour on February 1, 2021...."
# from https://eyeradio.org/south-sudan-adopts-new-time-zone-makuei/
-# South Sudan
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Africa/Juba 2:06:28 - LMT 1931
2:00 Sudan CA%sT 2000 Jan 15 12:00
@@ -1637,7 +1512,7 @@ Rule Tunisia 2005 only - Sep 30 1:00s 0 -
Rule Tunisia 2006 2008 - Mar lastSun 2:00s 1:00 S
Rule Tunisia 2006 2008 - Oct lastSun 2:00s 0 -
-# See Europe/Paris for PMT-related transitions.
+# See Europe/Paris commentary for PMT-related transitions.
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Africa/Tunis 0:40:44 - LMT 1881 May 12
0:09:21 - PMT 1911 Mar 11 # Paris Mean Time
diff --git a/tz/antarctica b/tz/antarctica
index ed750a8..70a5422 100644
--- a/tz/antarctica
+++ b/tz/antarctica
@@ -148,7 +148,7 @@ Zone Antarctica/Mawson 0 - -00 1954 Feb 13
#
# Alfred Faure, Possession Island, Crozet Islands, -462551+0515152, since 1964;
# sealing & whaling stations operated variously 1802/1911+;
-# see Indian/Reunion.
+# see Asia/Dubai.
#
# Martin-de-Viviès, Amsterdam Island, -374105+0773155, since 1950
# Port-aux-Français, Kerguelen Islands, -492110+0701303, since 1951;
@@ -162,17 +162,7 @@ Zone Indian/Kerguelen 0 - -00 1950 # Port-aux-Français
5:00 - +05
#
# year-round base in the main continent
-# Dumont d'Urville, Île des Pétrels, -6640+14001, since 1956-11
-# <https://en.wikipedia.org/wiki/Dumont_d'Urville_Station> (2005-12-05)
-#
-# Another base at Port-Martin, 50km east, began operation in 1947.
-# It was destroyed by fire on 1952-01-14.
-#
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone Antarctica/DumontDUrville 0 - -00 1947
- 10:00 - +10 1952 Jan 14
- 0 - -00 1956 Nov
- 10:00 - +10
+# Dumont d'Urville - see Pacific/Port_Moresby.
# France & Italy - year-round base
# Concordia, -750600+1232000, since 2005
@@ -188,20 +178,7 @@ Zone Antarctica/DumontDUrville 0 - -00 1947
# Zuchelli, Terra Nova Bay, -744140+1640647, since 1986
# Japan - year-round bases
-# Syowa (also known as Showa), -690022+0393524, since 1957
-#
-# From Hideyuki Suzuki (1999-02-06):
-# In all Japanese stations, +0300 is used as the standard time.
-#
-# Syowa station, which is the first antarctic station of Japan,
-# was established on 1957-01-29. Since Syowa station is still the main
-# station of Japan, it's appropriate for the principal location.
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone Antarctica/Syowa 0 - -00 1957 Jan 29
- 3:00 - +03
-# See:
-# NIPR Antarctic Research Activities (1999-08-17)
-# http://www.nipr.ac.jp/english/ara01.html
+# See Asia/Riyadh.
# S Korea - year-round base
# Jang Bogo, Terra Nova Bay, -743700+1641205 since 2014
diff --git a/tz/asia b/tz/asia
index ed94413..73e0183 100644
--- a/tz/asia
+++ b/tz/asia
@@ -34,9 +34,6 @@
# Byalokoz EL. New Counting of Time in Russia since July 1, 1919.
# (See the 'europe' file for a fuller citation.)
#
-# A reliable and entertaining source about time zones is
-# Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997).
-#
# The following alphabetic abbreviations appear in these tables
# (corrections are welcome):
# std dst
@@ -1176,10 +1173,9 @@ Zone Asia/Famagusta 2:15:48 - LMT 1921 Nov 14
2:00 EUAsia EE%sT 2016 Sep 8
3:00 - +03 2017 Oct 29 1:00u
2:00 EUAsia EE%sT
-
# Classically, Cyprus belongs to Asia; e.g. see Herodotus, Histories, I.72.
# However, for various reasons many users expect to find it under Europe.
-Link Asia/Nicosia Europe/Nicosia
+# See the 'backward' file for the Europe/Nicosia link.
# Georgia
# From Paul Eggert (1994-11-19):
@@ -2234,6 +2230,14 @@ Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u
# From Paul Eggert (2013-12-11):
# As Steffen suggested, consider the past 21-month experiment to be DST.
+# From Steffen Thorsen (2021-09-24):
+# The Jordanian Government announced yesterday that they will start DST
+# in February instead of March:
+# https://petra.gov.jo/Include/InnerPage.jsp?ID=37683&lang=en&name=en_news (English)
+# https://petra.gov.jo/Include/InnerPage.jsp?ID=189969&lang=ar&name=news (Arabic)
+# From the Arabic version, it seems to say it would be at midnight
+# (assume 24:00) on the last Thursday in February, starting from 2022.
+
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule Jordan 1973 only - Jun 6 0:00 1:00 S
Rule Jordan 1973 1975 - Oct 1 0:00 0 -
@@ -2264,8 +2268,9 @@ Rule Jordan 2004 only - Oct 15 0:00s 0 -
Rule Jordan 2005 only - Sep lastFri 0:00s 0 -
Rule Jordan 2006 2011 - Oct lastFri 0:00s 0 -
Rule Jordan 2013 only - Dec 20 0:00 0 -
-Rule Jordan 2014 max - Mar lastThu 24:00 1:00 S
+Rule Jordan 2014 2021 - Mar lastThu 24:00 1:00 S
Rule Jordan 2014 max - Oct lastFri 0:00s 0 -
+Rule Jordan 2022 max - Feb lastThu 24:00 1:00 S
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Amman 2:23:44 - LMT 1931
2:00 Jordan EE%sT
@@ -2740,7 +2745,8 @@ Rule NBorneo 1935 1941 - Dec 14 0:00 0 -
#
# peninsular Malaysia
# taken from Mok Ly Yng (2003-10-30)
-# http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html
+# https://web.archive.org/web/20190822231045/http://www.math.nus.edu.sg/~mathelmr/teaching/timezone.html
+# This agrees with Singapore since 1905-06-01.
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Kuala_Lumpur 6:46:46 - LMT 1901 Jan 1
6:55:25 - SMT 1905 Jun 1 # Singapore M.T.
@@ -3500,6 +3506,12 @@ Zone Asia/Hebron 2:20:23 - LMT 1900 Oct
# influence of the sources. There is no current abbreviation for DST,
# so use "PDT", the usual American style.
+# From P Chan (2021-05-10):
+# Here's a fairly comprehensive article in Japanese:
+# https://wiki.suikawiki.org/n/Philippine%20Time
+# From Paul Eggert (2021-05-10):
+# The info in the Japanese table has not been absorbed (yet) below.
+
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule Phil 1936 only - Nov 1 0:00 1:00 D
Rule Phil 1937 only - Feb 1 0:00 0 S
@@ -3519,7 +3531,6 @@ Zone Asia/Manila -15:56:00 - LMT 1844 Dec 31
Zone Asia/Qatar 3:26:08 - LMT 1920 # Al Dawhah / Doha
4:00 - +04 1972 Jun
3:00 - +03
-Link Asia/Qatar Asia/Bahrain
# Saudi Arabia
#
@@ -3566,12 +3577,10 @@ Link Asia/Qatar Asia/Bahrain
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Riyadh 3:06:52 - LMT 1947 Mar 14
3:00 - +03
-Link Asia/Riyadh Asia/Aden # Yemen
-Link Asia/Riyadh Asia/Kuwait
# Singapore
# taken from Mok Ly Yng (2003-10-30)
-# http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html
+# https://web.archive.org/web/20190822231045/http://www.math.nus.edu.sg/~mathelmr/teaching/timezone.html
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1
6:55:25 - SMT 1905 Jun 1 # Singapore M.T.
@@ -3824,8 +3833,6 @@ Zone Asia/Dushanbe 4:35:12 - LMT 1924 May 2
Zone Asia/Bangkok 6:42:04 - LMT 1880
6:42:04 - BMT 1920 Apr # Bangkok Mean Time
7:00 - +07
-Link Asia/Bangkok Asia/Phnom_Penh # Cambodia
-Link Asia/Bangkok Asia/Vientiane # Laos
# Turkmenistan
# From Shanks & Pottenger.
@@ -3840,7 +3847,6 @@ Zone Asia/Ashgabat 3:53:32 - LMT 1924 May 2 # or Ashkhabad
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Asia/Dubai 3:41:12 - LMT 1920
4:00 - +04
-Link Asia/Dubai Asia/Muscat # Oman
# Uzbekistan
# Byalokoz 1919 says Uzbekistan was 4:27:53.
diff --git a/tz/australasia b/tz/australasia
index cf8a063..8e76c27 100644
--- a/tz/australasia
+++ b/tz/australasia
@@ -458,13 +458,12 @@ Zone Pacific/Guam -14:21:00 - LMT 1844 Dec 31
9:00 - +09 1944 Jul 31
10:00 Guam G%sT 2000 Dec 23
10:00 - ChST # Chamorro Standard Time
-Link Pacific/Guam Pacific/Saipan # N Mariana Is
# Kiribati
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Pacific/Tarawa 11:32:04 - LMT 1901 # Bairiki
12:00 - +12
-Zone Pacific/Enderbury -11:24:20 - LMT 1901
+Zone Pacific/Kanton 0 - -00 1937 Aug 31
-12:00 - -12 1979 Oct
-11:00 - -11 1994 Dec 31
13:00 - +13
@@ -584,8 +583,6 @@ Zone Pacific/Chatham 12:13:48 - LMT 1868 Nov 2
12:15 - +1215 1946 Jan 1
12:45 Chatham +1245/+1345
-Link Pacific/Auckland Antarctica/McMurdo
-
# Auckland Is
# uninhabited; Māori and Moriori, colonial settlers, pastoralists, sealers,
# and scientific personnel have wintered
@@ -597,13 +594,46 @@ Link Pacific/Auckland Antarctica/McMurdo
# was probably like Pacific/Auckland
# Cook Is
-# From Shanks & Pottenger:
+#
+# From Alexander Krivenyshev (2021-03-24):
+# In 1899 the Cook Islands celebrated Christmas twice to correct the calendar.
+# According to the old books, missionaries were unaware of
+# the International Date line, when they came from Sydney.
+# Thus the Cook Islands were one day ahead....
+# http://nzetc.victoria.ac.nz/tm/scholarly/tei-KloDisc-t1-body-d18.html
+# ... Appendix to the Journals of the House of Representatives, 1900
+# https://atojs.natlib.govt.nz/cgi-bin/atojs?a=d&d=AJHR1900-I.2.1.2.3
+# (page 20)
+#
+# From Michael Deckers (2021-03-24):
+# ... in the Cook Island Act of 1915-10-11, online at
+# http://www.paclii.org/ck/legis/ck-nz_act/cia1915132/
+# "651. The hour of the day shall in each of the islands included in the
+# Cook Islands be determined in accordance with the meridian of that island."
+# so that local (mean?) time was still used in Rarotonga (and Niue) in 1915.
+# This was changed in the Cook Island Amendment Act of 1952-10-16 ...
+# http://www.paclii.org/ck/legis/ck-nz_act/ciaa1952212/
+# "651 (1) The hour of the day in each of the islands included in the Cook
+# Islands, other than Niue, shall be determined as if each island were
+# situated on the meridian one hundred and fifty-seven degrees thirty minutes
+# West of Greenwich. (2) The hour of the day in the Island of Niue shall be
+# determined as if that island were situated on the meridian one hundred and
+# seventy degrees West of Greenwich."
+# This act does not state when it takes effect, so one has to assume it
+# applies since 1952-10-16. But there is the possibility that the act just
+# legalized prior existing practice, as we had seen with the Guernsey law of
+# 1913-06-18 for the switch in 1909-04-19.
+#
+# From Paul Eggert (2021-03-24):
+# Transitions after 1952 are from Shanks & Pottenger.
+#
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule Cook 1978 only - Nov 12 0:00 0:30 -
Rule Cook 1979 1991 - Mar Sun>=1 0:00 0 -
Rule Cook 1979 1990 - Oct lastSun 0:00 0:30 -
# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone Pacific/Rarotonga -10:39:04 - LMT 1901 # Avarua
+Zone Pacific/Rarotonga 13:20:56 - LMT 1899 Dec 26 # Avarua
+ -10:39:04 - LMT 1952 Oct 16
-10:30 - -1030 1978 Nov 12
-10:00 Cook -10/-0930
@@ -611,10 +641,18 @@ Zone Pacific/Rarotonga -10:39:04 - LMT 1901 # Avarua
# Niue
+# See Pacific/Raratonga comments for 1952 transition.
+#
+# From Tim Parenti (2021-09-13):
+# Consecutive contemporaneous editions of The Air Almanac listed -11:20 for
+# Niue as of Apr 1964 but -11 as of Aug 1964:
+# Apr 1964: https://books.google.com/books?id=_1So677Y5vUC&pg=SL1-PA23
+# Aug 1964: https://books.google.com/books?id=MbJloqd-zyUC&pg=SL1-PA23
+# Without greater specificity, guess 1964-07-01 for this transition.
+
# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone Pacific/Niue -11:19:40 - LMT 1901 # Alofi
- -11:20 - -1120 1951
- -11:30 - -1130 1978 Oct 1
+Zone Pacific/Niue -11:19:40 - LMT 1952 Oct 16 # Alofi
+ -11:20 - -1120 1964 Jul
-11:00 - -11
# Norfolk
@@ -673,7 +711,6 @@ Zone Pacific/Pitcairn -8:40:20 - LMT 1901 # Adamstown
Zone Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5
-11:22:48 - LMT 1911
-11:00 - SST # S=Samoa
-Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands
# Samoa (formerly and also known as Western Samoa)
@@ -742,13 +779,17 @@ Link Pacific/Pago_Pago Pacific/Midway # in US minor outlying islands
# From Paul Eggert (2014-07-08):
# That web page currently lists transitions for 2012/3 and 2013/4.
# Assume the pattern instituted in 2012 will continue indefinitely.
+#
+# From Geoffrey D. Bennett (2021-09-20):
+# https://www.mcil.gov.ws/storage/2021/09/MCIL-Scan_20210920_120553.pdf
+# DST has been cancelled for this year.
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule WS 2010 only - Sep lastSun 0:00 1 -
Rule WS 2011 only - Apr Sat>=1 4:00 0 -
Rule WS 2011 only - Sep lastSat 3:00 1 -
-Rule WS 2012 max - Apr Sun>=1 4:00 0 -
-Rule WS 2012 max - Sep lastSun 3:00 1 -
+Rule WS 2012 2021 - Apr Sun>=1 4:00 0 -
+Rule WS 2012 2020 - Sep lastSun 3:00 1 -
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Pacific/Apia 12:33:04 - LMT 1892 Jul 5
-11:26:56 - LMT 1911
@@ -795,8 +836,8 @@ Rule Tonga 2001 2002 - Jan lastSun 2:00 0 -
Rule Tonga 2016 only - Nov Sun>=1 2:00 1:00 -
Rule Tonga 2017 only - Jan Sun>=15 3:00 0 -
# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone Pacific/Tongatapu 12:19:20 - LMT 1901
- 12:20 - +1220 1941
+Zone Pacific/Tongatapu 12:19:12 - LMT 1945 Sep 10
+ 12:20 - +1220 1961
13:00 - +13 1999
13:00 Tonga +13/+14
@@ -1738,6 +1779,23 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901
# One source for this is page 202 of: Bartky IR. One Time Fits All:
# The Campaigns for Global Uniformity (2007).
+# Kanton
+
+# From Paul Eggert (2021-05-27):
+# Kiribati's +13 timezone is represented by Kanton, its only populated
+# island. (It was formerly spelled "Canton", but Gilbertese lacks "C".)
+# Kanton was settled on 1937-08-31 by two British radio operators
+# <https://history.state.gov/historicaldocuments/frus1937v02/d94>;
+# Americans came the next year and built an airfield, partly to
+# establish airline service and perhaps partly anticipating the
+# next war. Aside from the war, the airfield was used by commercial
+# airlines until long-range jets became standard; although currently
+# for emergency use only, China says it is considering rebuilding the
+# airfield for high-end niche tourism. Kanton has about two dozen
+# people, caretakers who rotate in from the rest of Kiribati in 2-5
+# year shifts, and who use some of the leftover structures
+# <http://pipa.neaq.org/2012/06/images-of-kanton-island.html>.
+
# Kwajalein
# From an AP article (1993-08-22):
@@ -2021,6 +2079,17 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901
# Tonga
+# From Paul Eggert (2021-03-04):
+# In 1943 "The standard time kept is 12 hrs. 19 min. 12 sec. fast
+# on Greenwich mean time." according to the Admiralty's Hydrographic
+# Dept., Pacific Islands Pilot, Vol. II, 7th ed., 1943, p 360.
+
+# From Michael Deckers (2021-03-03):
+# [Ian R Bartky: "One Time Fits All: The Campaigns for Global Uniformity".
+# Stanford University Press. 2007. p. 255]:
+# On 10 September 1945 Tonga adopted a standard time 12 hours,
+# 20 minutes in advance of Greenwich.
+
# From Paul Eggert (1996-01-22):
# Today's _Wall Street Journal_ (p 1) reports that "Tonga has been plotting
# to sneak ahead of [New Zealanders] by introducing daylight-saving time."
@@ -2049,9 +2118,26 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901
# The Crown Prince, presented an unanswerable argument: "Remember that
# on the World Day of Prayer, you would be the first people on Earth
# to say your prayers in the morning."
-
-# From Paul Eggert (2006-03-22):
-# Shanks & Pottenger say the transition was on 1968-10-01; go with Mundell.
+#
+# From Tim Parenti (2021-09-13), per Paul Eggert (2006-03-22) and Michael
+# Deckers (2021-03-03):
+# Mundell places the transition from +12:20 to +13 in 1941, while Shanks &
+# Pottenger say the transition was on 1968-10-01.
+#
+# The Air Almanac published contemporaneous tables of standard times,
+# which listed +12:20 as of Nov 1960 and +13 as of Mar 1961:
+# Nov 1960: https://books.google.com/books?id=bVgtWM6kPZUC&pg=SL1-PA19
+# Mar 1961: https://books.google.com/books?id=W2nItAul4g0C&pg=SL1-PA19
+# (Thanks to P Chan for pointing us toward these sources.)
+# This agrees with Bartky, who writes that "since 1961 [Tonga's] official time
+# has been thirteen hours in advance of Greenwich time" (p. 202) and further
+# writes in an endnote that this was because "the legislation was amended" on
+# 1960-10-19. (p. 255)
+#
+# Without greater specificity, presume that Bartky and the Air Almanac point to
+# a 1961-01-01 transition, as Tāufaʻāhau Tupou IV was still Crown Prince in
+# 1961 and this still jives with the gist of Mundell's telling, and go with
+# this over Shanks & Pottenger.
# From Eric Ulevik (1999-05-03):
# Tonga's director of tourism, who is also secretary of the National Millennium
diff --git a/tz/backward b/tz/backward
index 0c55be2..7c288e3 100644
--- a/tz/backward
+++ b/tz/backward
@@ -3,50 +3,121 @@
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
-# This file provides links between current names for timezones
-# and their old names. Many names changed in late 1993.
+# This file provides links from old or merged timezone names to current ones.
+# Many names changed in late 1993, and many merged names moved here
+# in the period from 2013 through 2021. Several of these names are
+# also present in the file 'backzone', which has data important only
+# for pre-1970 timestamps and so is out of scope for tzdb proper.
# Link TARGET LINK-NAME
+Link Africa/Abidjan Africa/Accra
+Link Africa/Nairobi Africa/Addis_Ababa
+Link Africa/Nairobi Africa/Asmara
Link Africa/Nairobi Africa/Asmera
+Link Africa/Abidjan Africa/Bamako
+Link Africa/Lagos Africa/Bangui
+Link Africa/Abidjan Africa/Banjul
+Link Africa/Maputo Africa/Blantyre
+Link Africa/Lagos Africa/Brazzaville
+Link Africa/Maputo Africa/Bujumbura
+Link Africa/Abidjan Africa/Conakry
+Link Africa/Abidjan Africa/Dakar
+Link Africa/Nairobi Africa/Dar_es_Salaam
+Link Africa/Nairobi Africa/Djibouti
+Link Africa/Lagos Africa/Douala
+Link Africa/Abidjan Africa/Freetown
+Link Africa/Maputo Africa/Gaborone
+Link Africa/Maputo Africa/Harare
+Link Africa/Nairobi Africa/Kampala
+Link Africa/Maputo Africa/Kigali
+Link Africa/Lagos Africa/Kinshasa
+Link Africa/Lagos Africa/Libreville
+Link Africa/Abidjan Africa/Lome
+Link Africa/Lagos Africa/Luanda
+Link Africa/Maputo Africa/Lubumbashi
+Link Africa/Maputo Africa/Lusaka
+Link Africa/Lagos Africa/Malabo
+Link Africa/Johannesburg Africa/Maseru
+Link Africa/Johannesburg Africa/Mbabane
+Link Africa/Nairobi Africa/Mogadishu
+Link Africa/Lagos Africa/Niamey
+Link Africa/Abidjan Africa/Nouakchott
+Link Africa/Abidjan Africa/Ouagadougou
+Link Africa/Lagos Africa/Porto-Novo
Link Africa/Abidjan Africa/Timbuktu
+Link America/Puerto_Rico America/Anguilla
+Link America/Puerto_Rico America/Antigua
Link America/Argentina/Catamarca America/Argentina/ComodRivadavia
+Link America/Puerto_Rico America/Aruba
+Link America/Panama America/Atikokan
Link America/Adak America/Atka
+Link America/Puerto_Rico America/Blanc-Sablon
Link America/Argentina/Buenos_Aires America/Buenos_Aires
Link America/Argentina/Catamarca America/Catamarca
-Link America/Atikokan America/Coral_Harbour
+Link America/Panama America/Cayman
+Link America/Panama America/Coral_Harbour
Link America/Argentina/Cordoba America/Cordoba
+Link America/Phoenix America/Creston
+Link America/Puerto_Rico America/Curacao
+Link America/Puerto_Rico America/Dominica
Link America/Tijuana America/Ensenada
Link America/Indiana/Indianapolis America/Fort_Wayne
Link America/Nuuk America/Godthab
+Link America/Puerto_Rico America/Grenada
+Link America/Puerto_Rico America/Guadeloupe
Link America/Indiana/Indianapolis America/Indianapolis
Link America/Argentina/Jujuy America/Jujuy
Link America/Indiana/Knox America/Knox_IN
+Link America/Puerto_Rico America/Kralendijk
Link America/Kentucky/Louisville America/Louisville
+Link America/Puerto_Rico America/Lower_Princes
+Link America/Puerto_Rico America/Marigot
Link America/Argentina/Mendoza America/Mendoza
Link America/Toronto America/Montreal
+Link America/Puerto_Rico America/Montserrat
+Link America/Toronto America/Nassau
+Link America/Puerto_Rico America/Port_of_Spain
Link America/Rio_Branco America/Porto_Acre
Link America/Argentina/Cordoba America/Rosario
Link America/Tijuana America/Santa_Isabel
Link America/Denver America/Shiprock
-Link America/Port_of_Spain America/Virgin
+Link America/Puerto_Rico America/St_Barthelemy
+Link America/Puerto_Rico America/St_Kitts
+Link America/Puerto_Rico America/St_Lucia
+Link America/Puerto_Rico America/St_Thomas
+Link America/Puerto_Rico America/St_Vincent
+Link America/Puerto_Rico America/Tortola
+Link America/Puerto_Rico America/Virgin
+Link Pacific/Port_Moresby Antarctica/DumontDUrville
+Link Pacific/Auckland Antarctica/McMurdo
Link Pacific/Auckland Antarctica/South_Pole
+Link Asia/Riyadh Antarctica/Syowa
+Link Europe/Oslo Arctic/Longyearbyen
+Link Asia/Riyadh Asia/Aden
Link Asia/Ashgabat Asia/Ashkhabad
+Link Asia/Qatar Asia/Bahrain
Link Asia/Kolkata Asia/Calcutta
Link Asia/Shanghai Asia/Chongqing
Link Asia/Shanghai Asia/Chungking
Link Asia/Dhaka Asia/Dacca
Link Asia/Shanghai Asia/Harbin
+Link Europe/Istanbul Asia/Istanbul
Link Asia/Urumqi Asia/Kashgar
Link Asia/Kathmandu Asia/Katmandu
+Link Asia/Riyadh Asia/Kuwait
Link Asia/Macau Asia/Macao
+Link Asia/Dubai Asia/Muscat
+Link Asia/Bangkok Asia/Phnom_Penh
Link Asia/Yangon Asia/Rangoon
Link Asia/Ho_Chi_Minh Asia/Saigon
Link Asia/Jerusalem Asia/Tel_Aviv
Link Asia/Thimphu Asia/Thimbu
Link Asia/Makassar Asia/Ujung_Pandang
Link Asia/Ulaanbaatar Asia/Ulan_Bator
+Link Asia/Bangkok Asia/Vientiane
Link Atlantic/Faroe Atlantic/Faeroe
-Link Europe/Oslo Atlantic/Jan_Mayen
+Link Europe/Berlin Atlantic/Jan_Mayen
+Link Africa/Abidjan Atlantic/St_Helena
Link Australia/Sydney Australia/ACT
Link Australia/Sydney Australia/Canberra
Link Australia/Hobart Australia/Currie
@@ -81,7 +152,22 @@ Link Africa/Cairo Egypt
Link Europe/Dublin Eire
Link Etc/UTC Etc/UCT
Link Europe/London Europe/Belfast
+Link Europe/Prague Europe/Bratislava
+Link Europe/Zurich Europe/Busingen
+Link Europe/London Europe/Guernsey
+Link Europe/London Europe/Isle_of_Man
+Link Europe/London Europe/Jersey
+Link Europe/Belgrade Europe/Ljubljana
+Link Europe/Helsinki Europe/Mariehamn
+Link Asia/Nicosia Europe/Nicosia
+Link Europe/Belgrade Europe/Podgorica
+Link Europe/Rome Europe/San_Marino
+Link Europe/Belgrade Europe/Sarajevo
+Link Europe/Belgrade Europe/Skopje
Link Europe/Chisinau Europe/Tiraspol
+Link Europe/Zurich Europe/Vaduz
+Link Europe/Rome Europe/Vatican
+Link Europe/Belgrade Europe/Zagreb
Link Europe/London GB
Link Europe/London GB-Eire
Link Etc/GMT GMT+0
@@ -90,6 +176,9 @@ Link Etc/GMT GMT0
Link Etc/GMT Greenwich
Link Asia/Hong_Kong Hongkong
Link Atlantic/Reykjavik Iceland
+Link Africa/Nairobi Indian/Antananarivo
+Link Africa/Nairobi Indian/Comoro
+Link Africa/Nairobi Indian/Mayotte
Link Asia/Tehran Iran
Link Asia/Jerusalem Israel
Link America/Jamaica Jamaica
@@ -103,8 +192,11 @@ Link Pacific/Auckland NZ
Link Pacific/Chatham NZ-CHAT
Link America/Denver Navajo
Link Asia/Shanghai PRC
+Link Pacific/Kanton Pacific/Enderbury
Link Pacific/Honolulu Pacific/Johnston
+Link Pacific/Pago_Pago Pacific/Midway
Link Pacific/Pohnpei Pacific/Ponape
+Link Pacific/Guam Pacific/Saipan
Link Pacific/Pago_Pago Pacific/Samoa
Link Pacific/Chuuk Pacific/Truk
Link Pacific/Chuuk Pacific/Yap
diff --git a/tz/backzone b/tz/backzone
index 3ce7277..fc5e8bf 100644
--- a/tz/backzone
+++ b/tz/backzone
@@ -68,6 +68,91 @@
#
# As explained in the zic man page, the zone columns are:
# Zone NAME STDOFF RULES FORMAT [UNTIL]
+# and the rule columns are:
+# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
+
+
+# Ghana
+
+# From P Chan (2020-11-20):
+# Interpretation Amendment Ordinance, 1915 (No.24 of 1915) [1915-11-02]
+# Ordinances of the Gold Coast, Ashanti, Northern Territories 1915, p 69-71
+# https://books.google.com/books?id=ErA-AQAAIAAJ&pg=PA70
+# This Ordinance added "'Time' shall mean Greenwich Mean Time" to the
+# Interpretation Ordinance, 1876.
+#
+# Determination of the Time Ordinance, 1919 (No. 18 of 1919) [1919-11-24]
+# Ordinances of the Gold Coast, Ashanti, Northern Territories 1919, p 75-76
+# https://books.google.com/books?id=MbA-AQAAIAAJ&pg=PA75
+# This Ordinance removed the previous definition of time and introduced DST.
+#
+# Time Determination Ordinance (Cap. 214)
+# The Laws of the Gold Coast (including Togoland Under British Mandate)
+# Vol. II (1937), p 2328
+# https://books.google.com/books?id=Z7M-AQAAIAAJ&pg=PA2328
+# Revised edition of the 1919 Ordinance.
+#
+# Time Determination (Amendment) Ordinance, 1940 (No. 9 of 1940) [1940-04-06]
+# Annual Volume of the Laws of the Gold Coast:
+# Containing All Legislation Enacted During Year 1940, p 22
+# https://books.google.com/books?id=1ao-AQAAIAAJ&pg=PA22
+# This Ordinance changed the forward transition from September to May.
+#
+# Defence (Time Determination Ordinance Amendment) Regulations, 1942
+# (Regulations No. 6 of 1942) [1942-01-31, commenced on 1942-02-08]
+# Annual Volume of the Laws of the Gold Coast:
+# Containing All Legislation Enacted During Year 1942, p 48
+# https://books.google.com/books?id=Das-AQAAIAAJ&pg=PA48
+# These regulations advanced the [standard] time by thirty minutes.
+#
+# Defence (Time Determination Ordinance Amendment (No.2)) Regulations,
+# 1942 (Regulations No. 28 of 1942) [1942-04-25]
+# Annual Volume of the Laws of the Gold Coast:
+# Containing All Legislation Enacted During Year 1942, p 87
+# https://books.google.com/books?id=Das-AQAAIAAJ&pg=PA87
+# These regulations abolished DST and changed the time to GMT+0:30.
+#
+# Defence (Revocation) (No.4) Regulations, 1945 (Regulations No. 45 of
+# 1945) [1945-10-24, commenced on 1946-01-06]
+# Annual Volume of the Laws of the Gold Coast:
+# Containing All Legislation Enacted During Year 1945, p 256
+# https://books.google.com/books?id=9as-AQAAIAAJ&pg=PA256
+# These regulations revoked the previous two sets of Regulations.
+#
+# Time Determination (Amendment) Ordinance, 1945 (No. 18 of 1945) [1946-01-06]
+# Annual Volume of the Laws of the Gold Coast:
+# Containing All Legislation Enacted During Year 1945, p 69
+# https://books.google.com/books?id=9as-AQAAIAAJ&pg=PA69
+# This Ordinance abolished DST.
+#
+# Time Determination (Amendment) Ordinance, 1950 (No. 26 of 1950) [1950-07-22]
+# Annual Volume of the Laws of the Gold Coast:
+# Containing All Legislation Enacted During Year 1950, p 35
+# https://books.google.com/books?id=e60-AQAAIAAJ&pg=PA35
+# This Ordinance restored DST but with thirty minutes offset.
+#
+# Time Determination Ordinance (Cap. 264)
+# The Laws of the Gold Coast, Vol. V (1954), p 380
+# https://books.google.com/books?id=Mqc-AQAAIAAJ&pg=PA380
+# Revised edition of the Time Determination Ordinance.
+#
+# Time Determination (Amendment) Ordinance, 1956 (No. 21 of 1956) [1956-08-29]
+# Annual Volume of the Ordinances of the Gold Coast Enacted During the
+# Year 1956, p 83
+# https://books.google.com/books?id=VLE-AQAAIAAJ&pg=PA83
+# This Ordinance abolished DST.
+
+Rule Ghana 1919 only - Nov 24 0:00 0:20 +0020
+Rule Ghana 1920 1942 - Jan 1 2:00 0 GMT
+Rule Ghana 1920 1939 - Sep 1 2:00 0:20 +0020
+Rule Ghana 1940 1941 - May 1 2:00 0:20 +0020
+Rule Ghana 1950 1955 - Sep 1 2:00 0:30 +0030
+Rule Ghana 1951 1956 - Jan 1 2:00 0 GMT
+
+Zone Africa/Accra -0:00:52 - LMT 1915 Nov 2
+ 0:00 Ghana %s 1942 Feb 8
+ 0:30 - +0030 1946 Jan 6
+ 0:00 Ghana %s
# Ethiopia
# From Paul Eggert (2014-07-31):
@@ -101,14 +186,36 @@ Zone Africa/Bamako -0:32:00 - LMT 1912
Zone Africa/Bangui 1:14:20 - LMT 1912
1:00 - WAT
-# Gambia
+# The Gambia
+# From P Chan (2020-12-09):
+# Standard time of GMT-1 was adopted on 1933-04-01. On 1942-02-01, GMT was
+# adopted as a war time measure. This was made permanent in 1946.
+#
+# Interpretation Ordinance, 1914 (No. 12 of 1914) [1914-09-29]
+# Interpretation Ordinance, 1933 (No. 10 of 1933) [1933-03-31]
+# Notice No. 5 of 1942, Colony of the Gambia Government Gazette, Vol. LIX,
+# No.2, 1942-01-15, p 2
+# Interpretation (Amendment) Ordinance, 1946 (No. 3 of 1946) [1946-07-15]
Zone Africa/Banjul -1:06:36 - LMT 1912
- -1:06:36 - BMT 1935 # Banjul Mean Time
- -1:00 - -01 1964
+ -1:06:36 - BMT 1933 Apr 1 # Banjul Mean Time
+ -1:00 - -01 1942 Feb 1 0:00
0:00 - GMT
# Malawi
-Zone Africa/Blantyre 2:20:00 - LMT 1903 Mar
+# From P Chan (2020-12-09):
+# In 1911, Zomba mean time was adopted as the legal time of Nyasaland. In
+# 1914, Zomba mean time switched from GMT+2:21:10 to GMT+2:21. On 1925-07-01,
+# GMT+2 was adopted.
+#
+# Interpretation and General Clauses Ordinance, 1911 (No. 12 of 1911)
+# [1911-07-24]
+# Notice No. 124 of 1914, 1914-06-30, The Nyasaland Government Gazette, Vol.
+# XXI, No. 8, 1914-06-30, p 122
+# Interpretation and General Clauses (Amendment) Ordinance, 1925 (No. 3 of
+# 1925) [1925-04-02]
+Zone Africa/Blantyre 2:20:00 - LMT 1911 Jul 24
+ 2:21:10 - ZMT 1914 Jun 30 # Zomba Mean Time
+ 2:21 - ZMT 1925 Jul 1
2:00 - CAT
# Republic of the Congo
@@ -145,19 +252,48 @@ Zone Africa/Djibouti 2:52:36 - LMT 1911 Jul
Zone Africa/Douala 0:38:48 - LMT 1912
1:00 - WAT
# Sierra Leone
-# From Paul Eggert (2014-08-12):
-# The following table is from Shanks & Pottenger, but it can't be right.
-# Whitman gives Mar 31 - Aug 31 for 1931 on.
-# The International Hydrographic Bulletin, 1932-33, p 63 says that
-# Sierra Leone would advance its clocks by 20 minutes on 1933-10-01.
-# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
-Rule SL 1935 1942 - Jun 1 0:00 0:40 -0020
-Rule SL 1935 1942 - Oct 1 0:00 0 -01
-Rule SL 1957 1962 - Jun 1 0:00 1:00 +01
-Rule SL 1957 1962 - Sep 1 0:00 0 GMT
+# From P Chan (2020-12-09):
+# Standard time of GMT-1 was adopted on 1913-07-01. Twenty minutes of DST was
+# introduce[d] in 1932 and was suspended in 1939. In 1941, GMT was adopted by
+# Defence Regulations. This was made permanent in 1946.
+#
+# Government Notice No. 121 of 1913, 1913-06-06, Sierra Leone Royal Gazette,
+# Vol. XLIV, No. 1384, 1913-06-14, p 347
+# Alteration of Time Ordinance, 1932 (No. 34 of 1932) [1932-12-01]
+# Alteration of Time (Amendment) Ordinance, 1938 (No. 25 of 1938) [1938-11-24]
+# Defence Regulations (No. 9), 1939 (Regulations No. 9 of 1939), 1939-09-05
+# Defence Regulations (No. 11), 1939 (Regulations No. 11 of 1939), 1939-09-27
+# Defence (Amendment) (No. 17) Regulations, 1941 (Public Notice No. 157 of
+# 1941), 1914-12-04
+# Alteration of Time (Amendment) Ordinance, 1946 (No. 2 of 1946) [1946-02-07]
+
+# From Tim Parenti (2021-03-02), per P Chan (2021-02-25):
+# For Sierra Leone in 1957-1962, the standard time was defined in the
+# Alteration of Time Ordinance, 1932 (as amended in 1946, renamed to Local Time
+# Ordinance in 1960 and Local Time Act in 1961). It was unamended throughout
+# that period. See references to "Time" in the Alphabetical Index of the
+# Legislation in force on the 31st day of December,
+# 1957: https://books.google.com/books?id=lvQ-AQAAIAAJ&pg=RA2-PA49
+# 1958: https://books.google.com/books?id=4fQ-AQAAIAAJ&pg=RA2-PA50
+# 1959: https://books.google.com/books?id=p_U-AQAAIAAJ&pg=RA2-PA55
+# 1960: https://books.google.com/books?id=JPY-AQAAIAAJ&pg=RA3-PA37
+# 1961: https://books.google.com/books?id=7vY-AQAAIAAJ&pg=RA3-PA41
+# 1962: https://books.google.com/books?id=W_c-AQAAIAAJ&pg=RA3-PA44
+# 1963: https://books.google.com/books?id=9vk-AQAAIAAJ&pg=RA1-PA47
+#
+# Although Shanks & Pottenger had DST from Jun 1 00:00 to Sep 1 00:00 in this
+# period, many contemporaneous almanacs agree that it wasn't used:
+# https://mm.icann.org/pipermail/tz/2021-February/029866.html
+# Go with the above.
+
+Rule SL 1932 only - Dec 1 0:00 0:20 -0040
+Rule SL 1933 1938 - Mar 31 24:00 0 -01
+Rule SL 1933 1939 - Aug 31 24:00 0:20 -0040
+Rule SL 1939 only - May 31 24:00 0 -01
Zone Africa/Freetown -0:53:00 - LMT 1882
- -0:53:00 - FMT 1913 Jun # Freetown Mean Time
- -1:00 SL %s 1957
+ -0:53:00 - FMT 1913 Jul 1 # Freetown MT
+ -1:00 SL %s 1939 Sep 5
+ -1:00 - -01 1941 Dec 6 24:00
0:00 SL GMT/+01
# Botswana
@@ -298,6 +434,85 @@ Zone America/Aruba -4:40:24 - LMT 1912 Feb 12 # Oranjestad
-4:30 - -0430 1965
-4:00 - AST
+# Atikokan, Ontario
+
+# From Paul Eggert (1997-10-17):
+# Mark Brader writes that an article in the 1997-10-14 Toronto Star
+# says that Atikokan, Ontario currently does not observe DST,
+# but will vote on 11-10 whether to use EST/EDT.
+# He also writes that the Ontario Time Act (1990, Chapter T.9)
+# http://www.gov.on.ca/MBS/english/publications/statregs/conttext.html
+# says that Ontario east of 90W uses EST/EDT, and west of 90W uses CST/CDT.
+# Officially Atikokan is therefore on CST/CDT, and most likely this report
+# concerns a non-official time observed as a matter of local practice.
+#
+# From Paul Eggert (2000-10-02):
+# Matthews and Vincent (1998) write that Atikokan, Pickle Lake, and
+# New Osnaburgh observe CST all year, that Big Trout Lake observes
+# CST/CDT, and that Upsala and Shebandowan observe EST/EDT, all in
+# violation of the official Ontario rules.
+#
+# From Paul Eggert (2006-07-09):
+# Chris Walton (2006-07-06) mentioned an article by Stephanie MacLellan in the
+# 2005-07-21 Chronicle-Journal, which said:
+#
+# The clocks in Atikokan stay set on standard time year-round.
+# This means they spend about half the time on central time and
+# the other half on eastern time.
+#
+# For the most part, the system works, Mayor Dennis Brown said.
+#
+# "The majority of businesses in Atikokan deal more with Eastern
+# Canada, but there are some that deal with Western Canada," he
+# said. "I don't see any changes happening here."
+#
+# Walton also writes "Supposedly Pickle Lake and Mishkeegogamang
+# [New Osnaburgh] follow the same practice."
+
+# From Garry McKinnon (2006-07-14) via Chris Walton:
+# I chatted with a member of my board who has an outstanding memory
+# and a long history in Atikokan (and in the telecom industry) and he
+# can say for certain that Atikokan has been practicing the current
+# time keeping since 1952, at least.
+
+# From Paul Eggert (2006-07-17):
+# Shanks & Pottenger say that Atikokan has agreed with Rainy River
+# ever since standard time was introduced, but the information from
+# McKinnon sounds more authoritative. For now, assume that Atikokan
+# switched to EST immediately after WWII era daylight saving time
+# ended. This matches the old (less-populous) America/Coral_Harbour
+# entry since our cutoff date of 1970, so we can move
+# America/Coral_Harbour to the 'backward' file.
+
+Zone America/Atikokan -6:06:28 - LMT 1895
+ -6:00 Canada C%sT 1940 Sep 29
+ -6:00 1:00 CDT 1942 Feb 9 2:00s
+ -6:00 Canada C%sT 1945 Sep 30 2:00
+ -5:00 - EST
+
+# Quebec east of Natashquan
+
+# From Paul Eggert (2021-05-09):
+# H. David Matthews and Mary Vincent's map
+# "It's about TIME", _Canadian Geographic_ (September-October 1998)
+# http://www.canadiangeographic.ca/Magazine/SO98/alacarte.asp
+# says that Quebec east of the -63 meridian is supposed to observe
+# AST, but residents as far east as Natashquan use EST/EDT, and
+# residents east of Natashquan use AST.
+# The Quebec department of justice writes in
+# "The situation in Minganie and Basse-Côte-Nord"
+# https://www.justice.gouv.qc.ca/en/department/ministre/functions-and-responsabilities/legal-time-in-quebec/the-situation-in-minganie-and-basse-cote-nord/
+# that the coastal strip from just east of Natashquan to Blanc-Sablon
+# observes Atlantic standard time all year round.
+# This common practice was codified into law as of 2007; see Legal Time Act,
+# CQLR c T-5.1 <http://legisquebec.gouv.qc.ca/en/ShowDoc/cs/T-5.1>.
+# For lack of better info, guess this practice began around 1970, contra to
+# Shanks & Pottenger who have this region observing AST/ADT.
+
+Zone America/Blanc-Sablon -3:48:28 - LMT 1884
+ -4:00 Canada A%sT 1970
+ -4:00 - AST
+
# Cayman Is
Zone America/Cayman -5:25:32 - LMT 1890 # Georgetown
-5:07:10 - KMT 1912 Feb # Kingston Mean Time
@@ -318,6 +533,85 @@ Zone America/Coral_Harbour -5:32:40 - LMT 1884
-5:00 NT_YK E%sT 1946
-5:00 - EST
+# From Chris Walton (2011-12-01):
+# There are two areas within the Canadian province of British Columbia
+# that do not currently observe daylight saving:
+# a) The Creston Valley (includes the town of Creston and surrounding area)
+# b) The eastern half of the Peace River Regional District
+# (includes the cities of Dawson Creek and Fort St. John)
+
+# Earlier this year I stumbled across a detailed article about the time
+# keeping history of Creston; it was written by Tammy Hardwick who is the
+# manager of the Creston & District Museum. The article was written in May 2009.
+# http://www.ilovecreston.com/?p=articles&t=spec&ar=260
+# According to the article, Creston has not changed its clocks since June 1918.
+# i.e. Creston has been stuck on UT-7 for 93 years.
+# Dawson Creek, on the other hand, changed its clocks as recently as April 1972.
+
+# Unfortunately the exact date for the time change in June 1918 remains
+# unknown and will be difficult to ascertain. I e-mailed Tammy a few months
+# ago to ask if Sunday June 2 was a reasonable guess. She said it was just
+# as plausible as any other date (in June). She also said that after writing
+# the article she had discovered another time change in 1916; this is the
+# subject of another article which she wrote in October 2010.
+# http://www.creston.museum.bc.ca/index.php?module=comments&uop=view_comment&cm+id=56
+
+# Here is a summary of the three clock change events in Creston's history:
+# 1. 1884 or 1885: adoption of Mountain Standard Time (GMT-7)
+# Exact date unknown
+# 2. Oct 1916: switch to Pacific Standard Time (GMT-8)
+# Exact date in October unknown; Sunday October 1 is a reasonable guess.
+# 3. June 1918: switch to Pacific Daylight Time (GMT-7)
+# Exact date in June unknown; Sunday June 2 is a reasonable guess.
+# note 1:
+# On Oct 27/1918 when daylight saving ended in the rest of Canada,
+# Creston did not change its clocks.
+# note 2:
+# During WWII when the Federal Government legislated a mandatory clock change,
+# Creston did not oblige.
+# note 3:
+# There is no guarantee that Creston will remain on Mountain Standard Time
+# (UTC-7) forever.
+# The subject was debated at least once this year by the town Council.
+# http://www.bclocalnews.com/kootenay_rockies/crestonvalleyadvance/news/116760809.html
+
+# During a period WWII, summer time (Daylight saying) was mandatory in Canada.
+# In Creston, that was handled by shifting the area to PST (-8:00) then applying
+# summer time to cause the offset to be -7:00, the same as it had been before
+# the change. It can be argued that the timezone abbreviation during this
+# period should be PDT rather than MST, but that doesn't seem important enough
+# (to anyone) to further complicate the rules.
+
+# The transition dates (and times) are guesses.
+
+Zone America/Creston -7:46:04 - LMT 1884
+ -7:00 - MST 1916 Oct 1
+ -8:00 - PST 1918 Jun 2
+ -7:00 - MST
+
+# Curaçao
+# Milne gives 4:35:46.9 for Curaçao mean time; round to nearest.
+#
+# From Paul Eggert (2006-03-22):
+# Shanks & Pottenger say that The Bottom and Philipsburg have been at
+# -4:00 since standard time was introduced on 1912-03-02; and that
+# Kralendijk and Rincon used Kralendijk Mean Time (-4:33:08) from
+# 1912-02-02 to 1965-01-01. The former is dubious, since S&P also say
+# Saba Island has been like Curaçao.
+# This all predates our 1970 cutoff, though.
+#
+# By July 2007 Curaçao and St Maarten are planned to become
+# associated states within the Netherlands, much like Aruba;
+# Bonaire, Saba and St Eustatius would become directly part of the
+# Netherlands as Kingdom Islands. This won't affect their time zones
+# though, as far as we know.
+#
+Zone America/Curacao -4:35:47 - LMT 1912 Feb 12 # Willemstad
+ -4:30 - -0430 1965
+ -4:00 - AST
+Link America/Curacao America/Kralendijk
+Link America/Curacao America/Lower_Princes
+
# Dominica
Zone America/Dominica -4:05:36 - LMT 1911 Jul 1 0:01 # Roseau
-4:00 - AST
@@ -340,6 +634,7 @@ Zone America/Grenada -4:07:00 - LMT 1911 Jul # St George's
Zone America/Guadeloupe -4:06:08 - LMT 1911 Jun 8 # Pointe-à-Pitre
-4:00 - AST
+
# Canada
#
# From Paul Eggert (2015-03-24):
@@ -351,7 +646,6 @@ Zone America/Guadeloupe -4:06:08 - LMT 1911 Jun 8 # Pointe-à-Pitre
# Pottenger data. The post-1970 entries have been corrected, but the
# pre-1970 entries are unchecked and probably have errors.
#
-# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule Mont 1917 only - Mar 25 2:00 1:00 D
Rule Mont 1917 only - Apr 24 0:00 0 S
Rule Mont 1919 only - Mar 31 2:30 1:00 D
@@ -387,6 +681,48 @@ Zone America/Montreal -4:54:16 - LMT 1884
Zone America/Montserrat -4:08:52 - LMT 1911 Jul 1 0:01 # Cork Hill
-4:00 - AST
+# The Bahamas
+#
+# For 1899 Milne gives -5:09:29.5; round that.
+#
+# From P Chan (2020-11-27, corrected on 2020-12-02):
+# There were two periods of DST observed in 1942-1945: 1942-05-01
+# midnight to 1944-12-31 midnight and 1945-02-01 to 1945-10-17 midnight.
+# "midnight" should mean 24:00 from the context.
+#
+# War Time Order 1942 [1942-05-01] and War Time (No. 2) Order 1942 [1942-09-29]
+# Appendix to the Statutes of 7 George VI. and the Year 1942. p 34, 43
+# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA3-PA34
+# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA3-PA43
+#
+# War Time Order 1943 [1943-03-31] and War Time Order 1944 [1943-12-29]
+# Appendix to the Statutes of 8 George VI. and the Year 1943. p 9-10, 28-29
+# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA4-PA9
+# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA4-PA28
+#
+# War Time Order 1945 [1945-01-31] and the Order which revoke War Time Order
+# 1945 [1945-10-16] Appendix to the Statutes of 9 George VI. and the Year
+# 1945. p 160, 247-248
+# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA6-PA160
+# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA6-PA247
+#
+# From Sue Williams (2006-12-07):
+# The Bahamas announced about a month ago that they plan to change their DST
+# rules to sync with the U.S. starting in 2007....
+# http://www.jonesbahamas.com/?c=45&a=10412
+
+Rule Bahamas 1942 only - May 1 24:00 1:00 W
+Rule Bahamas 1944 only - Dec 31 24:00 0 S
+Rule Bahamas 1945 only - Feb 1 0:00 1:00 W
+Rule Bahamas 1945 only - Aug 14 23:00u 1:00 P # Peace
+Rule Bahamas 1945 only - Oct 17 24:00 0 S
+Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S
+Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D
+
+Zone America/Nassau -5:09:30 - LMT 1912 Mar 2
+ -5:00 Bahamas E%sT 1976
+ -5:00 US E%sT
+
# United States
#
# From Paul Eggert (2018-03-18):
@@ -411,6 +747,13 @@ Zone America/Montserrat -4:08:52 - LMT 1911 Jul 1 0:01 # Cork Hill
# https://cdnc.ucr.edu/cgi-bin/cdnc?a=d&d=DS19470110
# front page reports on end.
+# Trinidad and Tobago
+Zone America/Port_of_Spain -4:06:04 - LMT 1912 Mar 2
+ -4:00 - AST
+Link America/Port_of_Spain America/Marigot
+Link America/Port_of_Spain America/St_Barthelemy
+Link America/Port_of_Spain America/Virgin
+
# Argentina
# This entry was intended for the following areas, but has been superseded by
# more detailed zones.
@@ -434,7 +777,7 @@ Zone America/St_Lucia -4:04:00 - LMT 1890 # Castries
-4:04:00 - CMT 1912 # Castries Mean Time
-4:00 - AST
-# Virgin Is
+# US Virgin Is
Zone America/St_Thomas -4:19:44 - LMT 1911 Jul # Charlotte Amalie
-4:00 - AST
@@ -447,11 +790,36 @@ Zone America/St_Vincent -4:04:56 - LMT 1890 # Kingstown
Zone America/Tortola -4:18:28 - LMT 1911 Jul # Road Town
-4:00 - AST
+# Dumont d'Urville, Île des Pétrels, -6640+14001, since 1956-11
+# <https://en.wikipedia.org/wiki/Dumont_d'Urville_Station> (2005-12-05)
+#
+# Another base at Port-Martin, 50km east, began operation in 1947.
+# It was destroyed by fire on 1952-01-14.
+#
+Zone Antarctica/DumontDUrville 0 - -00 1947
+ 10:00 - +10 1952 Jan 14
+ 0 - -00 1956 Nov
+ 10:00 - +10
+
# McMurdo, Ross Island, since 1955-12
Zone Antarctica/McMurdo 0 - -00 1956
12:00 NZ NZ%sT
Link Antarctica/McMurdo Antarctica/South_Pole
+# Syowa, Antarctica
+#
+# From Hideyuki Suzuki (1999-02-06):
+# In all Japanese stations, +0300 is used as the standard time.
+#
+# Syowa station, which is the first antarctic station of Japan,
+# was established on 1957-01-29. Since Syowa station is still the main
+# station of Japan, it's appropriate for the principal location.
+Zone Antarctica/Syowa 0 - -00 1957 Jan 29
+ 3:00 - +03
+# See:
+# NIPR Antarctic Research Activities (1999-08-17)
+# http://www.nipr.ac.jp/english/ara01.html
+
# Yemen
# Milne says 2:59:54 was the meridian of the saluting battery at Aden,
# and that Yemen was at 1:55:56, the meridian of the Hagia Sophia.
@@ -711,6 +1079,8 @@ Zone Europe/Skopje 1:25:44 - LMT 1884
1:00 - CET 1982 Nov 27
1:00 EU CE%sT
+
+
# Moldova / Transnistria
Zone Europe/Tiraspol 1:58:32 - LMT 1880
1:55 - CMT 1918 Feb 15 # Chisinau MT
@@ -749,10 +1119,22 @@ Zone Indian/Comoro 2:53:04 - LMT 1911 Jul # Moroni, Gran Comoro
Zone Indian/Mayotte 3:00:56 - LMT 1911 Jul # Mamoutzou
3:00 - EAT
-# US minor outlying islands
+# Phoenix Islands, Kiribati
+# From Paul Eggert (2021-05-27):
+# Enderbury was inhabited 1860/1880s to mine guano, and 1938-03-06/1942-02-09
+# for aviation (ostensibly commercial, but military uses foreseen).
+# The 19th-century dates are approximate. See Pacific/Kanton for
+# the currently-inhabited representative for this timezone.
+Zone Pacific/Enderbury 0 - -00 1860
+ -11:24:20 - LMT 1885
+ 0 - -00 1938 Mar 6
+ -12:00 - -12 1942 Feb 9
+ 0 - -00
+
+# Johnston
Zone Pacific/Johnston -10:00 - HST
-# US minor outlying islands
+# Midway
#
# From Mark Brader (2005-01-23):
# [Fallacies and Fantasies of Air Transport History, by R.E.G. Davies,
@@ -775,3 +1157,7 @@ Zone Pacific/Saipan -14:17:00 - LMT 1844 Dec 31
9:00 - +09 1969 Oct
10:00 - +10 2000 Dec 23
10:00 - ChST # Chamorro Standard Time
+
+# Local Variables:
+# coding: utf-8
+# End:
diff --git a/tz/checktab.awk b/tz/checktab.awk
index ec145b5..23e0a3a 100644
--- a/tz/checktab.awk
+++ b/tz/checktab.awk
@@ -58,11 +58,12 @@ BEGIN {
zone_table, zone_NR >>"/dev/stderr"
status = 1
}
- split($1, cca, /,/)
- cc = cca[1]
+ ccs = input_ccs[zone_NR] = $1
coordinates = $2
tz = $3
- comments = $4
+ comments = input_comments[zone_NR] = $4
+ split(ccs, cca, /,/)
+ cc = cca[1]
# Don't complain about a special case for Crimea in zone.tab.
# FIXME: zone.tab should be removed, since it is obsolete.
@@ -77,12 +78,9 @@ BEGIN {
cc0 = cc
tz0 = tz
tztab[tz] = 1
- tz2comments[tz] = comments
tz2NR[tz] = zone_NR
for (i in cca) {
cc = cca[i]
- cctz = cc tz
- cctztab[cctz] = 1
if (cc2name[cc]) {
cc_used[cc]++
} else {
@@ -99,27 +97,27 @@ BEGIN {
}
}
- for (cctz in cctztab) {
- cc = substr (cctz, 1, 2)
- tz = substr (cctz, 3)
- if (1 < cc_used[cc]) {
- comments_needed[tz] = cc
- }
- }
- for (cctz in cctztab) {
- cc = substr (cctz, 1, 2)
- tz = substr (cctz, 3)
- if (!comments_needed[tz] && tz2comments[tz]) {
+ for (i = 1; i <= zone_NR; i++) {
+ ccs = input_ccs[i]
+ if (!ccs) continue
+ comments = input_comments[i]
+ split(ccs, cca, /,/)
+ used_max = 0
+ for (j in cca) {
+ cc = cca[j]
+ if (used_max < cc_used[cc]) {
+ used_max = cc_used[cc]
+ }
+ }
+ if (used_max <= 1 && comments) {
printf "%s:%d: unnecessary comment '%s'\n", \
- zone_table, tz2NR[tz], tz2comments[tz] \
- >>"/dev/stderr"
- tz2comments[tz] = 0
+ zone_table, i, comments \
+ >>"/dev/stderr"
status = 1
- } else if (comments_needed[tz] && !tz2comments[tz]) {
+ } else if (1 < cc_used[cc] && !comments) {
printf "%s:%d: missing comment for %s\n", \
- zone_table, tz2NR[tz], comments_needed[tz] \
+ zone_table, i, cc \
>>"/dev/stderr"
- tz2comments[tz] = 1
status = 1
}
}
@@ -149,8 +147,8 @@ $1 ~ /^#/ { next }
ruleUsed[$2] = 1
if ($3 ~ /%/) rulePercentUsed[$2] = 1
}
- if (tz && tz ~ /\//) {
- if (!tztab[tz]) {
+ if (tz && tz ~ /\// && tz !~ /^Etc\//) {
+ if (!tztab[tz] && FILENAME != "backward") {
printf "%s: no data for '%s'\n", zone_table, tz \
>>"/dev/stderr"
status = 1
@@ -173,7 +171,7 @@ END {
}
}
for (tz in tztab) {
- if (!zoneSeen[tz]) {
+ if (!zoneSeen[tz] && tz !~ /^Etc\//) {
printf "%s:%d: no Zone table for '%s'\n", \
zone_table, tz2NR[tz], tz >>"/dev/stderr"
status = 1
diff --git a/tz/date.c b/tz/date.c
index ef4e4d4..b04d3f2 100644
--- a/tz/date.c
+++ b/tz/date.c
@@ -92,7 +92,7 @@ main(const int argc, char *argv[])
}
rflag = true;
errno = 0;
- secs = strtoimax (optarg, &endarg, 0);
+ secs = strtoimax(optarg, &endarg, 0);
if (*endarg || optarg == endarg)
errno = EINVAL;
else if (! (TIME_T_MIN <= secs && secs <= TIME_T_MAX))
@@ -138,7 +138,7 @@ dogmt(void)
continue;
fakeenv = malloc((n + 2) * sizeof *fakeenv);
if (fakeenv == NULL) {
- perror(_("Memory exhausted"));
+ fprintf(stderr, _("date: Memory exhausted\n"));
errensure();
exit(retval);
}
diff --git a/tz/difftime.c b/tz/difftime.c
index 7b96927..ff78f03 100644
--- a/tz/difftime.c
+++ b/tz/difftime.c
@@ -23,7 +23,7 @@ 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)) {
+ if (sizeof(time_t) < sizeof(double)) {
double t1 = time1, t0 = time0;
return t1 - t0;
}
@@ -36,7 +36,7 @@ difftime(time_t time1, time_t time0)
return time0 <= time1 ? time1 - time0 : dminus(time0 - time1);
/* Use uintmax_t if wide enough. */
- if (sizeof (time_t) <= sizeof (uintmax_t)) {
+ if (sizeof(time_t) <= sizeof(uintmax_t)) {
uintmax_t t1 = time1, t0 = time0;
return time0 <= time1 ? t1 - t0 : dminus(t0 - t1);
}
diff --git a/tz/europe b/tz/europe
index bba4d56..1204c09 100644
--- a/tz/europe
+++ b/tz/europe
@@ -68,7 +68,6 @@
# 0:00 GMT BST BDST Greenwich, British Summer
# 0:00 GMT IST Greenwich, Irish Summer
# 0:00 WET WEST WEMT Western Europe
-# 0:19:32.13 AMT* NST* Amsterdam, Netherlands Summer (1835-1937)
# 1:00 BST British Standard (1968-1971)
# 1:00 IST GMT Irish Standard (1968-) with winter DST
# 1:00 CET CEST CEMT Central Europe
@@ -506,9 +505,6 @@ Zone Europe/London -0:01:15 - LMT 1847 Dec 1 0:00s
1:00 - BST 1971 Oct 31 2:00u
0:00 GB-Eire %s 1996
0:00 EU GMT/BST
-Link Europe/London Europe/Jersey
-Link Europe/London Europe/Guernsey
-Link Europe/London Europe/Isle_of_Man
# From Paul Eggert (2018-02-15):
# In January 2018 we discovered that the negative SAVE values in the
@@ -1307,9 +1303,8 @@ Zone Europe/Helsinki 1:39:49 - LMT 1878 May 31
1:39:49 - HMT 1921 May # Helsinki Mean Time
2:00 Finland EE%sT 1983
2:00 EU EE%sT
-
# Åland Is
-Link Europe/Helsinki Europe/Mariehamn
+# See Europe/Helsinki.
# France
@@ -1505,8 +1500,7 @@ Zone Europe/Berlin 0:53:28 - LMT 1893 Apr
# From Arthur David Olson (2012-03-03):
# Büsingen and Zurich have shared clocks since 1970.
-
-Link Europe/Zurich Europe/Busingen
+# See Europe/Zurich.
# Georgia
# Please see the "asia" file for Asia/Tbilisi.
@@ -1800,8 +1794,9 @@ Zone Europe/Rome 0:49:56 - LMT 1866 Dec 12
1:00 Italy CE%sT 1980
1:00 EU CE%sT
-Link Europe/Rome Europe/Vatican
-Link Europe/Rome Europe/San_Marino
+# Kosovo
+# See Europe/Belgrade.
+
# Latvia
@@ -1895,7 +1890,7 @@ Zone Europe/Riga 1:36:34 - LMT 1880
# I could confirm from the paper that Liechtenstein did in fact follow
# the same DST in 1941 and 1942 as Switzerland did.
-Link Europe/Zurich Europe/Vaduz
+# See Europe/Zurich.
# Lithuania
@@ -2150,6 +2145,10 @@ Zone Europe/Monaco 0:29:32 - LMT 1892 Jun 1
# The data entries before 1945 are taken from
# https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm
+# From Paul Eggert (2021-05-09):
+# I invented the abbreviations AMT for Amsterdam Mean Time and NST for
+# Netherlands Summer Time, used in the Netherlands from 1835 to 1937.
+
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule Neth 1916 only - May 1 0:00 1:00 NST # Netherlands Summer Time
Rule Neth 1916 only - Oct 1 0:00 0 AMT # Amsterdam Mean Time
@@ -2248,8 +2247,7 @@ Zone Europe/Oslo 0:43:00 - LMT 1895 Jan 1
# Haudegen did not surrender to the Allies until September 1945.
#
# All these events predate our cutoff date of 1970, so use Europe/Oslo
-# for these regions.
-Link Europe/Oslo Arctic/Longyearbyen
+# for these regions; see 'backward'.
# Poland
@@ -2376,12 +2374,10 @@ Rule Port 1943 1945 - Aug Sat>=25 22:00s 1:00 S
Rule Port 1944 1945 - Apr Sat>=21 22:00s 2:00 M
Rule Port 1946 only - Apr Sat>=1 23:00s 1:00 S
Rule Port 1946 only - Oct Sat>=1 23:00s 0 -
-Rule Port 1947 1949 - Apr Sun>=1 2:00s 1:00 S
-Rule Port 1947 1949 - Oct Sun>=1 2:00s 0 -
-# Shanks & Pottenger say DST was observed in 1950; go with Whitman.
+# Whitman says DST was not observed in 1950; go with Shanks & Pottenger.
# Whitman gives Oct lastSun for 1952 on; go with Shanks & Pottenger.
-Rule Port 1951 1965 - Apr Sun>=1 2:00s 1:00 S
-Rule Port 1951 1965 - Oct Sun>=1 2:00s 0 -
+Rule Port 1947 1965 - Apr Sun>=1 2:00s 1:00 S
+Rule Port 1947 1965 - Oct Sun>=1 2:00s 0 -
Rule Port 1977 only - Mar 27 0:00s 1:00 S
Rule Port 1977 only - Sep 25 0:00s 0 -
Rule Port 1978 1979 - Apr Sun>=1 0:00s 1:00 S
@@ -3520,14 +3516,9 @@ Zone Europe/Belgrade 1:22:00 - LMT 1884
# Shanks & Pottenger don't give as much detail, so go with Koželj.
1:00 - CET 1982 Nov 27
1:00 EU CE%sT
-Link Europe/Belgrade Europe/Ljubljana # Slovenia
-Link Europe/Belgrade Europe/Podgorica # Montenegro
-Link Europe/Belgrade Europe/Sarajevo # Bosnia and Herzegovina
-Link Europe/Belgrade Europe/Skopje # North Macedonia
-Link Europe/Belgrade Europe/Zagreb # Croatia
# Slovakia
-Link Europe/Prague Europe/Bratislava
+# See Europe/Prague.
# Slovenia
# See Europe/Belgrade.
@@ -3683,6 +3674,9 @@ Zone Atlantic/Canary -1:01:36 - LMT 1922 Mar # Las Palmas de Gran C.
#
# Source: The newspaper "Dagens Nyheter", 1916-10-01, page 7 upper left.
+# An extra-special abbreviation style is SET for Swedish Time (svensk
+# normaltid) 1879-1899, 3° west of the Stockholm Observatory.
+
# Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Europe/Stockholm 1:12:12 - LMT 1879 Jan 1
1:00:14 - SET 1900 Jan 1 # Swedish Time
@@ -3995,7 +3989,7 @@ Zone Europe/Istanbul 1:55:52 - LMT 1880
2:00 1:00 EEST 2015 Nov 8 1:00u
2:00 EU EE%sT 2016 Sep 7
3:00 - +03
-Link Europe/Istanbul Asia/Istanbul # Istanbul is in both continents.
+# See the 'backward' file for Asia/Istanbul.
# Ukraine
#
diff --git a/tz/leap-seconds.list b/tz/leap-seconds.list
index 3198d65..cfda3e8 100644
--- a/tz/leap-seconds.list
+++ b/tz/leap-seconds.list
@@ -204,10 +204,10 @@
# current -- the update time stamp, the data and the name of the file
# will not change.
#
-# Updated through IERS Bulletin C61
-# File expires on: 28 December 2021
+# Updated through IERS Bulletin C62
+# File expires on: 28 June 2022
#
-#@ 3849638400
+#@ 3865363200
#
2272060800 10 # 1 Jan 1972
2287785600 11 # 1 Jul 1972
@@ -252,4 +252,4 @@
# the hash line is also ignored in the
# computation.
#
-#h 2ab8253d d4380d28 75f01343 381504f8 8f8a4bfc
+#h 599d45bf accd4b4f 8b60e46 49b623 7d13b825
diff --git a/tz/localtime.c b/tz/localtime.c
index d3e406b..6a687d7 100644
--- a/tz/localtime.c
+++ b/tz/localtime.c
@@ -28,14 +28,6 @@ static int lock(void) { return 0; }
static void unlock(void) { }
#endif
-/* 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
-
#ifndef TZ_ABBR_MAX_LEN
#define TZ_ABBR_MAX_LEN 16
#endif /* !defined TZ_ABBR_MAX_LEN */
@@ -107,7 +99,7 @@ struct ttinfo { /* time type information */
struct lsinfo { /* leap second information */
time_t ls_trans; /* transition time */
- int_fast64_t ls_corr; /* correction to apply */
+ int_fast32_t ls_corr; /* correction to apply */
};
#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
@@ -158,12 +150,12 @@ 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_fast64_t leapcorr(struct state const *, time_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 *, bool);
+static bool tzparse(char const *, struct state *, struct state *);
#ifdef ALL_STATE
static struct state * lclptr;
@@ -245,7 +237,7 @@ detzcode(const char *const codep)
static int_fast64_t
detzcode64(const char *const codep)
{
- register uint_fast64_t result;
+ register int_fast64_t result;
register int i;
int_fast64_t one = 1;
int_fast64_t halfmaxval = one << (64 - 2);
@@ -342,21 +334,13 @@ scrub_abbrs(struct state *sp)
}
}
-static bool
-differ_by_repeat(const time_t t1, const time_t t0)
-{
- if (TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
- return 0;
- return t1 - t0 == SECSPERREPEAT;
-}
-
/* 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)
+ char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state)
+ 4 * TZ_MAX_TIMES];
};
@@ -375,7 +359,7 @@ union local_storage {
} u;
/* The file name to be opened. */
- char fullname[BIGGEST(sizeof (struct file_analysis),
+ char fullname[BIGGEST(sizeof(struct file_analysis),
sizeof tzdirslash + 1024)];
};
@@ -392,7 +376,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
register ssize_t nread;
register bool doaccess;
register union input_buffer *up = &lsp->u.u;
- register int tzheadsize = sizeof (struct tzhead);
+ register int tzheadsize = sizeof(struct tzhead);
sp->goback = sp->goahead = false;
@@ -452,8 +436,8 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
for (stored = 4; stored <= 8; stored *= 2) {
int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
- int_fast64_t prevtr = 0;
- int_fast32_t prevcorr = 0;
+ 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);
@@ -542,17 +526,21 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
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. */
- if (tr < 0)
+ /* Leap seconds cannot occur before the Epoch,
+ or out of order. */
+ if (tr <= prevtr)
return EINVAL;
if (tr <= TIME_T_MAX) {
- /* Leap seconds cannot occur more than once per UTC month,
- and UTC months are at least 28 days long (minus 1
- second for a negative leap second). Each leap second's
+ /* To avoid other botches in this code, each leap second's
correction must differ from the previous one's by 1
- second. */
- if (tr - prevtr < 28 * SECSPERDAY - 1
- || (corr != prevcorr - 1 && corr != prevcorr + 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;
sp->lsis[leapcnt].ls_trans = prevtr = tr;
sp->lsis[leapcnt].ls_corr = prevcorr = corr;
@@ -599,7 +587,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
struct state *ts = &lsp->u.st;
up->buf[nread - 1] = '\0';
- if (tzparse(&up->buf[1], ts, false)) {
+ if (tzparse(&up->buf[1], ts, sp)) {
/* Attempt to reuse existing abbreviations.
Without this, America/Anchorage would be right on
@@ -640,19 +628,18 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
== sp->types[sp->timecnt - 2]))
sp->timecnt--;
- for (i = 0; i < ts->timecnt; i++)
- if (sp->timecnt == 0
- || (sp->ats[sp->timecnt - 1]
- < ts->ats[i] + leapcorr(sp, ts->ats[i])))
- break;
- while (i < ts->timecnt
- && sp->timecnt < TZ_MAX_TIMES) {
- sp->ats[sp->timecnt]
- = ts->ats[i] + leapcorr(sp, ts->ats[i]);
+ 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++;
- i++;
}
for (i = 0; i < ts->typecnt; i++)
sp->ttis[sp->typecnt++] = ts->ttis[i];
@@ -662,20 +649,26 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
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 (typesequiv(sp, sp->types[i], sp->types[0]) &&
- differ_by_repeat(sp->ats[i], sp->ats[0])) {
+ 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 (typesequiv(sp, sp->types[sp->timecnt - 1],
- sp->types[i]) &&
- differ_by_repeat(sp->ats[sp->timecnt - 1],
- sp->ats[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
@@ -740,9 +733,9 @@ tzload(char const *name, struct state *sp, bool doextend)
{
#ifdef ALL_STATE
union local_storage *lsp = malloc(sizeof *lsp);
- if (!lsp)
- return errno;
- else {
+ if (!lsp) {
+ return HAVE_MALLOC_ERRNO ? errno : ENOMEM;
+ } else {
int err = tzloadbody(name, sp, doextend, lsp);
free(lsp);
return err;
@@ -785,6 +778,13 @@ 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.
@@ -862,6 +862,7 @@ 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
@@ -872,7 +873,7 @@ getsecs(register const char *strp, int_fast32_t *const secsp)
strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
if (strp == NULL)
return NULL;
- *secsp = num * (int_fast32_t) SECSPERHOUR;
+ *secsp = num * secsperhour;
if (*strp == ':') {
++strp;
strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
@@ -983,7 +984,6 @@ transtime(const int year, register const struct rule *const rulep,
register int i;
int d, m1, yy0, yy1, yy2, dow;
- INITIALIZE(value);
leapyear = isleap(year);
switch (rulep->r_type) {
@@ -1049,6 +1049,8 @@ transtime(const int year, register const struct rule *const rulep,
for (i = 0; i < rulep->r_mon - 1; ++i)
value += mon_lengths[leapyear][i] * SECSPERDAY;
break;
+
+ default: UNREACHABLE();
}
/*
@@ -1066,7 +1068,7 @@ transtime(const int year, register const struct rule *const rulep,
*/
static bool
-tzparse(const char *name, struct state *sp, bool lastditch)
+tzparse(const char *name, struct state *sp, struct state *basep)
{
const char * stdname;
const char * dstname;
@@ -1077,37 +1079,42 @@ tzparse(const char *name, struct state *sp, bool lastditch)
int_fast32_t dstoffset;
register char * cp;
register bool load_ok;
+ time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN;
stdname = name;
- if (lastditch) {
- stdlen = sizeof gmt - 1;
- name += stdlen;
- stdoffset = 0;
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ stdlen = name - stdname;
+ name++;
} else {
- 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;
+ 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;
- load_ok = tzload(TZDEFRULES, sp, false) == 0;
- if (!load_ok)
- sp->leapcnt = 0; /* so, we're off a little */
+ 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;
@@ -1137,11 +1144,10 @@ tzparse(const char *name, struct state *sp, bool lastditch)
struct rule start;
struct rule end;
register int year;
- register int yearlim;
register int timecnt;
time_t janfirst;
int_fast32_t janoffset = 0;
- int yearbeg;
+ int yearbeg, yearlim;
++name;
if ((name = getrule(name, &start)) == NULL)
@@ -1171,9 +1177,25 @@ tzparse(const char *name, struct state *sp, bool lastditch)
janoffset = -yearsecs;
break;
}
- } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
+ } 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 + YEARSPERREPEAT + 1;
+ 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),
@@ -1189,24 +1211,29 @@ tzparse(const char *name, struct state *sp, bool lastditch)
}
if (reversed
|| (starttime < endtime
- && (endtime - starttime
- < (yearsecs
- + (stdoffset - dstoffset))))) {
+ && endtime - starttime < yearsecs)) {
if (TZ_MAX_TIMES - 2 < timecnt)
break;
sp->ats[timecnt] = janfirst;
if (! increment_overflow_time
(&sp->ats[timecnt],
- janoffset + starttime))
+ janoffset + starttime)
+ && atlo <= sp->ats[timecnt])
sp->types[timecnt++] = !reversed;
sp->ats[timecnt] = janfirst;
if (! increment_overflow_time
(&sp->ats[timecnt],
- janoffset + endtime)) {
+ janoffset + endtime)
+ && atlo <= sp->ats[timecnt]) {
sp->types[timecnt++] = reversed;
- yearlim = year + YEARSPERREPEAT + 1;
}
}
+ if (endtime < leaplo) {
+ yearlim = year;
+ if (increment_overflow(&yearlim,
+ YEARSPERREPEAT + 1))
+ yearlim = INT_MAX;
+ }
if (increment_overflow_time
(&janfirst, janoffset + yearsecs))
break;
@@ -1253,7 +1280,6 @@ tzparse(const char *name, struct state *sp, bool lastditch)
** Initially we're assumed to be in standard time.
*/
isdst = false;
- theiroffset = theirstdoffset;
/*
** Now juggle transition times and types
** tracking offsets as you do.
@@ -1323,7 +1349,7 @@ static void
gmtload(struct state *const sp)
{
if (tzload(gmt, sp, true) != 0)
- tzparse(gmt, sp, true);
+ tzparse("GMT0", sp, NULL);
}
/* Initialize *SP to a value appropriate for the TZ setting NAME.
@@ -1346,7 +1372,7 @@ zoneinit(struct state *sp, char const *name)
return 0;
} else {
int err = tzload(name, sp, true);
- if (err != 0 && name && name[0] != ':' && tzparse(name, sp, false))
+ if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
err = 0;
if (err == 0)
scrub_abbrs(sp);
@@ -1417,7 +1443,8 @@ tzalloc(char const *name)
errno = err;
return NULL;
}
- }
+ } else if (!HAVE_MALLOC_ERRNO)
+ errno = ENOMEM;
return sp;
}
@@ -1469,7 +1496,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
}
if ((sp->goback && t < sp->ats[0]) ||
(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
- time_t newt = t;
+ time_t newt;
register time_t seconds;
register time_t years;
@@ -1477,11 +1504,17 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
seconds = sp->ats[0] - t;
else seconds = t - sp->ats[sp->timecnt - 1];
--seconds;
- years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
+
+ /* 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 += seconds;
- else newt -= seconds;
+ newt = t + seconds + SECSPERREPEAT;
+ else
+ newt = t - seconds - SECSPERREPEAT;
+
if (newt < sp->ats[0] ||
newt > sp->ats[sp->timecnt - 1])
return NULL; /* "cannot happen" */
@@ -1512,7 +1545,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
hi = mid;
else lo = mid + 1;
}
- i = (int) sp->types[lo - 1];
+ i = sp->types[lo - 1];
}
ttisp = &sp->ttis[i];
/*
@@ -1626,14 +1659,14 @@ offtime(const time_t *timep, long offset)
** where, to make the math easy, the answer for year zero is defined as zero.
*/
-static int
-leaps_thru_end_of_nonneg(int y)
+static time_t
+leaps_thru_end_of_nonneg(time_t y)
{
return y / 4 - y / 100 + y / 400;
}
-static int
-leaps_thru_end_of(register const int y)
+static time_t
+leaps_thru_end_of(time_t y)
{
return (y < 0
? -1 - leaps_thru_end_of_nonneg(-1 - y)
@@ -1646,111 +1679,106 @@ timesub(const time_t *timep, int_fast32_t offset,
{
register const struct lsinfo * lp;
register time_t tdays;
- register int idays; /* unsigned would be so 2003 */
- register int_fast64_t rem;
- int y;
register const int * ip;
- register int_fast64_t corr;
- register bool hit;
+ 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;
- hit = false;
i = (sp == NULL) ? 0 : sp->leapcnt;
while (--i >= 0) {
lp = &sp->lsis[i];
if (*timep >= lp->ls_trans) {
corr = lp->ls_corr;
- hit = (*timep == lp->ls_trans
- && (i == 0 ? 0 : lp[-1].ls_corr) < corr);
+ if ((i == 0 ? 0 : lp[-1].ls_corr) < corr)
+ secs_since_posleap = *timep - lp->ls_trans;
break;
}
}
- y = EPOCH_YEAR;
+
+ /* Calculate the year, avoiding integer overflow even if
+ time_t is unsigned. */
tdays = *timep / SECSPERDAY;
rem = *timep % SECSPERDAY;
- while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
- int newy;
- register time_t tdelta;
- register int idelta;
+ 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;
-
- tdelta = tdays / DAYSPERLYEAR;
- if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta)
- && tdelta <= INT_MAX))
- goto out_of_range;
- idelta = tdelta;
- if (idelta == 0)
- idelta = (tdays < 0) ? -1 : 1;
- newy = y;
- if (increment_overflow(&newy, idelta))
- goto out_of_range;
leapdays = leaps_thru_end_of(newy - 1) -
leaps_thru_end_of(y - 1);
- tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
- tdays -= leapdays;
+ idays -= ydelta * DAYSPERNYEAR;
+ idays -= leapdays;
y = newy;
}
- /*
- ** 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))
- goto out_of_range;
- idays += year_lengths[isleap(y)];
- }
- while (idays >= year_lengths[isleap(y)]) {
- idays -= year_lengths[isleap(y)];
- if (increment_overflow(&y, 1))
- goto out_of_range;
+
+ 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;
}
- tmp->tm_year = y;
- if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
- goto out_of_range;
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 = (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 = (int) (rem / SECSPERHOUR);
+ tmp->tm_hour = 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;
+ 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 = (int) (idays + 1);
+ tmp->tm_mday = idays + 1;
tmp->tm_isdst = 0;
#ifdef TM_GMTOFF
tmp->TM_GMTOFF = offset;
#endif /* defined TM_GMTOFF */
return tmp;
-
- out_of_range:
- errno = EOVERFLOW;
- return NULL;
}
char *
@@ -2005,10 +2033,10 @@ time2sub(struct tm *const tmp,
&& (yourtm.TM_GMTOFF < 0
? (-SECSPERDAY <= yourtm.TM_GMTOFF
&& (mytm.TM_GMTOFF <=
- (SMALLEST (INT_FAST32_MAX, LONG_MAX)
+ (SMALLEST(INT_FAST32_MAX, LONG_MAX)
+ yourtm.TM_GMTOFF)))
: (yourtm.TM_GMTOFF <= SECSPERDAY
- && ((BIGGEST (INT_FAST32_MIN, LONG_MIN)
+ && ((BIGGEST(INT_FAST32_MIN, LONG_MIN)
+ yourtm.TM_GMTOFF)
<= mytm.TM_GMTOFF)))) {
/* MYTM matches YOURTM except with the wrong UT offset.
@@ -2093,8 +2121,8 @@ time2(struct tm * const tmp,
static time_t
time1(struct tm *const tmp,
- struct tm *(*funcp) (struct state const *, time_t const *,
- int_fast32_t, struct tm *),
+ struct tm *(*funcp)(struct state const *, time_t const *,
+ int_fast32_t, struct tm *),
struct state const *sp,
const int_fast32_t offset)
{
@@ -2226,7 +2254,7 @@ timeoff(struct tm *tmp, long offset)
#endif /* defined STD_INSPIRED */
-static int_fast64_t
+static int_fast32_t
leapcorr(struct state const *sp, time_t t)
{
register struct lsinfo const * lp;
@@ -2247,6 +2275,14 @@ leapcorr(struct state const *sp, time_t t)
#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
@@ -2352,7 +2388,7 @@ time(time_t *p)
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)) {
+ || increment_overflow_time(&r, offset)) {
errno = EOVERFLOW;
r = -1;
}
diff --git a/tz/northamerica b/tz/northamerica
index ddc5759..a2f2b43 100644
--- a/tz/northamerica
+++ b/tz/northamerica
@@ -729,7 +729,11 @@ Zone America/Adak 12:13:22 - LMT 1867 Oct 19 12:44:35
-11:00 US B%sT 1983 Oct 30 2:00
-10:00 US AH%sT 1983 Nov 30
-10:00 US H%sT
-# The following switches don't quite make our 1970 cutoff.
+# The following switches don't make our 1970 cutoff.
+#
+# Kiska observed Tokyo date and time during Japanese occupation from
+# 1942-06-06 to 1943-07-29, and similarly for Attu from 1942-06-07 to
+# 1943-05-29 (all dates American). Both islands are now uninhabited.
#
# Shanks writes that part of southwest Alaska (e.g. Aniak)
# switched from -11:00 to -10:00 on 1968-09-22 at 02:00,
@@ -1593,24 +1597,7 @@ Zone America/Moncton -4:19:08 - LMT 1883 Dec 9
# From Paul Eggert (2020-01-10):
# See America/Toronto for most of Quebec, including Montreal.
# See America/Halifax for the Îles de la Madeleine and the Listuguj reserve.
-#
-# Matthews and Vincent (1998) also write that Quebec east of the -63
-# meridian is supposed to observe AST, but residents as far east as
-# Natashquan use EST/EDT, and residents east of Natashquan use AST.
-# The Quebec department of justice writes in
-# "The situation in Minganie and Basse-Côte-Nord"
-# https://www.justice.gouv.qc.ca/en/department/ministre/functions-and-responsabilities/legal-time-in-quebec/the-situation-in-minganie-and-basse-cote-nord/
-# that the coastal strip from just east of Natashquan to Blanc-Sablon
-# observes Atlantic standard time all year round.
-# This common practice was codified into law as of 2007; see Legal Time Act,
-# CQLR c T-5.1 <http://legisquebec.gouv.qc.ca/en/ShowDoc/cs/T-5.1>.
-# For lack of better info, guess this practice began around 1970, contra to
-# Shanks & Pottenger who have this region observing AST/ADT.
-
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone America/Blanc-Sablon -3:48:28 - LMT 1884
- -4:00 Canada A%sT 1970
- -4:00 - AST
+# See America/Puerto_Rico for east of Natashquan.
# Ontario
@@ -1649,54 +1636,6 @@ Zone America/Blanc-Sablon -3:48:28 - LMT 1884
# time became a comic failure in Orillia. Toronto Star 2017-07-08.
# https://www.thestar.com/news/insight/2017/07/08/bold-attempt-at-daylight-saving-time-became-a-comic-failure-in-orillia.html
-# From Paul Eggert (1997-10-17):
-# Mark Brader writes that an article in the 1997-10-14 Toronto Star
-# says that Atikokan, Ontario currently does not observe DST,
-# but will vote on 11-10 whether to use EST/EDT.
-# He also writes that the Ontario Time Act (1990, Chapter T.9)
-# http://www.gov.on.ca/MBS/english/publications/statregs/conttext.html
-# says that Ontario east of 90W uses EST/EDT, and west of 90W uses CST/CDT.
-# Officially Atikokan is therefore on CST/CDT, and most likely this report
-# concerns a non-official time observed as a matter of local practice.
-#
-# From Paul Eggert (2000-10-02):
-# Matthews and Vincent (1998) write that Atikokan, Pickle Lake, and
-# New Osnaburgh observe CST all year, that Big Trout Lake observes
-# CST/CDT, and that Upsala and Shebandowan observe EST/EDT, all in
-# violation of the official Ontario rules.
-#
-# From Paul Eggert (2006-07-09):
-# Chris Walton (2006-07-06) mentioned an article by Stephanie MacLellan in the
-# 2005-07-21 Chronicle-Journal, which said:
-#
-# The clocks in Atikokan stay set on standard time year-round.
-# This means they spend about half the time on central time and
-# the other half on eastern time.
-#
-# For the most part, the system works, Mayor Dennis Brown said.
-#
-# "The majority of businesses in Atikokan deal more with Eastern
-# Canada, but there are some that deal with Western Canada," he
-# said. "I don't see any changes happening here."
-#
-# Walton also writes "Supposedly Pickle Lake and Mishkeegogamang
-# [New Osnaburgh] follow the same practice."
-
-# From Garry McKinnon (2006-07-14) via Chris Walton:
-# I chatted with a member of my board who has an outstanding memory
-# and a long history in Atikokan (and in the telecom industry) and he
-# can say for certain that Atikokan has been practicing the current
-# time keeping since 1952, at least.
-
-# From Paul Eggert (2006-07-17):
-# Shanks & Pottenger say that Atikokan has agreed with Rainy River
-# ever since standard time was introduced, but the information from
-# McKinnon sounds more authoritative. For now, assume that Atikokan
-# switched to EST immediately after WWII era daylight saving time
-# ended. This matches the old (less-populous) America/Coral_Harbour
-# entry since our cutoff date of 1970, so we can move
-# America/Coral_Harbour to the 'backward' file.
-
# From Mark Brader (2010-03-06):
#
# Currently the database has:
@@ -1842,11 +1781,7 @@ Zone America/Rainy_River -6:18:16 - LMT 1895
-6:00 Canada C%sT 1940 Sep 29
-6:00 1:00 CDT 1942 Feb 9 2:00s
-6:00 Canada C%sT
-Zone America/Atikokan -6:06:28 - LMT 1895
- -6:00 Canada C%sT 1940 Sep 29
- -6:00 1:00 CDT 1942 Feb 9 2:00s
- -6:00 Canada C%sT 1945 Sep 30 2:00
- -5:00 - EST
+# For Atikokan see America/Panama.
# Manitoba
@@ -2037,60 +1972,6 @@ Zone America/Edmonton -7:33:52 - LMT 1906 Sep
# Shanks & Pottenger write that since 1970 most of this region has
# been like Vancouver.
# Dawson Creek uses MST. Much of east BC is like Edmonton.
-# Matthews and Vincent (1998) write that Creston is like Dawson Creek.
-
-# It seems though that (re: Creston) is not entirely correct:
-
-# From Chris Walton (2011-12-01):
-# There are two areas within the Canadian province of British Columbia
-# that do not currently observe daylight saving:
-# a) The Creston Valley (includes the town of Creston and surrounding area)
-# b) The eastern half of the Peace River Regional District
-# (includes the cities of Dawson Creek and Fort St. John)
-
-# Earlier this year I stumbled across a detailed article about the time
-# keeping history of Creston; it was written by Tammy Hardwick who is the
-# manager of the Creston & District Museum. The article was written in May 2009.
-# http://www.ilovecreston.com/?p=articles&t=spec&ar=260
-# According to the article, Creston has not changed its clocks since June 1918.
-# i.e. Creston has been stuck on UT-7 for 93 years.
-# Dawson Creek, on the other hand, changed its clocks as recently as April 1972.
-
-# Unfortunately the exact date for the time change in June 1918 remains
-# unknown and will be difficult to ascertain. I e-mailed Tammy a few months
-# ago to ask if Sunday June 2 was a reasonable guess. She said it was just
-# as plausible as any other date (in June). She also said that after writing
-# the article she had discovered another time change in 1916; this is the
-# subject of another article which she wrote in October 2010.
-# http://www.creston.museum.bc.ca/index.php?module=comments&uop=view_comment&cm+id=56
-
-# Here is a summary of the three clock change events in Creston's history:
-# 1. 1884 or 1885: adoption of Mountain Standard Time (GMT-7)
-# Exact date unknown
-# 2. Oct 1916: switch to Pacific Standard Time (GMT-8)
-# Exact date in October unknown; Sunday October 1 is a reasonable guess.
-# 3. June 1918: switch to Pacific Daylight Time (GMT-7)
-# Exact date in June unknown; Sunday June 2 is a reasonable guess.
-# note 1:
-# On Oct 27/1918 when daylight saving ended in the rest of Canada,
-# Creston did not change its clocks.
-# note 2:
-# During WWII when the Federal Government legislated a mandatory clock change,
-# Creston did not oblige.
-# note 3:
-# There is no guarantee that Creston will remain on Mountain Standard Time
-# (UTC-7) forever.
-# The subject was debated at least once this year by the town Council.
-# http://www.bclocalnews.com/kootenay_rockies/crestonvalleyadvance/news/116760809.html
-
-# During a period WWII, summer time (Daylight saying) was mandatory in Canada.
-# In Creston, that was handled by shifting the area to PST (-8:00) then applying
-# summer time to cause the offset to be -7:00, the same as it had been before
-# the change. It can be argued that the timezone abbreviation during this
-# period should be PDT rather than MST, but that doesn't seem important enough
-# (to anyone) to further complicate the rules.
-
-# The transition dates (and times) are guesses.
# From Matt Johnson (2015-09-21):
# Fort Nelson, BC, Canada will cancel DST this year. So while previously they
@@ -2144,10 +2025,7 @@ Zone America/Fort_Nelson -8:10:47 - LMT 1884
-8:00 Vanc P%sT 1987
-8:00 Canada P%sT 2015 Mar 8 2:00
-7:00 - MST
-Zone America/Creston -7:46:04 - LMT 1884
- -7:00 - MST 1916 Oct 1
- -8:00 - PST 1918 Jun 2
- -7:00 - MST
+# For Creston see America/Phoenix.
# Northwest Territories, Nunavut, Yukon
@@ -2929,64 +2807,61 @@ Zone America/Tijuana -7:48:04 - LMT 1922 Jan 1 0:11:56
# Anguilla
# Antigua and Barbuda
-# See America/Port_of_Spain.
+# See America/Puerto_Rico.
-# Bahamas
-#
-# For 1899 Milne gives -5:09:29.5; round that.
-#
-# From P Chan (2020-11-27, corrected on 2020-12-02):
-# There were two periods of DST observed in 1942-1945: 1942-05-01
-# midnight to 1944-12-31 midnight and 1945-02-01 to 1945-10-17 midnight.
-# "midnight" should mean 24:00 from the context.
-#
-# War Time Order 1942 [1942-05-01] and War Time (No. 2) Order 1942 [1942-09-29]
-# Appendix to the Statutes of 7 George VI. and the Year 1942. p 34, 43
-# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA3-PA34
-# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA3-PA43
-#
-# War Time Order 1943 [1943-03-31] and War Time Order 1944 [1943-12-29]
-# Appendix to the Statutes of 8 George VI. and the Year 1943. p 9-10, 28-29
-# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA4-PA9
-# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA4-PA28
-#
-# War Time Order 1945 [1945-01-31] and the Order which revoke War Time Order
-# 1945 [1945-10-16] Appendix to the Statutes of 9 George VI. and the Year
-# 1945. p 160, 247-248
-# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA6-PA160
-# https://books.google.com/books?id=5rlNAQAAIAAJ&pg=RA6-PA247
-#
-# From Sue Williams (2006-12-07):
-# The Bahamas announced about a month ago that they plan to change their DST
-# rules to sync with the U.S. starting in 2007....
-# http://www.jonesbahamas.com/?c=45&a=10412
+# The Bahamas
+# See America/Montreal.
-# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
-Rule Bahamas 1942 only - May 1 24:00 1:00 W
-Rule Bahamas 1944 only - Dec 31 24:00 0 S
-Rule Bahamas 1945 only - Feb 1 0:00 1:00 W
-Rule Bahamas 1945 only - Aug 14 23:00u 1:00 P # Peace
-Rule Bahamas 1945 only - Oct 17 24:00 0 S
-Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S
-Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone America/Nassau -5:09:30 - LMT 1912 Mar 2
- -5:00 Bahamas E%sT 1976
- -5:00 US E%sT
# Barbados
# For 1899 Milne gives -3:58:29.2; round that.
+# From P Chan (2020-12-09 and 2020-12-11):
+# Standard time of GMT-4 was adopted in 1911.
+# Definition of Time Act, 1911 (1911-7) [1911-08-28]
+# 1912, Laws of Barbados (5 v.), OCLC Number: 919801291, Vol. 4, Image No. 522
+# 1944, Laws of Barbados (5 v.), OCLC Number: 84548697, Vol. 4, Image No. 122
+# http://llmc.com/browse.aspx?type=2&coll=85&div=297
+#
+# DST was observed in 1942-44.
+# Defence (Daylight Saving) Regulations, 1942, 1942-04-13
+# Defence (Daylight Saving) (Repeal) Regulations, 1942, 1942-08-22
+# Defence (Daylight Saving) Regulations, 1943, 1943-04-16
+# Defence (Daylight Saving) (Repeal) Regulations, 1943, 1943-09-01
+# Defence (Daylight Saving) Regulations, 1944, 1944-03-21
+# [Defence (Daylight Saving) (Amendment) Regulations 1944, 1944-03-28]
+# Defence (Daylight Saving) (Repeal) Regulations, 1944, 1944-08-30
+#
+# 1914-, Subsidiary Legis., Annual Vols. OCLC Number: 226290591
+# 1942: Image Nos. 527-528, 555-556
+# 1943: Image Nos. 178-179, 198
+# 1944: Image Nos. 113-115, 129
+# http://llmc.com/titledescfull.aspx?type=2&coll=85&div=297&set=98437
+#
+# From Tim Parenti (2021-02-20):
+# The transitions below are derived from P Chan's sources, except that the 1977
+# through 1980 transitions are from Shanks & Pottenger since we have no better
+# data there. Of particular note, the 1944 DST regulation only advanced the
+# time to "exactly three and a half hours later than Greenwich mean time", as
+# opposed to "three hours" in the 1942 and 1943 regulations.
+
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
+Rule Barb 1942 only - Apr 19 5:00u 1:00 D
+Rule Barb 1942 only - Aug 31 6:00u 0 S
+Rule Barb 1943 only - May 2 5:00u 1:00 D
+Rule Barb 1943 only - Sep 5 6:00u 0 S
+Rule Barb 1944 only - Apr 10 5:00u 0:30 -
+Rule Barb 1944 only - Sep 10 6:00u 0 S
Rule Barb 1977 only - Jun 12 2:00 1:00 D
Rule Barb 1977 1978 - Oct Sun>=1 2:00 0 S
Rule Barb 1978 1980 - Apr Sun>=15 2:00 1:00 D
Rule Barb 1979 only - Sep 30 2:00 0 S
Rule Barb 1980 only - Sep 25 2:00 0 S
# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone America/Barbados -3:58:29 - LMT 1924 # Bridgetown
- -3:58:29 - BMT 1932 # Bridgetown Mean Time
+Zone America/Barbados -3:58:29 - LMT 1911 Aug 28 # Bridgetown
+ -4:00 Barb A%sT 1944
+ -4:00 Barb AST/-0330 1945
-4:00 Barb A%sT
# Belize
@@ -3148,6 +3023,9 @@ Zone Atlantic/Bermuda -4:19:18 - LMT 1890 # Hamilton
-4:00 Canada A%sT 1976
-4:00 US A%sT
+# Caribbean Netherlands
+# See America/Puerto_Rico.
+
# Cayman Is
# See America/Panama.
@@ -3376,7 +3254,7 @@ Zone America/Havana -5:29:28 - LMT 1890
-5:00 Cuba C%sT
# Dominica
-# See America/Port_of_Spain.
+# See America/Puerto_Rico.
# Dominican Republic
@@ -3428,7 +3306,7 @@ Zone America/El_Salvador -5:56:48 - LMT 1921 # San Salvador
# Guadeloupe
# St Barthélemy
# St Martin (French part)
-# See America/Port_of_Spain.
+# See America/Puerto_Rico.
# Guatemala
#
@@ -3615,7 +3493,7 @@ Zone America/Martinique -4:04:20 - LMT 1890 # Fort-de-France
-4:00 - AST
# Montserrat
-# See America/Port_of_Spain.
+# See America/Puerto_Rico.
# Nicaragua
#
@@ -3687,7 +3565,6 @@ Zone America/Managua -5:45:08 - LMT 1890
Zone America/Panama -5:18:08 - LMT 1890
-5:19:36 - CMT 1908 Apr 22 # Colón Mean Time
-5:00 - EST
-Link America/Panama America/Cayman
# Puerto Rico
# There are too many San Juans elsewhere, so we'll use 'Puerto_Rico'.
@@ -3699,7 +3576,7 @@ Zone America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12:00 # San Juan
# St Kitts-Nevis
# St Lucia
-# See America/Port_of_Spain.
+# See America/Puerto_Rico.
# St Pierre and Miquelon
# There are too many St Pierres elsewhere, so we'll use 'Miquelon'.
@@ -3710,7 +3587,10 @@ Zone America/Miquelon -3:44:40 - LMT 1911 May 15 # St Pierre
-3:00 Canada -03/-02
# St Vincent and the Grenadines
-# See America/Port_of_Spain.
+# See America/Puerto_Rico.
+
+# Sint Maarten
+# See America/Puerto_Rico.
# Turks and Caicos
#
@@ -3781,8 +3661,8 @@ Zone America/Grand_Turk -4:44:32 - LMT 1890
-5:00 US E%sT
# British Virgin Is
-# Virgin Is
-# See America/Port_of_Spain.
+# US Virgin Is
+# See America/Puerto_Rico.
# Local Variables:
diff --git a/tz/private.h b/tz/private.h
index 9efcd78..4c03324 100644
--- a/tz/private.h
+++ b/tz/private.h
@@ -66,6 +66,10 @@
#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
@@ -90,10 +94,6 @@
#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 */
@@ -164,9 +164,16 @@
#include <errno.h>
+#ifndef EINVAL
+# define EINVAL ERANGE
+#endif
+
#ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL
#endif
+#ifndef ENOMEM
+# define ENOMEM EINVAL
+#endif
#ifndef ENOTSUP
# define ENOTSUP EINVAL
#endif
@@ -218,9 +225,6 @@
#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__ and INTMAX_MAX's values depend on
@@ -321,6 +325,10 @@ typedef long intmax_t;
# endif
#endif
+#ifndef UINT_FAST32_MAX
+typedef unsigned long uint_fast32_t;
+#endif
+
#ifndef UINT_FAST64_MAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
typedef unsigned long long uint_fast64_t;
@@ -361,10 +369,10 @@ typedef unsigned long uintmax_t;
#endif
#if 3 <= __GNUC__
-# define ATTRIBUTE_CONST __attribute__ ((const))
-# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
-# define ATTRIBUTE_PURE __attribute__ ((__pure__))
-# define ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
+# define ATTRIBUTE_CONST __attribute__((const))
+# define ATTRIBUTE_MALLOC __attribute__((__malloc__))
+# define ATTRIBUTE_PURE __attribute__((__pure__))
+# define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
#else
# define ATTRIBUTE_CONST /* empty */
# define ATTRIBUTE_MALLOC /* empty */
@@ -374,7 +382,7 @@ typedef unsigned long uintmax_t;
#if !defined _Noreturn && __STDC_VERSION__ < 201112
# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
-# define _Noreturn __attribute__ ((__noreturn__))
+# define _Noreturn __attribute__((__noreturn__))
# else
# define _Noreturn
# endif
@@ -418,10 +426,11 @@ typedef unsigned long uintmax_t;
# define TZ_TIME_T 0
#endif
-#if TZ_TIME_T
-# ifdef LOCALTIME_IMPLEMENTATION
+#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
static time_t sys_time(time_t *x) { return time(x); }
-# endif
+#endif
+
+#if TZ_TIME_T
typedef time_tz tz_time_t;
@@ -619,7 +628,7 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
# define bool int
#endif
-#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#define TYPE_BIT(type) (sizeof(type) * CHAR_BIT)
#define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
@@ -637,7 +646,7 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
#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 problem occur only when compiling .c files that use
+ 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
@@ -684,6 +693,19 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
# define UNINIT_TRAP 0
#endif
+#ifdef DEBUG
+# define UNREACHABLE() abort()
+#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
+# define UNREACHABLE() __builtin_unreachable()
+#elif defined __has_builtin
+# if __has_builtin(__builtin_unreachable)
+# define UNREACHABLE() __builtin_unreachable()
+# endif
+#endif
+#ifndef UNREACHABLE
+# define UNREACHABLE() ((void) 0)
+#endif
+
/*
** For the benefit of GNU folk...
** '_(MSGID)' uses the current locale's message library string for MSGID.
@@ -709,8 +731,6 @@ char *ctime_r(time_t const *, char *);
/* Handy macros that are independent of tzfile implementation. */
-#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
-
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
@@ -721,6 +741,11 @@ char *ctime_r(time_t const *, char *);
#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
+#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
+#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
+#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
+
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
@@ -743,6 +768,7 @@ char *ctime_r(time_t const *, char *);
#define TM_DECEMBER 11
#define TM_YEAR_BASE 1900
+#define TM_WDAY_BASE TM_MONDAY
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
@@ -763,14 +789,4 @@ char *ctime_r(time_t const *, char *);
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
-
-/*
-** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
-*/
-
-#define AVGSECSPERYEAR 31556952L
-#define SECSPERREPEAT \
- ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
-#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
-
#endif /* !defined PRIVATE_H */
diff --git a/tz/southamerica b/tz/southamerica
index aad8b2d..9d8f460 100644
--- a/tz/southamerica
+++ b/tz/southamerica
@@ -574,7 +574,7 @@ Zone America/Argentina/Ushuaia -4:33:12 - LMT 1894 Oct 31
-3:00 - -03
# Aruba
-Link America/Curacao America/Aruba
+# See America/Puerto_Rico.
# Bolivia
# Zone NAME STDOFF RULES FORMAT [UNTIL]
@@ -1369,35 +1369,14 @@ Zone America/Bogota -4:56:16 - LMT 1884 Mar 13
# no information; probably like America/Bogota
# Curaçao
-
-# Milne gives 4:35:46.9 for Curaçao mean time; round to nearest.
-#
-# From Paul Eggert (2006-03-22):
-# Shanks & Pottenger say that The Bottom and Philipsburg have been at
-# -4:00 since standard time was introduced on 1912-03-02; and that
-# Kralendijk and Rincon used Kralendijk Mean Time (-4:33:08) from
-# 1912-02-02 to 1965-01-01. The former is dubious, since S&P also say
-# Saba Island has been like Curaçao.
-# This all predates our 1970 cutoff, though.
-#
-# By July 2007 Curaçao and St Maarten are planned to become
-# associated states within the Netherlands, much like Aruba;
-# Bonaire, Saba and St Eustatius would become directly part of the
-# Netherlands as Kingdom Islands. This won't affect their time zones
-# though, as far as we know.
+# See America/Puerto_Rico.
#
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone America/Curacao -4:35:47 - LMT 1912 Feb 12 # Willemstad
- -4:30 - -0430 1965
- -4:00 - AST
-
# From Arthur David Olson (2011-06-15):
# use links for places with new iso3166 codes.
# The name "Lower Prince's Quarter" is both longer than fourteen characters
-# and contains an apostrophe; use "Lower_Princes" below.
-
-Link America/Curacao America/Lower_Princes # Sint Maarten
-Link America/Curacao America/Kralendijk # Caribbean Netherlands
+# and contains an apostrophe; use "Lower_Princes"....
+# From Paul Eggert (2021-05-06):
+# These backward-compatibility links now are in the 'backward' file.
# Ecuador
#
@@ -1540,11 +1519,40 @@ Zone America/Cayenne -3:29:20 - LMT 1911 Jul
-3:00 - -03
# Guyana
+
+# From P Chan (2020-11-27):
+# https://books.google.com/books?id=5-5CAQAAMAAJ&pg=SA1-PA547
+# The Official Gazette of British Guiana. (New Series.) Vol. XL. July to
+# December, 1915, p 1547, lists as several notes:
+# "Local Mean Time 3 hours 52 mins. 39 secs. slow of Greenwich Mean Time
+# (Georgetown.) From 1st August, 1911, British Guiana Standard Mean Time 4
+# hours slow of Greenwich Mean Time, by notice in Official Gazette on 1st July,
+# 1911. From 1st March, 1915, British Guiana Standard Mean Time 3 hours 45
+# mins. 0 secs. slow of Greenwich Mean Time, by notice in Official Gazette on
+# 23rd January, 1915."
+#
+# https://parliament.gov.gy/documents/acts/10923-act_no._27_of_1975_-_interpretation_and_general_clauses_(amendment)_act_1975.pdf
+# Interpretation and general clauses (Amendment) Act 1975 (Act No. 27 of 1975)
+# [dated 1975-07-31]
+# "This Act...shall come into operation on 1st August, 1975."
+# "...where any expression of time occurs...the time referred to shall signify
+# the standard time of Guyana which shall be three hours behind Greenwich Mean
+# Time."
+#
+# Circular No. 10/1992 dated 1992-03-20
+# https://dps.gov.gy/wp-content/uploads/2018/12/1992-03-20-Circular-010.pdf
+# "...cabinet has decided that with effect from Sunday 29th March, 1992, Guyana
+# Standard Time would be re-established at 01:00 hours by adjusting the hands
+# of the clock back to 24:00 hours."
+# Legislated in the Interpretation and general clauses (Amendment) Act 1992
+# (Act No. 6 of 1992) [passed 1992-03-27, published 1992-04-18]
+# https://parliament.gov.gy/documents/acts/5885-6_of_1992_interpretation_and_general_clauses_(amendment)_act_1992.pdf
+
# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone America/Guyana -3:52:40 - LMT 1915 Mar # Georgetown
- -3:45 - -0345 1975 Jul 31
- -3:00 - -03 1991
-# IATA SSIM (1996-06) says -4:00. Assume a 1991 switch.
+Zone America/Guyana -3:52:39 - LMT 1911 Aug 1 # Georgetown
+ -4:00 - -04 1915 Mar 1
+ -3:45 - -0345 1975 Aug 1
+ -3:00 - -03 1992 Mar 29 1:00
-4:00 - -04
# Paraguay
@@ -1685,24 +1693,7 @@ Zone America/Paramaribo -3:40:40 - LMT 1911
-3:00 - -03
# Trinidad and Tobago
-# Zone NAME STDOFF RULES FORMAT [UNTIL]
-Zone America/Port_of_Spain -4:06:04 - LMT 1912 Mar 2
- -4:00 - AST
-
-# These all agree with Trinidad and Tobago since 1970.
-Link America/Port_of_Spain America/Anguilla
-Link America/Port_of_Spain America/Antigua
-Link America/Port_of_Spain America/Dominica
-Link America/Port_of_Spain America/Grenada
-Link America/Port_of_Spain America/Guadeloupe
-Link America/Port_of_Spain America/Marigot # St Martin (French part)
-Link America/Port_of_Spain America/Montserrat
-Link America/Port_of_Spain America/St_Barthelemy # St Barthélemy
-Link America/Port_of_Spain America/St_Kitts # St Kitts & Nevis
-Link America/Port_of_Spain America/St_Lucia
-Link America/Port_of_Spain America/St_Thomas # Virgin Islands (US)
-Link America/Port_of_Spain America/St_Vincent
-Link America/Port_of_Spain America/Tortola # Virgin Islands (UK)
+# See America/Puerto_Rico.
# Uruguay
# From Paul Eggert (1993-11-18):
diff --git a/tz/strftime.c b/tz/strftime.c
index 4f871cd..5273155 100644
--- a/tz/strftime.c
+++ b/tz/strftime.c
@@ -335,11 +335,13 @@ label:
&& tm.tm_sec == tm_1.tm_sec))
return NULL;
}
- if (TYPE_SIGNED(time_t))
- sprintf(buf, "%"PRIdMAX,
- (intmax_t) mkt);
- else sprintf(buf, "%"PRIuMAX,
- (uintmax_t) mkt);
+ 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;
diff --git a/tz/theory.html b/tz/theory.html
index 28f6b8e..5a05f4b 100644
--- a/tz/theory.html
+++ b/tz/theory.html
@@ -42,12 +42,13 @@ 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>).
-The database labels each timezone with a notable location and
-records all known clock transitions for that location.
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>
@@ -58,8 +59,9 @@ 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, <code>America/Mazatlan</code> which observes Mexican-style DST,
-and <code>America/Phoenix</code> which does not observe DST.
+time (<abbr>DST</abbr>),
+<code>America/Mazatlan</code> which observes Mexican-style <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
@@ -68,7 +70,7 @@ time but differs from other timezones for some timestamps after 1970.
</p>
<p>
-Clock transitions before 1970 are recorded for each timezone,
+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
@@ -190,8 +192,8 @@ in decreasing order of importance:
<code>TZ</code> strings</a>.
A file name component must not exceed 14 characters or start with
'<code>-</code>'.
- E.g., prefer <code>Asia/Brunei</code> to
- <code>Asia/Bandar_Seri_Begawan</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>
@@ -473,10 +475,10 @@ in decreasing order of importance:
<p>
<small>These abbreviations are:
- AMT Amsterdam, Asunción, Athens;
+ AMT Asunción, Athens;
BMT Baghdad, Bangkok, Batavia, Bermuda, Bern, Bogotá, Bridgetown,
Brussels, Bucharest;
- CMT Calamarca, Caracas, Chisinau, Colón, Copenhagen, Córdoba;
+ CMT Calamarca, Caracas, Chisinau, Colón, Córdoba;
DMT Dublin/Dunsink;
EMT Easter;
FFMT Fort-de-France;
@@ -499,7 +501,8 @@ in decreasing order of importance:
SMT Santiago, Simferopol, Singapore, Stanley;
TBMT Tbilisi;
TMT Tallinn, Tehran;
- WMT Warsaw</small>.
+ WMT Warsaw;
+ ZMT Zomba.</small>
</p>
<p>
@@ -513,9 +516,7 @@ in decreasing order of importance:
1880&ndash;1916,
MMT/MST/MDST for Moscow 1880&ndash;1919, and
RMT/LST for Riga Mean Time and Latvian Summer time 1880&ndash;1926.
- An extra-special case is SET for Swedish Time (<em>svensk
- normaltid</em>) 1879&ndash;1899, 3&deg; west of the Stockholm
- Observatory.</small>
+ </small>
</p>
</li>
<li>
@@ -702,11 +703,9 @@ href="https://www.dissentmagazine.org/blog/booked-a-global-history-of-time-vanes
<li>
Sometimes historical timekeeping was specified more precisely
than what the <code><abbr>tz</abbr></code> code can handle.
- For example, from 1909 to 1937 <a
- href="https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm"
- hreflang="nl">Netherlands clocks</a> were legally Amsterdam Mean
+ For example, from 1880 to 1916 clocks in Ireland observed Dublin Mean
Time (estimated to be <abbr>UT</abbr>
- +00:19:32.13), but the <code><abbr>tz</abbr></code>
+ &minus;00:25:21.1), but the <code><abbr>tz</abbr></code>
code cannot represent the fractional second.
In practice these old specifications were rarely if ever
implemented to subsecond precision.
@@ -753,7 +752,8 @@ href="https://www.dissentmagazine.org/blog/booked-a-global-history-of-time-vanes
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 DST is not used a different time zone
+ such as that required by POSIX.
+ If <abbr>DST</abbr> is not used a different time zone
can often do the trick; for example, in Kenya a <code>TZ</code> setting
like <code>&lt;-03&gt;3</code> or <code>America/Cayenne</code> starts
the day six hours later than <code>Africa/Nairobi</code> does.
@@ -1271,7 +1271,8 @@ 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 DST adjustments are applied &ndash;
+at the same point that time zone and <abbr>DST</abbr>
+adjustments are applied &ndash;
namely, at calls to <code>localtime</code> and analogous functions &ndash;
and the process is driven by leap second information
stored in alternate versions of the <abbr>TZif</abbr> files.
diff --git a/tz/tz-art.html b/tz/tz-art.html
index 72b9654..73be9f9 100644
--- a/tz/tz-art.html
+++ b/tz/tz-art.html
@@ -144,12 +144,13 @@ In the "14 Days to Go"/"T Minus..." episode of
(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 DST.)
+(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 DST, Marshall changes his clock with
-unusual consequences.
+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>".
@@ -157,7 +158,8 @@ Indiana</em> was a few dimensions ahead of its time</a>".
<li>
"Time Tunnel", <em>The Adventures of Pete &amp; Pete</em>, season 2, episode 5,
Nickelodeon, 1994-10-23.
-The two Petes travel back in time an hour on the day that DST ends.
+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.
@@ -178,7 +180,7 @@ 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 DST ends up impressing a wealthy
+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>
@@ -223,7 +225,7 @@ of the 1999-11 <em>Atlantic Monthly</em>.
<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 DST as a way of lessening wintertime despair.
+year-round <abbr>DST</abbr> as a way of lessening wintertime despair.
</li>
</ul>
<h2>Music</h2>
@@ -395,8 +397,8 @@ Richard Teitelbaum, modular moog and micromoog synthesizer</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 DST inventor William Willett. The song's first
-line is "Lights go out and I can't be saved".</td></tr>
+great-great-grandson of <abbr>DST</abbr> inventor William Willett.
+The song's first line is "Lights go out and I can't be saved".</td></tr>
<tr><td>&nbsp;</td><td></td></tr>
<tr><td>Artist</td><td>Jaime Guevara</td></tr>
diff --git a/tz/tz-link.html b/tz/tz-link.html
index abe38d8..d2661ad 100644
--- a/tz/tz-link.html
+++ b/tz/tz-link.html
@@ -167,6 +167,10 @@ 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 1999g, 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>,
@@ -241,7 +245,7 @@ data yourself. System-specific instructions for installing the
latest <code><abbr>tz</abbr></code> data have also been published
for <a href="https://www.ibm.com/developerworks/aix/library/au-aix-olson-time-zone/index.html"><abbr>AIX</abbr></a>,
<a
-href="https://play.google.com/store/apps/details?id=com.google.android.timezone.data">Android</a>,
+href="https://source.android.com/devices/tech/config/timezone-rules">Android</a>,
<a
href="http://userguide.icu-project.org/datetime/timezone"><abbr
title="International Components for Unicode">ICU</abbr></a>,
@@ -295,6 +299,8 @@ database format.</li>
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>
@@ -326,7 +332,8 @@ Time in 1000 Places</a> uses descriptions of the values.</li>
<li><a href="https://timezoneconverterapp.com/">Time Zone Converter</a>
uses a pulldown menu.</li>
<li><a href="https://home.kpn.nl/vanadovv/time/TZworld.html">Complete
-timezone information for all countries</a> displays tables of DST rules.
+timezone information for all countries</a>
+displays tables of <abbr>DST</abbr> rules.
<li><a href="https://www.timeanddate.com/worldclock/">The World Clock &ndash;
Worldwide</a> lets you sort zone names and convert times.</li>
<li><a href="https://24timezones.com">24TimeZones</a> has a world
@@ -474,7 +481,8 @@ It is freely available under the Apache License.</li>
<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>.
-On other runtimes, you can use one of the following.
+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>
@@ -485,17 +493,42 @@ href="https://github.com/kshetline/compact-time-zone-generator">CompactTimeZoneG
compiles time zone data into a compact form designed for
JavaScript. It is freely available under a combination of
the <abbr>MIT</abbr> license and the Apache 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
-plugin for the <a href="https://momentjs.com">Moment.js</a> date
-manipulation library. It is freely available under the <abbr>MIT</abbr>
-license.</li>
+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://github.com/mde/timezone-js">TimezoneJS.Date</a>'s
-<abbr>API</abbr> is upward compatible with standard JavaScript
-Dates. It is freely available under the Apache 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
@@ -561,9 +594,6 @@ the Apache License.</li>
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="http://bmsi.com/java/#TZ">ZoneInfo.java</a>
-is a <abbr>TZif</abbr> file reader written in Java.
-It is freely available under the <abbr>LGPL</abbr>.</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.
@@ -648,15 +678,6 @@ 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>
-<li><a href="https://relativedata.com/page/Time-Zone-Master">Time Zone
-Master</a> is a Microsoft Windows clock program that can automatically
-download, compile and use <code><abbr>tz</abbr></code> releases.
-The Basic version is free.</li>
-<li><a
-href="http://veladg.com/velaterra.html">VelaTerra</a> is
-a macOS program. Its developers
-<a href="http://veladg.com/tzoffer.html">offer free
-licenses</a> to <code><abbr>tz</abbr></code> contributors.</li>
</ul>
</section>
@@ -749,7 +770,7 @@ is written in Go and is freely available under the Apache License.</li>
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 <a href="https://nodejs.org/en/">Node.js</a>,
+<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>
@@ -865,7 +886,7 @@ 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 DST regulations.</dd>
+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
@@ -889,6 +910,10 @@ covers the history of local time in the Netherlands from ancient times.</dd>
<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
@@ -933,23 +958,24 @@ saving save electricity? A meta-analysis</a>. <em>Energy J.</em>
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 DST."</li>
+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
-DST transitions and permanent DST.</li>
+<abbr>DST</abbr> transitions and permanent <abbr>DST</abbr>.</li>
<li>Roenneberg T, Winnebeck EC, Klerman EB.
<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6692659/">Daylight
saving time and artificial time zones &ndash; a battle between
biological and social times</a>. <em>Front Physiol.</em> 2019;10:944.
doi:<a href="https://doi.org/10.3389/fphys.2019.00944">10.3389/fphys.2019.00944</a>.
-This reviews evidence about the health effects of DST and concludes,
+This reviews evidence about the health effects of <abbr>DST</abbr>
+and concludes,
"In summary, the scientific literature strongly argues against the
-switching between DST and Standard Time and even more so against
-adopting DST permanently."</li>
+switching between <abbr>DST</abbr> and Standard Time and even more so against
+adopting <abbr>DST</abbr> permanently."</li>
</ul>
</section>
diff --git a/tz/tzfile.5 b/tz/tzfile.5
index 2642978..74564cb 100644
--- a/tz/tzfile.5
+++ b/tz/tzfile.5
@@ -30,10 +30,11 @@ The magic four-byte ASCII sequence
identifies the file as a timezone information file.
.IP *
A byte identifying the version of the file's format
-(as of 2017, either an ASCII NUL, or
+(as of 2021, either an ASCII NUL,
.q "2",
+.q "3",
or
-.q "3" ).
+.q "4" ).
.IP *
Fifteen bytes containing zeros reserved for future use.
.IP *
@@ -128,19 +129,40 @@ 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].
.IP *
+.B tzh_charcnt
+bytes that represent time zone designations,
+which are null-terminated byte strings, each indexed by the
+.B 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.
+.IP *
.B 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
.BR time (2))
-at which a leap second occurs;
-the second is a signed integer specifying the
+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
.I total
number of leap seconds to be applied during the time period
starting at the given time.
-The pairs of values are sorted in ascending order by time.
-Each transition is for one leap second, either positive or negative;
-transitions always separated by at least 28 days minus 1 second.
+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).
.IP *
.B tzh_ttisstdcnt
standard/wall indicators, each stored as a one-byte boolean;
@@ -190,7 +212,7 @@ 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 representation for such instants.
+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
@@ -212,33 +234,52 @@ 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 be avoided, as they do not support transition
+should not be generated, as they do not support transition
times after the year 2038.
-Readers that only understand Version 1 must ignore
+Readers that understand only Version 1 must ignore
any data that extends beyond the calculated end of the version
1 data block.
.PP
-Writers should generate a version 3 file if
+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.
-Otherwise, version 2 files should be generated.
.PP
The sequence of time changes defined by the version 1
-header and data block should be a contiguous subsequence
+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 subsequence. It also lets writers not
+contiguous sub-sequence. It also lets writers not
supporting obsolescent readers use a
.B 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,
@@ -248,13 +289,20 @@ and
This is for compatibility with POSIX requirements for
time zone abbreviations.
.PP
-When reading a version 2 or 3 file, readers
+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
@@ -275,7 +323,7 @@ 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 to allow
+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
@@ -292,20 +340,33 @@ version 2+ data even if the reader's native timestamps have only
32 bits.
.IP *
Some readers designed for version 2 might mishandle
-timestamps after a version 3 file's last transition, because
+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.
.IP *
Some readers designed for version 2 do not support
-permanent daylight saving time, e.g., a TZ string
+permanent daylight saving time with transitions after 24:00
+\(en e.g., a TZ string
.q "EST5EDT,0/0,J365/25"
-denoting permanent Eastern Daylight Time (\-04).
-As a partial workaround, a writer can substitute standard time
-for the next time zone east, e.g.,
+denoting permanent Eastern Daylight Time
+(\-04).
+As a workaround, a writer can substitute standard time
+for two time zones east, e.g.,
+.q "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.,
.q "AST4"
-for permanent Atlantic Standard Time (\-04).
+for permanent
+Atlantic Standard Time (\-04).
+.IP *
+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.
.IP *
Some readers ignore the footer, and instead predict future
timestamps from the time type of the last transition.
@@ -370,6 +431,17 @@ 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.
+.IP *
+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.
.PP
Some interoperability problems are reader bugs that
are listed here mostly as warnings to developers of readers.
diff --git a/tz/tzfile.h b/tz/tzfile.h
index ee91104..9c3ea4e 100644
--- a/tz/tzfile.h
+++ b/tz/tzfile.h
@@ -44,7 +44,7 @@
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
- char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ 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 */
diff --git a/tz/zdump.8 b/tz/zdump.8
index f3e4e4e..a5e2042 100644
--- a/tz/zdump.8
+++ b/tz/zdump.8
@@ -44,12 +44,14 @@ Output a verbose description of time intervals.
For each
.I timezone
on the command line,
-print the time at the lowest possible time value,
-the time one day after the lowest possible time value,
+print the times at the two extreme time values,
+the times (if present) at and just beyond the boundaries of years that
+.BR localtime (3)
+and
+.BR gmtime (3)
+can represent, and
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 detected time discontinuity.
Each line is followed by
.BI isdst= D
where
@@ -66,7 +68,7 @@ seconds east of Greenwich.
.B \*-V
Like
.BR \*-v ,
-except omit the times relative to the extreme time values.
+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.
.TP
@@ -75,10 +77,10 @@ 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 exclusive and the upper is inclusive; for example,
+timestamp is inclusive and the upper is exclusive; for example,
.B "\*-c 1970,2070"
-selects transitions after 1970-01-01 00:00:00 UTC
-and on or before 2070-01-01 00:00:00 UTC.
+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
.BR \*-500,2500 .
.TP
@@ -91,7 +93,7 @@ The
determines whether the count includes leap seconds.
As with
.BR \*-c ,
-the cutoff's lower bound is exclusive and its upper bound is inclusive.
+the cutoff's lower bound is inclusive and its upper bound is exclusive.
.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,
@@ -200,7 +202,8 @@ 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 localtime
+Time discontinuities are found by sampling the results returned by
+.BR localtime (3)
at twelve-hour intervals.
This works in all real-world cases;
one can construct artificial time zones for which this fails.
diff --git a/tz/zdump.c b/tz/zdump.c
index b532fe3..e198b04 100644
--- a/tz/zdump.c
+++ b/tz/zdump.c
@@ -42,10 +42,6 @@
#define ZDUMP_HI_YEAR 2500
#endif /* !defined ZDUMP_HI_YEAR */
-#ifndef MAX_STRING_LENGTH
-#define MAX_STRING_LENGTH 1024
-#endif /* !defined MAX_STRING_LENGTH */
-
#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
@@ -77,7 +73,7 @@ extern int optind;
#endif
/* The minimum and maximum finite time values. */
-enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 };
+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)
@@ -95,15 +91,20 @@ static bool errout;
static char const *abbr(struct tm const *);
static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE;
static void dumptime(struct tm const *);
-static time_t hunt(timezone_t, char *, time_t, time_t);
+static time_t hunt(timezone_t, char *, 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_PURE;
-/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
-#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+/* 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
@@ -143,7 +144,7 @@ xmalloc(size_t size)
{
void *p = malloc(size);
if (!p) {
- perror(progname);
+ fprintf(stderr, _("%s: Memory exhausted\n"), progname);
exit(EXIT_FAILURE);
}
return p;
@@ -263,11 +264,23 @@ static void
gmtzinit(void)
{
if (USE_LOCALTIME_RZ) {
- static char const utc[] = "UTC0";
- gmtz = tzalloc(utc);
+ /* 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 "Link Etc/GMT GMT" line in the "etcetera" file. If "GMT"
+ fails, fall back on "GMT0" which might be similar due to the
+ "Link Etc/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) {
- perror(utc);
- exit(EXIT_FAILURE);
+ static char const gmt0[] = "GMT0";
+ gmtz = tzalloc(gmt0);
+ if (!gmtz) {
+ perror(gmt0);
+ exit(EXIT_FAILURE);
+ }
}
}
}
@@ -484,8 +497,8 @@ main(int argc, char *argv[])
if (cuttimes != loend && !*loend) {
hi = lo;
if (hi < cuthitime) {
- if (hi < absolute_min_time)
- hi = absolute_min_time;
+ if (hi < absolute_min_time + 1)
+ hi = absolute_min_time + 1;
cuthitime = hi;
}
} else if (cuttimes != loend && *loend == ','
@@ -497,8 +510,8 @@ main(int argc, char *argv[])
cutlotime = lo;
}
if (hi < cuthitime) {
- if (hi < absolute_min_time)
- hi = absolute_min_time;
+ if (hi < absolute_min_time + 1)
+ hi = absolute_min_time + 1;
cuthitime = hi;
}
} else {
@@ -510,9 +523,12 @@ main(int argc, char *argv[])
}
}
gmtzinit();
- INITIALIZE (now);
- if (! (iflag | vflag | Vflag))
+ 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]);
@@ -530,7 +546,7 @@ main(int argc, char *argv[])
perror(argv[i]);
return EXIT_FAILURE;
}
- if (! (iflag | vflag | Vflag)) {
+ if (now) {
show(tz, argv[i], now, false);
tzfree(tz);
continue;
@@ -539,12 +555,15 @@ main(int argc, char *argv[])
t = absolute_min_time;
if (! (iflag | Vflag)) {
show(tz, argv[i], t, true);
- t += SECSPERDAY;
- 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 < cutlotime)
- t = cutlotime;
- INITIALIZE (ab);
+ if (t + 1 < cutlotime)
+ t = cutlotime - 1;
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
if (tm_ok) {
ab = saveabbr(&abbrev, &abbrevsize, &tm);
@@ -552,19 +571,20 @@ main(int argc, char *argv[])
showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
}
- }
- while (t < cuthitime) {
+ } else
+ ab = NULL;
+ while (t < cuthitime - 1) {
time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
- && t + SECSPERDAY / 2 < cuthitime)
+ && t + SECSPERDAY / 2 < cuthitime - 1)
? t + SECSPERDAY / 2
- : cuthitime);
+ : cuthitime - 1);
struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
bool newtm_ok = newtmp != NULL;
if (tm_ok != newtm_ok
- || (tm_ok && (delta(&newtm, &tm) != newt - t
- || newtm.tm_isdst != tm.tm_isdst
- || strcmp(abbr(&newtm), ab) != 0))) {
- newt = hunt(tz, argv[i], t, newt);
+ || (ab && (delta(&newtm, &tm) != newt - t
+ || newtm.tm_isdst != tm.tm_isdst
+ || strcmp(abbr(&newtm), ab) != 0))) {
+ newt = hunt(tz, argv[i], t, newt, false);
newtmp = localtime_rz(tz, &newt, &newtm);
newtm_ok = newtmp != NULL;
if (iflag)
@@ -583,11 +603,15 @@ main(int argc, char *argv[])
}
}
if (! (iflag | Vflag)) {
- t = absolute_max_time;
- t -= SECSPERDAY;
- show(tz, argv[i], t, true);
- t += SECSPERDAY;
- show(tz, argv[i], t, true);
+ 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);
}
@@ -640,36 +664,46 @@ yeartot(intmax_t y)
return t;
}
+/* Search for a discontinuity in timezone TZ with name NAME, 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, char *name, time_t lot, time_t hit)
+hunt(timezone_t tz, char *name, time_t lot, time_t hit, bool only_ok)
{
static char * loab;
static size_t loabsize;
- char const * ab;
- time_t t;
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;
- if (lotm_ok)
- ab = saveabbr(&loab, &loabsize, &lotm);
for ( ; ; ) {
- time_t diff = hit - lot;
- if (diff < 2)
+ /* 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;
- t = lot;
- t += diff / 2;
- if (t <= lot)
- ++t;
- else if (t >= hit)
- --t;
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
- if (lotm_ok & tm_ok
- ? (delta(&tm, &lotm) == t - lot
- && tm.tm_isdst == lotm.tm_isdst
- && strcmp(abbr(&tm), ab) == 0)
- : lotm_ok == tm_ok) {
+ 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;
@@ -685,11 +719,11 @@ hunt(timezone_t tz, char *name, time_t lot, time_t hit)
static intmax_t
delta_nonneg(struct tm *newp, struct tm *oldp)
{
- register intmax_t result;
- register int tmy;
-
- result = 0;
- for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
+ 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;
@@ -787,6 +821,51 @@ show(timezone_t tz, char *zone, time_t t, bool v)
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, zone, 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
@@ -1047,21 +1126,21 @@ static const char *
tformat(void)
{
if (0 > (time_t) -1) { /* signed */
- if (sizeof (time_t) == sizeof (intmax_t))
+ if (sizeof(time_t) == sizeof(intmax_t))
return "%"PRIdMAX;
- if (sizeof (time_t) > sizeof (long))
+ if (sizeof(time_t) > sizeof(long))
return "%lld";
- if (sizeof (time_t) > sizeof (int))
+ if (sizeof(time_t) > sizeof(int))
return "%ld";
return "%d";
}
#ifdef PRIuMAX
- if (sizeof (time_t) == sizeof (uintmax_t))
+ if (sizeof(time_t) == sizeof(uintmax_t))
return "%"PRIuMAX;
#endif
- if (sizeof (time_t) > sizeof (unsigned long))
+ if (sizeof(time_t) > sizeof(unsigned long))
return "%llu";
- if (sizeof (time_t) > sizeof (unsigned int))
+ if (sizeof(time_t) > sizeof(unsigned int))
return "%lu";
return "%u";
}
@@ -1076,8 +1155,6 @@ dumptime(register const struct tm *timeptr)
"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;
@@ -1090,16 +1167,13 @@ dumptime(register const struct tm *timeptr)
** values in tm_wday or tm_mon, but since this code might be compiled
** with other (perhaps experimental) versions, paranoia is in order.
*/
- if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
- (int) (sizeof wday_name / sizeof wday_name[0]))
- wn = "???";
- else wn = wday_name[timeptr->tm_wday];
- if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
- (int) (sizeof mon_name / sizeof mon_name[0]))
- mn = "???";
- else mn = mon_name[timeptr->tm_mon];
printf("%s %s%3d %.2d:%.2d:%.2d ",
- wn, mn,
+ ((0 <= timeptr->tm_wday
+ && timeptr->tm_wday < sizeof wday_name / sizeof wday_name[0])
+ ? wday_name[timeptr->tm_wday] : "???"),
+ ((0 <= timeptr->tm_mon
+ && timeptr->tm_mon < sizeof mon_name / sizeof mon_name[0])
+ ? mon_name[timeptr->tm_mon] : "???"),
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec);
#define DIVISOR 10
diff --git a/tz/zic.8 b/tz/zic.8
index 60262d7..ea7bab9 100644
--- a/tz/zic.8
+++ b/tz/zic.8
@@ -31,7 +31,8 @@ zic \- timezone compiler
The
.B zic
program reads text from the file(s) named on the command line
-and creates the time conversion information files specified in this input.
+and creates the timezone information format (TZif) files
+specified in this input.
If a
.I filename
is
@@ -213,6 +214,15 @@ code designed for older
output formats. These compatibility issues affect only timestamps
before 1970 or after the start of 2038.
.PP
+The output contains a truncated leap second table,
+which can cause some older TZif readers to misbehave.
+This can occur if the
+.B "\*-L"
+option is used, and either an Expires line is present or
+the
+.B "\*-r"
+option is also used.
+.PP
The output file contains more than 1200 transitions,
which may be mishandled by some clients.
The current reference client supports at most 2000 transitions;
@@ -242,7 +252,7 @@ zero or more lines, each ending in a newline byte and containing at
most 511 bytes, 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)
-\*<http://pubs\*:.opengroup\*:.org/\*:onlinepubs/\*:9699919799/\*:basedefs/\*:V1_chap06\*:.html\*>
+\*<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
@@ -540,7 +550,7 @@ using the shortest form that does not lose information, where
.IR mm ,
and
.I ss
-are the hours, minutes, and seconds east (+) or west (\(mi) of UT.
+are the hours, minutes, and seconds east (+) or west (\-) of UT.
Alternatively,
a slash (/)
separates standard and daylight abbreviations.
@@ -700,6 +710,19 @@ or
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
+.q "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
+.B \*-r
+option is used.
+.PP
The expiration line, if present, has the form:
.nf
.ti +.5i
@@ -720,22 +743,6 @@ The
and
.B HH:MM:SS
fields give the expiration timestamp in UTC for the leap second table;
-.B zic
-outputs this expiration timestamp by truncating the end of the output
-file to the timestamp.
-If there is no expiration line,
-.B zic
-also accepts a comment
-.q "#expires \fIE\fP ...\&"
-where
-.I E
-is the expiration timestamp as a decimal integer count of seconds
-since the Epoch, not counting leap seconds.
-However, the
-.q "#expires"
-comment is an obsolescent feature,
-and the leap second file should use an expiration line
-instead of relying on a comment.
.SH "EXTENDED EXAMPLE"
Here is an extended example of
.B zic
diff --git a/tz/zic.c b/tz/zic.c
index a902b34..c6feb8b 100644
--- a/tz/zic.c
+++ b/tz/zic.c
@@ -5,23 +5,27 @@
** 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 <stddef.h>
#include <stdio.h>
-#define ZIC_VERSION_PRE_2013 '2'
-#define ZIC_VERSION '3'
-
typedef int_fast64_t zic_t;
#define ZIC_MIN INT_FAST64_MIN
#define ZIC_MAX INT_FAST64_MAX
-#define PRIdZIC PRIdFAST64
#define SCNdZIC SCNdFAST64
#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
@@ -43,21 +47,6 @@ typedef int_fast64_t zic_t;
#else
#define MKDIR_UMASK 0755
#endif
-/* Port to native MS-Windows and to ancient UNIX. */
-#if !defined S_ISDIR && defined S_IFDIR && defined S_IFMT
-# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
-#endif
-
-#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 */
/* The maximum ptrdiff_t value, for pre-C99 platforms. */
#ifndef PTRDIFF_MAX
@@ -167,7 +156,6 @@ 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 itsdir(char const *);
static bool itssymlink(char const *);
static bool is_alpha(char a);
static char lowerit(char);
@@ -176,7 +164,7 @@ 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 void rulesub(struct rule * rp,
+static bool rulesub(struct rule * rp,
const char * loyearp, const char * hiyearp,
const char * typep, const char * monthp,
const char * dayp, const char * timep);
@@ -462,7 +450,7 @@ static void *
memcheck(void *ptr)
{
if (ptr == NULL)
- memory_exhausted(strerror(errno));
+ memory_exhausted(strerror(HAVE_MALLOC_ERRNO ? errno : ENOMEM));
return ptr;
}
@@ -479,7 +467,7 @@ erealloc(void *ptr, size_t size)
}
static char * ATTRIBUTE_MALLOC
-ecpyalloc (char const *str)
+ecpyalloc(char const *str)
{
return memcheck(strdup(str));
}
@@ -556,8 +544,11 @@ warning(const char *const string, ...)
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)
+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);
@@ -566,6 +557,8 @@ close_file(FILE *stream, char const *dir, char const *name)
dir ? dir : "", dir ? "/" : "",
name ? name : "", name ? ": " : "",
e);
+ if (tempname)
+ remove(tempname);
exit(EXIT_FAILURE);
}
}
@@ -582,7 +575,7 @@ usage(FILE *stream, int status)
"Report bugs to %s.\n"),
progname, progname, REPORT_BUGS_TO);
if (status == EXIT_SUCCESS)
- close_file(stream, NULL, NULL);
+ close_file(stream, NULL, NULL, NULL);
exit(status);
}
@@ -590,7 +583,7 @@ usage(FILE *stream, int status)
ancestors. After this is done, all files are accessed with names
relative to DIR. */
static void
-change_directory (char const *dir)
+change_directory(char const *dir)
{
if (chdir(dir) != 0) {
int chdir_errno = errno;
@@ -606,6 +599,68 @@ change_directory (char const *dir)
}
}
+/* 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
+ };
+ int 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'. */
+ }
+}
+
#define TIME_T_BITS_IN_FILE 64
/* The minimum and maximum values representable in a TZif file. */
@@ -620,9 +675,6 @@ static zic_t hi_time = MAXVAL(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;
-/* The time specified by an #expires comment, or negative if no such line. */
-static zic_t comment_leapexpires = -1;
-
/* Set the time range of the output to TIMERANGE.
Return true if successful. */
static bool
@@ -632,14 +684,14 @@ timerange_option(char *timerange)
char *lo_end = timerange, *hi_end;
if (*timerange == '@') {
errno = 0;
- lo = strtoimax (timerange + 1, &lo_end, 10);
+ 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);
+ 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);
@@ -698,7 +750,7 @@ main(int argc, char **argv)
for (k = 1; k < argc; k++)
if (strcmp(argv[k], "--version") == 0) {
printf("zic %s%s\n", PKGVERSION, TZVERSION);
- close_file(stdout, NULL, NULL);
+ close_file(stdout, NULL, NULL, NULL);
return EXIT_SUCCESS;
} else if (strcmp(argv[k], "--help") == 0) {
usage(stdout, EXIT_SUCCESS);
@@ -821,6 +873,7 @@ _("%s: invalid time range: %s\n"),
return EXIT_FAILURE;
associate();
change_directory(directory);
+ catch_signals();
for (i = 0; i < nzones; i = j) {
/*
** Find the next non-continuation zone entry.
@@ -862,9 +915,9 @@ componentcheck(char const *name, char const *component,
ptrdiff_t component_len = component_end - component;
if (component_len == 0) {
if (!*name)
- error (_("empty file name"));
+ error(_("empty file name"));
else
- error (_(component == name
+ error(_(component == name
? "file name '%s' begins with '/'"
: *component_end
? "file name '%s' contains '//'"
@@ -925,6 +978,99 @@ namecheck(const char *name)
return componentcheck(name, component, cp);
}
+/* 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;
+ static unsigned short initialized;
+ int i;
+
+ if (!dst) {
+ dst = emalloc(dirlen + prefixlen + suffixlen + 1);
+ memcpy(dst, src, dirlen);
+ memcpy(dst + dirlen, prefix, prefixlen);
+ dst[dirlen + prefixlen + suffixlen] = '\0';
+ *name = *namealloc = dst;
+ }
+
+ /* This randomization is not the best, but is portable to C89. */
+ if (!initialized++) {
+ unsigned now = time(NULL);
+ srand(rand() ^ now);
+ }
+ for (i = 0; i < suffixlen; i++)
+ dst[dirlen + prefixlen + i] = alphabet[rand() % 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);
+
+ 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
@@ -963,63 +1109,75 @@ relname(char const *target, char const *linkname)
return result;
}
-/* Hard link FROM to TO, following any symbolic links.
- Return 0 if successful, an error number otherwise. */
-static int
-hardlinkerr(char const *target, char const *linkname)
-{
- int r = linkat(AT_FDCWD, target, AT_FDCWD, linkname, AT_SYMLINK_FOLLOW);
- return r == 0 ? 0 : errno;
-}
-
static void
dolink(char const *target, char const *linkname, bool staysymlink)
{
- bool remove_only = strcmp(target, "-") == 0;
bool linkdirs_made = false;
int link_errno;
+ char *tempname = NULL;
+ char const *outname = linkname;
- /*
- ** We get to be careful here since
- ** there's a fair chance of root running us.
- */
- if (!remove_only && itsdir(target)) {
- fprintf(stderr, _("%s: linking target %s/%s failed: %s\n"),
- progname, directory, target, strerror(EPERM));
- exit(EXIT_FAILURE);
- }
- if (staysymlink)
- staysymlink = itssymlink(linkname);
- if (remove(linkname) == 0)
- linkdirs_made = true;
- else if (errno != ENOENT) {
- char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
- progname, directory, linkname, e);
- exit(EXIT_FAILURE);
+ 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);
+ }
}
- if (remove_only)
- return;
- link_errno = staysymlink ? ENOTSUP : hardlinkerr(target, linkname);
- if (link_errno == ENOENT && !linkdirs_made) {
- mkdirs(linkname, true);
- linkdirs_made = true;
- link_errno = hardlinkerr(target, linkname);
+
+ 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 = symlink(contents, linkname) == 0 ? 0 : errno;
- if (!linkdirs_made
- && (symlink_errno == ENOENT || symlink_errno == ENOTSUP)) {
- mkdirs(linkname, true);
- if (symlink_errno == ENOENT)
- symlink_errno = symlink(contents, linkname) == 0 ? 0 : errno;
+ 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)
+ if (link_errno != ENOTSUP && link_errno != EEXIST)
warning(_("symbolic link used because hard link failed: %s"),
strerror(link_errno));
} else {
@@ -1032,17 +1190,11 @@ dolink(char const *target, char const *linkname, bool staysymlink)
progname, directory, target, e);
exit(EXIT_FAILURE);
}
- tp = fopen(linkname, "wb");
- if (!tp) {
- char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
- progname, directory, linkname, e);
- exit(EXIT_FAILURE);
- }
+ tp = open_outfile(&outname, &tempname);
while ((c = getc(fp)) != EOF)
putc(c, tp);
- close_file(fp, directory, target);
- close_file(tp, directory, linkname);
+ 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));
@@ -1051,29 +1203,7 @@ dolink(char const *target, char const *linkname, bool staysymlink)
strerror(symlink_errno));
}
}
-}
-
-/* Return true if NAME is a directory. */
-static bool
-itsdir(char const *name)
-{
- struct stat st;
- int res = stat(name, &st);
-#ifdef S_ISDIR
- if (res == 0)
- return S_ISDIR(st.st_mode) != 0;
-#endif
- if (res == 0 || errno == EOVERFLOW) {
- size_t n = strlen(name);
- char *nameslashdot = emalloc(n + 3);
- bool dir;
- memcpy(nameslashdot, name, n);
- strcpy(&nameslashdot[n], &"/."[! (n && name[n - 1] != '/')]);
- dir = stat(nameslashdot, &st) == 0 || errno == EOVERFLOW;
- free(nameslashdot);
- return dir;
- }
- return false;
+ rename_dest(tempname, linkname);
}
/* Return true if NAME is a symbolic link. */
@@ -1095,8 +1225,8 @@ itssymlink(char const *name)
static int
rcomp(const void *cp1, const void *cp2)
{
- return strcmp(((const struct rule *) cp1)->r_name,
- ((const struct rule *) cp2)->r_name);
+ struct rule const *r1 = cp1, *r2 = cp2;
+ return strcmp(r1->r_name, r2->r_name);
}
static void
@@ -1215,8 +1345,7 @@ infile(const char *name)
++nfields;
}
if (nfields == 0) {
- if (name == leapsec && *buf == '#')
- sscanf(buf, "#expires %"SCNdZIC, &comment_leapexpires);
+ /* nothing to do */
} else if (wantcont) {
wantcont = inzcont(fields, nfields);
} else {
@@ -1245,16 +1374,12 @@ infile(const char *name)
inexpires(fields, nfields);
wantcont = false;
break;
- default: /* "cannot happen" */
- fprintf(stderr,
-_("%s: panic: Invalid l_value %d\n"),
- progname, lp->l_value);
- exit(EXIT_FAILURE);
+ default: UNREACHABLE();
}
}
free(fields);
}
- close_file(fp, NULL, filename);
+ close_file(fp, NULL, filename, NULL);
if (wantcont)
error(_("expected continuation line not found"));
}
@@ -1342,7 +1467,7 @@ getsave(char *field, bool *isdst)
static void
inrule(char **fields, int nfields)
{
- static struct rule r;
+ struct rule r;
if (nfields != RULE_FIELDS) {
error(_("wrong number of fields on Rule line"));
@@ -1360,8 +1485,10 @@ inrule(char **fields, int nfields)
r.r_filename = filename;
r.r_linenum = linenum;
r.r_save = getsave(fields[RF_SAVE], &r.r_isdst);
- rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
- fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
+ 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 = ecpyalloc(fields[RF_NAME]);
r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
if (max_abbrvar_len < strlen(r.r_abbrvar))
@@ -1419,7 +1546,8 @@ inzsub(char **fields, int nfields, bool iscont)
{
register char * cp;
char * cp1;
- static struct zone z;
+ struct zone z;
+ size_t format_len;
register int i_stdoff, i_rule, i_format;
register int i_untilyear, i_untilmonth;
register int i_untilday, i_untiltime;
@@ -1433,7 +1561,6 @@ inzsub(char **fields, int nfields, bool iscont)
i_untilmonth = ZFC_TILMONTH;
i_untilday = ZFC_TILDAY;
i_untiltime = ZFC_TILTIME;
- z.z_name = NULL;
} else if (!namecheck(fields[ZF_NAME]))
return false;
else {
@@ -1444,7 +1571,6 @@ inzsub(char **fields, int nfields, bool iscont)
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;
@@ -1456,29 +1582,24 @@ inzsub(char **fields, int nfields, bool iscont)
return false;
}
}
- z.z_rule = ecpyalloc(fields[i_rule]);
- z.z_format = cp1 = ecpyalloc(fields[i_format]);
z.z_format_specifier = cp ? *cp : '\0';
- if (z.z_format_specifier == 'z') {
- if (noise)
- warning(_("format '%s' not handled by pre-2015 versions of zic"),
- z.z_format);
- cp1[cp - fields[i_format]] = 's';
- }
- if (max_format_len < strlen(z.z_format))
- max_format_len = strlen(z.z_format);
+ 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_filename = filename;
z.z_untilrule.r_linenum = linenum;
- rulesub(&z.z_untilrule,
+ 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");
+ (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 &&
@@ -1493,6 +1614,15 @@ inzsub(char **fields, int nfields, bool iscont)
return false;
}
}
+ z.z_name = iscont ? NULL : ecpyalloc(fields[ZF_NAME]);
+ z.z_rule = ecpyalloc(fields[i_rule]);
+ z.z_format = cp1 = ecpyalloc(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;
/*
@@ -1635,7 +1765,7 @@ inlink(char **fields, int nfields)
links[nlinks++] = l;
}
-static void
+static bool
rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
const char *typep, const char *monthp, const char *dayp,
const char *timep)
@@ -1648,7 +1778,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
if ((lp = byword(monthp, mon_names)) == NULL) {
error(_("invalid month name"));
- return;
+ return false;
}
rp->r_month = lp->l_value;
rp->r_todisstd = false;
@@ -1691,14 +1821,10 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
case YR_MAXIMUM:
rp->r_loyear = ZIC_MAX;
break;
- default: /* "cannot happen" */
- fprintf(stderr,
- _("%s: panic: Invalid l_value %d\n"),
- progname, lp->l_value);
- exit(EXIT_FAILURE);
+ default: UNREACHABLE();
} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
error(_("invalid starting year"));
- return;
+ return false;
}
cp = hiyearp;
lp = byword(cp, end_years);
@@ -1713,23 +1839,19 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
case YR_ONLY:
rp->r_hiyear = rp->r_loyear;
break;
- default: /* "cannot happen" */
- fprintf(stderr,
- _("%s: panic: Invalid l_value %d\n"),
- progname, lp->l_value);
- exit(EXIT_FAILURE);
+ default: UNREACHABLE();
} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
error(_("invalid ending year"));
- return;
+ return false;
}
if (rp->r_loyear > rp->r_hiyear) {
error(_("starting year greater than ending year"));
- return;
+ return false;
}
if (*typep != '\0') {
error(_("year type \"%s\" is unsupported; use \"-\" instead"),
typep);
- return;
+ return false;
}
/*
** Day work.
@@ -1759,12 +1881,12 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
if (*ep++ != '=') {
error(_("invalid day of month"));
free(dp);
- return;
+ return false;
}
if ((lp = byword(dp, wday_names)) == NULL) {
error(_("invalid weekday name"));
free(dp);
- return;
+ return false;
}
rp->r_wday = lp->l_value;
}
@@ -1773,32 +1895,33 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
(rp->r_dayofmonth > len_months[1][rp->r_month])) {
error(_("invalid day of month"));
free(dp);
- return;
+ return false;
}
}
free(dp);
+ return true;
}
static void
-convert(const int_fast32_t val, char *const buf)
+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;
+ b[i] = (val >> shift) & 0xff;
}
static void
-convert64(const zic_t val, char *const buf)
+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;
+ b[i] = (val >> shift) & 0xff;
}
static void
@@ -1826,32 +1949,47 @@ puttzcodepass(zic_t val, FILE *fp, int pass)
static int
atcomp(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);
+ 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 pretrans, leapexpiry;
};
static struct timerange
-limitrange(struct timerange r, zic_t lo, zic_t hi,
+limitrange(struct timerange r, bool locut, 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++;
}
- while (0 < r.leapcount && trans[r.leapbase] < lo) {
+
+ /* 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 < ZIC_MAX) {
while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
r.count--;
@@ -1859,6 +1997,17 @@ limitrange(struct timerange r, zic_t lo, zic_t hi,
r.leapcount--;
}
+ /* Determine whether to keep the last too-low transition if no
+ transition is exactly at LO. The kept transition will be output
+ as a LO "transition"; see "Output a LO_TIME transition" below.
+ 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. */
+ r.pretrans = locut && ! (r.count && ats[r.base] == lo);
+
+ /* Determine whether to append an expiration to the leap second table. */
+ r.leapexpiry = 0 <= leapexpires && leapexpires - 1 <= hi;
+
return r;
}
@@ -1869,12 +2018,11 @@ writezone(const char *const name, const char *const string, char version,
register FILE * fp;
register ptrdiff_t i, j;
register int pass;
- static const struct tzhead tzh0;
- static struct tzhead tzh;
- bool dir_checked = false;
zic_t one = 1;
zic_t y2038_boundary = one << 31;
ptrdiff_t nats = timecnt + WORK_AROUND_QTBUG_53071;
+ char *tempname = NULL;
+ char const *outname = name;
/* Allocate the ATS and TYPES arrays via a single malloc,
as this is a bit faster. */
@@ -1969,40 +2117,46 @@ writezone(const char *const name, const char *const string, char version,
rangeall.base = rangeall.leapbase = 0;
rangeall.count = timecnt;
rangeall.leapcount = leapcnt;
- range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
- range32 = limitrange(range64, INT32_MIN, INT32_MAX, ats, types);
-
- /*
- ** Remove old file, if any, to snap links.
- */
- if (remove(name) == 0)
- dir_checked = true;
- else if (errno != ENOENT) {
- const char *e = strerror(errno);
-
- fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
- progname, directory, name, e);
- exit(EXIT_FAILURE);
- }
- fp = fopen(name, "wb");
- if (!fp) {
- int fopen_errno = errno;
- if (fopen_errno == ENOENT && !dir_checked) {
- mkdirs(name, true);
- fp = fopen(name, "wb");
- fopen_errno = errno;
+ rangeall.pretrans = rangeall.leapexpiry = false;
+ range64 = limitrange(rangeall, min_time < lo_time,
+ lo_time, hi_time, ats, types);
+ range32 = limitrange(range64, INT32_MIN < lo_time,
+ INT32_MIN, INT32_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 (!fp) {
- fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
- progname, directory, name, strerror(fopen_errno));
- exit(EXIT_FAILURE);
+ 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 currenttype, thisdefaulttype;
- bool locut, hicut;
+ bool hicut, pretrans, thisleapexpiry;
zic_t lo;
int old0;
char omittype[TZ_MAX_TYPES];
@@ -2033,7 +2187,8 @@ writezone(const char *const name, const char *const string, char version,
toomanytimes = thistimecnt >> 31 >> 1 != 0;
thisleapi = range32.leapbase;
thisleapcnt = range32.leapcount;
- locut = INT32_MIN < lo_time;
+ pretrans = range32.pretrans;
+ thisleapexpiry = range32.leapexpiry;
hicut = hi_time < INT32_MAX;
} else {
thisdefaulttype = range64.defaulttype;
@@ -2042,33 +2197,17 @@ writezone(const char *const name, const char *const string, char version,
toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
thisleapi = range64.leapbase;
thisleapcnt = range64.leapcount;
- locut = min_time < lo_time;
+ pretrans = range64.pretrans;
+ thisleapexpiry = range64.leapexpiry;
hicut = hi_time < max_time;
}
if (toomanytimes)
error(_("too many transition times"));
- /* Keep the last too-low transition if no transition is
- exactly at LO. The kept transition will be output as
- a LO "transition"; see "Output a LO_TIME transition"
- below. 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 (0 < thistimei && ats[thistimei] != lo_time) {
- thistimei--;
- thistimecnt++;
- locut = false;
- }
-
thistimelim = thistimei + thistimecnt;
- thisleaplim = thisleapi + thisleapcnt;
- if (thistimecnt != 0) {
- if (ats[thistimei] == lo_time)
- locut = false;
- if (hi_time < ZIC_MAX && ats[thistimelim - 1] == hi_time + 1)
- hicut = false;
- }
+ if (thistimecnt && hi_time < ZIC_MAX
+ && ats[thistimelim - 1] == hi_time + 1)
+ hicut = false;
memset(omittype, true, typecnt);
omittype[thisdefaulttype] = false;
for (i = thistimei; i < thistimelim; i++)
@@ -2163,19 +2302,18 @@ writezone(const char *const name, const char *const string, char version,
indmap[desigidx[i]] = j;
}
if (pass == 1 && !want_bloat()) {
- utcnt = stdcnt = thisleapcnt = 0;
- thistimecnt = - (locut + hicut);
+ pretrans = hicut = thisleapexpiry = false;
+ thistimecnt = thisleapcnt = 0;
thistypecnt = thischarcnt = 1;
- thistimelim = thistimei;
}
#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
- tzh = tzh0;
+ 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, tzh.tzh_leapcnt);
- convert(locut + thistimecnt + hicut, tzh.tzh_timecnt);
+ convert(thisleapcnt + thisleapexpiry, tzh.tzh_leapcnt);
+ convert(pretrans + thistimecnt + hicut, tzh.tzh_timecnt);
convert(thistypecnt, tzh.tzh_typecnt);
convert(thischarcnt, tzh.tzh_charcnt);
DO(tzh_magic);
@@ -2202,16 +2340,15 @@ writezone(const char *const name, const char *const string, char version,
for this pass. */
lo = pass == 1 && lo_time < INT32_MIN ? INT32_MIN : lo_time;
- if (locut)
+ if (pretrans)
puttzcodepass(lo, fp, pass);
for (i = thistimei; i < thistimelim; ++i) {
- zic_t at = ats[i] < lo ? lo : ats[i];
- puttzcodepass(at, fp, pass);
+ puttzcodepass(ats[i], fp, pass);
}
if (hicut)
puttzcodepass(hi_time + 1, fp, pass);
currenttype = 0;
- if (locut)
+ if (pretrans)
putc(currenttype, fp);
for (i = thistimei; i < thistimelim; ++i) {
currenttype = typemap[types[i]];
@@ -2232,6 +2369,7 @@ writezone(const char *const name, const char *const string, char version,
if (thischarcnt != 0)
fwrite(thischars, sizeof thischars[0],
thischarcnt, fp);
+ thisleaplim = thisleapi + thisleapcnt;
for (i = thisleapi; i < thisleaplim; ++i) {
register zic_t todo;
@@ -2255,6 +2393,15 @@ writezone(const char *const name, const char *const string, char version,
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])
@@ -2265,7 +2412,8 @@ writezone(const char *const name, const char *const string, char version,
putc(ttisuts[i], fp);
}
fprintf(fp, "\n%s\n", string);
- close_file(fp, directory, name);
+ close_file(fp, directory, name, tempname);
+ rename_dest(tempname, name);
free(ats);
}
@@ -2458,6 +2606,8 @@ rule_cmp(struct rule const *a, struct rule const *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;
@@ -2471,12 +2621,16 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
register struct rule * stdrp;
register struct rule * dstrp;
register ptrdiff_t i;
- register const char * abbrvar;
register int compat = 0;
register int c;
size_t len;
int offsetlen;
struct rule stdr, dstr;
+ int dstcmp;
+ struct rule *lastrp[2] = { NULL, NULL };
+ struct zone zstr[2];
+ struct zone const *stdzp;
+ struct zone const *dstzp;
result[0] = '\0';
@@ -2486,63 +2640,64 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
return -1;
zp = zpfirst + zonecount - 1;
- stdrp = dstrp = NULL;
for (i = 0; i < zp->z_nrules; ++i) {
+ struct rule **last;
+ int cmp;
rp = &zp->z_rules[i];
- if (rp->r_hiwasnum || rp->r_hiyear != ZIC_MAX)
- continue;
- if (!rp->r_isdst) {
- if (stdrp == NULL)
- stdrp = rp;
- else return -1;
- } else {
- if (dstrp == NULL)
- dstrp = rp;
- else return -1;
- }
- }
- if (stdrp == NULL && dstrp == NULL) {
- /*
- ** There are no rules running through "max".
- ** Find the latest std rule in stdabbrrp
- ** and latest rule of any type in stdrp.
- */
- register struct rule *stdabbrrp = NULL;
- for (i = 0; i < zp->z_nrules; ++i) {
- rp = &zp->z_rules[i];
- if (!rp->r_isdst && rule_cmp(stdabbrrp, rp) < 0)
- stdabbrrp = rp;
- if (rule_cmp(stdrp, rp) < 0)
- stdrp = rp;
- }
- if (stdrp != NULL && stdrp->r_isdst) {
- /* Perpetual DST. */
- 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 = stdrp->r_isdst;
- dstr.r_save = stdrp->r_save;
- dstr.r_abbrvar = stdrp->r_abbrvar;
- stdr.r_month = TM_DECEMBER;
- stdr.r_dycode = DC_DOM;
- stdr.r_dayofmonth = 31;
- stdr.r_tod = SECSPERDAY + stdrp->r_save;
- stdr.r_todisstd = stdr.r_todisut = false;
- stdr.r_isdst = false;
- stdr.r_save = 0;
- stdr.r_abbrvar
- = (stdabbrrp ? stdabbrrp->r_abbrvar : "");
- dstrp = &dstr;
- stdrp = &stdr;
- }
- }
- if (stdrp == NULL && (zp->z_nrules != 0 || zp->z_isdst))
- return -1;
- abbrvar = (stdrp == NULL) ? "" : stdrp->r_abbrvar;
- len = doabbr(result, zp, abbrvar, false, 0, true);
- offsetlen = stringoffset(result + len, - zp->z_stdoff);
+ 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;
@@ -2550,11 +2705,11 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
len += offsetlen;
if (dstrp == NULL)
return compat;
- len += doabbr(result + len, zp, dstrp->r_abbrvar,
+ 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,
- - (zp->z_stdoff + dstrp->r_save));
+ - (dstzp->z_stdoff + dstrp->r_save));
if (! offsetlen) {
result[0] = '\0';
return -1;
@@ -2562,7 +2717,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
len += offsetlen;
}
result[len++] = ',';
- c = stringrule(result + len, dstrp, dstrp->r_save, zp->z_stdoff);
+ c = stringrule(result + len, dstrp, dstrp->r_save, stdzp->z_stdoff);
if (c < 0) {
result[0] = '\0';
return -1;
@@ -2571,7 +2726,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
compat = c;
len += strlen(result + len);
result[len++] = ',';
- c = stringrule(result + len, stdrp, dstrp->r_save, zp->z_stdoff);
+ c = stringrule(result + len, stdrp, dstrp->r_save, stdzp->z_stdoff);
if (c < 0) {
result[0] = '\0';
return -1;
@@ -2611,6 +2766,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
zic_t max_year0;
int defaulttype = -1;
+ check_for_signal();
+
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);
@@ -2654,7 +2811,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
** Generate lots of data if a rule can't cover all future times.
*/
compat = stringzone(envvar, zpfirst, zonecount);
- version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION;
+ version = compat < 2013 ? '2' : '3';
do_extend = compat < 0;
if (noise) {
if (!*envvar)
@@ -2715,6 +2872,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
for (i = 0; i < zonecount; ++i) {
struct rule *prevrp = NULL;
+ zic_t prevktime;
+ INITIALIZE(prevktime);
/*
** A guess that may well be corrected later.
*/
@@ -2851,7 +3010,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
rp->r_isdst, rp->r_save, false);
offset = oadd(zp->z_stdoff, rp->r_save);
if (!want_bloat() && !useuntil && !do_extend
- && prevrp
+ && prevrp && lo_time <= prevktime
&& rp->r_hiyear == ZIC_MAX
&& prevrp->r_hiyear == ZIC_MAX)
break;
@@ -2865,6 +3024,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
lastatmax = timecnt;
addtt(ktime, type);
prevrp = rp;
+ prevktime = ktime;
}
}
if (usestart) {
@@ -2994,6 +3154,10 @@ leapadd(zic_t t, int correction, int rolling)
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;
@@ -3026,20 +3190,12 @@ adjleap(void)
last = corr[i] += last;
}
- if (leapexpires < 0) {
- leapexpires = comment_leapexpires;
- if (0 <= leapexpires)
- warning(_("\"#expires\" is obsolescent; use \"Expires\""));
- }
-
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);
}
- if (leapexpires <= hi_time)
- hi_time = leapexpires - 1;
}
}
@@ -3266,32 +3422,28 @@ 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;
- dayoff = 0;
m = TM_JANUARY;
y = EPOCH_YEAR;
- if (y < wantedy) {
- wantedy -= y;
- dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
- wantedy %= YEARSPERREPEAT;
- wantedy += y;
- } else if (wantedy < 0) {
- dayoff = (wantedy / YEARSPERREPEAT) * (SECSPERREPEAT / SECSPERDAY);
- wantedy %= YEARSPERREPEAT;
- }
+
+ /* 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) {
- if (wantedy > y) {
- i = len_years[isleap(y)];
- ++y;
- } else {
- --y;
- i = -len_years[isleap(y)];
- }
+ i = len_years[isleap(y)];
dayoff = oadd(dayoff, i);
+ y++;
}
while (m != rp->r_month) {
i = len_months[isleap(y)][m];
@@ -3310,30 +3462,21 @@ rpytime(const struct rule *rp, zic_t wantedy)
--i;
dayoff = oadd(dayoff, i);
if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
- register zic_t wday;
-
-#define LDAYSPERWEEK ((zic_t) DAYSPERWEEK)
- wday = 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;
- }
+ 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 >= LDAYSPERWEEK)
+ if (++wday >= DAYSPERWEEK)
wday = 0;
++i;
} else {
dayoff = oadd(dayoff, -1);
if (--wday < 0)
- wday = LDAYSPERWEEK - 1;
+ wday = DAYSPERWEEK - 1;
--i;
}
if (i < 0 || i >= len_months[isleap(y)][m]) {
@@ -3385,7 +3528,7 @@ mp = _("time zone abbreviation differs from POSIX standard");
/* 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 non-directory to be trouble. */
+ Do not consider an existing file to be trouble. */
static void
mkdirs(char const *argname, bool ancestors)
{
@@ -3416,11 +3559,11 @@ mkdirs(char const *argname, bool ancestors)
** is checked anyway if the mkdir fails.
*/
if (mkdir(name, MKDIR_UMASK) != 0) {
- /* For speed, skip itsdir if errno == EEXIST. Since
- mkdirs is called only after open fails with ENOENT
- on a subfile, EEXIST implies itsdir here. */
+ /* Do not report an error if err == EEXIST, because
+ some other process might have made the directory
+ in the meantime. */
int err = errno;
- if (err != EEXIST && !itsdir(name)) {
+ if (err != EEXIST) {
error(_("%s: Can't create directory %s: %s"),
progname, name, strerror(err));
exit(EXIT_FAILURE);
diff --git a/tz/ziguard.awk b/tz/ziguard.awk
index 7d6f7c9..6888c27 100644
--- a/tz/ziguard.awk
+++ b/tz/ziguard.awk
@@ -114,15 +114,16 @@ DATAFORM != "main" {
}
}
-# If a Link line is followed by a Zone line for the same data, comment
+# If a Link line is followed by a Link or Zone line for the same data, comment
# out the Link line. This can happen if backzone overrides a Link
-# with a Zone.
-/^Link/ {
- linkline[$3] = NR
-}
+# with a Zone or a different Link.
/^Zone/ {
sub(/^Link/, "#Link", line[linkline[$2]])
}
+/^Link/ {
+ sub(/^Link/, "#Link", line[linkline[$3]])
+ linkline[$3] = NR
+}
{ line[NR] = $0 }
diff --git a/tz/zone.tab b/tz/zone.tab
index 1f0128f..086458f 100644
--- a/tz/zone.tab
+++ b/tz/zone.tab
@@ -3,7 +3,7 @@
# This file is in the public domain, so clarified as of
# 2009-05-17 by Arthur David Olson.
#
-# From Paul Eggert (2018-06-27):
+# From Paul Eggert (2021-09-20):
# This file is intended as a backward-compatibility aid for older programs.
# New programs should use zone1970.tab. This file is like zone1970.tab (see
# zone1970.tab's comments), but with the following additional restrictions:
@@ -16,6 +16,9 @@
# clocks have agreed since 1970; this is a narrower definition than
# that of zone1970.tab.
#
+# Unlike zone1970.tab, a row's third column can be a Link from
+# 'backward' instead of a Zone.
+#
# This table is intended as an aid for users, to help them select timezones
# appropriate for their practical needs. It is not intended to take or
# endorse any position on legal or territorial claims.
@@ -228,7 +231,7 @@ KE -0117+03649 Africa/Nairobi
KG +4254+07436 Asia/Bishkek
KH +1133+10455 Asia/Phnom_Penh
KI +0125+17300 Pacific/Tarawa Gilbert Islands
-KI -0308-17105 Pacific/Enderbury Phoenix Islands
+KI -0247-17143 Pacific/Kanton Phoenix Islands
KI +0152-15720 Pacific/Kiritimati Line Islands
KM -1141+04316 Indian/Comoro
KN +1718-06243 America/St_Kitts
@@ -391,7 +394,7 @@ TK -0922-17114 Pacific/Fakaofo
TL -0833+12535 Asia/Dili
TM +3757+05823 Asia/Ashgabat
TN +3648+01011 Africa/Tunis
-TO -2110-17510 Pacific/Tongatapu
+TO -210800-1751200 Pacific/Tongatapu
TR +4101+02858 Europe/Istanbul
TT +1039-06131 America/Port_of_Spain
TV -0831+17913 Pacific/Funafuti
diff --git a/tz/zone1970.tab b/tz/zone1970.tab
index 396e4d3..c614be8 100644
--- a/tz/zone1970.tab
+++ b/tz/zone1970.tab
@@ -40,11 +40,9 @@ AL +4120+01950 Europe/Tirane
AM +4011+04430 Asia/Yerevan
AQ -6617+11031 Antarctica/Casey Casey
AQ -6835+07758 Antarctica/Davis Davis
-AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville
AQ -6736+06253 Antarctica/Mawson Mawson
AQ -6448-06406 Antarctica/Palmer Palmer
AQ -6734-06808 Antarctica/Rothera Rothera
-AQ -690022+0393524 Antarctica/Syowa Syowa
AQ -720041+0023206 Antarctica/Troll Troll
AQ -7824+10654 Antarctica/Vostok Vostok
AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF)
@@ -97,7 +95,6 @@ BR +0249-06040 America/Boa_Vista Roraima
BR -0308-06001 America/Manaus Amazonas (east)
BR -0640-06952 America/Eirunepe Amazonas (west)
BR -0958-06748 America/Rio_Branco Acre
-BS +2505-07721 America/Nassau
BT +2728+08939 Asia/Thimphu
BY +5354+02734 Europe/Minsk
BZ +1730-08812 America/Belize
@@ -106,13 +103,11 @@ CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE
CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton)
CA +4606-06447 America/Moncton Atlantic - New Brunswick
CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas)
-CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore)
-CA +4339-07923 America/Toronto Eastern - ON, QC (most areas)
+CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas
CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73)
CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay)
CA +6344-06828 America/Iqaluit Eastern - NU (most east areas)
CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung)
-CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H)
CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba
CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances)
CA +744144-0944945 America/Resolute Central - NU (Resolute)
@@ -123,7 +118,6 @@ CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W)
CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west)
CA +6227-11421 America/Yellowknife Mountain - NT (central)
CA +682059-1334300 America/Inuvik Mountain - NT (west)
-CA +4906-11631 America/Creston MST - BC (Creston)
CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John)
CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson)
CA +6043-13503 America/Whitehorse MST - Yukon (east)
@@ -131,7 +125,7 @@ CA +6404-13925 America/Dawson MST - Yukon (west)
CA +4916-12307 America/Vancouver Pacific - BC (most areas)
CC -1210+09655 Indian/Cocos
CH,DE,LI +4723+00832 Europe/Zurich Swiss time
-CI,BF,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan
+CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan
CK -2114-15946 Pacific/Rarotonga
CL -3327-07040 America/Santiago Chile (most areas)
CL -5309-07055 America/Punta_Arenas Region of Magallanes
@@ -142,7 +136,6 @@ CO +0436-07405 America/Bogota
CR +0956-08405 America/Costa_Rica
CU +2308-08222 America/Havana
CV +1455-02331 Atlantic/Cape_Verde
-CW,AW,BQ,SX +1211-06900 America/Curacao
CX -1025+10543 Indian/Christmas
CY +3510+03322 Asia/Nicosia Cyprus (most areas)
CY +3507+03357 Asia/Famagusta Northern Cyprus
@@ -170,7 +163,6 @@ FR +4852+00220 Europe/Paris
GB,GG,IM,JE +513030-0000731 Europe/London
GE +4143+04449 Asia/Tbilisi
GF +0456-05220 America/Cayenne
-GH +0533-00013 Africa/Accra
GI +3608-00521 Europe/Gibraltar
GL +6411-05144 America/Nuuk Greenland (most areas)
GL +7646-01840 America/Danmarkshavn National Park (east coast)
@@ -204,7 +196,7 @@ JP +353916+1394441 Asia/Tokyo
KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi
KG +4254+07436 Asia/Bishkek
KI +0125+17300 Pacific/Tarawa Gilbert Islands
-KI -0308-17105 Pacific/Enderbury Phoenix Islands
+KI -0247-17143 Pacific/Kanton Phoenix Islands
KI +0152-15720 Pacific/Kiritimati Line Islands
KP +3901+12545 Asia/Pyongyang
KR +3733+12658 Asia/Seoul
@@ -262,19 +254,19 @@ NR -0031+16655 Pacific/Nauru
NU -1901-16955 Pacific/Niue
NZ,AQ -3652+17446 Pacific/Auckland New Zealand time
NZ -4357-17633 Pacific/Chatham Chatham Islands
-PA,KY +0858-07932 America/Panama
+PA,CA,KY +0858-07932 America/Panama EST - Panama, Cayman, ON (Atikokan), NU (Coral H)
PE -1203-07703 America/Lima
PF -1732-14934 Pacific/Tahiti Society Islands
PF -0900-13930 Pacific/Marquesas Marquesas Islands
PF -2308-13457 Pacific/Gambier Gambier Islands
-PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas)
+PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville
PG -0613+15534 Pacific/Bougainville Bougainville
PH +1435+12100 Asia/Manila
PK +2452+06703 Asia/Karachi
PL +5215+02100 Europe/Warsaw
PM +4703-05620 America/Miquelon
PN -2504-13005 Pacific/Pitcairn
-PR +182806-0660622 America/Puerto_Rico
+PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST
PS +3130+03428 Asia/Gaza Gaza Strip
PS +313200+0350542 Asia/Hebron West Bank
PT +3843-00908 Europe/Lisbon Portugal (mainland)
@@ -314,12 +306,12 @@ RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island
RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is
RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka
RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea
-SA,KW,YE +2438+04643 Asia/Riyadh
+SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa
SB -0932+16012 Pacific/Guadalcanal
SC -0440+05528 Indian/Mahe
SD +1536+03232 Africa/Khartoum
SE +5920+01803 Europe/Stockholm
-SG +0117+10351 Asia/Singapore
+SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia
SR +0550-05510 America/Paramaribo
SS +0451+03137 Africa/Juba
ST +0020+00644 Africa/Sao_Tome
@@ -334,9 +326,8 @@ TK -0922-17114 Pacific/Fakaofo
TL -0833+12535 Asia/Dili
TM +3757+05823 Asia/Ashgabat
TN +3648+01011 Africa/Tunis
-TO -2110-17510 Pacific/Tongatapu
+TO -210800-1751200 Pacific/Tongatapu
TR +4101+02858 Europe/Istanbul
-TT,AG,AI,BL,DM,GD,GP,KN,LC,MF,MS,VC,VG,VI +1039-06131 America/Port_of_Spain
TV -0831+17913 Pacific/Funafuti
TW +2503+12130 Asia/Taipei
UA +5026+03031 Europe/Kiev Ukraine (most areas)
@@ -362,7 +353,7 @@ US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural)
US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer)
US +394421-1045903 America/Denver Mountain (most areas)
US +433649-1161209 America/Boise Mountain - ID (south); OR (east)
-US +332654-1120424 America/Phoenix MST - Arizona (except Navajo)
+US,CA +332654-1120424 America/Phoenix MST - Arizona (except Navajo), Creston BC
US +340308-1181434 America/Los_Angeles Pacific
US +611305-1495401 America/Anchorage Alaska (most areas)
US +581807-1342511 America/Juneau Alaska - Juneau area