summaryrefslogtreecommitdiffstats
path: root/src/timezone
diff options
context:
space:
mode:
Diffstat (limited to 'src/timezone')
-rw-r--r--src/timezone/.gitignore2
-rw-r--r--src/timezone/Makefile79
-rw-r--r--src/timezone/README134
-rw-r--r--src/timezone/data/tzdata.zi4437
-rw-r--r--src/timezone/known_abbrevs.txt104
-rw-r--r--src/timezone/localtime.c1904
-rw-r--r--src/timezone/pgtz.c502
-rw-r--r--src/timezone/pgtz.h81
-rw-r--r--src/timezone/private.h163
-rw-r--r--src/timezone/strftime.c571
-rw-r--r--src/timezone/tzfile.h110
-rw-r--r--src/timezone/tznames/Africa.txt176
-rw-r--r--src/timezone/tznames/America.txt257
-rw-r--r--src/timezone/tznames/Antarctica.txt27
-rw-r--r--src/timezone/tznames/Asia.txt190
-rw-r--r--src/timezone/tznames/Atlantic.txt85
-rw-r--r--src/timezone/tznames/Australia27
-rw-r--r--src/timezone/tznames/Australia.txt71
-rw-r--r--src/timezone/tznames/Default632
-rw-r--r--src/timezone/tznames/Etc.txt34
-rw-r--r--src/timezone/tznames/Europe.txt219
-rw-r--r--src/timezone/tznames/India19
-rw-r--r--src/timezone/tznames/Indian.txt30
-rw-r--r--src/timezone/tznames/Makefile30
-rw-r--r--src/timezone/tznames/Pacific.txt84
-rw-r--r--src/timezone/tznames/README40
-rw-r--r--src/timezone/zic.c4013
27 files changed, 14021 insertions, 0 deletions
diff --git a/src/timezone/.gitignore b/src/timezone/.gitignore
new file mode 100644
index 0000000..673f864
--- /dev/null
+++ b/src/timezone/.gitignore
@@ -0,0 +1,2 @@
+/zic
+/abbrevs.txt
diff --git a/src/timezone/Makefile b/src/timezone/Makefile
new file mode 100644
index 0000000..fbbaae4
--- /dev/null
+++ b/src/timezone/Makefile
@@ -0,0 +1,79 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for the timezone library
+
+# IDENTIFICATION
+# src/timezone/Makefile
+#
+#-------------------------------------------------------------------------
+
+PGFILEDESC = "zic - time zone compiler"
+PGAPPICON = win32
+
+subdir = src/timezone
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+
+# files to build into backend
+OBJS = \
+ localtime.o \
+ pgtz.o \
+ strftime.o
+
+# files needed to build zic utility program
+ZICOBJS = \
+ $(WIN32RES) \
+ zic.o
+
+# we now distribute the timezone data as a single file
+TZDATAFILES = $(srcdir)/data/tzdata.zi
+
+# any custom options you might want to pass to zic while installing data files
+ZIC_OPTIONS =
+
+# use system timezone data?
+ifneq (,$(with_system_tzdata))
+override CPPFLAGS += '-DSYSTEMTZDIR="$(with_system_tzdata)"'
+endif
+
+include $(top_srcdir)/src/backend/common.mk
+
+ifeq (,$(with_system_tzdata))
+all: zic
+endif
+
+# We could do this test in the action section:
+# $(if $(ZIC),$(ZIC),./zic)
+# but GNU make versions <= 3.78.1 or perhaps later have a bug
+# that causes a segfault; GNU make 3.81 or later fixes this.
+ifeq (,$(ZIC))
+ZIC= ./zic
+endif
+
+zic: $(ZICOBJS) | submake-libpgport
+ $(CC) $(CFLAGS) $(ZICOBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
+
+install: all installdirs
+ifeq (,$(with_system_tzdata))
+ $(ZIC) -d '$(DESTDIR)$(datadir)/timezone' $(ZIC_OPTIONS) $(TZDATAFILES)
+endif
+ $(MAKE) -C tznames $@
+
+# Note: -P code currently depends on '-b fat'. Not worth fixing right now.
+abbrevs.txt: zic $(TZDATAFILES)
+ mkdir junkdir
+ $(ZIC) -P -b fat -d junkdir $(TZDATAFILES) | LANG=C sort | uniq >abbrevs.txt
+ rm -rf junkdir
+
+installdirs:
+ $(MKDIR_P) '$(DESTDIR)$(datadir)'
+
+uninstall:
+ifeq (,$(with_system_tzdata))
+ rm -rf '$(DESTDIR)$(datadir)/timezone'
+endif
+ $(MAKE) -C tznames $@
+
+clean distclean maintainer-clean:
+ rm -f zic$(X) $(ZICOBJS) abbrevs.txt
diff --git a/src/timezone/README b/src/timezone/README
new file mode 100644
index 0000000..f588d1f
--- /dev/null
+++ b/src/timezone/README
@@ -0,0 +1,134 @@
+src/timezone/README
+
+This is a PostgreSQL adapted version of the IANA timezone library from
+
+ https://www.iana.org/time-zones
+
+The latest version of the timezone data and library source code is
+available right from that page. It's best to get the merged file
+tzdb-NNNNX.tar.lz, since the other archive formats omit tzdata.zi.
+Historical versions, as well as release announcements, can be found
+elsewhere on the site.
+
+Since time zone rules change frequently in some parts of the world,
+we should endeavor to update the data files before each PostgreSQL
+release. The code need not be updated as often, but we must track
+changes that might affect interpretation of the data files.
+
+
+Time Zone data
+==============
+
+We distribute the time zone source data as-is under src/timezone/data/.
+Currently, we distribute just the abbreviated single-file format
+"tzdata.zi", to reduce the size of our tarballs as well as churn
+in our git repo. Feeding that file to zic produces the same compiled
+output as feeding the bulkier individual data files would do.
+
+While data/tzdata.zi can just be duplicated when updating, manual effort
+is needed to update the time zone abbreviation lists under tznames/.
+These need to be changed whenever new abbreviations are invented or the
+UTC offset associated with an existing abbreviation changes. To detect
+if this has happened, after installing new files under data/ do
+ make abbrevs.txt
+which will produce a file showing all abbreviations that are in current
+use according to the data/ files. Compare this to known_abbrevs.txt,
+which is the list that existed last time the tznames/ files were updated.
+Update tznames/ as seems appropriate, then replace known_abbrevs.txt
+in the same commit. Usually, if a known abbreviation has changed meaning,
+the appropriate fix is to make it refer to a long-form zone name instead
+of a fixed GMT offset.
+
+The core regression test suite does some simple validation of the zone
+data and abbreviations data (notably by checking that the pg_timezone_names
+and pg_timezone_abbrevs views don't throw errors). It's worth running it
+as a cross-check on proposed updates.
+
+When there has been a new release of Windows (probably including Service
+Packs), the list of matching timezones need to be updated. Run the
+script in src/tools/win32tzlist.pl on a Windows machine running this new
+release and apply any new timezones that it detects. Never remove any
+mappings in case they are removed in Windows, since we still need to
+match properly on the old version.
+
+
+Time Zone code
+==============
+
+The code in this directory is currently synced with tzcode release 2020d.
+There are many cosmetic (and not so cosmetic) differences from the
+original tzcode library, but diffs in the upstream version should usually
+be propagated to our version. Here are some notes about that.
+
+For the most part we want to use the upstream code as-is, but there are
+several considerations preventing an exact match:
+
+* For readability/maintainability we reformat the code to match our own
+conventions; this includes pgindent'ing it and getting rid of upstream's
+overuse of "register" declarations. (It used to include conversion of
+old-style function declarations to C89 style, but thank goodness they
+fixed that.)
+
+* We need the code to follow Postgres' portability conventions; this
+includes relying on configure's results rather than hand-hacked
+#defines (see private.h in particular).
+
+* Similarly, avoid relying on <stdint.h> features that may not exist on old
+systems. In particular this means using Postgres' definitions of the int32
+and int64 typedefs, not int_fast32_t/int_fast64_t. Likewise we use
+PG_INT32_MIN/MAX not INT32_MIN/MAX. (Once we desupport all PG versions
+that don't require C99, it'd be practical to rely on <stdint.h> and remove
+this set of diffs; but that day is not yet.)
+
+* Since Postgres is typically built on a system that has its own copy
+of the <time.h> functions, we must avoid conflicting with those. This
+mandates renaming typedef time_t to pg_time_t, and similarly for most
+other exposed names.
+
+* zic.c's typedef "lineno" is renamed to "lineno_t", because having
+"lineno" in our typedefs list would cause unfortunate pgindent behavior
+in some other files where we have variables named that.
+
+* We have exposed the tzload() and tzparse() internal functions, and
+slightly modified the API of the former, in part because it now relies
+on our own pg_open_tzfile() rather than opening files for itself.
+
+* tzparse() is adjusted to never try to load the TZDEFRULES zone.
+
+* There's a fair amount of code we don't need and have removed,
+including all the nonstandard optional APIs. We have also added
+a few functions of our own at the bottom of localtime.c.
+
+* In zic.c, we have added support for a -P (print_abbrevs) switch, which
+is used to create the "abbrevs.txt" summary of currently-in-use zone
+abbreviations that was described above.
+
+
+The most convenient way to compare a new tzcode release to our code is
+to first run the tzcode source files through a sed filter like this:
+
+ sed -r \
+ -e 's/^([ \t]*)\*\*([ \t])/\1 *\2/' \
+ -e 's/^([ \t]*)\*\*$/\1 */' \
+ -e 's|^\*/| */|' \
+ -e 's/\bregister[ \t]//g' \
+ -e 's/\bATTRIBUTE_PURE[ \t]//g' \
+ -e 's/int_fast32_t/int32/g' \
+ -e 's/int_fast64_t/int64/g' \
+ -e 's/intmax_t/int64/g' \
+ -e 's/INT32_MIN/PG_INT32_MIN/g' \
+ -e 's/INT32_MAX/PG_INT32_MAX/g' \
+ -e 's/INTMAX_MIN/PG_INT64_MIN/g' \
+ -e 's/INTMAX_MAX/PG_INT64_MAX/g' \
+ -e 's/struct[ \t]+tm\b/struct pg_tm/g' \
+ -e 's/\btime_t\b/pg_time_t/g' \
+ -e 's/lineno/lineno_t/g' \
+
+and then run them through pgindent. (The first three sed patterns deal
+with conversion of their block comment style to something pgindent
+won't make a hash of; the remainder address other points noted above.)
+After that, the files can be diff'd directly against our corresponding
+files. Also, it's typically helpful to diff against the previous tzcode
+release (after processing that the same way), and then try to apply the
+diff to our files. This will take care of most of the changes
+mechanically.
diff --git a/src/timezone/data/tzdata.zi b/src/timezone/data/tzdata.zi
new file mode 100644
index 0000000..c38197e
--- /dev/null
+++ b/src/timezone/data/tzdata.zi
@@ -0,0 +1,4437 @@
+# version 2022a
+# This zic input file is in the public domain.
+R d 1916 o - Jun 14 23s 1 S
+R d 1916 1919 - O Su>=1 23s 0 -
+R d 1917 o - Mar 24 23s 1 S
+R d 1918 o - Mar 9 23s 1 S
+R d 1919 o - Mar 1 23s 1 S
+R d 1920 o - F 14 23s 1 S
+R d 1920 o - O 23 23s 0 -
+R d 1921 o - Mar 14 23s 1 S
+R d 1921 o - Jun 21 23s 0 -
+R d 1939 o - S 11 23s 1 S
+R d 1939 o - N 19 1 0 -
+R d 1944 1945 - Ap M>=1 2 1 S
+R d 1944 o - O 8 2 0 -
+R d 1945 o - S 16 1 0 -
+R d 1971 o - Ap 25 23s 1 S
+R d 1971 o - S 26 23s 0 -
+R d 1977 o - May 6 0 1 S
+R d 1977 o - O 21 0 0 -
+R d 1978 o - Mar 24 1 1 S
+R d 1978 o - S 22 3 0 -
+R d 1980 o - Ap 25 0 1 S
+R d 1980 o - O 31 2 0 -
+Z Africa/Algiers 0:12:12 - LMT 1891 Mar 16
+0:9:21 - PMT 1911 Mar 11
+0 d WE%sT 1940 F 25 2
+1 d CE%sT 1946 O 7
+0 - WET 1956 Ja 29
+1 - CET 1963 Ap 14
+0 d WE%sT 1977 O 21
+1 d CE%sT 1979 O 26
+0 d WE%sT 1981 May
+1 - CET
+Z Atlantic/Cape_Verde -1:34:4 - LMT 1912 Ja 1 2u
+-2 - -02 1942 S
+-2 1 -01 1945 O 15
+-2 - -02 1975 N 25 2
+-1 - -01
+Z Africa/Ndjamena 1:0:12 - LMT 1912
+1 - WAT 1979 O 14
+1 1 WAST 1980 Mar 8
+1 - WAT
+Z Africa/Abidjan -0:16:8 - LMT 1912
+0 - GMT
+L Africa/Abidjan Africa/Accra
+L Africa/Abidjan Africa/Bamako
+L Africa/Abidjan Africa/Banjul
+L Africa/Abidjan Africa/Conakry
+L Africa/Abidjan Africa/Dakar
+L Africa/Abidjan Africa/Freetown
+L Africa/Abidjan Africa/Lome
+L Africa/Abidjan Africa/Nouakchott
+L Africa/Abidjan Africa/Ouagadougou
+L Africa/Abidjan Atlantic/St_Helena
+R K 1940 o - Jul 15 0 1 S
+R K 1940 o - O 1 0 0 -
+R K 1941 o - Ap 15 0 1 S
+R K 1941 o - S 16 0 0 -
+R K 1942 1944 - Ap 1 0 1 S
+R K 1942 o - O 27 0 0 -
+R K 1943 1945 - N 1 0 0 -
+R K 1945 o - Ap 16 0 1 S
+R K 1957 o - May 10 0 1 S
+R K 1957 1958 - O 1 0 0 -
+R K 1958 o - May 1 0 1 S
+R K 1959 1981 - May 1 1 1 S
+R K 1959 1965 - S 30 3 0 -
+R K 1966 1994 - O 1 3 0 -
+R K 1982 o - Jul 25 1 1 S
+R K 1983 o - Jul 12 1 1 S
+R K 1984 1988 - May 1 1 1 S
+R K 1989 o - May 6 1 1 S
+R K 1990 1994 - May 1 1 1 S
+R K 1995 2010 - Ap lastF 0s 1 S
+R K 1995 2005 - S lastTh 24 0 -
+R K 2006 o - S 21 24 0 -
+R K 2007 o - S Th>=1 24 0 -
+R K 2008 o - Au lastTh 24 0 -
+R K 2009 o - Au 20 24 0 -
+R K 2010 o - Au 10 24 0 -
+R K 2010 o - S 9 24 1 S
+R K 2010 o - S lastTh 24 0 -
+R K 2014 o - May 15 24 1 S
+R K 2014 o - Jun 26 24 0 -
+R K 2014 o - Jul 31 24 1 S
+R K 2014 o - S lastTh 24 0 -
+Z Africa/Cairo 2:5:9 - LMT 1900 O
+2 K EE%sT
+Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u
+-1 - -01 1975
+0 - GMT
+Z Africa/Nairobi 2:27:16 - LMT 1908 May
+2:30 - +0230 1928 Jun 30 24
+3 - EAT 1930 Ja 4 24
+2:30 - +0230 1936 D 31 24
+2:45 - +0245 1942 Jul 31 24
+3 - EAT
+L Africa/Nairobi Africa/Addis_Ababa
+L Africa/Nairobi Africa/Asmara
+L Africa/Nairobi Africa/Dar_es_Salaam
+L Africa/Nairobi Africa/Djibouti
+L Africa/Nairobi Africa/Kampala
+L Africa/Nairobi Africa/Mogadishu
+L Africa/Nairobi Indian/Antananarivo
+L Africa/Nairobi Indian/Comoro
+L Africa/Nairobi Indian/Mayotte
+Z Africa/Monrovia -0:43:8 - LMT 1882
+-0:43:8 - MMT 1919 Mar
+-0:44:30 - MMT 1972 Ja 7
+0 - GMT
+R L 1951 o - O 14 2 1 S
+R L 1952 o - Ja 1 0 0 -
+R L 1953 o - O 9 2 1 S
+R L 1954 o - Ja 1 0 0 -
+R L 1955 o - S 30 0 1 S
+R L 1956 o - Ja 1 0 0 -
+R L 1982 1984 - Ap 1 0 1 S
+R L 1982 1985 - O 1 0 0 -
+R L 1985 o - Ap 6 0 1 S
+R L 1986 o - Ap 4 0 1 S
+R L 1986 o - O 3 0 0 -
+R L 1987 1989 - Ap 1 0 1 S
+R L 1987 1989 - O 1 0 0 -
+R L 1997 o - Ap 4 0 1 S
+R L 1997 o - O 4 0 0 -
+R L 2013 o - Mar lastF 1 1 S
+R L 2013 o - O lastF 2 0 -
+Z Africa/Tripoli 0:52:44 - LMT 1920
+1 L CE%sT 1959
+2 - EET 1982
+1 L CE%sT 1990 May 4
+2 - EET 1996 S 30
+1 L CE%sT 1997 O 4
+2 - EET 2012 N 10 2
+1 L CE%sT 2013 O 25 2
+2 - EET
+R MU 1982 o - O 10 0 1 -
+R MU 1983 o - Mar 21 0 0 -
+R MU 2008 o - O lastSu 2 1 -
+R MU 2009 o - Mar lastSu 2 0 -
+Z Indian/Mauritius 3:50 - LMT 1907
+4 MU +04/+05
+R M 1939 o - S 12 0 1 -
+R M 1939 o - N 19 0 0 -
+R M 1940 o - F 25 0 1 -
+R M 1945 o - N 18 0 0 -
+R M 1950 o - Jun 11 0 1 -
+R M 1950 o - O 29 0 0 -
+R M 1967 o - Jun 3 12 1 -
+R M 1967 o - O 1 0 0 -
+R M 1974 o - Jun 24 0 1 -
+R M 1974 o - S 1 0 0 -
+R M 1976 1977 - May 1 0 1 -
+R M 1976 o - Au 1 0 0 -
+R M 1977 o - S 28 0 0 -
+R M 1978 o - Jun 1 0 1 -
+R M 1978 o - Au 4 0 0 -
+R M 2008 o - Jun 1 0 1 -
+R M 2008 o - S 1 0 0 -
+R M 2009 o - Jun 1 0 1 -
+R M 2009 o - Au 21 0 0 -
+R M 2010 o - May 2 0 1 -
+R M 2010 o - Au 8 0 0 -
+R M 2011 o - Ap 3 0 1 -
+R M 2011 o - Jul 31 0 0 -
+R M 2012 2013 - Ap lastSu 2 1 -
+R M 2012 o - Jul 20 3 0 -
+R M 2012 o - Au 20 2 1 -
+R M 2012 o - S 30 3 0 -
+R M 2013 o - Jul 7 3 0 -
+R M 2013 o - Au 10 2 1 -
+R M 2013 2018 - O lastSu 3 0 -
+R M 2014 2018 - Mar lastSu 2 1 -
+R M 2014 o - Jun 28 3 0 -
+R M 2014 o - Au 2 2 1 -
+R M 2015 o - Jun 14 3 0 -
+R M 2015 o - Jul 19 2 1 -
+R M 2016 o - Jun 5 3 0 -
+R M 2016 o - Jul 10 2 1 -
+R M 2017 o - May 21 3 0 -
+R M 2017 o - Jul 2 2 1 -
+R M 2018 o - May 13 3 0 -
+R M 2018 o - Jun 17 2 1 -
+R M 2019 o - May 5 3 -1 -
+R M 2019 o - Jun 9 2 0 -
+R M 2020 o - Ap 19 3 -1 -
+R M 2020 o - May 31 2 0 -
+R M 2021 o - Ap 11 3 -1 -
+R M 2021 o - May 16 2 0 -
+R M 2022 o - Mar 27 3 -1 -
+R M 2022 o - May 8 2 0 -
+R M 2023 o - Mar 19 3 -1 -
+R M 2023 o - Ap 30 2 0 -
+R M 2024 o - Mar 10 3 -1 -
+R M 2024 o - Ap 14 2 0 -
+R M 2025 o - F 23 3 -1 -
+R M 2025 o - Ap 6 2 0 -
+R M 2026 o - F 15 3 -1 -
+R M 2026 o - Mar 22 2 0 -
+R M 2027 o - F 7 3 -1 -
+R M 2027 o - Mar 14 2 0 -
+R M 2028 o - Ja 23 3 -1 -
+R M 2028 o - Mar 5 2 0 -
+R M 2029 o - Ja 14 3 -1 -
+R M 2029 o - F 18 2 0 -
+R M 2029 o - D 30 3 -1 -
+R M 2030 o - F 10 2 0 -
+R M 2030 o - D 22 3 -1 -
+R M 2031 o - F 2 2 0 -
+R M 2031 o - D 14 3 -1 -
+R M 2032 o - Ja 18 2 0 -
+R M 2032 o - N 28 3 -1 -
+R M 2033 o - Ja 9 2 0 -
+R M 2033 o - N 20 3 -1 -
+R M 2033 o - D 25 2 0 -
+R M 2034 o - N 5 3 -1 -
+R M 2034 o - D 17 2 0 -
+R M 2035 o - O 28 3 -1 -
+R M 2035 o - D 9 2 0 -
+R M 2036 o - O 19 3 -1 -
+R M 2036 o - N 23 2 0 -
+R M 2037 o - O 4 3 -1 -
+R M 2037 o - N 15 2 0 -
+R M 2038 o - S 26 3 -1 -
+R M 2038 o - N 7 2 0 -
+R M 2039 o - S 18 3 -1 -
+R M 2039 o - O 23 2 0 -
+R M 2040 o - S 2 3 -1 -
+R M 2040 o - O 14 2 0 -
+R M 2041 o - Au 25 3 -1 -
+R M 2041 o - S 29 2 0 -
+R M 2042 o - Au 10 3 -1 -
+R M 2042 o - S 21 2 0 -
+R M 2043 o - Au 2 3 -1 -
+R M 2043 o - S 13 2 0 -
+R M 2044 o - Jul 24 3 -1 -
+R M 2044 o - Au 28 2 0 -
+R M 2045 o - Jul 9 3 -1 -
+R M 2045 o - Au 20 2 0 -
+R M 2046 o - Jul 1 3 -1 -
+R M 2046 o - Au 12 2 0 -
+R M 2047 o - Jun 23 3 -1 -
+R M 2047 o - Jul 28 2 0 -
+R M 2048 o - Jun 7 3 -1 -
+R M 2048 o - Jul 19 2 0 -
+R M 2049 o - May 30 3 -1 -
+R M 2049 o - Jul 4 2 0 -
+R M 2050 o - May 15 3 -1 -
+R M 2050 o - Jun 26 2 0 -
+R M 2051 o - May 7 3 -1 -
+R M 2051 o - Jun 18 2 0 -
+R M 2052 o - Ap 28 3 -1 -
+R M 2052 o - Jun 2 2 0 -
+R M 2053 o - Ap 13 3 -1 -
+R M 2053 o - May 25 2 0 -
+R M 2054 o - Ap 5 3 -1 -
+R M 2054 o - May 17 2 0 -
+R M 2055 o - Mar 28 3 -1 -
+R M 2055 o - May 2 2 0 -
+R M 2056 o - Mar 12 3 -1 -
+R M 2056 o - Ap 23 2 0 -
+R M 2057 o - Mar 4 3 -1 -
+R M 2057 o - Ap 8 2 0 -
+R M 2058 o - F 17 3 -1 -
+R M 2058 o - Mar 31 2 0 -
+R M 2059 o - F 9 3 -1 -
+R M 2059 o - Mar 23 2 0 -
+R M 2060 o - F 1 3 -1 -
+R M 2060 o - Mar 7 2 0 -
+R M 2061 o - Ja 16 3 -1 -
+R M 2061 o - F 27 2 0 -
+R M 2062 o - Ja 8 3 -1 -
+R M 2062 o - F 19 2 0 -
+R M 2062 o - D 31 3 -1 -
+R M 2063 o - F 4 2 0 -
+R M 2063 o - D 16 3 -1 -
+R M 2064 o - Ja 27 2 0 -
+R M 2064 o - D 7 3 -1 -
+R M 2065 o - Ja 11 2 0 -
+R M 2065 o - N 22 3 -1 -
+R M 2066 o - Ja 3 2 0 -
+R M 2066 o - N 14 3 -1 -
+R M 2066 o - D 26 2 0 -
+R M 2067 o - N 6 3 -1 -
+R M 2067 o - D 11 2 0 -
+R M 2068 o - O 21 3 -1 -
+R M 2068 o - D 2 2 0 -
+R M 2069 o - O 13 3 -1 -
+R M 2069 o - N 24 2 0 -
+R M 2070 o - O 5 3 -1 -
+R M 2070 o - N 9 2 0 -
+R M 2071 o - S 20 3 -1 -
+R M 2071 o - N 1 2 0 -
+R M 2072 o - S 11 3 -1 -
+R M 2072 o - O 16 2 0 -
+R M 2073 o - Au 27 3 -1 -
+R M 2073 o - O 8 2 0 -
+R M 2074 o - Au 19 3 -1 -
+R M 2074 o - S 30 2 0 -
+R M 2075 o - Au 11 3 -1 -
+R M 2075 o - S 15 2 0 -
+R M 2076 o - Jul 26 3 -1 -
+R M 2076 o - S 6 2 0 -
+R M 2077 o - Jul 18 3 -1 -
+R M 2077 o - Au 29 2 0 -
+R M 2078 o - Jul 10 3 -1 -
+R M 2078 o - Au 14 2 0 -
+R M 2079 o - Jun 25 3 -1 -
+R M 2079 o - Au 6 2 0 -
+R M 2080 o - Jun 16 3 -1 -
+R M 2080 o - Jul 21 2 0 -
+R M 2081 o - Jun 1 3 -1 -
+R M 2081 o - Jul 13 2 0 -
+R M 2082 o - May 24 3 -1 -
+R M 2082 o - Jul 5 2 0 -
+R M 2083 o - May 16 3 -1 -
+R M 2083 o - Jun 20 2 0 -
+R M 2084 o - Ap 30 3 -1 -
+R M 2084 o - Jun 11 2 0 -
+R M 2085 o - Ap 22 3 -1 -
+R M 2085 o - Jun 3 2 0 -
+R M 2086 o - Ap 14 3 -1 -
+R M 2086 o - May 19 2 0 -
+R M 2087 o - Mar 30 3 -1 -
+R M 2087 o - May 11 2 0 -
+Z Africa/Casablanca -0:30:20 - LMT 1913 O 26
+0 M +00/+01 1984 Mar 16
+1 - +01 1986
+0 M +00/+01 2018 O 28 3
+1 M +01/+00
+Z Africa/El_Aaiun -0:52:48 - LMT 1934
+-1 - -01 1976 Ap 14
+0 M +00/+01 2018 O 28 3
+1 M +01/+00
+Z Africa/Maputo 2:10:20 - LMT 1903 Mar
+2 - CAT
+L Africa/Maputo Africa/Blantyre
+L Africa/Maputo Africa/Bujumbura
+L Africa/Maputo Africa/Gaborone
+L Africa/Maputo Africa/Harare
+L Africa/Maputo Africa/Kigali
+L Africa/Maputo Africa/Lubumbashi
+L Africa/Maputo Africa/Lusaka
+R NA 1994 o - Mar 21 0 -1 WAT
+R NA 1994 2017 - S Su>=1 2 0 CAT
+R NA 1995 2017 - Ap Su>=1 2 -1 WAT
+Z Africa/Windhoek 1:8:24 - LMT 1892 F 8
+1:30 - +0130 1903 Mar
+2 - SAST 1942 S 20 2
+2 1 SAST 1943 Mar 21 2
+2 - SAST 1990 Mar 21
+2 NA %s
+Z Africa/Lagos 0:13:35 - LMT 1905 Jul
+0 - GMT 1908 Jul
+0:13:35 - LMT 1914
+0:30 - +0030 1919 S
+1 - WAT
+L Africa/Lagos Africa/Bangui
+L Africa/Lagos Africa/Brazzaville
+L Africa/Lagos Africa/Douala
+L Africa/Lagos Africa/Kinshasa
+L Africa/Lagos Africa/Libreville
+L Africa/Lagos Africa/Luanda
+L Africa/Lagos Africa/Malabo
+L Africa/Lagos Africa/Niamey
+L Africa/Lagos Africa/Porto-Novo
+Z Indian/Reunion 3:41:52 - LMT 1911 Jun
+4 - +04
+Z Africa/Sao_Tome 0:26:56 - LMT 1884
+-0:36:45 - LMT 1912 Ja 1 0u
+0 - GMT 2018 Ja 1 1
+1 - WAT 2019 Ja 1 2
+0 - GMT
+Z Indian/Mahe 3:41:48 - LMT 1907
+4 - +04
+R SA 1942 1943 - S Su>=15 2 1 -
+R SA 1943 1944 - Mar Su>=15 2 0 -
+Z Africa/Johannesburg 1:52 - LMT 1892 F 8
+1:30 - SAST 1903 Mar
+2 SA SAST
+L Africa/Johannesburg Africa/Maseru
+L Africa/Johannesburg Africa/Mbabane
+R SD 1970 o - May 1 0 1 S
+R SD 1970 1985 - O 15 0 0 -
+R SD 1971 o - Ap 30 0 1 S
+R SD 1972 1985 - Ap lastSu 0 1 S
+Z Africa/Khartoum 2:10:8 - LMT 1931
+2 SD CA%sT 2000 Ja 15 12
+3 - EAT 2017 N
+2 - CAT
+Z Africa/Juba 2:6:28 - LMT 1931
+2 SD CA%sT 2000 Ja 15 12
+3 - EAT 2021 F
+2 - CAT
+R n 1939 o - Ap 15 23s 1 S
+R n 1939 o - N 18 23s 0 -
+R n 1940 o - F 25 23s 1 S
+R n 1941 o - O 6 0 0 -
+R n 1942 o - Mar 9 0 1 S
+R n 1942 o - N 2 3 0 -
+R n 1943 o - Mar 29 2 1 S
+R n 1943 o - Ap 17 2 0 -
+R n 1943 o - Ap 25 2 1 S
+R n 1943 o - O 4 2 0 -
+R n 1944 1945 - Ap M>=1 2 1 S
+R n 1944 o - O 8 0 0 -
+R n 1945 o - S 16 0 0 -
+R n 1977 o - Ap 30 0s 1 S
+R n 1977 o - S 24 0s 0 -
+R n 1978 o - May 1 0s 1 S
+R n 1978 o - O 1 0s 0 -
+R n 1988 o - Jun 1 0s 1 S
+R n 1988 1990 - S lastSu 0s 0 -
+R n 1989 o - Mar 26 0s 1 S
+R n 1990 o - May 1 0s 1 S
+R n 2005 o - May 1 0s 1 S
+R n 2005 o - S 30 1s 0 -
+R n 2006 2008 - Mar lastSu 2s 1 S
+R n 2006 2008 - O lastSu 2s 0 -
+Z Africa/Tunis 0:40:44 - LMT 1881 May 12
+0:9:21 - PMT 1911 Mar 11
+1 n CE%sT
+Z Antarctica/Casey 0 - -00 1969
+8 - +08 2009 O 18 2
+11 - +11 2010 Mar 5 2
+8 - +08 2011 O 28 2
+11 - +11 2012 F 21 17u
+8 - +08 2016 O 22
+11 - +11 2018 Mar 11 4
+8 - +08 2018 O 7 4
+11 - +11 2019 Mar 17 3
+8 - +08 2019 O 4 3
+11 - +11 2020 Mar 8 3
+8 - +08 2020 O 4 0:1
+11 - +11
+Z Antarctica/Davis 0 - -00 1957 Ja 13
+7 - +07 1964 N
+0 - -00 1969 F
+7 - +07 2009 O 18 2
+5 - +05 2010 Mar 10 20u
+7 - +07 2011 O 28 2
+5 - +05 2012 F 21 20u
+7 - +07
+Z Antarctica/Mawson 0 - -00 1954 F 13
+6 - +06 2009 O 18 2
+5 - +05
+Z Indian/Kerguelen 0 - -00 1950
+5 - +05
+R Tr 2005 ma - Mar lastSu 1u 2 +02
+R Tr 2004 ma - O lastSu 1u 0 +00
+Z Antarctica/Troll 0 - -00 2005 F 12
+0 Tr %s
+Z Antarctica/Vostok 0 - -00 1957 D 16
+6 - +06
+Z Antarctica/Rothera 0 - -00 1976 D
+-3 - -03
+Z Asia/Kabul 4:36:48 - LMT 1890
+4 - +04 1945
+4:30 - +0430
+R AM 2011 o - Mar lastSu 2s 1 -
+R AM 2011 o - O lastSu 2s 0 -
+Z Asia/Yerevan 2:58 - LMT 1924 May 2
+3 - +03 1957 Mar
+4 R +04/+05 1991 Mar 31 2s
+3 R +03/+04 1995 S 24 2s
+4 - +04 1997
+4 R +04/+05 2011
+4 AM +04/+05
+R AZ 1997 2015 - Mar lastSu 4 1 -
+R AZ 1997 2015 - O lastSu 5 0 -
+Z Asia/Baku 3:19:24 - LMT 1924 May 2
+3 - +03 1957 Mar
+4 R +04/+05 1991 Mar 31 2s
+3 R +03/+04 1992 S lastSu 2s
+4 - +04 1996
+4 E +04/+05 1997
+4 AZ +04/+05
+R BD 2009 o - Jun 19 23 1 -
+R BD 2009 o - D 31 24 0 -
+Z Asia/Dhaka 6:1:40 - LMT 1890
+5:53:20 - HMT 1941 O
+6:30 - +0630 1942 May 15
+5:30 - +0530 1942 S
+6:30 - +0630 1951 S 30
+6 - +06 2009
+6 BD +06/+07
+Z Asia/Thimphu 5:58:36 - LMT 1947 Au 15
+5:30 - +0530 1987 O
+6 - +06
+Z Indian/Chagos 4:49:40 - LMT 1907
+5 - +05 1996
+6 - +06
+Z Asia/Brunei 7:39:40 - LMT 1926 Mar
+7:30 - +0730 1933
+8 - +08
+Z Asia/Yangon 6:24:47 - LMT 1880
+6:24:47 - RMT 1920
+6:30 - +0630 1942 May
+9 - +09 1945 May 3
+6:30 - +0630
+R Sh 1919 o - Ap 12 24 1 D
+R Sh 1919 o - S 30 24 0 S
+R Sh 1940 o - Jun 1 0 1 D
+R Sh 1940 o - O 12 24 0 S
+R Sh 1941 o - Mar 15 0 1 D
+R Sh 1941 o - N 1 24 0 S
+R Sh 1942 o - Ja 31 0 1 D
+R Sh 1945 o - S 1 24 0 S
+R Sh 1946 o - May 15 0 1 D
+R Sh 1946 o - S 30 24 0 S
+R Sh 1947 o - Ap 15 0 1 D
+R Sh 1947 o - O 31 24 0 S
+R Sh 1948 1949 - May 1 0 1 D
+R Sh 1948 1949 - S 30 24 0 S
+R CN 1986 o - May 4 2 1 D
+R CN 1986 1991 - S Su>=11 2 0 S
+R CN 1987 1991 - Ap Su>=11 2 1 D
+Z Asia/Shanghai 8:5:43 - LMT 1901
+8 Sh C%sT 1949 May 28
+8 CN C%sT
+Z Asia/Urumqi 5:50:20 - LMT 1928
+6 - +06
+R HK 1946 o - Ap 21 0 1 S
+R HK 1946 o - D 1 3:30s 0 -
+R HK 1947 o - Ap 13 3:30s 1 S
+R HK 1947 o - N 30 3:30s 0 -
+R HK 1948 o - May 2 3:30s 1 S
+R HK 1948 1952 - O Su>=28 3:30s 0 -
+R HK 1949 1953 - Ap Su>=1 3:30 1 S
+R HK 1953 1964 - O Su>=31 3:30 0 -
+R HK 1954 1964 - Mar Su>=18 3:30 1 S
+R HK 1965 1976 - Ap Su>=16 3:30 1 S
+R HK 1965 1976 - O Su>=16 3:30 0 -
+R HK 1973 o - D 30 3:30 1 S
+R HK 1979 o - May 13 3:30 1 S
+R HK 1979 o - O 21 3:30 0 -
+Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42
+8 - HKT 1941 Jun 15 3
+8 1 HKST 1941 O 1 4
+8 0:30 HKWT 1941 D 25
+9 - JST 1945 N 18 2
+8 HK HK%sT
+R f 1946 o - May 15 0 1 D
+R f 1946 o - O 1 0 0 S
+R f 1947 o - Ap 15 0 1 D
+R f 1947 o - N 1 0 0 S
+R f 1948 1951 - May 1 0 1 D
+R f 1948 1951 - O 1 0 0 S
+R f 1952 o - Mar 1 0 1 D
+R f 1952 1954 - N 1 0 0 S
+R f 1953 1959 - Ap 1 0 1 D
+R f 1955 1961 - O 1 0 0 S
+R f 1960 1961 - Jun 1 0 1 D
+R f 1974 1975 - Ap 1 0 1 D
+R f 1974 1975 - O 1 0 0 S
+R f 1979 o - Jul 1 0 1 D
+R f 1979 o - O 1 0 0 S
+Z Asia/Taipei 8:6 - LMT 1896
+8 - CST 1937 O
+9 - JST 1945 S 21 1
+8 f C%sT
+R _ 1942 1943 - Ap 30 23 1 -
+R _ 1942 o - N 17 23 0 -
+R _ 1943 o - S 30 23 0 S
+R _ 1946 o - Ap 30 23s 1 D
+R _ 1946 o - S 30 23s 0 S
+R _ 1947 o - Ap 19 23s 1 D
+R _ 1947 o - N 30 23s 0 S
+R _ 1948 o - May 2 23s 1 D
+R _ 1948 o - O 31 23s 0 S
+R _ 1949 1950 - Ap Sa>=1 23s 1 D
+R _ 1949 1950 - O lastSa 23s 0 S
+R _ 1951 o - Mar 31 23s 1 D
+R _ 1951 o - O 28 23s 0 S
+R _ 1952 1953 - Ap Sa>=1 23s 1 D
+R _ 1952 o - N 1 23s 0 S
+R _ 1953 1954 - O lastSa 23s 0 S
+R _ 1954 1956 - Mar Sa>=17 23s 1 D
+R _ 1955 o - N 5 23s 0 S
+R _ 1956 1964 - N Su>=1 3:30 0 S
+R _ 1957 1964 - Mar Su>=18 3:30 1 D
+R _ 1965 1973 - Ap Su>=16 3:30 1 D
+R _ 1965 1966 - O Su>=16 2:30 0 S
+R _ 1967 1976 - O Su>=16 3:30 0 S
+R _ 1973 o - D 30 3:30 1 D
+R _ 1975 1976 - Ap Su>=16 3:30 1 D
+R _ 1979 o - May 13 3:30 1 D
+R _ 1979 o - O Su>=16 3:30 0 S
+Z Asia/Macau 7:34:10 - LMT 1904 O 30
+8 - CST 1941 D 21 23
+9 _ +09/+10 1945 S 30 24
+8 _ C%sT
+R CY 1975 o - Ap 13 0 1 S
+R CY 1975 o - O 12 0 0 -
+R CY 1976 o - May 15 0 1 S
+R CY 1976 o - O 11 0 0 -
+R CY 1977 1980 - Ap Su>=1 0 1 S
+R CY 1977 o - S 25 0 0 -
+R CY 1978 o - O 2 0 0 -
+R CY 1979 1997 - S lastSu 0 0 -
+R CY 1981 1998 - Mar lastSu 0 1 S
+Z Asia/Nicosia 2:13:28 - LMT 1921 N 14
+2 CY EE%sT 1998 S
+2 E EE%sT
+Z Asia/Famagusta 2:15:48 - LMT 1921 N 14
+2 CY EE%sT 1998 S
+2 E EE%sT 2016 S 8
+3 - +03 2017 O 29 1u
+2 E EE%sT
+L Asia/Nicosia Europe/Nicosia
+Z Asia/Tbilisi 2:59:11 - LMT 1880
+2:59:11 - TBMT 1924 May 2
+3 - +03 1957 Mar
+4 R +04/+05 1991 Mar 31 2s
+3 R +03/+04 1992
+3 e +03/+04 1994 S lastSu
+4 e +04/+05 1996 O lastSu
+4 1 +05 1997 Mar lastSu
+4 e +04/+05 2004 Jun 27
+3 R +03/+04 2005 Mar lastSu 2
+4 - +04
+Z Asia/Dili 8:22:20 - LMT 1912
+8 - +08 1942 F 21 23
+9 - +09 1976 May 3
+8 - +08 2000 S 17
+9 - +09
+Z Asia/Kolkata 5:53:28 - LMT 1854 Jun 28
+5:53:20 - HMT 1870
+5:21:10 - MMT 1906
+5:30 - IST 1941 O
+5:30 1 +0630 1942 May 15
+5:30 - IST 1942 S
+5:30 1 +0630 1945 O 15
+5:30 - IST
+Z Asia/Jakarta 7:7:12 - LMT 1867 Au 10
+7:7:12 - BMT 1923 D 31 23:47:12
+7:20 - +0720 1932 N
+7:30 - +0730 1942 Mar 23
+9 - +09 1945 S 23
+7:30 - +0730 1948 May
+8 - +08 1950 May
+7:30 - +0730 1964
+7 - WIB
+Z Asia/Pontianak 7:17:20 - LMT 1908 May
+7:17:20 - PMT 1932 N
+7:30 - +0730 1942 Ja 29
+9 - +09 1945 S 23
+7:30 - +0730 1948 May
+8 - +08 1950 May
+7:30 - +0730 1964
+8 - WITA 1988
+7 - WIB
+Z Asia/Makassar 7:57:36 - LMT 1920
+7:57:36 - MMT 1932 N
+8 - +08 1942 F 9
+9 - +09 1945 S 23
+8 - WITA
+Z Asia/Jayapura 9:22:48 - LMT 1932 N
+9 - +09 1944 S
+9:30 - +0930 1964
+9 - WIT
+R i 1978 1980 - Mar 20 24 1 -
+R i 1978 o - O 20 24 0 -
+R i 1979 o - S 18 24 0 -
+R i 1980 o - S 22 24 0 -
+R i 1991 o - May 2 24 1 -
+R i 1992 1995 - Mar 21 24 1 -
+R i 1991 1995 - S 21 24 0 -
+R i 1996 o - Mar 20 24 1 -
+R i 1996 o - S 20 24 0 -
+R i 1997 1999 - Mar 21 24 1 -
+R i 1997 1999 - S 21 24 0 -
+R i 2000 o - Mar 20 24 1 -
+R i 2000 o - S 20 24 0 -
+R i 2001 2003 - Mar 21 24 1 -
+R i 2001 2003 - S 21 24 0 -
+R i 2004 o - Mar 20 24 1 -
+R i 2004 o - S 20 24 0 -
+R i 2005 o - Mar 21 24 1 -
+R i 2005 o - S 21 24 0 -
+R i 2008 o - Mar 20 24 1 -
+R i 2008 o - S 20 24 0 -
+R i 2009 2011 - Mar 21 24 1 -
+R i 2009 2011 - S 21 24 0 -
+R i 2012 o - Mar 20 24 1 -
+R i 2012 o - S 20 24 0 -
+R i 2013 2015 - Mar 21 24 1 -
+R i 2013 2015 - S 21 24 0 -
+R i 2016 o - Mar 20 24 1 -
+R i 2016 o - S 20 24 0 -
+R i 2017 2019 - Mar 21 24 1 -
+R i 2017 2019 - S 21 24 0 -
+R i 2020 o - Mar 20 24 1 -
+R i 2020 o - S 20 24 0 -
+R i 2021 2023 - Mar 21 24 1 -
+R i 2021 2023 - S 21 24 0 -
+R i 2024 o - Mar 20 24 1 -
+R i 2024 o - S 20 24 0 -
+R i 2025 2027 - Mar 21 24 1 -
+R i 2025 2027 - S 21 24 0 -
+R i 2028 2029 - Mar 20 24 1 -
+R i 2028 2029 - S 20 24 0 -
+R i 2030 2031 - Mar 21 24 1 -
+R i 2030 2031 - S 21 24 0 -
+R i 2032 2033 - Mar 20 24 1 -
+R i 2032 2033 - S 20 24 0 -
+R i 2034 2035 - Mar 21 24 1 -
+R i 2034 2035 - S 21 24 0 -
+R i 2036 2037 - Mar 20 24 1 -
+R i 2036 2037 - S 20 24 0 -
+R i 2038 2039 - Mar 21 24 1 -
+R i 2038 2039 - S 21 24 0 -
+R i 2040 2041 - Mar 20 24 1 -
+R i 2040 2041 - S 20 24 0 -
+R i 2042 2043 - Mar 21 24 1 -
+R i 2042 2043 - S 21 24 0 -
+R i 2044 2045 - Mar 20 24 1 -
+R i 2044 2045 - S 20 24 0 -
+R i 2046 2047 - Mar 21 24 1 -
+R i 2046 2047 - S 21 24 0 -
+R i 2048 2049 - Mar 20 24 1 -
+R i 2048 2049 - S 20 24 0 -
+R i 2050 2051 - Mar 21 24 1 -
+R i 2050 2051 - S 21 24 0 -
+R i 2052 2053 - Mar 20 24 1 -
+R i 2052 2053 - S 20 24 0 -
+R i 2054 2055 - Mar 21 24 1 -
+R i 2054 2055 - S 21 24 0 -
+R i 2056 2057 - Mar 20 24 1 -
+R i 2056 2057 - S 20 24 0 -
+R i 2058 2059 - Mar 21 24 1 -
+R i 2058 2059 - S 21 24 0 -
+R i 2060 2062 - Mar 20 24 1 -
+R i 2060 2062 - S 20 24 0 -
+R i 2063 o - Mar 21 24 1 -
+R i 2063 o - S 21 24 0 -
+R i 2064 2066 - Mar 20 24 1 -
+R i 2064 2066 - S 20 24 0 -
+R i 2067 o - Mar 21 24 1 -
+R i 2067 o - S 21 24 0 -
+R i 2068 2070 - Mar 20 24 1 -
+R i 2068 2070 - S 20 24 0 -
+R i 2071 o - Mar 21 24 1 -
+R i 2071 o - S 21 24 0 -
+R i 2072 2074 - Mar 20 24 1 -
+R i 2072 2074 - S 20 24 0 -
+R i 2075 o - Mar 21 24 1 -
+R i 2075 o - S 21 24 0 -
+R i 2076 2078 - Mar 20 24 1 -
+R i 2076 2078 - S 20 24 0 -
+R i 2079 o - Mar 21 24 1 -
+R i 2079 o - S 21 24 0 -
+R i 2080 2082 - Mar 20 24 1 -
+R i 2080 2082 - S 20 24 0 -
+R i 2083 o - Mar 21 24 1 -
+R i 2083 o - S 21 24 0 -
+R i 2084 2086 - Mar 20 24 1 -
+R i 2084 2086 - S 20 24 0 -
+R i 2087 o - Mar 21 24 1 -
+R i 2087 o - S 21 24 0 -
+R i 2088 ma - Mar 20 24 1 -
+R i 2088 ma - S 20 24 0 -
+Z Asia/Tehran 3:25:44 - LMT 1916
+3:25:44 - TMT 1946
+3:30 - +0330 1977 N
+4 i +04/+05 1979
+3:30 i +0330/+0430
+R IQ 1982 o - May 1 0 1 -
+R IQ 1982 1984 - O 1 0 0 -
+R IQ 1983 o - Mar 31 0 1 -
+R IQ 1984 1985 - Ap 1 0 1 -
+R IQ 1985 1990 - S lastSu 1s 0 -
+R IQ 1986 1990 - Mar lastSu 1s 1 -
+R IQ 1991 2007 - Ap 1 3s 1 -
+R IQ 1991 2007 - O 1 3s 0 -
+Z Asia/Baghdad 2:57:40 - LMT 1890
+2:57:36 - BMT 1918
+3 - +03 1982 May
+3 IQ +03/+04
+R Z 1940 o - May 31 24u 1 D
+R Z 1940 o - S 30 24u 0 S
+R Z 1940 o - N 16 24u 1 D
+R Z 1942 1946 - O 31 24u 0 S
+R Z 1943 1944 - Mar 31 24u 1 D
+R Z 1945 1946 - Ap 15 24u 1 D
+R Z 1948 o - May 22 24u 2 DD
+R Z 1948 o - Au 31 24u 1 D
+R Z 1948 1949 - O 31 24u 0 S
+R Z 1949 o - Ap 30 24u 1 D
+R Z 1950 o - Ap 15 24u 1 D
+R Z 1950 o - S 14 24u 0 S
+R Z 1951 o - Mar 31 24u 1 D
+R Z 1951 o - N 10 24u 0 S
+R Z 1952 o - Ap 19 24u 1 D
+R Z 1952 o - O 18 24u 0 S
+R Z 1953 o - Ap 11 24u 1 D
+R Z 1953 o - S 12 24u 0 S
+R Z 1954 o - Jun 12 24u 1 D
+R Z 1954 o - S 11 24u 0 S
+R Z 1955 o - Jun 11 24u 1 D
+R Z 1955 o - S 10 24u 0 S
+R Z 1956 o - Jun 2 24u 1 D
+R Z 1956 o - S 29 24u 0 S
+R Z 1957 o - Ap 27 24u 1 D
+R Z 1957 o - S 21 24u 0 S
+R Z 1974 o - Jul 6 24 1 D
+R Z 1974 o - O 12 24 0 S
+R Z 1975 o - Ap 19 24 1 D
+R Z 1975 o - Au 30 24 0 S
+R Z 1980 o - Au 2 24s 1 D
+R Z 1980 o - S 13 24s 0 S
+R Z 1984 o - May 5 24s 1 D
+R Z 1984 o - Au 25 24s 0 S
+R Z 1985 o - Ap 13 24 1 D
+R Z 1985 o - Au 31 24 0 S
+R Z 1986 o - May 17 24 1 D
+R Z 1986 o - S 6 24 0 S
+R Z 1987 o - Ap 14 24 1 D
+R Z 1987 o - S 12 24 0 S
+R Z 1988 o - Ap 9 24 1 D
+R Z 1988 o - S 3 24 0 S
+R Z 1989 o - Ap 29 24 1 D
+R Z 1989 o - S 2 24 0 S
+R Z 1990 o - Mar 24 24 1 D
+R Z 1990 o - Au 25 24 0 S
+R Z 1991 o - Mar 23 24 1 D
+R Z 1991 o - Au 31 24 0 S
+R Z 1992 o - Mar 28 24 1 D
+R Z 1992 o - S 5 24 0 S
+R Z 1993 o - Ap 2 0 1 D
+R Z 1993 o - S 5 0 0 S
+R Z 1994 o - Ap 1 0 1 D
+R Z 1994 o - Au 28 0 0 S
+R Z 1995 o - Mar 31 0 1 D
+R Z 1995 o - S 3 0 0 S
+R Z 1996 o - Mar 14 24 1 D
+R Z 1996 o - S 15 24 0 S
+R Z 1997 o - Mar 20 24 1 D
+R Z 1997 o - S 13 24 0 S
+R Z 1998 o - Mar 20 0 1 D
+R Z 1998 o - S 6 0 0 S
+R Z 1999 o - Ap 2 2 1 D
+R Z 1999 o - S 3 2 0 S
+R Z 2000 o - Ap 14 2 1 D
+R Z 2000 o - O 6 1 0 S
+R Z 2001 o - Ap 9 1 1 D
+R Z 2001 o - S 24 1 0 S
+R Z 2002 o - Mar 29 1 1 D
+R Z 2002 o - O 7 1 0 S
+R Z 2003 o - Mar 28 1 1 D
+R Z 2003 o - O 3 1 0 S
+R Z 2004 o - Ap 7 1 1 D
+R Z 2004 o - S 22 1 0 S
+R Z 2005 2012 - Ap F<=1 2 1 D
+R Z 2005 o - O 9 2 0 S
+R Z 2006 o - O 1 2 0 S
+R Z 2007 o - S 16 2 0 S
+R Z 2008 o - O 5 2 0 S
+R Z 2009 o - S 27 2 0 S
+R Z 2010 o - S 12 2 0 S
+R Z 2011 o - O 2 2 0 S
+R Z 2012 o - S 23 2 0 S
+R Z 2013 ma - Mar F>=23 2 1 D
+R Z 2013 ma - O lastSu 2 0 S
+Z Asia/Jerusalem 2:20:54 - LMT 1880
+2:20:40 - JMT 1918
+2 Z I%sT
+R JP 1948 o - May Sa>=1 24 1 D
+R JP 1948 1951 - S Sa>=8 25 0 S
+R JP 1949 o - Ap Sa>=1 24 1 D
+R JP 1950 1951 - May Sa>=1 24 1 D
+Z Asia/Tokyo 9:18:59 - LMT 1887 D 31 15u
+9 JP J%sT
+R J 1973 o - Jun 6 0 1 S
+R J 1973 1975 - O 1 0 0 -
+R J 1974 1977 - May 1 0 1 S
+R J 1976 o - N 1 0 0 -
+R J 1977 o - O 1 0 0 -
+R J 1978 o - Ap 30 0 1 S
+R J 1978 o - S 30 0 0 -
+R J 1985 o - Ap 1 0 1 S
+R J 1985 o - O 1 0 0 -
+R J 1986 1988 - Ap F>=1 0 1 S
+R J 1986 1990 - O F>=1 0 0 -
+R J 1989 o - May 8 0 1 S
+R J 1990 o - Ap 27 0 1 S
+R J 1991 o - Ap 17 0 1 S
+R J 1991 o - S 27 0 0 -
+R J 1992 o - Ap 10 0 1 S
+R J 1992 1993 - O F>=1 0 0 -
+R J 1993 1998 - Ap F>=1 0 1 S
+R J 1994 o - S F>=15 0 0 -
+R J 1995 1998 - S F>=15 0s 0 -
+R J 1999 o - Jul 1 0s 1 S
+R J 1999 2002 - S lastF 0s 0 -
+R J 2000 2001 - Mar lastTh 0s 1 S
+R J 2002 2012 - Mar lastTh 24 1 S
+R J 2003 o - O 24 0s 0 -
+R J 2004 o - O 15 0s 0 -
+R J 2005 o - S lastF 0s 0 -
+R J 2006 2011 - O lastF 0s 0 -
+R J 2013 o - D 20 0 0 -
+R J 2014 2021 - Mar lastTh 24 1 S
+R J 2014 ma - O lastF 0s 0 -
+R J 2022 ma - F lastTh 24 1 S
+Z Asia/Amman 2:23:44 - LMT 1931
+2 J EE%sT
+Z Asia/Almaty 5:7:48 - LMT 1924 May 2
+5 - +05 1930 Jun 21
+6 R +06/+07 1991 Mar 31 2s
+5 R +05/+06 1992 Ja 19 2s
+6 R +06/+07 2004 O 31 2s
+6 - +06
+Z Asia/Qyzylorda 4:21:52 - LMT 1924 May 2
+4 - +04 1930 Jun 21
+5 - +05 1981 Ap
+5 1 +06 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1991 Mar 31 2s
+4 R +04/+05 1991 S 29 2s
+5 R +05/+06 1992 Ja 19 2s
+6 R +06/+07 1992 Mar 29 2s
+5 R +05/+06 2004 O 31 2s
+6 - +06 2018 D 21
+5 - +05
+Z Asia/Qostanay 4:14:28 - LMT 1924 May 2
+4 - +04 1930 Jun 21
+5 - +05 1981 Ap
+5 1 +06 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1991 Mar 31 2s
+4 R +04/+05 1992 Ja 19 2s
+5 R +05/+06 2004 O 31 2s
+6 - +06
+Z Asia/Aqtobe 3:48:40 - LMT 1924 May 2
+4 - +04 1930 Jun 21
+5 - +05 1981 Ap
+5 1 +06 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1991 Mar 31 2s
+4 R +04/+05 1992 Ja 19 2s
+5 R +05/+06 2004 O 31 2s
+5 - +05
+Z Asia/Aqtau 3:21:4 - LMT 1924 May 2
+4 - +04 1930 Jun 21
+5 - +05 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1991 Mar 31 2s
+4 R +04/+05 1992 Ja 19 2s
+5 R +05/+06 1994 S 25 2s
+4 R +04/+05 2004 O 31 2s
+5 - +05
+Z Asia/Atyrau 3:27:44 - LMT 1924 May 2
+3 - +03 1930 Jun 21
+5 - +05 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1991 Mar 31 2s
+4 R +04/+05 1992 Ja 19 2s
+5 R +05/+06 1999 Mar 28 2s
+4 R +04/+05 2004 O 31 2s
+5 - +05
+Z Asia/Oral 3:25:24 - LMT 1924 May 2
+3 - +03 1930 Jun 21
+5 - +05 1981 Ap
+5 1 +06 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1989 Mar 26 2s
+4 R +04/+05 1992 Ja 19 2s
+5 R +05/+06 1992 Mar 29 2s
+4 R +04/+05 2004 O 31 2s
+5 - +05
+R KG 1992 1996 - Ap Su>=7 0s 1 -
+R KG 1992 1996 - S lastSu 0 0 -
+R KG 1997 2005 - Mar lastSu 2:30 1 -
+R KG 1997 2004 - O lastSu 2:30 0 -
+Z Asia/Bishkek 4:58:24 - LMT 1924 May 2
+5 - +05 1930 Jun 21
+6 R +06/+07 1991 Mar 31 2s
+5 R +05/+06 1991 Au 31 2
+5 KG +05/+06 2005 Au 12
+6 - +06
+R KR 1948 o - Jun 1 0 1 D
+R KR 1948 o - S 12 24 0 S
+R KR 1949 o - Ap 3 0 1 D
+R KR 1949 1951 - S Sa>=7 24 0 S
+R KR 1950 o - Ap 1 0 1 D
+R KR 1951 o - May 6 0 1 D
+R KR 1955 o - May 5 0 1 D
+R KR 1955 o - S 8 24 0 S
+R KR 1956 o - May 20 0 1 D
+R KR 1956 o - S 29 24 0 S
+R KR 1957 1960 - May Su>=1 0 1 D
+R KR 1957 1960 - S Sa>=17 24 0 S
+R KR 1987 1988 - May Su>=8 2 1 D
+R KR 1987 1988 - O Su>=8 3 0 S
+Z Asia/Seoul 8:27:52 - LMT 1908 Ap
+8:30 - KST 1912
+9 - JST 1945 S 8
+9 KR K%sT 1954 Mar 21
+8:30 KR K%sT 1961 Au 10
+9 KR K%sT
+Z Asia/Pyongyang 8:23 - LMT 1908 Ap
+8:30 - KST 1912
+9 - JST 1945 Au 24
+9 - KST 2015 Au 15
+8:30 - KST 2018 May 4 23:30
+9 - KST
+R l 1920 o - Mar 28 0 1 S
+R l 1920 o - O 25 0 0 -
+R l 1921 o - Ap 3 0 1 S
+R l 1921 o - O 3 0 0 -
+R l 1922 o - Mar 26 0 1 S
+R l 1922 o - O 8 0 0 -
+R l 1923 o - Ap 22 0 1 S
+R l 1923 o - S 16 0 0 -
+R l 1957 1961 - May 1 0 1 S
+R l 1957 1961 - O 1 0 0 -
+R l 1972 o - Jun 22 0 1 S
+R l 1972 1977 - O 1 0 0 -
+R l 1973 1977 - May 1 0 1 S
+R l 1978 o - Ap 30 0 1 S
+R l 1978 o - S 30 0 0 -
+R l 1984 1987 - May 1 0 1 S
+R l 1984 1991 - O 16 0 0 -
+R l 1988 o - Jun 1 0 1 S
+R l 1989 o - May 10 0 1 S
+R l 1990 1992 - May 1 0 1 S
+R l 1992 o - O 4 0 0 -
+R l 1993 ma - Mar lastSu 0 1 S
+R l 1993 1998 - S lastSu 0 0 -
+R l 1999 ma - O lastSu 0 0 -
+Z Asia/Beirut 2:22 - LMT 1880
+2 l EE%sT
+R NB 1935 1941 - S 14 0 0:20 -
+R NB 1935 1941 - D 14 0 0 -
+Z Asia/Kuala_Lumpur 6:46:46 - LMT 1901
+6:55:25 - SMT 1905 Jun
+7 - +07 1933
+7 0:20 +0720 1936
+7:20 - +0720 1941 S
+7:30 - +0730 1942 F 16
+9 - +09 1945 S 12
+7:30 - +0730 1982
+8 - +08
+Z Asia/Kuching 7:21:20 - LMT 1926 Mar
+7:30 - +0730 1933
+8 NB +08/+0820 1942 F 16
+9 - +09 1945 S 12
+8 - +08
+Z Indian/Maldives 4:54 - LMT 1880
+4:54 - MMT 1960
+5 - +05
+R X 1983 1984 - Ap 1 0 1 -
+R X 1983 o - O 1 0 0 -
+R X 1985 1998 - Mar lastSu 0 1 -
+R X 1984 1998 - S lastSu 0 0 -
+R X 2001 o - Ap lastSa 2 1 -
+R X 2001 2006 - S lastSa 2 0 -
+R X 2002 2006 - Mar lastSa 2 1 -
+R X 2015 2016 - Mar lastSa 2 1 -
+R X 2015 2016 - S lastSa 0 0 -
+Z Asia/Hovd 6:6:36 - LMT 1905 Au
+6 - +06 1978
+7 X +07/+08
+Z Asia/Ulaanbaatar 7:7:32 - LMT 1905 Au
+7 - +07 1978
+8 X +08/+09
+Z Asia/Choibalsan 7:38 - LMT 1905 Au
+7 - +07 1978
+8 - +08 1983 Ap
+9 X +09/+10 2008 Mar 31
+8 X +08/+09
+Z Asia/Kathmandu 5:41:16 - LMT 1920
+5:30 - +0530 1986
+5:45 - +0545
+R PK 2002 o - Ap Su>=2 0 1 S
+R PK 2002 o - O Su>=2 0 0 -
+R PK 2008 o - Jun 1 0 1 S
+R PK 2008 2009 - N 1 0 0 -
+R PK 2009 o - Ap 15 0 1 S
+Z Asia/Karachi 4:28:12 - LMT 1907
+5:30 - +0530 1942 S
+5:30 1 +0630 1945 O 15
+5:30 - +0530 1951 S 30
+5 - +05 1971 Mar 26
+5 PK PK%sT
+R P 1999 2005 - Ap F>=15 0 1 S
+R P 1999 2003 - O F>=15 0 0 -
+R P 2004 o - O 1 1 0 -
+R P 2005 o - O 4 2 0 -
+R P 2006 2007 - Ap 1 0 1 S
+R P 2006 o - S 22 0 0 -
+R P 2007 o - S 13 2 0 -
+R P 2008 2009 - Mar lastF 0 1 S
+R P 2008 o - S 1 0 0 -
+R P 2009 o - S 4 1 0 -
+R P 2010 o - Mar 26 0 1 S
+R P 2010 o - Au 11 0 0 -
+R P 2011 o - Ap 1 0:1 1 S
+R P 2011 o - Au 1 0 0 -
+R P 2011 o - Au 30 0 1 S
+R P 2011 o - S 30 0 0 -
+R P 2012 2014 - Mar lastTh 24 1 S
+R P 2012 o - S 21 1 0 -
+R P 2013 o - S 27 0 0 -
+R P 2014 o - O 24 0 0 -
+R P 2015 o - Mar 28 0 1 S
+R P 2015 o - O 23 1 0 -
+R P 2016 2018 - Mar Sa>=24 1 1 S
+R P 2016 2018 - O Sa>=24 1 0 -
+R P 2019 o - Mar 29 0 1 S
+R P 2019 o - O Sa>=24 0 0 -
+R P 2020 2021 - Mar Sa>=24 0 1 S
+R P 2020 o - O 24 1 0 -
+R P 2021 ma - O F>=23 1 0 -
+R P 2022 ma - Mar Su>=25 0 1 S
+Z Asia/Gaza 2:17:52 - LMT 1900 O
+2 Z EET/EEST 1948 May 15
+2 K EE%sT 1967 Jun 5
+2 Z I%sT 1996
+2 J EE%sT 1999
+2 P EE%sT 2008 Au 29
+2 - EET 2008 S
+2 P EE%sT 2010
+2 - EET 2010 Mar 27 0:1
+2 P EE%sT 2011 Au
+2 - EET 2012
+2 P EE%sT
+Z Asia/Hebron 2:20:23 - LMT 1900 O
+2 Z EET/EEST 1948 May 15
+2 K EE%sT 1967 Jun 5
+2 Z I%sT 1996
+2 J EE%sT 1999
+2 P EE%sT
+R PH 1936 o - N 1 0 1 D
+R PH 1937 o - F 1 0 0 S
+R PH 1954 o - Ap 12 0 1 D
+R PH 1954 o - Jul 1 0 0 S
+R PH 1978 o - Mar 22 0 1 D
+R PH 1978 o - S 21 0 0 S
+Z Asia/Manila -15:56 - LMT 1844 D 31
+8:4 - LMT 1899 May 11
+8 PH P%sT 1942 May
+9 - JST 1944 N
+8 PH P%sT
+Z Asia/Qatar 3:26:8 - LMT 1920
+4 - +04 1972 Jun
+3 - +03
+L Asia/Qatar Asia/Bahrain
+Z Asia/Riyadh 3:6:52 - LMT 1947 Mar 14
+3 - +03
+L Asia/Riyadh Antarctica/Syowa
+L Asia/Riyadh Asia/Aden
+L Asia/Riyadh Asia/Kuwait
+Z Asia/Singapore 6:55:25 - LMT 1901
+6:55:25 - SMT 1905 Jun
+7 - +07 1933
+7 0:20 +0720 1936
+7:20 - +0720 1941 S
+7:30 - +0730 1942 F 16
+9 - +09 1945 S 12
+7:30 - +0730 1982
+8 - +08
+Z Asia/Colombo 5:19:24 - LMT 1880
+5:19:32 - MMT 1906
+5:30 - +0530 1942 Ja 5
+5:30 0:30 +06 1942 S
+5:30 1 +0630 1945 O 16 2
+5:30 - +0530 1996 May 25
+6:30 - +0630 1996 O 26 0:30
+6 - +06 2006 Ap 15 0:30
+5:30 - +0530
+R S 1920 1923 - Ap Su>=15 2 1 S
+R S 1920 1923 - O Su>=1 2 0 -
+R S 1962 o - Ap 29 2 1 S
+R S 1962 o - O 1 2 0 -
+R S 1963 1965 - May 1 2 1 S
+R S 1963 o - S 30 2 0 -
+R S 1964 o - O 1 2 0 -
+R S 1965 o - S 30 2 0 -
+R S 1966 o - Ap 24 2 1 S
+R S 1966 1976 - O 1 2 0 -
+R S 1967 1978 - May 1 2 1 S
+R S 1977 1978 - S 1 2 0 -
+R S 1983 1984 - Ap 9 2 1 S
+R S 1983 1984 - O 1 2 0 -
+R S 1986 o - F 16 2 1 S
+R S 1986 o - O 9 2 0 -
+R S 1987 o - Mar 1 2 1 S
+R S 1987 1988 - O 31 2 0 -
+R S 1988 o - Mar 15 2 1 S
+R S 1989 o - Mar 31 2 1 S
+R S 1989 o - O 1 2 0 -
+R S 1990 o - Ap 1 2 1 S
+R S 1990 o - S 30 2 0 -
+R S 1991 o - Ap 1 0 1 S
+R S 1991 1992 - O 1 0 0 -
+R S 1992 o - Ap 8 0 1 S
+R S 1993 o - Mar 26 0 1 S
+R S 1993 o - S 25 0 0 -
+R S 1994 1996 - Ap 1 0 1 S
+R S 1994 2005 - O 1 0 0 -
+R S 1997 1998 - Mar lastM 0 1 S
+R S 1999 2006 - Ap 1 0 1 S
+R S 2006 o - S 22 0 0 -
+R S 2007 o - Mar lastF 0 1 S
+R S 2007 o - N F>=1 0 0 -
+R S 2008 o - Ap F>=1 0 1 S
+R S 2008 o - N 1 0 0 -
+R S 2009 o - Mar lastF 0 1 S
+R S 2010 2011 - Ap F>=1 0 1 S
+R S 2012 ma - Mar lastF 0 1 S
+R S 2009 ma - O lastF 0 0 -
+Z Asia/Damascus 2:25:12 - LMT 1920
+2 S EE%sT
+Z Asia/Dushanbe 4:35:12 - LMT 1924 May 2
+5 - +05 1930 Jun 21
+6 R +06/+07 1991 Mar 31 2s
+5 1 +05/+06 1991 S 9 2s
+5 - +05
+Z Asia/Bangkok 6:42:4 - LMT 1880
+6:42:4 - BMT 1920 Ap
+7 - +07
+L Asia/Bangkok Asia/Phnom_Penh
+L Asia/Bangkok Asia/Vientiane
+Z Asia/Ashgabat 3:53:32 - LMT 1924 May 2
+4 - +04 1930 Jun 21
+5 R +05/+06 1991 Mar 31 2
+4 R +04/+05 1992 Ja 19 2
+5 - +05
+Z Asia/Dubai 3:41:12 - LMT 1920
+4 - +04
+L Asia/Dubai Asia/Muscat
+Z Asia/Samarkand 4:27:53 - LMT 1924 May 2
+4 - +04 1930 Jun 21
+5 - +05 1981 Ap
+5 1 +06 1981 O
+6 - +06 1982 Ap
+5 R +05/+06 1992
+5 - +05
+Z Asia/Tashkent 4:37:11 - LMT 1924 May 2
+5 - +05 1930 Jun 21
+6 R +06/+07 1991 Mar 31 2
+5 R +05/+06 1992
+5 - +05
+Z Asia/Ho_Chi_Minh 7:6:40 - LMT 1906 Jul
+7:6:30 - PLMT 1911 May
+7 - +07 1942 D 31 23
+8 - +08 1945 Mar 14 23
+9 - +09 1945 S 2
+7 - +07 1947 Ap
+8 - +08 1955 Jul
+7 - +07 1959 D 31 23
+8 - +08 1975 Jun 13
+7 - +07
+R AU 1917 o - Ja 1 2s 1 D
+R AU 1917 o - Mar lastSu 2s 0 S
+R AU 1942 o - Ja 1 2s 1 D
+R AU 1942 o - Mar lastSu 2s 0 S
+R AU 1942 o - S 27 2s 1 D
+R AU 1943 1944 - Mar lastSu 2s 0 S
+R AU 1943 o - O 3 2s 1 D
+Z Australia/Darwin 8:43:20 - LMT 1895 F
+9 - ACST 1899 May
+9:30 AU AC%sT
+R AW 1974 o - O lastSu 2s 1 D
+R AW 1975 o - Mar Su>=1 2s 0 S
+R AW 1983 o - O lastSu 2s 1 D
+R AW 1984 o - Mar Su>=1 2s 0 S
+R AW 1991 o - N 17 2s 1 D
+R AW 1992 o - Mar Su>=1 2s 0 S
+R AW 2006 o - D 3 2s 1 D
+R AW 2007 2009 - Mar lastSu 2s 0 S
+R AW 2007 2008 - O lastSu 2s 1 D
+Z Australia/Perth 7:43:24 - LMT 1895 D
+8 AU AW%sT 1943 Jul
+8 AW AW%sT
+Z Australia/Eucla 8:35:28 - LMT 1895 D
+8:45 AU +0845/+0945 1943 Jul
+8:45 AW +0845/+0945
+R AQ 1971 o - O lastSu 2s 1 D
+R AQ 1972 o - F lastSu 2s 0 S
+R AQ 1989 1991 - O lastSu 2s 1 D
+R AQ 1990 1992 - Mar Su>=1 2s 0 S
+R Ho 1992 1993 - O lastSu 2s 1 D
+R Ho 1993 1994 - Mar Su>=1 2s 0 S
+Z Australia/Brisbane 10:12:8 - LMT 1895
+10 AU AE%sT 1971
+10 AQ AE%sT
+Z Australia/Lindeman 9:55:56 - LMT 1895
+10 AU AE%sT 1971
+10 AQ AE%sT 1992 Jul
+10 Ho AE%sT
+R AS 1971 1985 - O lastSu 2s 1 D
+R AS 1986 o - O 19 2s 1 D
+R AS 1987 2007 - O lastSu 2s 1 D
+R AS 1972 o - F 27 2s 0 S
+R AS 1973 1985 - Mar Su>=1 2s 0 S
+R AS 1986 1990 - Mar Su>=15 2s 0 S
+R AS 1991 o - Mar 3 2s 0 S
+R AS 1992 o - Mar 22 2s 0 S
+R AS 1993 o - Mar 7 2s 0 S
+R AS 1994 o - Mar 20 2s 0 S
+R AS 1995 2005 - Mar lastSu 2s 0 S
+R AS 2006 o - Ap 2 2s 0 S
+R AS 2007 o - Mar lastSu 2s 0 S
+R AS 2008 ma - Ap Su>=1 2s 0 S
+R AS 2008 ma - O Su>=1 2s 1 D
+Z Australia/Adelaide 9:14:20 - LMT 1895 F
+9 - ACST 1899 May
+9:30 AU AC%sT 1971
+9:30 AS AC%sT
+R AT 1916 o - O Su>=1 2s 1 D
+R AT 1917 o - Mar lastSu 2s 0 S
+R AT 1917 1918 - O Su>=22 2s 1 D
+R AT 1918 1919 - Mar Su>=1 2s 0 S
+R AT 1967 o - O Su>=1 2s 1 D
+R AT 1968 o - Mar Su>=29 2s 0 S
+R AT 1968 1985 - O lastSu 2s 1 D
+R AT 1969 1971 - Mar Su>=8 2s 0 S
+R AT 1972 o - F lastSu 2s 0 S
+R AT 1973 1981 - Mar Su>=1 2s 0 S
+R AT 1982 1983 - Mar lastSu 2s 0 S
+R AT 1984 1986 - Mar Su>=1 2s 0 S
+R AT 1986 o - O Su>=15 2s 1 D
+R AT 1987 1990 - Mar Su>=15 2s 0 S
+R AT 1987 o - O Su>=22 2s 1 D
+R AT 1988 1990 - O lastSu 2s 1 D
+R AT 1991 1999 - O Su>=1 2s 1 D
+R AT 1991 2005 - Mar lastSu 2s 0 S
+R AT 2000 o - Au lastSu 2s 1 D
+R AT 2001 ma - O Su>=1 2s 1 D
+R AT 2006 o - Ap Su>=1 2s 0 S
+R AT 2007 o - Mar lastSu 2s 0 S
+R AT 2008 ma - Ap Su>=1 2s 0 S
+Z Australia/Hobart 9:49:16 - LMT 1895 S
+10 AT AE%sT 1919 O 24
+10 AU AE%sT 1967
+10 AT AE%sT
+R AV 1971 1985 - O lastSu 2s 1 D
+R AV 1972 o - F lastSu 2s 0 S
+R AV 1973 1985 - Mar Su>=1 2s 0 S
+R AV 1986 1990 - Mar Su>=15 2s 0 S
+R AV 1986 1987 - O Su>=15 2s 1 D
+R AV 1988 1999 - O lastSu 2s 1 D
+R AV 1991 1994 - Mar Su>=1 2s 0 S
+R AV 1995 2005 - Mar lastSu 2s 0 S
+R AV 2000 o - Au lastSu 2s 1 D
+R AV 2001 2007 - O lastSu 2s 1 D
+R AV 2006 o - Ap Su>=1 2s 0 S
+R AV 2007 o - Mar lastSu 2s 0 S
+R AV 2008 ma - Ap Su>=1 2s 0 S
+R AV 2008 ma - O Su>=1 2s 1 D
+Z Australia/Melbourne 9:39:52 - LMT 1895 F
+10 AU AE%sT 1971
+10 AV AE%sT
+R AN 1971 1985 - O lastSu 2s 1 D
+R AN 1972 o - F 27 2s 0 S
+R AN 1973 1981 - Mar Su>=1 2s 0 S
+R AN 1982 o - Ap Su>=1 2s 0 S
+R AN 1983 1985 - Mar Su>=1 2s 0 S
+R AN 1986 1989 - Mar Su>=15 2s 0 S
+R AN 1986 o - O 19 2s 1 D
+R AN 1987 1999 - O lastSu 2s 1 D
+R AN 1990 1995 - Mar Su>=1 2s 0 S
+R AN 1996 2005 - Mar lastSu 2s 0 S
+R AN 2000 o - Au lastSu 2s 1 D
+R AN 2001 2007 - O lastSu 2s 1 D
+R AN 2006 o - Ap Su>=1 2s 0 S
+R AN 2007 o - Mar lastSu 2s 0 S
+R AN 2008 ma - Ap Su>=1 2s 0 S
+R AN 2008 ma - O Su>=1 2s 1 D
+Z Australia/Sydney 10:4:52 - LMT 1895 F
+10 AU AE%sT 1971
+10 AN AE%sT
+Z Australia/Broken_Hill 9:25:48 - LMT 1895 F
+10 - AEST 1896 Au 23
+9 - ACST 1899 May
+9:30 AU AC%sT 1971
+9:30 AN AC%sT 2000
+9:30 AS AC%sT
+R LH 1981 1984 - O lastSu 2 1 -
+R LH 1982 1985 - Mar Su>=1 2 0 -
+R LH 1985 o - O lastSu 2 0:30 -
+R LH 1986 1989 - Mar Su>=15 2 0 -
+R LH 1986 o - O 19 2 0:30 -
+R LH 1987 1999 - O lastSu 2 0:30 -
+R LH 1990 1995 - Mar Su>=1 2 0 -
+R LH 1996 2005 - Mar lastSu 2 0 -
+R LH 2000 o - Au lastSu 2 0:30 -
+R LH 2001 2007 - O lastSu 2 0:30 -
+R LH 2006 o - Ap Su>=1 2 0 -
+R LH 2007 o - Mar lastSu 2 0 -
+R LH 2008 ma - Ap Su>=1 2 0 -
+R LH 2008 ma - O Su>=1 2 0:30 -
+Z Australia/Lord_Howe 10:36:20 - LMT 1895 F
+10 - AEST 1981 Mar
+10:30 LH +1030/+1130 1985 Jul
+10:30 LH +1030/+11
+Z Antarctica/Macquarie 0 - -00 1899 N
+10 - AEST 1916 O 1 2
+10 1 AEDT 1917 F
+10 AU AE%sT 1919 Ap 1 0s
+0 - -00 1948 Mar 25
+10 AU AE%sT 1967
+10 AT AE%sT 2010
+10 1 AEDT 2011
+10 AT AE%sT
+Z Indian/Christmas 7:2:52 - LMT 1895 F
+7 - +07
+Z Indian/Cocos 6:27:40 - LMT 1900
+6:30 - +0630
+R FJ 1998 1999 - N Su>=1 2 1 -
+R FJ 1999 2000 - F lastSu 3 0 -
+R FJ 2009 o - N 29 2 1 -
+R FJ 2010 o - Mar lastSu 3 0 -
+R FJ 2010 2013 - O Su>=21 2 1 -
+R FJ 2011 o - Mar Su>=1 3 0 -
+R FJ 2012 2013 - Ja Su>=18 3 0 -
+R FJ 2014 o - Ja Su>=18 2 0 -
+R FJ 2014 2018 - N Su>=1 2 1 -
+R FJ 2015 2021 - Ja Su>=12 3 0 -
+R FJ 2019 o - N Su>=8 2 1 -
+R FJ 2020 o - D 20 2 1 -
+R FJ 2022 ma - N Su>=8 2 1 -
+R FJ 2023 ma - Ja Su>=12 3 0 -
+Z Pacific/Fiji 11:55:44 - LMT 1915 O 26
+12 FJ +12/+13
+Z Pacific/Gambier -8:59:48 - LMT 1912 O
+-9 - -09
+Z Pacific/Marquesas -9:18 - LMT 1912 O
+-9:30 - -0930
+Z Pacific/Tahiti -9:58:16 - LMT 1912 O
+-10 - -10
+R Gu 1959 o - Jun 27 2 1 D
+R Gu 1961 o - Ja 29 2 0 S
+R Gu 1967 o - S 1 2 1 D
+R Gu 1969 o - Ja 26 0:1 0 S
+R Gu 1969 o - Jun 22 2 1 D
+R Gu 1969 o - Au 31 2 0 S
+R Gu 1970 1971 - Ap lastSu 2 1 D
+R Gu 1970 1971 - S Su>=1 2 0 S
+R Gu 1973 o - D 16 2 1 D
+R Gu 1974 o - F 24 2 0 S
+R Gu 1976 o - May 26 2 1 D
+R Gu 1976 o - Au 22 2:1 0 S
+R Gu 1977 o - Ap 24 2 1 D
+R Gu 1977 o - Au 28 2 0 S
+Z Pacific/Guam -14:21 - LMT 1844 D 31
+9:39 - LMT 1901
+10 - GST 1941 D 10
+9 - +09 1944 Jul 31
+10 Gu G%sT 2000 D 23
+10 - ChST
+L Pacific/Guam Pacific/Saipan
+Z Pacific/Tarawa 11:32:4 - LMT 1901
+12 - +12
+Z Pacific/Kanton 0 - -00 1937 Au 31
+-12 - -12 1979 O
+-11 - -11 1994 D 31
+13 - +13
+Z Pacific/Kiritimati -10:29:20 - LMT 1901
+-10:40 - -1040 1979 O
+-10 - -10 1994 D 31
+14 - +14
+Z Pacific/Majuro 11:24:48 - LMT 1901
+11 - +11 1914 O
+9 - +09 1919 F
+11 - +11 1937
+10 - +10 1941 Ap
+9 - +09 1944 Ja 30
+11 - +11 1969 O
+12 - +12
+Z Pacific/Kwajalein 11:9:20 - LMT 1901
+11 - +11 1937
+10 - +10 1941 Ap
+9 - +09 1944 F 6
+11 - +11 1969 O
+-12 - -12 1993 Au 20 24
+12 - +12
+Z Pacific/Chuuk -13:52:52 - LMT 1844 D 31
+10:7:8 - LMT 1901
+10 - +10 1914 O
+9 - +09 1919 F
+10 - +10 1941 Ap
+9 - +09 1945 Au
+10 - +10
+Z Pacific/Pohnpei -13:27:8 - LMT 1844 D 31
+10:32:52 - LMT 1901
+11 - +11 1914 O
+9 - +09 1919 F
+11 - +11 1937
+10 - +10 1941 Ap
+9 - +09 1945 Au
+11 - +11
+Z Pacific/Kosrae -13:8:4 - LMT 1844 D 31
+10:51:56 - LMT 1901
+11 - +11 1914 O
+9 - +09 1919 F
+11 - +11 1937
+10 - +10 1941 Ap
+9 - +09 1945 Au
+11 - +11 1969 O
+12 - +12 1999
+11 - +11
+Z Pacific/Nauru 11:7:40 - LMT 1921 Ja 15
+11:30 - +1130 1942 Au 29
+9 - +09 1945 S 8
+11:30 - +1130 1979 F 10 2
+12 - +12
+R NC 1977 1978 - D Su>=1 0 1 -
+R NC 1978 1979 - F 27 0 0 -
+R NC 1996 o - D 1 2s 1 -
+R NC 1997 o - Mar 2 2s 0 -
+Z Pacific/Noumea 11:5:48 - LMT 1912 Ja 13
+11 NC +11/+12
+R NZ 1927 o - N 6 2 1 S
+R NZ 1928 o - Mar 4 2 0 M
+R NZ 1928 1933 - O Su>=8 2 0:30 S
+R NZ 1929 1933 - Mar Su>=15 2 0 M
+R NZ 1934 1940 - Ap lastSu 2 0 M
+R NZ 1934 1940 - S lastSu 2 0:30 S
+R NZ 1946 o - Ja 1 0 0 S
+R NZ 1974 o - N Su>=1 2s 1 D
+R k 1974 o - N Su>=1 2:45s 1 -
+R NZ 1975 o - F lastSu 2s 0 S
+R k 1975 o - F lastSu 2:45s 0 -
+R NZ 1975 1988 - O lastSu 2s 1 D
+R k 1975 1988 - O lastSu 2:45s 1 -
+R NZ 1976 1989 - Mar Su>=1 2s 0 S
+R k 1976 1989 - Mar Su>=1 2:45s 0 -
+R NZ 1989 o - O Su>=8 2s 1 D
+R k 1989 o - O Su>=8 2:45s 1 -
+R NZ 1990 2006 - O Su>=1 2s 1 D
+R k 1990 2006 - O Su>=1 2:45s 1 -
+R NZ 1990 2007 - Mar Su>=15 2s 0 S
+R k 1990 2007 - Mar Su>=15 2:45s 0 -
+R NZ 2007 ma - S lastSu 2s 1 D
+R k 2007 ma - S lastSu 2:45s 1 -
+R NZ 2008 ma - Ap Su>=1 2s 0 S
+R k 2008 ma - Ap Su>=1 2:45s 0 -
+Z Pacific/Auckland 11:39:4 - LMT 1868 N 2
+11:30 NZ NZ%sT 1946
+12 NZ NZ%sT
+Z Pacific/Chatham 12:13:48 - LMT 1868 N 2
+12:15 - +1215 1946
+12:45 k +1245/+1345
+L Pacific/Auckland Antarctica/McMurdo
+R CK 1978 o - N 12 0 0:30 -
+R CK 1979 1991 - Mar Su>=1 0 0 -
+R CK 1979 1990 - O lastSu 0 0:30 -
+Z Pacific/Rarotonga 13:20:56 - LMT 1899 D 26
+-10:39:4 - LMT 1952 O 16
+-10:30 - -1030 1978 N 12
+-10 CK -10/-0930
+Z Pacific/Niue -11:19:40 - LMT 1952 O 16
+-11:20 - -1120 1964 Jul
+-11 - -11
+Z Pacific/Norfolk 11:11:52 - LMT 1901
+11:12 - +1112 1951
+11:30 - +1130 1974 O 27 2s
+11:30 1 +1230 1975 Mar 2 2s
+11:30 - +1130 2015 O 4 2s
+11 - +11 2019 Jul
+11 AN +11/+12
+Z Pacific/Palau -15:2:4 - LMT 1844 D 31
+8:57:56 - LMT 1901
+9 - +09
+Z Pacific/Port_Moresby 9:48:40 - LMT 1880
+9:48:32 - PMMT 1895
+10 - +10
+L Pacific/Port_Moresby Antarctica/DumontDUrville
+Z Pacific/Bougainville 10:22:16 - LMT 1880
+9:48:32 - PMMT 1895
+10 - +10 1942 Jul
+9 - +09 1945 Au 21
+10 - +10 2014 D 28 2
+11 - +11
+Z Pacific/Pitcairn -8:40:20 - LMT 1901
+-8:30 - -0830 1998 Ap 27
+-8 - -08
+Z Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5
+-11:22:48 - LMT 1911
+-11 - SST
+L Pacific/Pago_Pago Pacific/Midway
+R WS 2010 o - S lastSu 0 1 -
+R WS 2011 o - Ap Sa>=1 4 0 -
+R WS 2011 o - S lastSa 3 1 -
+R WS 2012 2021 - Ap Su>=1 4 0 -
+R WS 2012 2020 - S lastSu 3 1 -
+Z Pacific/Apia 12:33:4 - LMT 1892 Jul 5
+-11:26:56 - LMT 1911
+-11:30 - -1130 1950
+-11 WS -11/-10 2011 D 29 24
+13 WS +13/+14
+Z Pacific/Guadalcanal 10:39:48 - LMT 1912 O
+11 - +11
+Z Pacific/Fakaofo -11:24:56 - LMT 1901
+-11 - -11 2011 D 30
+13 - +13
+R TO 1999 o - O 7 2s 1 -
+R TO 2000 o - Mar 19 2s 0 -
+R TO 2000 2001 - N Su>=1 2 1 -
+R TO 2001 2002 - Ja lastSu 2 0 -
+R TO 2016 o - N Su>=1 2 1 -
+R TO 2017 o - Ja Su>=15 3 0 -
+Z Pacific/Tongatapu 12:19:12 - LMT 1945 S 10
+12:20 - +1220 1961
+13 - +13 1999
+13 TO +13/+14
+Z Pacific/Funafuti 11:56:52 - LMT 1901
+12 - +12
+Z Pacific/Wake 11:6:28 - LMT 1901
+12 - +12
+R VU 1973 o - D 22 12u 1 -
+R VU 1974 o - Mar 30 12u 0 -
+R VU 1983 1991 - S Sa>=22 24 1 -
+R VU 1984 1991 - Mar Sa>=22 24 0 -
+R VU 1992 1993 - Ja Sa>=22 24 0 -
+R VU 1992 o - O Sa>=22 24 1 -
+Z Pacific/Efate 11:13:16 - LMT 1912 Ja 13
+11 VU +11/+12
+Z Pacific/Wallis 12:15:20 - LMT 1901
+12 - +12
+R G 1916 o - May 21 2s 1 BST
+R G 1916 o - O 1 2s 0 GMT
+R G 1917 o - Ap 8 2s 1 BST
+R G 1917 o - S 17 2s 0 GMT
+R G 1918 o - Mar 24 2s 1 BST
+R G 1918 o - S 30 2s 0 GMT
+R G 1919 o - Mar 30 2s 1 BST
+R G 1919 o - S 29 2s 0 GMT
+R G 1920 o - Mar 28 2s 1 BST
+R G 1920 o - O 25 2s 0 GMT
+R G 1921 o - Ap 3 2s 1 BST
+R G 1921 o - O 3 2s 0 GMT
+R G 1922 o - Mar 26 2s 1 BST
+R G 1922 o - O 8 2s 0 GMT
+R G 1923 o - Ap Su>=16 2s 1 BST
+R G 1923 1924 - S Su>=16 2s 0 GMT
+R G 1924 o - Ap Su>=9 2s 1 BST
+R G 1925 1926 - Ap Su>=16 2s 1 BST
+R G 1925 1938 - O Su>=2 2s 0 GMT
+R G 1927 o - Ap Su>=9 2s 1 BST
+R G 1928 1929 - Ap Su>=16 2s 1 BST
+R G 1930 o - Ap Su>=9 2s 1 BST
+R G 1931 1932 - Ap Su>=16 2s 1 BST
+R G 1933 o - Ap Su>=9 2s 1 BST
+R G 1934 o - Ap Su>=16 2s 1 BST
+R G 1935 o - Ap Su>=9 2s 1 BST
+R G 1936 1937 - Ap Su>=16 2s 1 BST
+R G 1938 o - Ap Su>=9 2s 1 BST
+R G 1939 o - Ap Su>=16 2s 1 BST
+R G 1939 o - N Su>=16 2s 0 GMT
+R G 1940 o - F Su>=23 2s 1 BST
+R G 1941 o - May Su>=2 1s 2 BDST
+R G 1941 1943 - Au Su>=9 1s 1 BST
+R G 1942 1944 - Ap Su>=2 1s 2 BDST
+R G 1944 o - S Su>=16 1s 1 BST
+R G 1945 o - Ap M>=2 1s 2 BDST
+R G 1945 o - Jul Su>=9 1s 1 BST
+R G 1945 1946 - O Su>=2 2s 0 GMT
+R G 1946 o - Ap Su>=9 2s 1 BST
+R G 1947 o - Mar 16 2s 1 BST
+R G 1947 o - Ap 13 1s 2 BDST
+R G 1947 o - Au 10 1s 1 BST
+R G 1947 o - N 2 2s 0 GMT
+R G 1948 o - Mar 14 2s 1 BST
+R G 1948 o - O 31 2s 0 GMT
+R G 1949 o - Ap 3 2s 1 BST
+R G 1949 o - O 30 2s 0 GMT
+R G 1950 1952 - Ap Su>=14 2s 1 BST
+R G 1950 1952 - O Su>=21 2s 0 GMT
+R G 1953 o - Ap Su>=16 2s 1 BST
+R G 1953 1960 - O Su>=2 2s 0 GMT
+R G 1954 o - Ap Su>=9 2s 1 BST
+R G 1955 1956 - Ap Su>=16 2s 1 BST
+R G 1957 o - Ap Su>=9 2s 1 BST
+R G 1958 1959 - Ap Su>=16 2s 1 BST
+R G 1960 o - Ap Su>=9 2s 1 BST
+R G 1961 1963 - Mar lastSu 2s 1 BST
+R G 1961 1968 - O Su>=23 2s 0 GMT
+R G 1964 1967 - Mar Su>=19 2s 1 BST
+R G 1968 o - F 18 2s 1 BST
+R G 1972 1980 - Mar Su>=16 2s 1 BST
+R G 1972 1980 - O Su>=23 2s 0 GMT
+R G 1981 1995 - Mar lastSu 1u 1 BST
+R G 1981 1989 - O Su>=23 1u 0 GMT
+R G 1990 1995 - O Su>=22 1u 0 GMT
+Z Europe/London -0:1:15 - LMT 1847 D 1 0s
+0 G %s 1968 O 27
+1 - BST 1971 O 31 2u
+0 G %s 1996
+0 E GMT/BST
+L Europe/London Europe/Jersey
+L Europe/London Europe/Guernsey
+L Europe/London Europe/Isle_of_Man
+R IE 1971 o - O 31 2u -1 -
+R IE 1972 1980 - Mar Su>=16 2u 0 -
+R IE 1972 1980 - O Su>=23 2u -1 -
+R IE 1981 ma - Mar lastSu 1u 0 -
+R IE 1981 1989 - O Su>=23 1u -1 -
+R IE 1990 1995 - O Su>=22 1u -1 -
+R IE 1996 ma - O lastSu 1u -1 -
+Z Europe/Dublin -0:25 - LMT 1880 Au 2
+-0:25:21 - DMT 1916 May 21 2s
+-0:25:21 1 IST 1916 O 1 2s
+0 G %s 1921 D 6
+0 G GMT/IST 1940 F 25 2s
+0 1 IST 1946 O 6 2s
+0 - GMT 1947 Mar 16 2s
+0 1 IST 1947 N 2 2s
+0 - GMT 1948 Ap 18 2s
+0 G GMT/IST 1968 O 27
+1 IE IST/GMT
+R E 1977 1980 - Ap Su>=1 1u 1 S
+R E 1977 o - S lastSu 1u 0 -
+R E 1978 o - O 1 1u 0 -
+R E 1979 1995 - S lastSu 1u 0 -
+R E 1981 ma - Mar lastSu 1u 1 S
+R E 1996 ma - O lastSu 1u 0 -
+R W- 1977 1980 - Ap Su>=1 1s 1 S
+R W- 1977 o - S lastSu 1s 0 -
+R W- 1978 o - O 1 1s 0 -
+R W- 1979 1995 - S lastSu 1s 0 -
+R W- 1981 ma - Mar lastSu 1s 1 S
+R W- 1996 ma - O lastSu 1s 0 -
+R c 1916 o - Ap 30 23 1 S
+R c 1916 o - O 1 1 0 -
+R c 1917 1918 - Ap M>=15 2s 1 S
+R c 1917 1918 - S M>=15 2s 0 -
+R c 1940 o - Ap 1 2s 1 S
+R c 1942 o - N 2 2s 0 -
+R c 1943 o - Mar 29 2s 1 S
+R c 1943 o - O 4 2s 0 -
+R c 1944 1945 - Ap M>=1 2s 1 S
+R c 1944 o - O 2 2s 0 -
+R c 1945 o - S 16 2s 0 -
+R c 1977 1980 - Ap Su>=1 2s 1 S
+R c 1977 o - S lastSu 2s 0 -
+R c 1978 o - O 1 2s 0 -
+R c 1979 1995 - S lastSu 2s 0 -
+R c 1981 ma - Mar lastSu 2s 1 S
+R c 1996 ma - O lastSu 2s 0 -
+R e 1977 1980 - Ap Su>=1 0 1 S
+R e 1977 o - S lastSu 0 0 -
+R e 1978 o - O 1 0 0 -
+R e 1979 1995 - S lastSu 0 0 -
+R e 1981 ma - Mar lastSu 0 1 S
+R e 1996 ma - O lastSu 0 0 -
+R R 1917 o - Jul 1 23 1 MST
+R R 1917 o - D 28 0 0 MMT
+R R 1918 o - May 31 22 2 MDST
+R R 1918 o - S 16 1 1 MST
+R R 1919 o - May 31 23 2 MDST
+R R 1919 o - Jul 1 0u 1 MSD
+R R 1919 o - Au 16 0 0 MSK
+R R 1921 o - F 14 23 1 MSD
+R R 1921 o - Mar 20 23 2 +05
+R R 1921 o - S 1 0 1 MSD
+R R 1921 o - O 1 0 0 -
+R R 1981 1984 - Ap 1 0 1 S
+R R 1981 1983 - O 1 0 0 -
+R R 1984 1995 - S lastSu 2s 0 -
+R R 1985 2010 - Mar lastSu 2s 1 S
+R R 1996 2010 - O lastSu 2s 0 -
+Z WET 0 E WE%sT
+Z CET 1 c CE%sT
+Z MET 1 c ME%sT
+Z EET 2 E EE%sT
+R q 1940 o - Jun 16 0 1 S
+R q 1942 o - N 2 3 0 -
+R q 1943 o - Mar 29 2 1 S
+R q 1943 o - Ap 10 3 0 -
+R q 1974 o - May 4 0 1 S
+R q 1974 o - O 2 0 0 -
+R q 1975 o - May 1 0 1 S
+R q 1975 o - O 2 0 0 -
+R q 1976 o - May 2 0 1 S
+R q 1976 o - O 3 0 0 -
+R q 1977 o - May 8 0 1 S
+R q 1977 o - O 2 0 0 -
+R q 1978 o - May 6 0 1 S
+R q 1978 o - O 1 0 0 -
+R q 1979 o - May 5 0 1 S
+R q 1979 o - S 30 0 0 -
+R q 1980 o - May 3 0 1 S
+R q 1980 o - O 4 0 0 -
+R q 1981 o - Ap 26 0 1 S
+R q 1981 o - S 27 0 0 -
+R q 1982 o - May 2 0 1 S
+R q 1982 o - O 3 0 0 -
+R q 1983 o - Ap 18 0 1 S
+R q 1983 o - O 1 0 0 -
+R q 1984 o - Ap 1 0 1 S
+Z Europe/Tirane 1:19:20 - LMT 1914
+1 - CET 1940 Jun 16
+1 q CE%sT 1984 Jul
+1 E CE%sT
+Z Europe/Andorra 0:6:4 - LMT 1901
+0 - WET 1946 S 30
+1 - CET 1985 Mar 31 2
+1 E CE%sT
+R a 1920 o - Ap 5 2s 1 S
+R a 1920 o - S 13 2s 0 -
+R a 1946 o - Ap 14 2s 1 S
+R a 1946 o - O 7 2s 0 -
+R a 1947 1948 - O Su>=1 2s 0 -
+R a 1947 o - Ap 6 2s 1 S
+R a 1948 o - Ap 18 2s 1 S
+R a 1980 o - Ap 6 0 1 S
+R a 1980 o - S 28 0 0 -
+Z Europe/Vienna 1:5:21 - LMT 1893 Ap
+1 c CE%sT 1920
+1 a CE%sT 1940 Ap 1 2s
+1 c CE%sT 1945 Ap 2 2s
+1 1 CEST 1945 Ap 12 2s
+1 - CET 1946
+1 a CE%sT 1981
+1 E CE%sT
+Z Europe/Minsk 1:50:16 - LMT 1880
+1:50 - MMT 1924 May 2
+2 - EET 1930 Jun 21
+3 - MSK 1941 Jun 28
+1 c CE%sT 1944 Jul 3
+3 R MSK/MSD 1990
+3 - MSK 1991 Mar 31 2s
+2 R EE%sT 2011 Mar 27 2s
+3 - +03
+R b 1918 o - Mar 9 0s 1 S
+R b 1918 1919 - O Sa>=1 23s 0 -
+R b 1919 o - Mar 1 23s 1 S
+R b 1920 o - F 14 23s 1 S
+R b 1920 o - O 23 23s 0 -
+R b 1921 o - Mar 14 23s 1 S
+R b 1921 o - O 25 23s 0 -
+R b 1922 o - Mar 25 23s 1 S
+R b 1922 1927 - O Sa>=1 23s 0 -
+R b 1923 o - Ap 21 23s 1 S
+R b 1924 o - Mar 29 23s 1 S
+R b 1925 o - Ap 4 23s 1 S
+R b 1926 o - Ap 17 23s 1 S
+R b 1927 o - Ap 9 23s 1 S
+R b 1928 o - Ap 14 23s 1 S
+R b 1928 1938 - O Su>=2 2s 0 -
+R b 1929 o - Ap 21 2s 1 S
+R b 1930 o - Ap 13 2s 1 S
+R b 1931 o - Ap 19 2s 1 S
+R b 1932 o - Ap 3 2s 1 S
+R b 1933 o - Mar 26 2s 1 S
+R b 1934 o - Ap 8 2s 1 S
+R b 1935 o - Mar 31 2s 1 S
+R b 1936 o - Ap 19 2s 1 S
+R b 1937 o - Ap 4 2s 1 S
+R b 1938 o - Mar 27 2s 1 S
+R b 1939 o - Ap 16 2s 1 S
+R b 1939 o - N 19 2s 0 -
+R b 1940 o - F 25 2s 1 S
+R b 1944 o - S 17 2s 0 -
+R b 1945 o - Ap 2 2s 1 S
+R b 1945 o - S 16 2s 0 -
+R b 1946 o - May 19 2s 1 S
+R b 1946 o - O 7 2s 0 -
+Z Europe/Brussels 0:17:30 - LMT 1880
+0:17:30 - BMT 1892 May 1 0:17:30
+0 - WET 1914 N 8
+1 - CET 1916 May
+1 c CE%sT 1918 N 11 11u
+0 b WE%sT 1940 May 20 2s
+1 c CE%sT 1944 S 3
+1 b CE%sT 1977
+1 E CE%sT
+R BG 1979 o - Mar 31 23 1 S
+R BG 1979 o - O 1 1 0 -
+R BG 1980 1982 - Ap Sa>=1 23 1 S
+R BG 1980 o - S 29 1 0 -
+R BG 1981 o - S 27 2 0 -
+Z Europe/Sofia 1:33:16 - LMT 1880
+1:56:56 - IMT 1894 N 30
+2 - EET 1942 N 2 3
+1 c CE%sT 1945
+1 - CET 1945 Ap 2 3
+2 - EET 1979 Mar 31 23
+2 BG EE%sT 1982 S 26 3
+2 c EE%sT 1991
+2 e EE%sT 1997
+2 E EE%sT
+R CZ 1945 o - Ap M>=1 2s 1 S
+R CZ 1945 o - O 1 2s 0 -
+R CZ 1946 o - May 6 2s 1 S
+R CZ 1946 1949 - O Su>=1 2s 0 -
+R CZ 1947 1948 - Ap Su>=15 2s 1 S
+R CZ 1949 o - Ap 9 2s 1 S
+Z Europe/Prague 0:57:44 - LMT 1850
+0:57:44 - PMT 1891 O
+1 c CE%sT 1945 May 9
+1 CZ CE%sT 1946 D 1 3
+1 -1 GMT 1947 F 23 2
+1 CZ CE%sT 1979
+1 E CE%sT
+R D 1916 o - May 14 23 1 S
+R D 1916 o - S 30 23 0 -
+R D 1940 o - May 15 0 1 S
+R D 1945 o - Ap 2 2s 1 S
+R D 1945 o - Au 15 2s 0 -
+R D 1946 o - May 1 2s 1 S
+R D 1946 o - S 1 2s 0 -
+R D 1947 o - May 4 2s 1 S
+R D 1947 o - Au 10 2s 0 -
+R D 1948 o - May 9 2s 1 S
+R D 1948 o - Au 8 2s 0 -
+Z Europe/Copenhagen 0:50:20 - LMT 1890
+0:50:20 - CMT 1894
+1 D CE%sT 1942 N 2 2s
+1 c CE%sT 1945 Ap 2 2
+1 D CE%sT 1980
+1 E CE%sT
+Z Atlantic/Faroe -0:27:4 - LMT 1908 Ja 11
+0 - WET 1981
+0 E WE%sT
+R Th 1991 1992 - Mar lastSu 2 1 D
+R Th 1991 1992 - S lastSu 2 0 S
+R Th 1993 2006 - Ap Su>=1 2 1 D
+R Th 1993 2006 - O lastSu 2 0 S
+R Th 2007 ma - Mar Su>=8 2 1 D
+R Th 2007 ma - N Su>=1 2 0 S
+Z America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28
+-3 - -03 1980 Ap 6 2
+-3 E -03/-02 1996
+0 - GMT
+Z America/Scoresbysund -1:27:52 - LMT 1916 Jul 28
+-2 - -02 1980 Ap 6 2
+-2 c -02/-01 1981 Mar 29
+-1 E -01/+00
+Z America/Nuuk -3:26:56 - LMT 1916 Jul 28
+-3 - -03 1980 Ap 6 2
+-3 E -03/-02
+Z America/Thule -4:35:8 - LMT 1916 Jul 28
+-4 Th A%sT
+Z Europe/Tallinn 1:39 - LMT 1880
+1:39 - TMT 1918 F
+1 c CE%sT 1919 Jul
+1:39 - TMT 1921 May
+2 - EET 1940 Au 6
+3 - MSK 1941 S 15
+1 c CE%sT 1944 S 22
+3 R MSK/MSD 1989 Mar 26 2s
+2 1 EEST 1989 S 24 2s
+2 c EE%sT 1998 S 22
+2 E EE%sT 1999 O 31 4
+2 - EET 2002 F 21
+2 E EE%sT
+R FI 1942 o - Ap 2 24 1 S
+R FI 1942 o - O 4 1 0 -
+R FI 1981 1982 - Mar lastSu 2 1 S
+R FI 1981 1982 - S lastSu 3 0 -
+Z Europe/Helsinki 1:39:49 - LMT 1878 May 31
+1:39:49 - HMT 1921 May
+2 FI EE%sT 1983
+2 E EE%sT
+L Europe/Helsinki Europe/Mariehamn
+R F 1916 o - Jun 14 23s 1 S
+R F 1916 1919 - O Su>=1 23s 0 -
+R F 1917 o - Mar 24 23s 1 S
+R F 1918 o - Mar 9 23s 1 S
+R F 1919 o - Mar 1 23s 1 S
+R F 1920 o - F 14 23s 1 S
+R F 1920 o - O 23 23s 0 -
+R F 1921 o - Mar 14 23s 1 S
+R F 1921 o - O 25 23s 0 -
+R F 1922 o - Mar 25 23s 1 S
+R F 1922 1938 - O Sa>=1 23s 0 -
+R F 1923 o - May 26 23s 1 S
+R F 1924 o - Mar 29 23s 1 S
+R F 1925 o - Ap 4 23s 1 S
+R F 1926 o - Ap 17 23s 1 S
+R F 1927 o - Ap 9 23s 1 S
+R F 1928 o - Ap 14 23s 1 S
+R F 1929 o - Ap 20 23s 1 S
+R F 1930 o - Ap 12 23s 1 S
+R F 1931 o - Ap 18 23s 1 S
+R F 1932 o - Ap 2 23s 1 S
+R F 1933 o - Mar 25 23s 1 S
+R F 1934 o - Ap 7 23s 1 S
+R F 1935 o - Mar 30 23s 1 S
+R F 1936 o - Ap 18 23s 1 S
+R F 1937 o - Ap 3 23s 1 S
+R F 1938 o - Mar 26 23s 1 S
+R F 1939 o - Ap 15 23s 1 S
+R F 1939 o - N 18 23s 0 -
+R F 1940 o - F 25 2 1 S
+R F 1941 o - May 5 0 2 M
+R F 1941 o - O 6 0 1 S
+R F 1942 o - Mar 9 0 2 M
+R F 1942 o - N 2 3 1 S
+R F 1943 o - Mar 29 2 2 M
+R F 1943 o - O 4 3 1 S
+R F 1944 o - Ap 3 2 2 M
+R F 1944 o - O 8 1 1 S
+R F 1945 o - Ap 2 2 2 M
+R F 1945 o - S 16 3 0 -
+R F 1976 o - Mar 28 1 1 S
+R F 1976 o - S 26 1 0 -
+Z Europe/Paris 0:9:21 - LMT 1891 Mar 16
+0:9:21 - PMT 1911 Mar 11
+0 F WE%sT 1940 Jun 14 23
+1 c CE%sT 1944 Au 25
+0 F WE%sT 1945 S 16 3
+1 F CE%sT 1977
+1 E CE%sT
+R DE 1946 o - Ap 14 2s 1 S
+R DE 1946 o - O 7 2s 0 -
+R DE 1947 1949 - O Su>=1 2s 0 -
+R DE 1947 o - Ap 6 3s 1 S
+R DE 1947 o - May 11 2s 2 M
+R DE 1947 o - Jun 29 3 1 S
+R DE 1948 o - Ap 18 2s 1 S
+R DE 1949 o - Ap 10 2s 1 S
+R So 1945 o - May 24 2 2 M
+R So 1945 o - S 24 3 1 S
+R So 1945 o - N 18 2s 0 -
+Z Europe/Berlin 0:53:28 - LMT 1893 Ap
+1 c CE%sT 1945 May 24 2
+1 So CE%sT 1946
+1 DE CE%sT 1980
+1 E CE%sT
+L Europe/Zurich Europe/Busingen
+Z Europe/Gibraltar -0:21:24 - LMT 1880 Au 2 0s
+0 G %s 1957 Ap 14 2
+1 - CET 1982
+1 E CE%sT
+R g 1932 o - Jul 7 0 1 S
+R g 1932 o - S 1 0 0 -
+R g 1941 o - Ap 7 0 1 S
+R g 1942 o - N 2 3 0 -
+R g 1943 o - Mar 30 0 1 S
+R g 1943 o - O 4 0 0 -
+R g 1952 o - Jul 1 0 1 S
+R g 1952 o - N 2 0 0 -
+R g 1975 o - Ap 12 0s 1 S
+R g 1975 o - N 26 0s 0 -
+R g 1976 o - Ap 11 2s 1 S
+R g 1976 o - O 10 2s 0 -
+R g 1977 1978 - Ap Su>=1 2s 1 S
+R g 1977 o - S 26 2s 0 -
+R g 1978 o - S 24 4 0 -
+R g 1979 o - Ap 1 9 1 S
+R g 1979 o - S 29 2 0 -
+R g 1980 o - Ap 1 0 1 S
+R g 1980 o - S 28 0 0 -
+Z Europe/Athens 1:34:52 - LMT 1895 S 14
+1:34:52 - AMT 1916 Jul 28 0:1
+2 g EE%sT 1941 Ap 30
+1 g CE%sT 1944 Ap 4
+2 g EE%sT 1981
+2 E EE%sT
+R h 1918 1919 - Ap 15 2 1 S
+R h 1918 1920 - S M>=15 3 0 -
+R h 1920 o - Ap 5 2 1 S
+R h 1945 o - May 1 23 1 S
+R h 1945 o - N 1 1 0 -
+R h 1946 o - Mar 31 2s 1 S
+R h 1946 o - O 7 2 0 -
+R h 1947 1949 - Ap Su>=4 2s 1 S
+R h 1947 1949 - O Su>=1 2s 0 -
+R h 1954 o - May 23 0 1 S
+R h 1954 o - O 3 0 0 -
+R h 1955 o - May 22 2 1 S
+R h 1955 o - O 2 3 0 -
+R h 1956 1957 - Jun Su>=1 2 1 S
+R h 1956 1957 - S lastSu 3 0 -
+R h 1980 o - Ap 6 0 1 S
+R h 1980 o - S 28 1 0 -
+R h 1981 1983 - Mar lastSu 0 1 S
+R h 1981 1983 - S lastSu 1 0 -
+Z Europe/Budapest 1:16:20 - LMT 1890 N
+1 c CE%sT 1918
+1 h CE%sT 1941 Ap 7 23
+1 c CE%sT 1945
+1 h CE%sT 1984
+1 E CE%sT
+R w 1917 1919 - F 19 23 1 -
+R w 1917 o - O 21 1 0 -
+R w 1918 1919 - N 16 1 0 -
+R w 1921 o - Mar 19 23 1 -
+R w 1921 o - Jun 23 1 0 -
+R w 1939 o - Ap 29 23 1 -
+R w 1939 o - O 29 2 0 -
+R w 1940 o - F 25 2 1 -
+R w 1940 1941 - N Su>=2 1s 0 -
+R w 1941 1942 - Mar Su>=2 1s 1 -
+R w 1943 1946 - Mar Su>=1 1s 1 -
+R w 1942 1948 - O Su>=22 1s 0 -
+R w 1947 1967 - Ap Su>=1 1s 1 -
+R w 1949 o - O 30 1s 0 -
+R w 1950 1966 - O Su>=22 1s 0 -
+R w 1967 o - O 29 1s 0 -
+Z Atlantic/Reykjavik -1:28 - LMT 1908
+-1 w -01/+00 1968 Ap 7 1s
+0 - GMT
+R I 1916 o - Jun 3 24 1 S
+R I 1916 1917 - S 30 24 0 -
+R I 1917 o - Mar 31 24 1 S
+R I 1918 o - Mar 9 24 1 S
+R I 1918 o - O 6 24 0 -
+R I 1919 o - Mar 1 24 1 S
+R I 1919 o - O 4 24 0 -
+R I 1920 o - Mar 20 24 1 S
+R I 1920 o - S 18 24 0 -
+R I 1940 o - Jun 14 24 1 S
+R I 1942 o - N 2 2s 0 -
+R I 1943 o - Mar 29 2s 1 S
+R I 1943 o - O 4 2s 0 -
+R I 1944 o - Ap 2 2s 1 S
+R I 1944 o - S 17 2s 0 -
+R I 1945 o - Ap 2 2 1 S
+R I 1945 o - S 15 1 0 -
+R I 1946 o - Mar 17 2s 1 S
+R I 1946 o - O 6 2s 0 -
+R I 1947 o - Mar 16 0s 1 S
+R I 1947 o - O 5 0s 0 -
+R I 1948 o - F 29 2s 1 S
+R I 1948 o - O 3 2s 0 -
+R I 1966 1968 - May Su>=22 0s 1 S
+R I 1966 o - S 24 24 0 -
+R I 1967 1969 - S Su>=22 0s 0 -
+R I 1969 o - Jun 1 0s 1 S
+R I 1970 o - May 31 0s 1 S
+R I 1970 o - S lastSu 0s 0 -
+R I 1971 1972 - May Su>=22 0s 1 S
+R I 1971 o - S lastSu 0s 0 -
+R I 1972 o - O 1 0s 0 -
+R I 1973 o - Jun 3 0s 1 S
+R I 1973 1974 - S lastSu 0s 0 -
+R I 1974 o - May 26 0s 1 S
+R I 1975 o - Jun 1 0s 1 S
+R I 1975 1977 - S lastSu 0s 0 -
+R I 1976 o - May 30 0s 1 S
+R I 1977 1979 - May Su>=22 0s 1 S
+R I 1978 o - O 1 0s 0 -
+R I 1979 o - S 30 0s 0 -
+Z Europe/Rome 0:49:56 - LMT 1866 D 12
+0:49:56 - RMT 1893 O 31 23:49:56
+1 I CE%sT 1943 S 10
+1 c CE%sT 1944 Jun 4
+1 I CE%sT 1980
+1 E CE%sT
+L Europe/Rome Europe/Vatican
+L Europe/Rome Europe/San_Marino
+R LV 1989 1996 - Mar lastSu 2s 1 S
+R LV 1989 1996 - S lastSu 2s 0 -
+Z Europe/Riga 1:36:34 - LMT 1880
+1:36:34 - RMT 1918 Ap 15 2
+1:36:34 1 LST 1918 S 16 3
+1:36:34 - RMT 1919 Ap 1 2
+1:36:34 1 LST 1919 May 22 3
+1:36:34 - RMT 1926 May 11
+2 - EET 1940 Au 5
+3 - MSK 1941 Jul
+1 c CE%sT 1944 O 13
+3 R MSK/MSD 1989 Mar lastSu 2s
+2 1 EEST 1989 S lastSu 2s
+2 LV EE%sT 1997 Ja 21
+2 E EE%sT 2000 F 29
+2 - EET 2001 Ja 2
+2 E EE%sT
+L Europe/Zurich Europe/Vaduz
+Z Europe/Vilnius 1:41:16 - LMT 1880
+1:24 - WMT 1917
+1:35:36 - KMT 1919 O 10
+1 - CET 1920 Jul 12
+2 - EET 1920 O 9
+1 - CET 1940 Au 3
+3 - MSK 1941 Jun 24
+1 c CE%sT 1944 Au
+3 R MSK/MSD 1989 Mar 26 2s
+2 R EE%sT 1991 S 29 2s
+2 c EE%sT 1998
+2 - EET 1998 Mar 29 1u
+1 E CE%sT 1999 O 31 1u
+2 - EET 2003
+2 E EE%sT
+R LX 1916 o - May 14 23 1 S
+R LX 1916 o - O 1 1 0 -
+R LX 1917 o - Ap 28 23 1 S
+R LX 1917 o - S 17 1 0 -
+R LX 1918 o - Ap M>=15 2s 1 S
+R LX 1918 o - S M>=15 2s 0 -
+R LX 1919 o - Mar 1 23 1 S
+R LX 1919 o - O 5 3 0 -
+R LX 1920 o - F 14 23 1 S
+R LX 1920 o - O 24 2 0 -
+R LX 1921 o - Mar 14 23 1 S
+R LX 1921 o - O 26 2 0 -
+R LX 1922 o - Mar 25 23 1 S
+R LX 1922 o - O Su>=2 1 0 -
+R LX 1923 o - Ap 21 23 1 S
+R LX 1923 o - O Su>=2 2 0 -
+R LX 1924 o - Mar 29 23 1 S
+R LX 1924 1928 - O Su>=2 1 0 -
+R LX 1925 o - Ap 5 23 1 S
+R LX 1926 o - Ap 17 23 1 S
+R LX 1927 o - Ap 9 23 1 S
+R LX 1928 o - Ap 14 23 1 S
+R LX 1929 o - Ap 20 23 1 S
+Z Europe/Luxembourg 0:24:36 - LMT 1904 Jun
+1 LX CE%sT 1918 N 25
+0 LX WE%sT 1929 O 6 2s
+0 b WE%sT 1940 May 14 3
+1 c WE%sT 1944 S 18 3
+1 b CE%sT 1977
+1 E CE%sT
+R MT 1973 o - Mar 31 0s 1 S
+R MT 1973 o - S 29 0s 0 -
+R MT 1974 o - Ap 21 0s 1 S
+R MT 1974 o - S 16 0s 0 -
+R MT 1975 1979 - Ap Su>=15 2 1 S
+R MT 1975 1980 - S Su>=15 2 0 -
+R MT 1980 o - Mar 31 2 1 S
+Z Europe/Malta 0:58:4 - LMT 1893 N 2 0s
+1 I CE%sT 1973 Mar 31
+1 MT CE%sT 1981
+1 E CE%sT
+R MD 1997 ma - Mar lastSu 2 1 S
+R MD 1997 ma - O lastSu 3 0 -
+Z Europe/Chisinau 1:55:20 - LMT 1880
+1:55 - CMT 1918 F 15
+1:44:24 - BMT 1931 Jul 24
+2 z EE%sT 1940 Au 15
+2 1 EEST 1941 Jul 17
+1 c CE%sT 1944 Au 24
+3 R MSK/MSD 1990 May 6 2
+2 R EE%sT 1992
+2 e EE%sT 1997
+2 MD EE%sT
+Z Europe/Monaco 0:29:32 - LMT 1892 Jun
+0:9:21 - PMT 1911 Mar 29
+0 F WE%sT 1945 S 16 3
+1 F CE%sT 1977
+1 E CE%sT
+R N 1916 o - May 1 0 1 NST
+R N 1916 o - O 1 0 0 AMT
+R N 1917 o - Ap 16 2s 1 NST
+R N 1917 o - S 17 2s 0 AMT
+R N 1918 1921 - Ap M>=1 2s 1 NST
+R N 1918 1921 - S lastM 2s 0 AMT
+R N 1922 o - Mar lastSu 2s 1 NST
+R N 1922 1936 - O Su>=2 2s 0 AMT
+R N 1923 o - Jun F>=1 2s 1 NST
+R N 1924 o - Mar lastSu 2s 1 NST
+R N 1925 o - Jun F>=1 2s 1 NST
+R N 1926 1931 - May 15 2s 1 NST
+R N 1932 o - May 22 2s 1 NST
+R N 1933 1936 - May 15 2s 1 NST
+R N 1937 o - May 22 2s 1 NST
+R N 1937 o - Jul 1 0 1 S
+R N 1937 1939 - O Su>=2 2s 0 -
+R N 1938 1939 - May 15 2s 1 S
+R N 1945 o - Ap 2 2s 1 S
+R N 1945 o - S 16 2s 0 -
+Z Europe/Amsterdam 0:19:32 - LMT 1835
+0:19:32 N %s 1937 Jul
+0:20 N +0020/+0120 1940 May 16
+1 c CE%sT 1945 Ap 2 2
+1 N CE%sT 1977
+1 E CE%sT
+R NO 1916 o - May 22 1 1 S
+R NO 1916 o - S 30 0 0 -
+R NO 1945 o - Ap 2 2s 1 S
+R NO 1945 o - O 1 2s 0 -
+R NO 1959 1964 - Mar Su>=15 2s 1 S
+R NO 1959 1965 - S Su>=15 2s 0 -
+R NO 1965 o - Ap 25 2s 1 S
+Z Europe/Oslo 0:43 - LMT 1895
+1 NO CE%sT 1940 Au 10 23
+1 c CE%sT 1945 Ap 2 2
+1 NO CE%sT 1980
+1 E CE%sT
+L Europe/Oslo Arctic/Longyearbyen
+R O 1918 1919 - S 16 2s 0 -
+R O 1919 o - Ap 15 2s 1 S
+R O 1944 o - Ap 3 2s 1 S
+R O 1944 o - O 4 2 0 -
+R O 1945 o - Ap 29 0 1 S
+R O 1945 o - N 1 0 0 -
+R O 1946 o - Ap 14 0s 1 S
+R O 1946 o - O 7 2s 0 -
+R O 1947 o - May 4 2s 1 S
+R O 1947 1949 - O Su>=1 2s 0 -
+R O 1948 o - Ap 18 2s 1 S
+R O 1949 o - Ap 10 2s 1 S
+R O 1957 o - Jun 2 1s 1 S
+R O 1957 1958 - S lastSu 1s 0 -
+R O 1958 o - Mar 30 1s 1 S
+R O 1959 o - May 31 1s 1 S
+R O 1959 1961 - O Su>=1 1s 0 -
+R O 1960 o - Ap 3 1s 1 S
+R O 1961 1964 - May lastSu 1s 1 S
+R O 1962 1964 - S lastSu 1s 0 -
+Z Europe/Warsaw 1:24 - LMT 1880
+1:24 - WMT 1915 Au 5
+1 c CE%sT 1918 S 16 3
+2 O EE%sT 1922 Jun
+1 O CE%sT 1940 Jun 23 2
+1 c CE%sT 1944 O
+1 O CE%sT 1977
+1 W- CE%sT 1988
+1 E CE%sT
+R p 1916 o - Jun 17 23 1 S
+R p 1916 o - N 1 1 0 -
+R p 1917 o - F 28 23s 1 S
+R p 1917 1921 - O 14 23s 0 -
+R p 1918 o - Mar 1 23s 1 S
+R p 1919 o - F 28 23s 1 S
+R p 1920 o - F 29 23s 1 S
+R p 1921 o - F 28 23s 1 S
+R p 1924 o - Ap 16 23s 1 S
+R p 1924 o - O 14 23s 0 -
+R p 1926 o - Ap 17 23s 1 S
+R p 1926 1929 - O Sa>=1 23s 0 -
+R p 1927 o - Ap 9 23s 1 S
+R p 1928 o - Ap 14 23s 1 S
+R p 1929 o - Ap 20 23s 1 S
+R p 1931 o - Ap 18 23s 1 S
+R p 1931 1932 - O Sa>=1 23s 0 -
+R p 1932 o - Ap 2 23s 1 S
+R p 1934 o - Ap 7 23s 1 S
+R p 1934 1938 - O Sa>=1 23s 0 -
+R p 1935 o - Mar 30 23s 1 S
+R p 1936 o - Ap 18 23s 1 S
+R p 1937 o - Ap 3 23s 1 S
+R p 1938 o - Mar 26 23s 1 S
+R p 1939 o - Ap 15 23s 1 S
+R p 1939 o - N 18 23s 0 -
+R p 1940 o - F 24 23s 1 S
+R p 1940 1941 - O 5 23s 0 -
+R p 1941 o - Ap 5 23s 1 S
+R p 1942 1945 - Mar Sa>=8 23s 1 S
+R p 1942 o - Ap 25 22s 2 M
+R p 1942 o - Au 15 22s 1 S
+R p 1942 1945 - O Sa>=24 23s 0 -
+R p 1943 o - Ap 17 22s 2 M
+R p 1943 1945 - Au Sa>=25 22s 1 S
+R p 1944 1945 - Ap Sa>=21 22s 2 M
+R p 1946 o - Ap Sa>=1 23s 1 S
+R p 1946 o - O Sa>=1 23s 0 -
+R p 1947 1965 - Ap Su>=1 2s 1 S
+R p 1947 1965 - O Su>=1 2s 0 -
+R p 1977 o - Mar 27 0s 1 S
+R p 1977 o - S 25 0s 0 -
+R p 1978 1979 - Ap Su>=1 0s 1 S
+R p 1978 o - O 1 0s 0 -
+R p 1979 1982 - S lastSu 1s 0 -
+R p 1980 o - Mar lastSu 0s 1 S
+R p 1981 1982 - Mar lastSu 1s 1 S
+R p 1983 o - Mar lastSu 2s 1 S
+Z Europe/Lisbon -0:36:45 - LMT 1884
+-0:36:45 - LMT 1912 Ja 1 0u
+0 p WE%sT 1966 Ap 3 2
+1 - CET 1976 S 26 1
+0 p WE%sT 1983 S 25 1s
+0 W- WE%sT 1992 S 27 1s
+1 E CE%sT 1996 Mar 31 1u
+0 E WE%sT
+Z Atlantic/Azores -1:42:40 - LMT 1884
+-1:54:32 - HMT 1912 Ja 1 2u
+-2 p -02/-01 1942 Ap 25 22s
+-2 p +00 1942 Au 15 22s
+-2 p -02/-01 1943 Ap 17 22s
+-2 p +00 1943 Au 28 22s
+-2 p -02/-01 1944 Ap 22 22s
+-2 p +00 1944 Au 26 22s
+-2 p -02/-01 1945 Ap 21 22s
+-2 p +00 1945 Au 25 22s
+-2 p -02/-01 1966 Ap 3 2
+-1 p -01/+00 1983 S 25 1s
+-1 W- -01/+00 1992 S 27 1s
+0 E WE%sT 1993 Mar 28 1u
+-1 E -01/+00
+Z Atlantic/Madeira -1:7:36 - LMT 1884
+-1:7:36 - FMT 1912 Ja 1 1u
+-1 p -01/+00 1942 Ap 25 22s
+-1 p +01 1942 Au 15 22s
+-1 p -01/+00 1943 Ap 17 22s
+-1 p +01 1943 Au 28 22s
+-1 p -01/+00 1944 Ap 22 22s
+-1 p +01 1944 Au 26 22s
+-1 p -01/+00 1945 Ap 21 22s
+-1 p +01 1945 Au 25 22s
+-1 p -01/+00 1966 Ap 3 2
+0 p WE%sT 1983 S 25 1s
+0 E WE%sT
+R z 1932 o - May 21 0s 1 S
+R z 1932 1939 - O Su>=1 0s 0 -
+R z 1933 1939 - Ap Su>=2 0s 1 S
+R z 1979 o - May 27 0 1 S
+R z 1979 o - S lastSu 0 0 -
+R z 1980 o - Ap 5 23 1 S
+R z 1980 o - S lastSu 1 0 -
+R z 1991 1993 - Mar lastSu 0s 1 S
+R z 1991 1993 - S lastSu 0s 0 -
+Z Europe/Bucharest 1:44:24 - LMT 1891 O
+1:44:24 - BMT 1931 Jul 24
+2 z EE%sT 1981 Mar 29 2s
+2 c EE%sT 1991
+2 z EE%sT 1994
+2 e EE%sT 1997
+2 E EE%sT
+Z Europe/Kaliningrad 1:22 - LMT 1893 Ap
+1 c CE%sT 1945 Ap 10
+2 O EE%sT 1946 Ap 7
+3 R MSK/MSD 1989 Mar 26 2s
+2 R EE%sT 2011 Mar 27 2s
+3 - +03 2014 O 26 2s
+2 - EET
+Z Europe/Moscow 2:30:17 - LMT 1880
+2:30:17 - MMT 1916 Jul 3
+2:31:19 R %s 1919 Jul 1 0u
+3 R %s 1921 O
+3 R MSK/MSD 1922 O
+2 - EET 1930 Jun 21
+3 R MSK/MSD 1991 Mar 31 2s
+2 R EE%sT 1992 Ja 19 2s
+3 R MSK/MSD 2011 Mar 27 2s
+4 - MSK 2014 O 26 2s
+3 - MSK
+Z Europe/Simferopol 2:16:24 - LMT 1880
+2:16 - SMT 1924 May 2
+2 - EET 1930 Jun 21
+3 - MSK 1941 N
+1 c CE%sT 1944 Ap 13
+3 R MSK/MSD 1990
+3 - MSK 1990 Jul 1 2
+2 - EET 1992 Mar 20
+2 c EE%sT 1994 May
+3 e MSK/MSD 1996 Mar 31 0s
+3 1 MSD 1996 O 27 3s
+3 R MSK/MSD 1997
+3 - MSK 1997 Mar lastSu 1u
+2 E EE%sT 2014 Mar 30 2
+4 - MSK 2014 O 26 2s
+3 - MSK
+Z Europe/Astrakhan 3:12:12 - LMT 1924 May
+3 - +03 1930 Jun 21
+4 R +04/+05 1989 Mar 26 2s
+3 R +03/+04 1991 Mar 31 2s
+4 - +04 1992 Mar 29 2s
+3 R +03/+04 2011 Mar 27 2s
+4 - +04 2014 O 26 2s
+3 - +03 2016 Mar 27 2s
+4 - +04
+Z Europe/Volgograd 2:57:40 - LMT 1920 Ja 3
+3 - +03 1930 Jun 21
+4 - +04 1961 N 11
+4 R +04/+05 1988 Mar 27 2s
+3 R +03/+04 1991 Mar 31 2s
+4 - +04 1992 Mar 29 2s
+3 R +03/+04 2011 Mar 27 2s
+4 - +04 2014 O 26 2s
+3 - +03 2018 O 28 2s
+4 - +04 2020 D 27 2s
+3 - +03
+Z Europe/Saratov 3:4:18 - LMT 1919 Jul 1 0u
+3 - +03 1930 Jun 21
+4 R +04/+05 1988 Mar 27 2s
+3 R +03/+04 1991 Mar 31 2s
+4 - +04 1992 Mar 29 2s
+3 R +03/+04 2011 Mar 27 2s
+4 - +04 2014 O 26 2s
+3 - +03 2016 D 4 2s
+4 - +04
+Z Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0u
+3 - +03 1930 Jun 21
+4 R +04/+05 1989 Mar 26 2s
+3 R +03/+04 1991 Mar 31 2s
+4 - +04 1992 Mar 29 2s
+3 R +03/+04 2011 Mar 27 2s
+4 - +04 2014 O 26 2s
+3 - +03
+Z Europe/Samara 3:20:20 - LMT 1919 Jul 1 0u
+3 - +03 1930 Jun 21
+4 - +04 1935 Ja 27
+4 R +04/+05 1989 Mar 26 2s
+3 R +03/+04 1991 Mar 31 2s
+2 R +02/+03 1991 S 29 2s
+3 - +03 1991 O 20 3
+4 R +04/+05 2010 Mar 28 2s
+3 R +03/+04 2011 Mar 27 2s
+4 - +04
+Z Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0u
+3 - +03 1930 Jun 21
+4 R +04/+05 1989 Mar 26 2s
+3 R +03/+04 1991 Mar 31 2s
+2 R +02/+03 1992 Ja 19 2s
+3 R +03/+04 2011 Mar 27 2s
+4 - +04 2014 O 26 2s
+3 - +03 2016 Mar 27 2s
+4 - +04
+Z Asia/Yekaterinburg 4:2:33 - LMT 1916 Jul 3
+3:45:5 - PMT 1919 Jul 15 4
+4 - +04 1930 Jun 21
+5 R +05/+06 1991 Mar 31 2s
+4 R +04/+05 1992 Ja 19 2s
+5 R +05/+06 2011 Mar 27 2s
+6 - +06 2014 O 26 2s
+5 - +05
+Z Asia/Omsk 4:53:30 - LMT 1919 N 14
+5 - +05 1930 Jun 21
+6 R +06/+07 1991 Mar 31 2s
+5 R +05/+06 1992 Ja 19 2s
+6 R +06/+07 2011 Mar 27 2s
+7 - +07 2014 O 26 2s
+6 - +06
+Z Asia/Barnaul 5:35 - LMT 1919 D 10
+6 - +06 1930 Jun 21
+7 R +07/+08 1991 Mar 31 2s
+6 R +06/+07 1992 Ja 19 2s
+7 R +07/+08 1995 May 28
+6 R +06/+07 2011 Mar 27 2s
+7 - +07 2014 O 26 2s
+6 - +06 2016 Mar 27 2s
+7 - +07
+Z Asia/Novosibirsk 5:31:40 - LMT 1919 D 14 6
+6 - +06 1930 Jun 21
+7 R +07/+08 1991 Mar 31 2s
+6 R +06/+07 1992 Ja 19 2s
+7 R +07/+08 1993 May 23
+6 R +06/+07 2011 Mar 27 2s
+7 - +07 2014 O 26 2s
+6 - +06 2016 Jul 24 2s
+7 - +07
+Z Asia/Tomsk 5:39:51 - LMT 1919 D 22
+6 - +06 1930 Jun 21
+7 R +07/+08 1991 Mar 31 2s
+6 R +06/+07 1992 Ja 19 2s
+7 R +07/+08 2002 May 1 3
+6 R +06/+07 2011 Mar 27 2s
+7 - +07 2014 O 26 2s
+6 - +06 2016 May 29 2s
+7 - +07
+Z Asia/Novokuznetsk 5:48:48 - LMT 1924 May
+6 - +06 1930 Jun 21
+7 R +07/+08 1991 Mar 31 2s
+6 R +06/+07 1992 Ja 19 2s
+7 R +07/+08 2010 Mar 28 2s
+6 R +06/+07 2011 Mar 27 2s
+7 - +07
+Z Asia/Krasnoyarsk 6:11:26 - LMT 1920 Ja 6
+6 - +06 1930 Jun 21
+7 R +07/+08 1991 Mar 31 2s
+6 R +06/+07 1992 Ja 19 2s
+7 R +07/+08 2011 Mar 27 2s
+8 - +08 2014 O 26 2s
+7 - +07
+Z Asia/Irkutsk 6:57:5 - LMT 1880
+6:57:5 - IMT 1920 Ja 25
+7 - +07 1930 Jun 21
+8 R +08/+09 1991 Mar 31 2s
+7 R +07/+08 1992 Ja 19 2s
+8 R +08/+09 2011 Mar 27 2s
+9 - +09 2014 O 26 2s
+8 - +08
+Z Asia/Chita 7:33:52 - LMT 1919 D 15
+8 - +08 1930 Jun 21
+9 R +09/+10 1991 Mar 31 2s
+8 R +08/+09 1992 Ja 19 2s
+9 R +09/+10 2011 Mar 27 2s
+10 - +10 2014 O 26 2s
+8 - +08 2016 Mar 27 2
+9 - +09
+Z Asia/Yakutsk 8:38:58 - LMT 1919 D 15
+8 - +08 1930 Jun 21
+9 R +09/+10 1991 Mar 31 2s
+8 R +08/+09 1992 Ja 19 2s
+9 R +09/+10 2011 Mar 27 2s
+10 - +10 2014 O 26 2s
+9 - +09
+Z Asia/Vladivostok 8:47:31 - LMT 1922 N 15
+9 - +09 1930 Jun 21
+10 R +10/+11 1991 Mar 31 2s
+9 R +09/+10 1992 Ja 19 2s
+10 R +10/+11 2011 Mar 27 2s
+11 - +11 2014 O 26 2s
+10 - +10
+Z Asia/Khandyga 9:2:13 - LMT 1919 D 15
+8 - +08 1930 Jun 21
+9 R +09/+10 1991 Mar 31 2s
+8 R +08/+09 1992 Ja 19 2s
+9 R +09/+10 2004
+10 R +10/+11 2011 Mar 27 2s
+11 - +11 2011 S 13 0s
+10 - +10 2014 O 26 2s
+9 - +09
+Z Asia/Sakhalin 9:30:48 - LMT 1905 Au 23
+9 - +09 1945 Au 25
+11 R +11/+12 1991 Mar 31 2s
+10 R +10/+11 1992 Ja 19 2s
+11 R +11/+12 1997 Mar lastSu 2s
+10 R +10/+11 2011 Mar 27 2s
+11 - +11 2014 O 26 2s
+10 - +10 2016 Mar 27 2s
+11 - +11
+Z Asia/Magadan 10:3:12 - LMT 1924 May 2
+10 - +10 1930 Jun 21
+11 R +11/+12 1991 Mar 31 2s
+10 R +10/+11 1992 Ja 19 2s
+11 R +11/+12 2011 Mar 27 2s
+12 - +12 2014 O 26 2s
+10 - +10 2016 Ap 24 2s
+11 - +11
+Z Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2
+10 - +10 1930 Jun 21
+11 R +11/+12 1991 Mar 31 2s
+10 R +10/+11 1992 Ja 19 2s
+11 R +11/+12 2011 Mar 27 2s
+12 - +12 2014 O 26 2s
+11 - +11
+Z Asia/Ust-Nera 9:32:54 - LMT 1919 D 15
+8 - +08 1930 Jun 21
+9 R +09/+10 1981 Ap
+11 R +11/+12 1991 Mar 31 2s
+10 R +10/+11 1992 Ja 19 2s
+11 R +11/+12 2011 Mar 27 2s
+12 - +12 2011 S 13 0s
+11 - +11 2014 O 26 2s
+10 - +10
+Z Asia/Kamchatka 10:34:36 - LMT 1922 N 10
+11 - +11 1930 Jun 21
+12 R +12/+13 1991 Mar 31 2s
+11 R +11/+12 1992 Ja 19 2s
+12 R +12/+13 2010 Mar 28 2s
+11 R +11/+12 2011 Mar 27 2s
+12 - +12
+Z Asia/Anadyr 11:49:56 - LMT 1924 May 2
+12 - +12 1930 Jun 21
+13 R +13/+14 1982 Ap 1 0s
+12 R +12/+13 1991 Mar 31 2s
+11 R +11/+12 1992 Ja 19 2s
+12 R +12/+13 2010 Mar 28 2s
+11 R +11/+12 2011 Mar 27 2s
+12 - +12
+Z Europe/Belgrade 1:22 - LMT 1884
+1 - CET 1941 Ap 18 23
+1 c CE%sT 1945
+1 - CET 1945 May 8 2s
+1 1 CEST 1945 S 16 2s
+1 - CET 1982 N 27
+1 E CE%sT
+L Europe/Belgrade Europe/Ljubljana
+L Europe/Belgrade Europe/Podgorica
+L Europe/Belgrade Europe/Sarajevo
+L Europe/Belgrade Europe/Skopje
+L Europe/Belgrade Europe/Zagreb
+L Europe/Prague Europe/Bratislava
+R s 1918 o - Ap 15 23 1 S
+R s 1918 1919 - O 6 24s 0 -
+R s 1919 o - Ap 6 23 1 S
+R s 1924 o - Ap 16 23 1 S
+R s 1924 o - O 4 24s 0 -
+R s 1926 o - Ap 17 23 1 S
+R s 1926 1929 - O Sa>=1 24s 0 -
+R s 1927 o - Ap 9 23 1 S
+R s 1928 o - Ap 15 0 1 S
+R s 1929 o - Ap 20 23 1 S
+R s 1937 o - Jun 16 23 1 S
+R s 1937 o - O 2 24s 0 -
+R s 1938 o - Ap 2 23 1 S
+R s 1938 o - Ap 30 23 2 M
+R s 1938 o - O 2 24 1 S
+R s 1939 o - O 7 24s 0 -
+R s 1942 o - May 2 23 1 S
+R s 1942 o - S 1 1 0 -
+R s 1943 1946 - Ap Sa>=13 23 1 S
+R s 1943 1944 - O Su>=1 1 0 -
+R s 1945 1946 - S lastSu 1 0 -
+R s 1949 o - Ap 30 23 1 S
+R s 1949 o - O 2 1 0 -
+R s 1974 1975 - Ap Sa>=12 23 1 S
+R s 1974 1975 - O Su>=1 1 0 -
+R s 1976 o - Mar 27 23 1 S
+R s 1976 1977 - S lastSu 1 0 -
+R s 1977 o - Ap 2 23 1 S
+R s 1978 o - Ap 2 2s 1 S
+R s 1978 o - O 1 2s 0 -
+R Sp 1967 o - Jun 3 12 1 S
+R Sp 1967 o - O 1 0 0 -
+R Sp 1974 o - Jun 24 0 1 S
+R Sp 1974 o - S 1 0 0 -
+R Sp 1976 1977 - May 1 0 1 S
+R Sp 1976 o - Au 1 0 0 -
+R Sp 1977 o - S 28 0 0 -
+R Sp 1978 o - Jun 1 0 1 S
+R Sp 1978 o - Au 4 0 0 -
+Z Europe/Madrid -0:14:44 - LMT 1900 D 31 23:45:16
+0 s WE%sT 1940 Mar 16 23
+1 s CE%sT 1979
+1 E CE%sT
+Z Africa/Ceuta -0:21:16 - LMT 1900 D 31 23:38:44
+0 - WET 1918 May 6 23
+0 1 WEST 1918 O 7 23
+0 - WET 1924
+0 s WE%sT 1929
+0 - WET 1967
+0 Sp WE%sT 1984 Mar 16
+1 - CET 1986
+1 E CE%sT
+Z Atlantic/Canary -1:1:36 - LMT 1922 Mar
+-1 - -01 1946 S 30 1
+0 - WET 1980 Ap 6 0s
+0 1 WEST 1980 S 28 1u
+0 E WE%sT
+Z Europe/Stockholm 1:12:12 - LMT 1879
+1:0:14 - SET 1900
+1 - CET 1916 May 14 23
+1 1 CEST 1916 O 1 1
+1 - CET 1980
+1 E CE%sT
+R CH 1941 1942 - May M>=1 1 1 S
+R CH 1941 1942 - O M>=1 2 0 -
+Z Europe/Zurich 0:34:8 - LMT 1853 Jul 16
+0:29:46 - BMT 1894 Jun
+1 CH CE%sT 1981
+1 E CE%sT
+R T 1916 o - May 1 0 1 S
+R T 1916 o - O 1 0 0 -
+R T 1920 o - Mar 28 0 1 S
+R T 1920 o - O 25 0 0 -
+R T 1921 o - Ap 3 0 1 S
+R T 1921 o - O 3 0 0 -
+R T 1922 o - Mar 26 0 1 S
+R T 1922 o - O 8 0 0 -
+R T 1924 o - May 13 0 1 S
+R T 1924 1925 - O 1 0 0 -
+R T 1925 o - May 1 0 1 S
+R T 1940 o - Jul 1 0 1 S
+R T 1940 o - O 6 0 0 -
+R T 1940 o - D 1 0 1 S
+R T 1941 o - S 21 0 0 -
+R T 1942 o - Ap 1 0 1 S
+R T 1945 o - O 8 0 0 -
+R T 1946 o - Jun 1 0 1 S
+R T 1946 o - O 1 0 0 -
+R T 1947 1948 - Ap Su>=16 0 1 S
+R T 1947 1951 - O Su>=2 0 0 -
+R T 1949 o - Ap 10 0 1 S
+R T 1950 o - Ap 16 0 1 S
+R T 1951 o - Ap 22 0 1 S
+R T 1962 o - Jul 15 0 1 S
+R T 1963 o - O 30 0 0 -
+R T 1964 o - May 15 0 1 S
+R T 1964 o - O 1 0 0 -
+R T 1973 o - Jun 3 1 1 S
+R T 1973 1976 - O Su>=31 2 0 -
+R T 1974 o - Mar 31 2 1 S
+R T 1975 o - Mar 22 2 1 S
+R T 1976 o - Mar 21 2 1 S
+R T 1977 1978 - Ap Su>=1 2 1 S
+R T 1977 1978 - O Su>=15 2 0 -
+R T 1978 o - Jun 29 0 0 -
+R T 1983 o - Jul 31 2 1 S
+R T 1983 o - O 2 2 0 -
+R T 1985 o - Ap 20 1s 1 S
+R T 1985 o - S 28 1s 0 -
+R T 1986 1993 - Mar lastSu 1s 1 S
+R T 1986 1995 - S lastSu 1s 0 -
+R T 1994 o - Mar 20 1s 1 S
+R T 1995 2006 - Mar lastSu 1s 1 S
+R T 1996 2006 - O lastSu 1s 0 -
+Z Europe/Istanbul 1:55:52 - LMT 1880
+1:56:56 - IMT 1910 O
+2 T EE%sT 1978 Jun 29
+3 T +03/+04 1984 N 1 2
+2 T EE%sT 2007
+2 E EE%sT 2011 Mar 27 1u
+2 - EET 2011 Mar 28 1u
+2 E EE%sT 2014 Mar 30 1u
+2 - EET 2014 Mar 31 1u
+2 E EE%sT 2015 O 25 1u
+2 1 EEST 2015 N 8 1u
+2 E EE%sT 2016 S 7
+3 - +03
+L Europe/Istanbul Asia/Istanbul
+Z Europe/Kiev 2:2:4 - LMT 1880
+2:2:4 - KMT 1924 May 2
+2 - EET 1930 Jun 21
+3 - MSK 1941 S 20
+1 c CE%sT 1943 N 6
+3 R MSK/MSD 1990 Jul 1 2
+2 1 EEST 1991 S 29 3
+2 c EE%sT 1996 May 13
+2 E EE%sT
+Z Europe/Uzhgorod 1:29:12 - LMT 1890 O
+1 - CET 1940
+1 c CE%sT 1944 O
+1 1 CEST 1944 O 26
+1 - CET 1945 Jun 29
+3 R MSK/MSD 1990
+3 - MSK 1990 Jul 1 2
+1 - CET 1991 Mar 31 3
+2 - EET 1992 Mar 20
+2 c EE%sT 1996 May 13
+2 E EE%sT
+Z Europe/Zaporozhye 2:20:40 - LMT 1880
+2:20 - +0220 1924 May 2
+2 - EET 1930 Jun 21
+3 - MSK 1941 Au 25
+1 c CE%sT 1943 O 25
+3 R MSK/MSD 1991 Mar 31 2
+2 e EE%sT 1992 Mar 20
+2 c EE%sT 1996 May 13
+2 E EE%sT
+R u 1918 1919 - Mar lastSu 2 1 D
+R u 1918 1919 - O lastSu 2 0 S
+R u 1942 o - F 9 2 1 W
+R u 1945 o - Au 14 23u 1 P
+R u 1945 o - S 30 2 0 S
+R u 1967 2006 - O lastSu 2 0 S
+R u 1967 1973 - Ap lastSu 2 1 D
+R u 1974 o - Ja 6 2 1 D
+R u 1975 o - F lastSu 2 1 D
+R u 1976 1986 - Ap lastSu 2 1 D
+R u 1987 2006 - Ap Su>=1 2 1 D
+R u 2007 ma - Mar Su>=8 2 1 D
+R u 2007 ma - N Su>=1 2 0 S
+Z EST -5 - EST
+Z MST -7 - MST
+Z HST -10 - HST
+Z EST5EDT -5 u E%sT
+Z CST6CDT -6 u C%sT
+Z MST7MDT -7 u M%sT
+Z PST8PDT -8 u P%sT
+R NY 1920 o - Mar lastSu 2 1 D
+R NY 1920 o - O lastSu 2 0 S
+R NY 1921 1966 - Ap lastSu 2 1 D
+R NY 1921 1954 - S lastSu 2 0 S
+R NY 1955 1966 - O lastSu 2 0 S
+Z America/New_York -4:56:2 - LMT 1883 N 18 12:3:58
+-5 u E%sT 1920
+-5 NY E%sT 1942
+-5 u E%sT 1946
+-5 NY E%sT 1967
+-5 u E%sT
+R Ch 1920 o - Jun 13 2 1 D
+R Ch 1920 1921 - O lastSu 2 0 S
+R Ch 1921 o - Mar lastSu 2 1 D
+R Ch 1922 1966 - Ap lastSu 2 1 D
+R Ch 1922 1954 - S lastSu 2 0 S
+R Ch 1955 1966 - O lastSu 2 0 S
+Z America/Chicago -5:50:36 - LMT 1883 N 18 12:9:24
+-6 u C%sT 1920
+-6 Ch C%sT 1936 Mar 1 2
+-5 - EST 1936 N 15 2
+-6 Ch C%sT 1942
+-6 u C%sT 1946
+-6 Ch C%sT 1967
+-6 u C%sT
+Z America/North_Dakota/Center -6:45:12 - LMT 1883 N 18 12:14:48
+-7 u M%sT 1992 O 25 2
+-6 u C%sT
+Z America/North_Dakota/New_Salem -6:45:39 - LMT 1883 N 18 12:14:21
+-7 u M%sT 2003 O 26 2
+-6 u C%sT
+Z America/North_Dakota/Beulah -6:47:7 - LMT 1883 N 18 12:12:53
+-7 u M%sT 2010 N 7 2
+-6 u C%sT
+R De 1920 1921 - Mar lastSu 2 1 D
+R De 1920 o - O lastSu 2 0 S
+R De 1921 o - May 22 2 0 S
+R De 1965 1966 - Ap lastSu 2 1 D
+R De 1965 1966 - O lastSu 2 0 S
+Z America/Denver -6:59:56 - LMT 1883 N 18 12:0:4
+-7 u M%sT 1920
+-7 De M%sT 1942
+-7 u M%sT 1946
+-7 De M%sT 1967
+-7 u M%sT
+R CA 1948 o - Mar 14 2:1 1 D
+R CA 1949 o - Ja 1 2 0 S
+R CA 1950 1966 - Ap lastSu 1 1 D
+R CA 1950 1961 - S lastSu 2 0 S
+R CA 1962 1966 - O lastSu 2 0 S
+Z America/Los_Angeles -7:52:58 - LMT 1883 N 18 12:7:2
+-8 u P%sT 1946
+-8 CA P%sT 1967
+-8 u P%sT
+Z America/Juneau 15:2:19 - LMT 1867 O 19 15:33:32
+-8:57:41 - LMT 1900 Au 20 12
+-8 - PST 1942
+-8 u P%sT 1946
+-8 - PST 1969
+-8 u P%sT 1980 Ap 27 2
+-9 u Y%sT 1980 O 26 2
+-8 u P%sT 1983 O 30 2
+-9 u Y%sT 1983 N 30
+-9 u AK%sT
+Z America/Sitka 14:58:47 - LMT 1867 O 19 15:30
+-9:1:13 - LMT 1900 Au 20 12
+-8 - PST 1942
+-8 u P%sT 1946
+-8 - PST 1969
+-8 u P%sT 1983 O 30 2
+-9 u Y%sT 1983 N 30
+-9 u AK%sT
+Z America/Metlakatla 15:13:42 - LMT 1867 O 19 15:44:55
+-8:46:18 - LMT 1900 Au 20 12
+-8 - PST 1942
+-8 u P%sT 1946
+-8 - PST 1969
+-8 u P%sT 1983 O 30 2
+-8 - PST 2015 N 1 2
+-9 u AK%sT 2018 N 4 2
+-8 - PST 2019 Ja 20 2
+-9 u AK%sT
+Z America/Yakutat 14:41:5 - LMT 1867 O 19 15:12:18
+-9:18:55 - LMT 1900 Au 20 12
+-9 - YST 1942
+-9 u Y%sT 1946
+-9 - YST 1969
+-9 u Y%sT 1983 N 30
+-9 u AK%sT
+Z America/Anchorage 14:0:24 - LMT 1867 O 19 14:31:37
+-9:59:36 - LMT 1900 Au 20 12
+-10 - AST 1942
+-10 u A%sT 1967 Ap
+-10 - AHST 1969
+-10 u AH%sT 1983 O 30 2
+-9 u Y%sT 1983 N 30
+-9 u AK%sT
+Z America/Nome 12:58:22 - LMT 1867 O 19 13:29:35
+-11:1:38 - LMT 1900 Au 20 12
+-11 - NST 1942
+-11 u N%sT 1946
+-11 - NST 1967 Ap
+-11 - BST 1969
+-11 u B%sT 1983 O 30 2
+-9 u Y%sT 1983 N 30
+-9 u AK%sT
+Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35
+-11:46:38 - LMT 1900 Au 20 12
+-11 - NST 1942
+-11 u N%sT 1946
+-11 - NST 1967 Ap
+-11 - BST 1969
+-11 u B%sT 1983 O 30 2
+-10 u AH%sT 1983 N 30
+-10 u H%sT
+Z Pacific/Honolulu -10:31:26 - LMT 1896 Ja 13 12
+-10:30 - HST 1933 Ap 30 2
+-10:30 1 HDT 1933 May 21 12
+-10:30 u H%sT 1947 Jun 8 2
+-10 - HST
+Z America/Phoenix -7:28:18 - LMT 1883 N 18 11:31:42
+-7 u M%sT 1944 Ja 1 0:1
+-7 - MST 1944 Ap 1 0:1
+-7 u M%sT 1944 O 1 0:1
+-7 - MST 1967
+-7 u M%sT 1968 Mar 21
+-7 - MST
+L America/Phoenix America/Creston
+Z America/Boise -7:44:49 - LMT 1883 N 18 12:15:11
+-8 u P%sT 1923 May 13 2
+-7 u M%sT 1974
+-7 - MST 1974 F 3 2
+-7 u M%sT
+R In 1941 o - Jun 22 2 1 D
+R In 1941 1954 - S lastSu 2 0 S
+R In 1946 1954 - Ap lastSu 2 1 D
+Z America/Indiana/Indianapolis -5:44:38 - LMT 1883 N 18 12:15:22
+-6 u C%sT 1920
+-6 In C%sT 1942
+-6 u C%sT 1946
+-6 In C%sT 1955 Ap 24 2
+-5 - EST 1957 S 29 2
+-6 - CST 1958 Ap 27 2
+-5 - EST 1969
+-5 u E%sT 1971
+-5 - EST 2006
+-5 u E%sT
+R Ma 1951 o - Ap lastSu 2 1 D
+R Ma 1951 o - S lastSu 2 0 S
+R Ma 1954 1960 - Ap lastSu 2 1 D
+R Ma 1954 1960 - S lastSu 2 0 S
+Z America/Indiana/Marengo -5:45:23 - LMT 1883 N 18 12:14:37
+-6 u C%sT 1951
+-6 Ma C%sT 1961 Ap 30 2
+-5 - EST 1969
+-5 u E%sT 1974 Ja 6 2
+-6 1 CDT 1974 O 27 2
+-5 u E%sT 1976
+-5 - EST 2006
+-5 u E%sT
+R V 1946 o - Ap lastSu 2 1 D
+R V 1946 o - S lastSu 2 0 S
+R V 1953 1954 - Ap lastSu 2 1 D
+R V 1953 1959 - S lastSu 2 0 S
+R V 1955 o - May 1 0 1 D
+R V 1956 1963 - Ap lastSu 2 1 D
+R V 1960 o - O lastSu 2 0 S
+R V 1961 o - S lastSu 2 0 S
+R V 1962 1963 - O lastSu 2 0 S
+Z America/Indiana/Vincennes -5:50:7 - LMT 1883 N 18 12:9:53
+-6 u C%sT 1946
+-6 V C%sT 1964 Ap 26 2
+-5 - EST 1969
+-5 u E%sT 1971
+-5 - EST 2006 Ap 2 2
+-6 u C%sT 2007 N 4 2
+-5 u E%sT
+R Pe 1955 o - May 1 0 1 D
+R Pe 1955 1960 - S lastSu 2 0 S
+R Pe 1956 1963 - Ap lastSu 2 1 D
+R Pe 1961 1963 - O lastSu 2 0 S
+Z America/Indiana/Tell_City -5:47:3 - LMT 1883 N 18 12:12:57
+-6 u C%sT 1946
+-6 Pe C%sT 1964 Ap 26 2
+-5 - EST 1967 O 29 2
+-6 u C%sT 1969 Ap 27 2
+-5 u E%sT 1971
+-5 - EST 2006 Ap 2 2
+-6 u C%sT
+R Pi 1955 o - May 1 0 1 D
+R Pi 1955 1960 - S lastSu 2 0 S
+R Pi 1956 1964 - Ap lastSu 2 1 D
+R Pi 1961 1964 - O lastSu 2 0 S
+Z America/Indiana/Petersburg -5:49:7 - LMT 1883 N 18 12:10:53
+-6 u C%sT 1955
+-6 Pi C%sT 1965 Ap 25 2
+-5 - EST 1966 O 30 2
+-6 u C%sT 1977 O 30 2
+-5 - EST 2006 Ap 2 2
+-6 u C%sT 2007 N 4 2
+-5 u E%sT
+R St 1947 1961 - Ap lastSu 2 1 D
+R St 1947 1954 - S lastSu 2 0 S
+R St 1955 1956 - O lastSu 2 0 S
+R St 1957 1958 - S lastSu 2 0 S
+R St 1959 1961 - O lastSu 2 0 S
+Z America/Indiana/Knox -5:46:30 - LMT 1883 N 18 12:13:30
+-6 u C%sT 1947
+-6 St C%sT 1962 Ap 29 2
+-5 - EST 1963 O 27 2
+-6 u C%sT 1991 O 27 2
+-5 - EST 2006 Ap 2 2
+-6 u C%sT
+R Pu 1946 1960 - Ap lastSu 2 1 D
+R Pu 1946 1954 - S lastSu 2 0 S
+R Pu 1955 1956 - O lastSu 2 0 S
+R Pu 1957 1960 - S lastSu 2 0 S
+Z America/Indiana/Winamac -5:46:25 - LMT 1883 N 18 12:13:35
+-6 u C%sT 1946
+-6 Pu C%sT 1961 Ap 30 2
+-5 - EST 1969
+-5 u E%sT 1971
+-5 - EST 2006 Ap 2 2
+-6 u C%sT 2007 Mar 11 2
+-5 u E%sT
+Z America/Indiana/Vevay -5:40:16 - LMT 1883 N 18 12:19:44
+-6 u C%sT 1954 Ap 25 2
+-5 - EST 1969
+-5 u E%sT 1973
+-5 - EST 2006
+-5 u E%sT
+R v 1921 o - May 1 2 1 D
+R v 1921 o - S 1 2 0 S
+R v 1941 o - Ap lastSu 2 1 D
+R v 1941 o - S lastSu 2 0 S
+R v 1946 o - Ap lastSu 0:1 1 D
+R v 1946 o - Jun 2 2 0 S
+R v 1950 1961 - Ap lastSu 2 1 D
+R v 1950 1955 - S lastSu 2 0 S
+R v 1956 1961 - O lastSu 2 0 S
+Z America/Kentucky/Louisville -5:43:2 - LMT 1883 N 18 12:16:58
+-6 u C%sT 1921
+-6 v C%sT 1942
+-6 u C%sT 1946
+-6 v C%sT 1961 Jul 23 2
+-5 - EST 1968
+-5 u E%sT 1974 Ja 6 2
+-6 1 CDT 1974 O 27 2
+-5 u E%sT
+Z America/Kentucky/Monticello -5:39:24 - LMT 1883 N 18 12:20:36
+-6 u C%sT 1946
+-6 - CST 1968
+-6 u C%sT 2000 O 29 2
+-5 u E%sT
+R Dt 1948 o - Ap lastSu 2 1 D
+R Dt 1948 o - S lastSu 2 0 S
+Z America/Detroit -5:32:11 - LMT 1905
+-6 - CST 1915 May 15 2
+-5 - EST 1942
+-5 u E%sT 1946
+-5 Dt E%sT 1967 Jun 14 0:1
+-5 u E%sT 1969
+-5 - EST 1973
+-5 u E%sT 1975
+-5 - EST 1975 Ap 27 2
+-5 u E%sT
+R Me 1946 o - Ap lastSu 2 1 D
+R Me 1946 o - S lastSu 2 0 S
+R Me 1966 o - Ap lastSu 2 1 D
+R Me 1966 o - O lastSu 2 0 S
+Z America/Menominee -5:50:27 - LMT 1885 S 18 12
+-6 u C%sT 1946
+-6 Me C%sT 1969 Ap 27 2
+-5 - EST 1973 Ap 29 2
+-6 u C%sT
+R C 1918 o - Ap 14 2 1 D
+R C 1918 o - O 27 2 0 S
+R C 1942 o - F 9 2 1 W
+R C 1945 o - Au 14 23u 1 P
+R C 1945 o - S 30 2 0 S
+R C 1974 1986 - Ap lastSu 2 1 D
+R C 1974 2006 - O lastSu 2 0 S
+R C 1987 2006 - Ap Su>=1 2 1 D
+R C 2007 ma - Mar Su>=8 2 1 D
+R C 2007 ma - N Su>=1 2 0 S
+R j 1917 o - Ap 8 2 1 D
+R j 1917 o - S 17 2 0 S
+R j 1919 o - May 5 23 1 D
+R j 1919 o - Au 12 23 0 S
+R j 1920 1935 - May Su>=1 23 1 D
+R j 1920 1935 - O lastSu 23 0 S
+R j 1936 1941 - May M>=9 0 1 D
+R j 1936 1941 - O M>=2 0 0 S
+R j 1946 1950 - May Su>=8 2 1 D
+R j 1946 1950 - O Su>=2 2 0 S
+R j 1951 1986 - Ap lastSu 2 1 D
+R j 1951 1959 - S lastSu 2 0 S
+R j 1960 1986 - O lastSu 2 0 S
+R j 1987 o - Ap Su>=1 0:1 1 D
+R j 1987 2006 - O lastSu 0:1 0 S
+R j 1988 o - Ap Su>=1 0:1 2 DD
+R j 1989 2006 - Ap Su>=1 0:1 1 D
+R j 2007 2011 - Mar Su>=8 0:1 1 D
+R j 2007 2010 - N Su>=1 0:1 0 S
+Z America/St_Johns -3:30:52 - LMT 1884
+-3:30:52 j N%sT 1918
+-3:30:52 C N%sT 1919
+-3:30:52 j N%sT 1935 Mar 30
+-3:30 j N%sT 1942 May 11
+-3:30 C N%sT 1946
+-3:30 j N%sT 2011 N
+-3:30 C N%sT
+Z America/Goose_Bay -4:1:40 - LMT 1884
+-3:30:52 - NST 1918
+-3:30:52 C N%sT 1919
+-3:30:52 - NST 1935 Mar 30
+-3:30 - NST 1936
+-3:30 j N%sT 1942 May 11
+-3:30 C N%sT 1946
+-3:30 j N%sT 1966 Mar 15 2
+-4 j A%sT 2011 N
+-4 C A%sT
+R H 1916 o - Ap 1 0 1 D
+R H 1916 o - O 1 0 0 S
+R H 1920 o - May 9 0 1 D
+R H 1920 o - Au 29 0 0 S
+R H 1921 o - May 6 0 1 D
+R H 1921 1922 - S 5 0 0 S
+R H 1922 o - Ap 30 0 1 D
+R H 1923 1925 - May Su>=1 0 1 D
+R H 1923 o - S 4 0 0 S
+R H 1924 o - S 15 0 0 S
+R H 1925 o - S 28 0 0 S
+R H 1926 o - May 16 0 1 D
+R H 1926 o - S 13 0 0 S
+R H 1927 o - May 1 0 1 D
+R H 1927 o - S 26 0 0 S
+R H 1928 1931 - May Su>=8 0 1 D
+R H 1928 o - S 9 0 0 S
+R H 1929 o - S 3 0 0 S
+R H 1930 o - S 15 0 0 S
+R H 1931 1932 - S M>=24 0 0 S
+R H 1932 o - May 1 0 1 D
+R H 1933 o - Ap 30 0 1 D
+R H 1933 o - O 2 0 0 S
+R H 1934 o - May 20 0 1 D
+R H 1934 o - S 16 0 0 S
+R H 1935 o - Jun 2 0 1 D
+R H 1935 o - S 30 0 0 S
+R H 1936 o - Jun 1 0 1 D
+R H 1936 o - S 14 0 0 S
+R H 1937 1938 - May Su>=1 0 1 D
+R H 1937 1941 - S M>=24 0 0 S
+R H 1939 o - May 28 0 1 D
+R H 1940 1941 - May Su>=1 0 1 D
+R H 1946 1949 - Ap lastSu 2 1 D
+R H 1946 1949 - S lastSu 2 0 S
+R H 1951 1954 - Ap lastSu 2 1 D
+R H 1951 1954 - S lastSu 2 0 S
+R H 1956 1959 - Ap lastSu 2 1 D
+R H 1956 1959 - S lastSu 2 0 S
+R H 1962 1973 - Ap lastSu 2 1 D
+R H 1962 1973 - O lastSu 2 0 S
+Z America/Halifax -4:14:24 - LMT 1902 Jun 15
+-4 H A%sT 1918
+-4 C A%sT 1919
+-4 H A%sT 1942 F 9 2s
+-4 C A%sT 1946
+-4 H A%sT 1974
+-4 C A%sT
+Z America/Glace_Bay -3:59:48 - LMT 1902 Jun 15
+-4 C A%sT 1953
+-4 H A%sT 1954
+-4 - AST 1972
+-4 H A%sT 1974
+-4 C A%sT
+R o 1933 1935 - Jun Su>=8 1 1 D
+R o 1933 1935 - S Su>=8 1 0 S
+R o 1936 1938 - Jun Su>=1 1 1 D
+R o 1936 1938 - S Su>=1 1 0 S
+R o 1939 o - May 27 1 1 D
+R o 1939 1941 - S Sa>=21 1 0 S
+R o 1940 o - May 19 1 1 D
+R o 1941 o - May 4 1 1 D
+R o 1946 1972 - Ap lastSu 2 1 D
+R o 1946 1956 - S lastSu 2 0 S
+R o 1957 1972 - O lastSu 2 0 S
+R o 1993 2006 - Ap Su>=1 0:1 1 D
+R o 1993 2006 - O lastSu 0:1 0 S
+Z America/Moncton -4:19:8 - LMT 1883 D 9
+-5 - EST 1902 Jun 15
+-4 C A%sT 1933
+-4 o A%sT 1942
+-4 C A%sT 1946
+-4 o A%sT 1973
+-4 C A%sT 1993
+-4 o A%sT 2007
+-4 C A%sT
+R t 1919 o - Mar 30 23:30 1 D
+R t 1919 o - O 26 0 0 S
+R t 1920 o - May 2 2 1 D
+R t 1920 o - S 26 0 0 S
+R t 1921 o - May 15 2 1 D
+R t 1921 o - S 15 2 0 S
+R t 1922 1923 - May Su>=8 2 1 D
+R t 1922 1926 - S Su>=15 2 0 S
+R t 1924 1927 - May Su>=1 2 1 D
+R t 1927 1937 - S Su>=25 2 0 S
+R t 1928 1937 - Ap Su>=25 2 1 D
+R t 1938 1940 - Ap lastSu 2 1 D
+R t 1938 1939 - S lastSu 2 0 S
+R t 1945 1946 - S lastSu 2 0 S
+R t 1946 o - Ap lastSu 2 1 D
+R t 1947 1949 - Ap lastSu 0 1 D
+R t 1947 1948 - S lastSu 0 0 S
+R t 1949 o - N lastSu 0 0 S
+R t 1950 1973 - Ap lastSu 2 1 D
+R t 1950 o - N lastSu 2 0 S
+R t 1951 1956 - S lastSu 2 0 S
+R t 1957 1973 - O lastSu 2 0 S
+Z America/Toronto -5:17:32 - LMT 1895
+-5 C E%sT 1919
+-5 t E%sT 1942 F 9 2s
+-5 C E%sT 1946
+-5 t E%sT 1974
+-5 C E%sT
+L America/Toronto America/Nassau
+Z America/Thunder_Bay -5:57 - LMT 1895
+-6 - CST 1910
+-5 - EST 1942
+-5 C E%sT 1970
+-5 t E%sT 1973
+-5 - EST 1974
+-5 C E%sT
+Z America/Nipigon -5:53:4 - LMT 1895
+-5 C E%sT 1940 S 29
+-5 1 EDT 1942 F 9 2s
+-5 C E%sT
+Z America/Rainy_River -6:18:16 - LMT 1895
+-6 C C%sT 1940 S 29
+-6 1 CDT 1942 F 9 2s
+-6 C C%sT
+R W 1916 o - Ap 23 0 1 D
+R W 1916 o - S 17 0 0 S
+R W 1918 o - Ap 14 2 1 D
+R W 1918 o - O 27 2 0 S
+R W 1937 o - May 16 2 1 D
+R W 1937 o - S 26 2 0 S
+R W 1942 o - F 9 2 1 W
+R W 1945 o - Au 14 23u 1 P
+R W 1945 o - S lastSu 2 0 S
+R W 1946 o - May 12 2 1 D
+R W 1946 o - O 13 2 0 S
+R W 1947 1949 - Ap lastSu 2 1 D
+R W 1947 1949 - S lastSu 2 0 S
+R W 1950 o - May 1 2 1 D
+R W 1950 o - S 30 2 0 S
+R W 1951 1960 - Ap lastSu 2 1 D
+R W 1951 1958 - S lastSu 2 0 S
+R W 1959 o - O lastSu 2 0 S
+R W 1960 o - S lastSu 2 0 S
+R W 1963 o - Ap lastSu 2 1 D
+R W 1963 o - S 22 2 0 S
+R W 1966 1986 - Ap lastSu 2s 1 D
+R W 1966 2005 - O lastSu 2s 0 S
+R W 1987 2005 - Ap Su>=1 2s 1 D
+Z America/Winnipeg -6:28:36 - LMT 1887 Jul 16
+-6 W C%sT 2006
+-6 C C%sT
+R r 1918 o - Ap 14 2 1 D
+R r 1918 o - O 27 2 0 S
+R r 1930 1934 - May Su>=1 0 1 D
+R r 1930 1934 - O Su>=1 0 0 S
+R r 1937 1941 - Ap Su>=8 0 1 D
+R r 1937 o - O Su>=8 0 0 S
+R r 1938 o - O Su>=1 0 0 S
+R r 1939 1941 - O Su>=8 0 0 S
+R r 1942 o - F 9 2 1 W
+R r 1945 o - Au 14 23u 1 P
+R r 1945 o - S lastSu 2 0 S
+R r 1946 o - Ap Su>=8 2 1 D
+R r 1946 o - O Su>=8 2 0 S
+R r 1947 1957 - Ap lastSu 2 1 D
+R r 1947 1957 - S lastSu 2 0 S
+R r 1959 o - Ap lastSu 2 1 D
+R r 1959 o - O lastSu 2 0 S
+R Sw 1957 o - Ap lastSu 2 1 D
+R Sw 1957 o - O lastSu 2 0 S
+R Sw 1959 1961 - Ap lastSu 2 1 D
+R Sw 1959 o - O lastSu 2 0 S
+R Sw 1960 1961 - S lastSu 2 0 S
+Z America/Regina -6:58:36 - LMT 1905 S
+-7 r M%sT 1960 Ap lastSu 2
+-6 - CST
+Z America/Swift_Current -7:11:20 - LMT 1905 S
+-7 C M%sT 1946 Ap lastSu 2
+-7 r M%sT 1950
+-7 Sw M%sT 1972 Ap lastSu 2
+-6 - CST
+R Ed 1918 1919 - Ap Su>=8 2 1 D
+R Ed 1918 o - O 27 2 0 S
+R Ed 1919 o - May 27 2 0 S
+R Ed 1920 1923 - Ap lastSu 2 1 D
+R Ed 1920 o - O lastSu 2 0 S
+R Ed 1921 1923 - S lastSu 2 0 S
+R Ed 1942 o - F 9 2 1 W
+R Ed 1945 o - Au 14 23u 1 P
+R Ed 1945 o - S lastSu 2 0 S
+R Ed 1947 o - Ap lastSu 2 1 D
+R Ed 1947 o - S lastSu 2 0 S
+R Ed 1972 1986 - Ap lastSu 2 1 D
+R Ed 1972 2006 - O lastSu 2 0 S
+Z America/Edmonton -7:33:52 - LMT 1906 S
+-7 Ed M%sT 1987
+-7 C M%sT
+R Va 1918 o - Ap 14 2 1 D
+R Va 1918 o - O 27 2 0 S
+R Va 1942 o - F 9 2 1 W
+R Va 1945 o - Au 14 23u 1 P
+R Va 1945 o - S 30 2 0 S
+R Va 1946 1986 - Ap lastSu 2 1 D
+R Va 1946 o - S 29 2 0 S
+R Va 1947 1961 - S lastSu 2 0 S
+R Va 1962 2006 - O lastSu 2 0 S
+Z America/Vancouver -8:12:28 - LMT 1884
+-8 Va P%sT 1987
+-8 C P%sT
+Z America/Dawson_Creek -8:0:56 - LMT 1884
+-8 C P%sT 1947
+-8 Va P%sT 1972 Au 30 2
+-7 - MST
+Z America/Fort_Nelson -8:10:47 - LMT 1884
+-8 Va P%sT 1946
+-8 - PST 1947
+-8 Va P%sT 1987
+-8 C P%sT 2015 Mar 8 2
+-7 - MST
+R Y 1918 o - Ap 14 2 1 D
+R Y 1918 o - O 27 2 0 S
+R Y 1919 o - May 25 2 1 D
+R Y 1919 o - N 1 0 0 S
+R Y 1942 o - F 9 2 1 W
+R Y 1945 o - Au 14 23u 1 P
+R Y 1945 o - S 30 2 0 S
+R Y 1965 o - Ap lastSu 0 2 DD
+R Y 1965 o - O lastSu 2 0 S
+R Y 1980 1986 - Ap lastSu 2 1 D
+R Y 1980 2006 - O lastSu 2 0 S
+R Y 1987 2006 - Ap Su>=1 2 1 D
+Z America/Pangnirtung 0 - -00 1921
+-4 Y A%sT 1995 Ap Su>=1 2
+-5 C E%sT 1999 O 31 2
+-6 C C%sT 2000 O 29 2
+-5 C E%sT
+Z America/Iqaluit 0 - -00 1942 Au
+-5 Y E%sT 1999 O 31 2
+-6 C C%sT 2000 O 29 2
+-5 C E%sT
+Z America/Resolute 0 - -00 1947 Au 31
+-6 Y C%sT 2000 O 29 2
+-5 - EST 2001 Ap 1 3
+-6 C C%sT 2006 O 29 2
+-5 - EST 2007 Mar 11 3
+-6 C C%sT
+Z America/Rankin_Inlet 0 - -00 1957
+-6 Y C%sT 2000 O 29 2
+-5 - EST 2001 Ap 1 3
+-6 C C%sT
+Z America/Cambridge_Bay 0 - -00 1920
+-7 Y M%sT 1999 O 31 2
+-6 C C%sT 2000 O 29 2
+-5 - EST 2000 N 5
+-6 - CST 2001 Ap 1 3
+-7 C M%sT
+Z America/Yellowknife 0 - -00 1935
+-7 Y M%sT 1980
+-7 C M%sT
+Z America/Inuvik 0 - -00 1953
+-8 Y P%sT 1979 Ap lastSu 2
+-7 Y M%sT 1980
+-7 C M%sT
+Z America/Whitehorse -9:0:12 - LMT 1900 Au 20
+-9 Y Y%sT 1967 May 28
+-8 Y P%sT 1980
+-8 C P%sT 2020 N
+-7 - MST
+Z America/Dawson -9:17:40 - LMT 1900 Au 20
+-9 Y Y%sT 1973 O 28
+-8 Y P%sT 1980
+-8 C P%sT 2020 N
+-7 - MST
+R m 1939 o - F 5 0 1 D
+R m 1939 o - Jun 25 0 0 S
+R m 1940 o - D 9 0 1 D
+R m 1941 o - Ap 1 0 0 S
+R m 1943 o - D 16 0 1 W
+R m 1944 o - May 1 0 0 S
+R m 1950 o - F 12 0 1 D
+R m 1950 o - Jul 30 0 0 S
+R m 1996 2000 - Ap Su>=1 2 1 D
+R m 1996 2000 - O lastSu 2 0 S
+R m 2001 o - May Su>=1 2 1 D
+R m 2001 o - S lastSu 2 0 S
+R m 2002 ma - Ap Su>=1 2 1 D
+R m 2002 ma - O lastSu 2 0 S
+Z America/Cancun -5:47:4 - LMT 1922 Ja 1 0:12:56
+-6 - CST 1981 D 23
+-5 m E%sT 1998 Au 2 2
+-6 m C%sT 2015 F 1 2
+-5 - EST
+Z America/Merida -5:58:28 - LMT 1922 Ja 1 0:1:32
+-6 - CST 1981 D 23
+-5 - EST 1982 D 2
+-6 m C%sT
+Z America/Matamoros -6:40 - LMT 1921 D 31 23:20
+-6 - CST 1988
+-6 u C%sT 1989
+-6 m C%sT 2010
+-6 u C%sT
+Z America/Monterrey -6:41:16 - LMT 1921 D 31 23:18:44
+-6 - CST 1988
+-6 u C%sT 1989
+-6 m C%sT
+Z America/Mexico_City -6:36:36 - LMT 1922 Ja 1 0:23:24
+-7 - MST 1927 Jun 10 23
+-6 - CST 1930 N 15
+-7 - MST 1931 May 1 23
+-6 - CST 1931 O
+-7 - MST 1932 Ap
+-6 m C%sT 2001 S 30 2
+-6 - CST 2002 F 20
+-6 m C%sT
+Z America/Ojinaga -6:57:40 - LMT 1922 Ja 1 0:2:20
+-7 - MST 1927 Jun 10 23
+-6 - CST 1930 N 15
+-7 - MST 1931 May 1 23
+-6 - CST 1931 O
+-7 - MST 1932 Ap
+-6 - CST 1996
+-6 m C%sT 1998
+-6 - CST 1998 Ap Su>=1 3
+-7 m M%sT 2010
+-7 u M%sT
+Z America/Chihuahua -7:4:20 - LMT 1921 D 31 23:55:40
+-7 - MST 1927 Jun 10 23
+-6 - CST 1930 N 15
+-7 - MST 1931 May 1 23
+-6 - CST 1931 O
+-7 - MST 1932 Ap
+-6 - CST 1996
+-6 m C%sT 1998
+-6 - CST 1998 Ap Su>=1 3
+-7 m M%sT
+Z America/Hermosillo -7:23:52 - LMT 1921 D 31 23:36:8
+-7 - MST 1927 Jun 10 23
+-6 - CST 1930 N 15
+-7 - MST 1931 May 1 23
+-6 - CST 1931 O
+-7 - MST 1932 Ap
+-6 - CST 1942 Ap 24
+-7 - MST 1949 Ja 14
+-8 - PST 1970
+-7 m M%sT 1999
+-7 - MST
+Z America/Mazatlan -7:5:40 - LMT 1921 D 31 23:54:20
+-7 - MST 1927 Jun 10 23
+-6 - CST 1930 N 15
+-7 - MST 1931 May 1 23
+-6 - CST 1931 O
+-7 - MST 1932 Ap
+-6 - CST 1942 Ap 24
+-7 - MST 1949 Ja 14
+-8 - PST 1970
+-7 m M%sT
+Z America/Bahia_Banderas -7:1 - LMT 1921 D 31 23:59
+-7 - MST 1927 Jun 10 23
+-6 - CST 1930 N 15
+-7 - MST 1931 May 1 23
+-6 - CST 1931 O
+-7 - MST 1932 Ap
+-6 - CST 1942 Ap 24
+-7 - MST 1949 Ja 14
+-8 - PST 1970
+-7 m M%sT 2010 Ap 4 2
+-6 m C%sT
+Z America/Tijuana -7:48:4 - LMT 1922 Ja 1 0:11:56
+-7 - MST 1924
+-8 - PST 1927 Jun 10 23
+-7 - MST 1930 N 15
+-8 - PST 1931 Ap
+-8 1 PDT 1931 S 30
+-8 - PST 1942 Ap 24
+-8 1 PWT 1945 Au 14 23u
+-8 1 PPT 1945 N 12
+-8 - PST 1948 Ap 5
+-8 1 PDT 1949 Ja 14
+-8 - PST 1954
+-8 CA P%sT 1961
+-8 - PST 1976
+-8 u P%sT 1996
+-8 m P%sT 2001
+-8 u P%sT 2002 F 20
+-8 m P%sT 2010
+-8 u P%sT
+R BB 1942 o - Ap 19 5u 1 D
+R BB 1942 o - Au 31 6u 0 S
+R BB 1943 o - May 2 5u 1 D
+R BB 1943 o - S 5 6u 0 S
+R BB 1944 o - Ap 10 5u 0:30 -
+R BB 1944 o - S 10 6u 0 S
+R BB 1977 o - Jun 12 2 1 D
+R BB 1977 1978 - O Su>=1 2 0 S
+R BB 1978 1980 - Ap Su>=15 2 1 D
+R BB 1979 o - S 30 2 0 S
+R BB 1980 o - S 25 2 0 S
+Z America/Barbados -3:58:29 - LMT 1911 Au 28
+-4 BB A%sT 1944
+-4 BB AST/-0330 1945
+-4 BB A%sT
+R BZ 1918 1941 - O Sa>=1 24 0:30 -0530
+R BZ 1919 1942 - F Sa>=8 24 0 CST
+R BZ 1942 o - Jun 27 24 1 CWT
+R BZ 1945 o - Au 14 23u 1 CPT
+R BZ 1945 o - D 15 24 0 CST
+R BZ 1947 1967 - O Sa>=1 24 0:30 -0530
+R BZ 1948 1968 - F Sa>=8 24 0 CST
+R BZ 1973 o - D 5 0 1 CDT
+R BZ 1974 o - F 9 0 0 CST
+R BZ 1982 o - D 18 0 1 CDT
+R BZ 1983 o - F 12 0 0 CST
+Z America/Belize -5:52:48 - LMT 1912 Ap
+-6 BZ %s
+R Be 1917 o - Ap 5 24 1 -
+R Be 1917 o - S 30 24 0 -
+R Be 1918 o - Ap 13 24 1 -
+R Be 1918 o - S 15 24 0 S
+R Be 1942 o - Ja 11 2 1 D
+R Be 1942 o - O 18 2 0 S
+R Be 1943 o - Mar 21 2 1 D
+R Be 1943 o - O 31 2 0 S
+R Be 1944 1945 - Mar Su>=8 2 1 D
+R Be 1944 1945 - N Su>=1 2 0 S
+R Be 1947 o - May Su>=15 2 1 D
+R Be 1947 o - S Su>=8 2 0 S
+R Be 1948 1952 - May Su>=22 2 1 D
+R Be 1948 1952 - S Su>=1 2 0 S
+R Be 1956 o - May Su>=22 2 1 D
+R Be 1956 o - O lastSu 2 0 S
+Z Atlantic/Bermuda -4:19:18 - LMT 1890
+-4:19:18 Be BMT/BST 1930 Ja 1 2
+-4 Be A%sT 1974 Ap 28 2
+-4 C A%sT 1976
+-4 u A%sT
+R CR 1979 1980 - F lastSu 0 1 D
+R CR 1979 1980 - Jun Su>=1 0 0 S
+R CR 1991 1992 - Ja Sa>=15 0 1 D
+R CR 1991 o - Jul 1 0 0 S
+R CR 1992 o - Mar 15 0 0 S
+Z America/Costa_Rica -5:36:13 - LMT 1890
+-5:36:13 - SJMT 1921 Ja 15
+-6 CR C%sT
+R Q 1928 o - Jun 10 0 1 D
+R Q 1928 o - O 10 0 0 S
+R Q 1940 1942 - Jun Su>=1 0 1 D
+R Q 1940 1942 - S Su>=1 0 0 S
+R Q 1945 1946 - Jun Su>=1 0 1 D
+R Q 1945 1946 - S Su>=1 0 0 S
+R Q 1965 o - Jun 1 0 1 D
+R Q 1965 o - S 30 0 0 S
+R Q 1966 o - May 29 0 1 D
+R Q 1966 o - O 2 0 0 S
+R Q 1967 o - Ap 8 0 1 D
+R Q 1967 1968 - S Su>=8 0 0 S
+R Q 1968 o - Ap 14 0 1 D
+R Q 1969 1977 - Ap lastSu 0 1 D
+R Q 1969 1971 - O lastSu 0 0 S
+R Q 1972 1974 - O 8 0 0 S
+R Q 1975 1977 - O lastSu 0 0 S
+R Q 1978 o - May 7 0 1 D
+R Q 1978 1990 - O Su>=8 0 0 S
+R Q 1979 1980 - Mar Su>=15 0 1 D
+R Q 1981 1985 - May Su>=5 0 1 D
+R Q 1986 1989 - Mar Su>=14 0 1 D
+R Q 1990 1997 - Ap Su>=1 0 1 D
+R Q 1991 1995 - O Su>=8 0s 0 S
+R Q 1996 o - O 6 0s 0 S
+R Q 1997 o - O 12 0s 0 S
+R Q 1998 1999 - Mar lastSu 0s 1 D
+R Q 1998 2003 - O lastSu 0s 0 S
+R Q 2000 2003 - Ap Su>=1 0s 1 D
+R Q 2004 o - Mar lastSu 0s 1 D
+R Q 2006 2010 - O lastSu 0s 0 S
+R Q 2007 o - Mar Su>=8 0s 1 D
+R Q 2008 o - Mar Su>=15 0s 1 D
+R Q 2009 2010 - Mar Su>=8 0s 1 D
+R Q 2011 o - Mar Su>=15 0s 1 D
+R Q 2011 o - N 13 0s 0 S
+R Q 2012 o - Ap 1 0s 1 D
+R Q 2012 ma - N Su>=1 0s 0 S
+R Q 2013 ma - Mar Su>=8 0s 1 D
+Z America/Havana -5:29:28 - LMT 1890
+-5:29:36 - HMT 1925 Jul 19 12
+-5 Q C%sT
+R DO 1966 o - O 30 0 1 EDT
+R DO 1967 o - F 28 0 0 EST
+R DO 1969 1973 - O lastSu 0 0:30 -0430
+R DO 1970 o - F 21 0 0 EST
+R DO 1971 o - Ja 20 0 0 EST
+R DO 1972 1974 - Ja 21 0 0 EST
+Z America/Santo_Domingo -4:39:36 - LMT 1890
+-4:40 - SDMT 1933 Ap 1 12
+-5 DO %s 1974 O 27
+-4 - AST 2000 O 29 2
+-5 u E%sT 2000 D 3 1
+-4 - AST
+R SV 1987 1988 - May Su>=1 0 1 D
+R SV 1987 1988 - S lastSu 0 0 S
+Z America/El_Salvador -5:56:48 - LMT 1921
+-6 SV C%sT
+R GT 1973 o - N 25 0 1 D
+R GT 1974 o - F 24 0 0 S
+R GT 1983 o - May 21 0 1 D
+R GT 1983 o - S 22 0 0 S
+R GT 1991 o - Mar 23 0 1 D
+R GT 1991 o - S 7 0 0 S
+R GT 2006 o - Ap 30 0 1 D
+R GT 2006 o - O 1 0 0 S
+Z America/Guatemala -6:2:4 - LMT 1918 O 5
+-6 GT C%sT
+R HT 1983 o - May 8 0 1 D
+R HT 1984 1987 - Ap lastSu 0 1 D
+R HT 1983 1987 - O lastSu 0 0 S
+R HT 1988 1997 - Ap Su>=1 1s 1 D
+R HT 1988 1997 - O lastSu 1s 0 S
+R HT 2005 2006 - Ap Su>=1 0 1 D
+R HT 2005 2006 - O lastSu 0 0 S
+R HT 2012 2015 - Mar Su>=8 2 1 D
+R HT 2012 2015 - N Su>=1 2 0 S
+R HT 2017 ma - Mar Su>=8 2 1 D
+R HT 2017 ma - N Su>=1 2 0 S
+Z America/Port-au-Prince -4:49:20 - LMT 1890
+-4:49 - PPMT 1917 Ja 24 12
+-5 HT E%sT
+R HN 1987 1988 - May Su>=1 0 1 D
+R HN 1987 1988 - S lastSu 0 0 S
+R HN 2006 o - May Su>=1 0 1 D
+R HN 2006 o - Au M>=1 0 0 S
+Z America/Tegucigalpa -5:48:52 - LMT 1921 Ap
+-6 HN C%sT
+Z America/Jamaica -5:7:10 - LMT 1890
+-5:7:10 - KMT 1912 F
+-5 - EST 1974
+-5 u E%sT 1984
+-5 - EST
+Z America/Martinique -4:4:20 - LMT 1890
+-4:4:20 - FFMT 1911 May
+-4 - AST 1980 Ap 6
+-4 1 ADT 1980 S 28
+-4 - AST
+R NI 1979 1980 - Mar Su>=16 0 1 D
+R NI 1979 1980 - Jun M>=23 0 0 S
+R NI 2005 o - Ap 10 0 1 D
+R NI 2005 o - O Su>=1 0 0 S
+R NI 2006 o - Ap 30 2 1 D
+R NI 2006 o - O Su>=1 1 0 S
+Z America/Managua -5:45:8 - LMT 1890
+-5:45:12 - MMT 1934 Jun 23
+-6 - CST 1973 May
+-5 - EST 1975 F 16
+-6 NI C%sT 1992 Ja 1 4
+-5 - EST 1992 S 24
+-6 - CST 1993
+-5 - EST 1997
+-6 NI C%sT
+Z America/Panama -5:18:8 - LMT 1890
+-5:19:36 - CMT 1908 Ap 22
+-5 - EST
+L America/Panama America/Atikokan
+L America/Panama America/Cayman
+Z America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12
+-4 - AST 1942 May 3
+-4 u A%sT 1946
+-4 - AST
+L America/Puerto_Rico America/Anguilla
+L America/Puerto_Rico America/Antigua
+L America/Puerto_Rico America/Aruba
+L America/Puerto_Rico America/Curacao
+L America/Puerto_Rico America/Blanc-Sablon
+L America/Puerto_Rico America/Dominica
+L America/Puerto_Rico America/Grenada
+L America/Puerto_Rico America/Guadeloupe
+L America/Puerto_Rico America/Kralendijk
+L America/Puerto_Rico America/Lower_Princes
+L America/Puerto_Rico America/Marigot
+L America/Puerto_Rico America/Montserrat
+L America/Puerto_Rico America/Port_of_Spain
+L America/Puerto_Rico America/St_Barthelemy
+L America/Puerto_Rico America/St_Kitts
+L America/Puerto_Rico America/St_Lucia
+L America/Puerto_Rico America/St_Thomas
+L America/Puerto_Rico America/St_Vincent
+L America/Puerto_Rico America/Tortola
+Z America/Miquelon -3:44:40 - LMT 1911 May 15
+-4 - AST 1980 May
+-3 - -03 1987
+-3 C -03/-02
+Z America/Grand_Turk -4:44:32 - LMT 1890
+-5:7:10 - KMT 1912 F
+-5 - EST 1979
+-5 u E%sT 2015 Mar 8 2
+-4 - AST 2018 Mar 11 3
+-5 u E%sT
+R A 1930 o - D 1 0 1 -
+R A 1931 o - Ap 1 0 0 -
+R A 1931 o - O 15 0 1 -
+R A 1932 1940 - Mar 1 0 0 -
+R A 1932 1939 - N 1 0 1 -
+R A 1940 o - Jul 1 0 1 -
+R A 1941 o - Jun 15 0 0 -
+R A 1941 o - O 15 0 1 -
+R A 1943 o - Au 1 0 0 -
+R A 1943 o - O 15 0 1 -
+R A 1946 o - Mar 1 0 0 -
+R A 1946 o - O 1 0 1 -
+R A 1963 o - O 1 0 0 -
+R A 1963 o - D 15 0 1 -
+R A 1964 1966 - Mar 1 0 0 -
+R A 1964 1966 - O 15 0 1 -
+R A 1967 o - Ap 2 0 0 -
+R A 1967 1968 - O Su>=1 0 1 -
+R A 1968 1969 - Ap Su>=1 0 0 -
+R A 1974 o - Ja 23 0 1 -
+R A 1974 o - May 1 0 0 -
+R A 1988 o - D 1 0 1 -
+R A 1989 1993 - Mar Su>=1 0 0 -
+R A 1989 1992 - O Su>=15 0 1 -
+R A 1999 o - O Su>=1 0 1 -
+R A 2000 o - Mar 3 0 0 -
+R A 2007 o - D 30 0 1 -
+R A 2008 2009 - Mar Su>=15 0 0 -
+R A 2008 o - O Su>=15 0 1 -
+Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 A -03/-02
+Z America/Argentina/Cordoba -4:16:48 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1991 Mar 3
+-4 - -04 1991 O 20
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 A -03/-02
+Z America/Argentina/Salta -4:21:40 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1991 Mar 3
+-4 - -04 1991 O 20
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/Argentina/Tucuman -4:20:52 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1991 Mar 3
+-4 - -04 1991 O 20
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 Jun
+-4 - -04 2004 Jun 13
+-3 A -03/-02
+Z America/Argentina/La_Rioja -4:27:24 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1991 Mar
+-4 - -04 1991 May 7
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 Jun
+-4 - -04 2004 Jun 20
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/Argentina/San_Juan -4:34:4 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1991 Mar
+-4 - -04 1991 May 7
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 May 31
+-4 - -04 2004 Jul 25
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/Argentina/Jujuy -4:21:12 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1990 Mar 4
+-4 - -04 1990 O 28
+-4 1 -03 1991 Mar 17
+-4 - -04 1991 O 6
+-3 1 -02 1992
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/Argentina/Catamarca -4:23:8 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1991 Mar 3
+-4 - -04 1991 O 20
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 Jun
+-4 - -04 2004 Jun 20
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/Argentina/Mendoza -4:35:16 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1990 Mar 4
+-4 - -04 1990 O 15
+-4 1 -03 1991 Mar
+-4 - -04 1991 O 15
+-4 1 -03 1992 Mar
+-4 - -04 1992 O 18
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 May 23
+-4 - -04 2004 S 26
+-3 A -03/-02 2008 O 18
+-3 - -03
+R Sa 2008 2009 - Mar Su>=8 0 0 -
+R Sa 2007 2008 - O Su>=8 0 1 -
+Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1990
+-3 1 -02 1990 Mar 14
+-4 - -04 1990 O 15
+-4 1 -03 1991 Mar
+-4 - -04 1991 Jun
+-3 - -03 1999 O 3
+-4 1 -03 2000 Mar 3
+-3 - -03 2004 May 31
+-4 - -04 2004 Jul 25
+-3 A -03/-02 2008 Ja 21
+-4 Sa -04/-03 2009 O 11
+-3 - -03
+Z America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 Jun
+-4 - -04 2004 Jun 20
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/Argentina/Ushuaia -4:33:12 - LMT 1894 O 31
+-4:16:48 - CMT 1920 May
+-4 - -04 1930 D
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1999 O 3
+-4 A -04/-03 2000 Mar 3
+-3 - -03 2004 May 30
+-4 - -04 2004 Jun 20
+-3 A -03/-02 2008 O 18
+-3 - -03
+Z America/La_Paz -4:32:36 - LMT 1890
+-4:32:36 - CMT 1931 O 15
+-4:32:36 1 BST 1932 Mar 21
+-4 - -04
+R B 1931 o - O 3 11 1 -
+R B 1932 1933 - Ap 1 0 0 -
+R B 1932 o - O 3 0 1 -
+R B 1949 1952 - D 1 0 1 -
+R B 1950 o - Ap 16 1 0 -
+R B 1951 1952 - Ap 1 0 0 -
+R B 1953 o - Mar 1 0 0 -
+R B 1963 o - D 9 0 1 -
+R B 1964 o - Mar 1 0 0 -
+R B 1965 o - Ja 31 0 1 -
+R B 1965 o - Mar 31 0 0 -
+R B 1965 o - D 1 0 1 -
+R B 1966 1968 - Mar 1 0 0 -
+R B 1966 1967 - N 1 0 1 -
+R B 1985 o - N 2 0 1 -
+R B 1986 o - Mar 15 0 0 -
+R B 1986 o - O 25 0 1 -
+R B 1987 o - F 14 0 0 -
+R B 1987 o - O 25 0 1 -
+R B 1988 o - F 7 0 0 -
+R B 1988 o - O 16 0 1 -
+R B 1989 o - Ja 29 0 0 -
+R B 1989 o - O 15 0 1 -
+R B 1990 o - F 11 0 0 -
+R B 1990 o - O 21 0 1 -
+R B 1991 o - F 17 0 0 -
+R B 1991 o - O 20 0 1 -
+R B 1992 o - F 9 0 0 -
+R B 1992 o - O 25 0 1 -
+R B 1993 o - Ja 31 0 0 -
+R B 1993 1995 - O Su>=11 0 1 -
+R B 1994 1995 - F Su>=15 0 0 -
+R B 1996 o - F 11 0 0 -
+R B 1996 o - O 6 0 1 -
+R B 1997 o - F 16 0 0 -
+R B 1997 o - O 6 0 1 -
+R B 1998 o - Mar 1 0 0 -
+R B 1998 o - O 11 0 1 -
+R B 1999 o - F 21 0 0 -
+R B 1999 o - O 3 0 1 -
+R B 2000 o - F 27 0 0 -
+R B 2000 2001 - O Su>=8 0 1 -
+R B 2001 2006 - F Su>=15 0 0 -
+R B 2002 o - N 3 0 1 -
+R B 2003 o - O 19 0 1 -
+R B 2004 o - N 2 0 1 -
+R B 2005 o - O 16 0 1 -
+R B 2006 o - N 5 0 1 -
+R B 2007 o - F 25 0 0 -
+R B 2007 o - O Su>=8 0 1 -
+R B 2008 2017 - O Su>=15 0 1 -
+R B 2008 2011 - F Su>=15 0 0 -
+R B 2012 o - F Su>=22 0 0 -
+R B 2013 2014 - F Su>=15 0 0 -
+R B 2015 o - F Su>=22 0 0 -
+R B 2016 2019 - F Su>=15 0 0 -
+R B 2018 o - N Su>=1 0 1 -
+Z America/Noronha -2:9:40 - LMT 1914
+-2 B -02/-01 1990 S 17
+-2 - -02 1999 S 30
+-2 B -02/-01 2000 O 15
+-2 - -02 2001 S 13
+-2 B -02/-01 2002 O
+-2 - -02
+Z America/Belem -3:13:56 - LMT 1914
+-3 B -03/-02 1988 S 12
+-3 - -03
+Z America/Santarem -3:38:48 - LMT 1914
+-4 B -04/-03 1988 S 12
+-4 - -04 2008 Jun 24
+-3 - -03
+Z America/Fortaleza -2:34 - LMT 1914
+-3 B -03/-02 1990 S 17
+-3 - -03 1999 S 30
+-3 B -03/-02 2000 O 22
+-3 - -03 2001 S 13
+-3 B -03/-02 2002 O
+-3 - -03
+Z America/Recife -2:19:36 - LMT 1914
+-3 B -03/-02 1990 S 17
+-3 - -03 1999 S 30
+-3 B -03/-02 2000 O 15
+-3 - -03 2001 S 13
+-3 B -03/-02 2002 O
+-3 - -03
+Z America/Araguaina -3:12:48 - LMT 1914
+-3 B -03/-02 1990 S 17
+-3 - -03 1995 S 14
+-3 B -03/-02 2003 S 24
+-3 - -03 2012 O 21
+-3 B -03/-02 2013 S
+-3 - -03
+Z America/Maceio -2:22:52 - LMT 1914
+-3 B -03/-02 1990 S 17
+-3 - -03 1995 O 13
+-3 B -03/-02 1996 S 4
+-3 - -03 1999 S 30
+-3 B -03/-02 2000 O 22
+-3 - -03 2001 S 13
+-3 B -03/-02 2002 O
+-3 - -03
+Z America/Bahia -2:34:4 - LMT 1914
+-3 B -03/-02 2003 S 24
+-3 - -03 2011 O 16
+-3 B -03/-02 2012 O 21
+-3 - -03
+Z America/Sao_Paulo -3:6:28 - LMT 1914
+-3 B -03/-02 1963 O 23
+-3 1 -02 1964
+-3 B -03/-02
+Z America/Campo_Grande -3:38:28 - LMT 1914
+-4 B -04/-03
+Z America/Cuiaba -3:44:20 - LMT 1914
+-4 B -04/-03 2003 S 24
+-4 - -04 2004 O
+-4 B -04/-03
+Z America/Porto_Velho -4:15:36 - LMT 1914
+-4 B -04/-03 1988 S 12
+-4 - -04
+Z America/Boa_Vista -4:2:40 - LMT 1914
+-4 B -04/-03 1988 S 12
+-4 - -04 1999 S 30
+-4 B -04/-03 2000 O 15
+-4 - -04
+Z America/Manaus -4:0:4 - LMT 1914
+-4 B -04/-03 1988 S 12
+-4 - -04 1993 S 28
+-4 B -04/-03 1994 S 22
+-4 - -04
+Z America/Eirunepe -4:39:28 - LMT 1914
+-5 B -05/-04 1988 S 12
+-5 - -05 1993 S 28
+-5 B -05/-04 1994 S 22
+-5 - -05 2008 Jun 24
+-4 - -04 2013 N 10
+-5 - -05
+Z America/Rio_Branco -4:31:12 - LMT 1914
+-5 B -05/-04 1988 S 12
+-5 - -05 2008 Jun 24
+-4 - -04 2013 N 10
+-5 - -05
+R x 1927 1931 - S 1 0 1 -
+R x 1928 1932 - Ap 1 0 0 -
+R x 1968 o - N 3 4u 1 -
+R x 1969 o - Mar 30 3u 0 -
+R x 1969 o - N 23 4u 1 -
+R x 1970 o - Mar 29 3u 0 -
+R x 1971 o - Mar 14 3u 0 -
+R x 1970 1972 - O Su>=9 4u 1 -
+R x 1972 1986 - Mar Su>=9 3u 0 -
+R x 1973 o - S 30 4u 1 -
+R x 1974 1987 - O Su>=9 4u 1 -
+R x 1987 o - Ap 12 3u 0 -
+R x 1988 1990 - Mar Su>=9 3u 0 -
+R x 1988 1989 - O Su>=9 4u 1 -
+R x 1990 o - S 16 4u 1 -
+R x 1991 1996 - Mar Su>=9 3u 0 -
+R x 1991 1997 - O Su>=9 4u 1 -
+R x 1997 o - Mar 30 3u 0 -
+R x 1998 o - Mar Su>=9 3u 0 -
+R x 1998 o - S 27 4u 1 -
+R x 1999 o - Ap 4 3u 0 -
+R x 1999 2010 - O Su>=9 4u 1 -
+R x 2000 2007 - Mar Su>=9 3u 0 -
+R x 2008 o - Mar 30 3u 0 -
+R x 2009 o - Mar Su>=9 3u 0 -
+R x 2010 o - Ap Su>=1 3u 0 -
+R x 2011 o - May Su>=2 3u 0 -
+R x 2011 o - Au Su>=16 4u 1 -
+R x 2012 2014 - Ap Su>=23 3u 0 -
+R x 2012 2014 - S Su>=2 4u 1 -
+R x 2016 2018 - May Su>=9 3u 0 -
+R x 2016 2018 - Au Su>=9 4u 1 -
+R x 2019 ma - Ap Su>=2 3u 0 -
+R x 2019 ma - S Su>=2 4u 1 -
+Z America/Santiago -4:42:45 - LMT 1890
+-4:42:45 - SMT 1910 Ja 10
+-5 - -05 1916 Jul
+-4:42:45 - SMT 1918 S 10
+-4 - -04 1919 Jul
+-4:42:45 - SMT 1927 S
+-5 x -05/-04 1932 S
+-4 - -04 1942 Jun
+-5 - -05 1942 Au
+-4 - -04 1946 Jul 15
+-4 1 -03 1946 S
+-4 - -04 1947 Ap
+-5 - -05 1947 May 21 23
+-4 x -04/-03
+Z America/Punta_Arenas -4:43:40 - LMT 1890
+-4:42:45 - SMT 1910 Ja 10
+-5 - -05 1916 Jul
+-4:42:45 - SMT 1918 S 10
+-4 - -04 1919 Jul
+-4:42:45 - SMT 1927 S
+-5 x -05/-04 1932 S
+-4 - -04 1942 Jun
+-5 - -05 1942 Au
+-4 - -04 1947 Ap
+-5 - -05 1947 May 21 23
+-4 x -04/-03 2016 D 4
+-3 - -03
+Z Pacific/Easter -7:17:28 - LMT 1890
+-7:17:28 - EMT 1932 S
+-7 x -07/-06 1982 Mar 14 3u
+-6 x -06/-05
+Z Antarctica/Palmer 0 - -00 1965
+-4 A -04/-03 1969 O 5
+-3 A -03/-02 1982 May
+-4 x -04/-03 2016 D 4
+-3 - -03
+R CO 1992 o - May 3 0 1 -
+R CO 1993 o - Ap 4 0 0 -
+Z America/Bogota -4:56:16 - LMT 1884 Mar 13
+-4:56:16 - BMT 1914 N 23
+-5 CO -05/-04
+R EC 1992 o - N 28 0 1 -
+R EC 1993 o - F 5 0 0 -
+Z America/Guayaquil -5:19:20 - LMT 1890
+-5:14 - QMT 1931
+-5 EC -05/-04
+Z Pacific/Galapagos -5:58:24 - LMT 1931
+-5 - -05 1986
+-6 EC -06/-05
+R FK 1937 1938 - S lastSu 0 1 -
+R FK 1938 1942 - Mar Su>=19 0 0 -
+R FK 1939 o - O 1 0 1 -
+R FK 1940 1942 - S lastSu 0 1 -
+R FK 1943 o - Ja 1 0 0 -
+R FK 1983 o - S lastSu 0 1 -
+R FK 1984 1985 - Ap lastSu 0 0 -
+R FK 1984 o - S 16 0 1 -
+R FK 1985 2000 - S Su>=9 0 1 -
+R FK 1986 2000 - Ap Su>=16 0 0 -
+R FK 2001 2010 - Ap Su>=15 2 0 -
+R FK 2001 2010 - S Su>=1 2 1 -
+Z Atlantic/Stanley -3:51:24 - LMT 1890
+-3:51:24 - SMT 1912 Mar 12
+-4 FK -04/-03 1983 May
+-3 FK -03/-02 1985 S 15
+-4 FK -04/-03 2010 S 5 2
+-3 - -03
+Z America/Cayenne -3:29:20 - LMT 1911 Jul
+-4 - -04 1967 O
+-3 - -03
+Z America/Guyana -3:52:39 - LMT 1911 Au
+-4 - -04 1915 Mar
+-3:45 - -0345 1975 Au
+-3 - -03 1992 Mar 29 1
+-4 - -04
+R y 1975 1988 - O 1 0 1 -
+R y 1975 1978 - Mar 1 0 0 -
+R y 1979 1991 - Ap 1 0 0 -
+R y 1989 o - O 22 0 1 -
+R y 1990 o - O 1 0 1 -
+R y 1991 o - O 6 0 1 -
+R y 1992 o - Mar 1 0 0 -
+R y 1992 o - O 5 0 1 -
+R y 1993 o - Mar 31 0 0 -
+R y 1993 1995 - O 1 0 1 -
+R y 1994 1995 - F lastSu 0 0 -
+R y 1996 o - Mar 1 0 0 -
+R y 1996 2001 - O Su>=1 0 1 -
+R y 1997 o - F lastSu 0 0 -
+R y 1998 2001 - Mar Su>=1 0 0 -
+R y 2002 2004 - Ap Su>=1 0 0 -
+R y 2002 2003 - S Su>=1 0 1 -
+R y 2004 2009 - O Su>=15 0 1 -
+R y 2005 2009 - Mar Su>=8 0 0 -
+R y 2010 ma - O Su>=1 0 1 -
+R y 2010 2012 - Ap Su>=8 0 0 -
+R y 2013 ma - Mar Su>=22 0 0 -
+Z America/Asuncion -3:50:40 - LMT 1890
+-3:50:40 - AMT 1931 O 10
+-4 - -04 1972 O
+-3 - -03 1974 Ap
+-4 y -04/-03
+R PE 1938 o - Ja 1 0 1 -
+R PE 1938 o - Ap 1 0 0 -
+R PE 1938 1939 - S lastSu 0 1 -
+R PE 1939 1940 - Mar Su>=24 0 0 -
+R PE 1986 1987 - Ja 1 0 1 -
+R PE 1986 1987 - Ap 1 0 0 -
+R PE 1990 o - Ja 1 0 1 -
+R PE 1990 o - Ap 1 0 0 -
+R PE 1994 o - Ja 1 0 1 -
+R PE 1994 o - Ap 1 0 0 -
+Z America/Lima -5:8:12 - LMT 1890
+-5:8:36 - LMT 1908 Jul 28
+-5 PE -05/-04
+Z Atlantic/South_Georgia -2:26:8 - LMT 1890
+-2 - -02
+Z America/Paramaribo -3:40:40 - LMT 1911
+-3:40:52 - PMT 1935
+-3:40:36 - PMT 1945 O
+-3:30 - -0330 1984 O
+-3 - -03
+R U 1923 1925 - O 1 0 0:30 -
+R U 1924 1926 - Ap 1 0 0 -
+R U 1933 1938 - O lastSu 0 0:30 -
+R U 1934 1941 - Mar lastSa 24 0 -
+R U 1939 o - O 1 0 0:30 -
+R U 1940 o - O 27 0 0:30 -
+R U 1941 o - Au 1 0 0:30 -
+R U 1942 o - D 14 0 0:30 -
+R U 1943 o - Mar 14 0 0 -
+R U 1959 o - May 24 0 0:30 -
+R U 1959 o - N 15 0 0 -
+R U 1960 o - Ja 17 0 1 -
+R U 1960 o - Mar 6 0 0 -
+R U 1965 o - Ap 4 0 1 -
+R U 1965 o - S 26 0 0 -
+R U 1968 o - May 27 0 0:30 -
+R U 1968 o - D 1 0 0 -
+R U 1970 o - Ap 25 0 1 -
+R U 1970 o - Jun 14 0 0 -
+R U 1972 o - Ap 23 0 1 -
+R U 1972 o - Jul 16 0 0 -
+R U 1974 o - Ja 13 0 1:30 -
+R U 1974 o - Mar 10 0 0:30 -
+R U 1974 o - S 1 0 0 -
+R U 1974 o - D 22 0 1 -
+R U 1975 o - Mar 30 0 0 -
+R U 1976 o - D 19 0 1 -
+R U 1977 o - Mar 6 0 0 -
+R U 1977 o - D 4 0 1 -
+R U 1978 1979 - Mar Su>=1 0 0 -
+R U 1978 o - D 17 0 1 -
+R U 1979 o - Ap 29 0 1 -
+R U 1980 o - Mar 16 0 0 -
+R U 1987 o - D 14 0 1 -
+R U 1988 o - F 28 0 0 -
+R U 1988 o - D 11 0 1 -
+R U 1989 o - Mar 5 0 0 -
+R U 1989 o - O 29 0 1 -
+R U 1990 o - F 25 0 0 -
+R U 1990 1991 - O Su>=21 0 1 -
+R U 1991 1992 - Mar Su>=1 0 0 -
+R U 1992 o - O 18 0 1 -
+R U 1993 o - F 28 0 0 -
+R U 2004 o - S 19 0 1 -
+R U 2005 o - Mar 27 2 0 -
+R U 2005 o - O 9 2 1 -
+R U 2006 2015 - Mar Su>=8 2 0 -
+R U 2006 2014 - O Su>=1 2 1 -
+Z America/Montevideo -3:44:51 - LMT 1908 Jun 10
+-3:44:51 - MMT 1920 May
+-4 - -04 1923 O
+-3:30 U -0330/-03 1942 D 14
+-3 U -03/-0230 1960
+-3 U -03/-02 1968
+-3 U -03/-0230 1970
+-3 U -03/-02 1974
+-3 U -03/-0130 1974 Mar 10
+-3 U -03/-0230 1974 D 22
+-3 U -03/-02
+Z America/Caracas -4:27:44 - LMT 1890
+-4:27:40 - CMT 1912 F 12
+-4:30 - -0430 1965
+-4 - -04 2007 D 9 3
+-4:30 - -0430 2016 May 1 2:30
+-4 - -04
+Z Etc/GMT 0 - GMT
+Z Etc/UTC 0 - UTC
+L Etc/GMT GMT
+L Etc/UTC Etc/Universal
+L Etc/UTC Etc/Zulu
+L Etc/GMT Etc/Greenwich
+L Etc/GMT Etc/GMT-0
+L Etc/GMT Etc/GMT+0
+L Etc/GMT Etc/GMT0
+Z Etc/GMT-14 14 - +14
+Z Etc/GMT-13 13 - +13
+Z Etc/GMT-12 12 - +12
+Z Etc/GMT-11 11 - +11
+Z Etc/GMT-10 10 - +10
+Z Etc/GMT-9 9 - +09
+Z Etc/GMT-8 8 - +08
+Z Etc/GMT-7 7 - +07
+Z Etc/GMT-6 6 - +06
+Z Etc/GMT-5 5 - +05
+Z Etc/GMT-4 4 - +04
+Z Etc/GMT-3 3 - +03
+Z Etc/GMT-2 2 - +02
+Z Etc/GMT-1 1 - +01
+Z Etc/GMT+1 -1 - -01
+Z Etc/GMT+2 -2 - -02
+Z Etc/GMT+3 -3 - -03
+Z Etc/GMT+4 -4 - -04
+Z Etc/GMT+5 -5 - -05
+Z Etc/GMT+6 -6 - -06
+Z Etc/GMT+7 -7 - -07
+Z Etc/GMT+8 -8 - -08
+Z Etc/GMT+9 -9 - -09
+Z Etc/GMT+10 -10 - -10
+Z Etc/GMT+11 -11 - -11
+Z Etc/GMT+12 -12 - -12
+Z Factory 0 - -00
+L Africa/Nairobi Africa/Asmera
+L Africa/Abidjan Africa/Timbuktu
+L America/Argentina/Catamarca America/Argentina/ComodRivadavia
+L America/Adak America/Atka
+L America/Argentina/Buenos_Aires America/Buenos_Aires
+L America/Argentina/Catamarca America/Catamarca
+L America/Panama America/Coral_Harbour
+L America/Argentina/Cordoba America/Cordoba
+L America/Tijuana America/Ensenada
+L America/Indiana/Indianapolis America/Fort_Wayne
+L America/Nuuk America/Godthab
+L America/Indiana/Indianapolis America/Indianapolis
+L America/Argentina/Jujuy America/Jujuy
+L America/Indiana/Knox America/Knox_IN
+L America/Kentucky/Louisville America/Louisville
+L America/Argentina/Mendoza America/Mendoza
+L America/Toronto America/Montreal
+L America/Rio_Branco America/Porto_Acre
+L America/Argentina/Cordoba America/Rosario
+L America/Tijuana America/Santa_Isabel
+L America/Denver America/Shiprock
+L America/Puerto_Rico America/Virgin
+L Pacific/Auckland Antarctica/South_Pole
+L Asia/Ashgabat Asia/Ashkhabad
+L Asia/Kolkata Asia/Calcutta
+L Asia/Shanghai Asia/Chongqing
+L Asia/Shanghai Asia/Chungking
+L Asia/Dhaka Asia/Dacca
+L Asia/Shanghai Asia/Harbin
+L Asia/Urumqi Asia/Kashgar
+L Asia/Kathmandu Asia/Katmandu
+L Asia/Macau Asia/Macao
+L Asia/Yangon Asia/Rangoon
+L Asia/Ho_Chi_Minh Asia/Saigon
+L Asia/Jerusalem Asia/Tel_Aviv
+L Asia/Thimphu Asia/Thimbu
+L Asia/Makassar Asia/Ujung_Pandang
+L Asia/Ulaanbaatar Asia/Ulan_Bator
+L Atlantic/Faroe Atlantic/Faeroe
+L Europe/Oslo Atlantic/Jan_Mayen
+L Australia/Sydney Australia/ACT
+L Australia/Sydney Australia/Canberra
+L Australia/Hobart Australia/Currie
+L Australia/Lord_Howe Australia/LHI
+L Australia/Sydney Australia/NSW
+L Australia/Darwin Australia/North
+L Australia/Brisbane Australia/Queensland
+L Australia/Adelaide Australia/South
+L Australia/Hobart Australia/Tasmania
+L Australia/Melbourne Australia/Victoria
+L Australia/Perth Australia/West
+L Australia/Broken_Hill Australia/Yancowinna
+L America/Rio_Branco Brazil/Acre
+L America/Noronha Brazil/DeNoronha
+L America/Sao_Paulo Brazil/East
+L America/Manaus Brazil/West
+L America/Halifax Canada/Atlantic
+L America/Winnipeg Canada/Central
+L America/Toronto Canada/Eastern
+L America/Edmonton Canada/Mountain
+L America/St_Johns Canada/Newfoundland
+L America/Vancouver Canada/Pacific
+L America/Regina Canada/Saskatchewan
+L America/Whitehorse Canada/Yukon
+L America/Santiago Chile/Continental
+L Pacific/Easter Chile/EasterIsland
+L America/Havana Cuba
+L Africa/Cairo Egypt
+L Europe/Dublin Eire
+L Etc/UTC Etc/UCT
+L Europe/London Europe/Belfast
+L Europe/Chisinau Europe/Tiraspol
+L Europe/London GB
+L Europe/London GB-Eire
+L Etc/GMT GMT+0
+L Etc/GMT GMT-0
+L Etc/GMT GMT0
+L Etc/GMT Greenwich
+L Asia/Hong_Kong Hongkong
+L Atlantic/Reykjavik Iceland
+L Asia/Tehran Iran
+L Asia/Jerusalem Israel
+L America/Jamaica Jamaica
+L Asia/Tokyo Japan
+L Pacific/Kwajalein Kwajalein
+L Africa/Tripoli Libya
+L America/Tijuana Mexico/BajaNorte
+L America/Mazatlan Mexico/BajaSur
+L America/Mexico_City Mexico/General
+L Pacific/Auckland NZ
+L Pacific/Chatham NZ-CHAT
+L America/Denver Navajo
+L Asia/Shanghai PRC
+L Pacific/Kanton Pacific/Enderbury
+L Pacific/Honolulu Pacific/Johnston
+L Pacific/Pohnpei Pacific/Ponape
+L Pacific/Pago_Pago Pacific/Samoa
+L Pacific/Chuuk Pacific/Truk
+L Pacific/Chuuk Pacific/Yap
+L Europe/Warsaw Poland
+L Europe/Lisbon Portugal
+L Asia/Taipei ROC
+L Asia/Seoul ROK
+L Asia/Singapore Singapore
+L Europe/Istanbul Turkey
+L Etc/UTC UCT
+L America/Anchorage US/Alaska
+L America/Adak US/Aleutian
+L America/Phoenix US/Arizona
+L America/Chicago US/Central
+L America/Indiana/Indianapolis US/East-Indiana
+L America/New_York US/Eastern
+L Pacific/Honolulu US/Hawaii
+L America/Indiana/Knox US/Indiana-Starke
+L America/Detroit US/Michigan
+L America/Denver US/Mountain
+L America/Los_Angeles US/Pacific
+L Pacific/Pago_Pago US/Samoa
+L Etc/UTC UTC
+L Etc/UTC Universal
+L Europe/Moscow W-SU
+L Etc/UTC Zulu
diff --git a/src/timezone/known_abbrevs.txt b/src/timezone/known_abbrevs.txt
new file mode 100644
index 0000000..6539e1c
--- /dev/null
+++ b/src/timezone/known_abbrevs.txt
@@ -0,0 +1,104 @@
++00 0
++00 0 D
++01 3600
++02 7200
++02 7200 D
++03 10800
++0330 12600
++04 14400
++0430 16200
++0430 16200 D
++05 18000
++0530 19800
++0545 20700
++06 21600
++0630 23400
++07 25200
++08 28800
++0845 31500
++09 32400
++10 36000
++1030 37800
++11 39600
++11 39600 D
++12 43200
++12 43200 D
++1245 45900
++13 46800
++13 46800 D
++1345 49500 D
++14 50400
+-00 0
+-01 -3600
+-02 -7200
+-02 -7200 D
+-03 -10800
+-03 -10800 D
+-04 -14400
+-05 -18000
+-05 -18000 D
+-06 -21600
+-07 -25200
+-08 -28800
+-09 -32400
+-0930 -34200
+-10 -36000
+-11 -39600
+-12 -43200
+ACDT 37800 D
+ACST 34200
+ADT -10800 D
+AEDT 39600 D
+AEST 36000
+AKDT -28800 D
+AKST -32400
+AST -14400
+AWST 28800
+BST 3600 D
+CAT 7200
+CDT -14400 D
+CDT -18000 D
+CEST 7200 D
+CET 3600
+CST -18000
+CST -21600
+CST 28800
+ChST 36000
+EAT 10800
+EDT -14400 D
+EEST 10800 D
+EET 7200
+EST -18000
+GMT 0
+GMT 0 D
+HDT -32400 D
+HKT 28800
+HST -36000
+IDT 10800 D
+IST 19800
+IST 3600
+IST 7200
+JST 32400
+KST 32400
+MDT -21600 D
+MEST 7200 D
+MET 3600
+MSK 10800
+MST -25200
+NDT -9000 D
+NST -12600
+NZDT 46800 D
+NZST 43200
+PDT -25200 D
+PKT 18000
+PST -28800
+PST 28800
+SAST 7200
+SST -39600
+UTC 0
+WAT 3600
+WEST 3600 D
+WET 0
+WIB 25200
+WIT 32400
+WITA 28800
diff --git a/src/timezone/localtime.c b/src/timezone/localtime.c
new file mode 100644
index 0000000..fa3c059
--- /dev/null
+++ b/src/timezone/localtime.c
@@ -0,0 +1,1904 @@
+/* Convert timestamp from pg_time_t to struct pg_tm. */
+
+/*
+ * This file is in the public domain, so clarified as of
+ * 1996-06-05 by Arthur David Olson.
+ *
+ * IDENTIFICATION
+ * src/timezone/localtime.c
+ */
+
+/*
+ * Leap second handling from Bradley White.
+ * POSIX-style TZ environment variable handling from Guy Harris.
+ */
+
+/* this file needs to build in both frontend and backend contexts */
+#include "c.h"
+
+#include <fcntl.h>
+
+#include "datatype/timestamp.h"
+#include "pgtz.h"
+
+#include "private.h"
+#include "tzfile.h"
+
+
+#ifndef WILDABBR
+/*
+ * Someone might make incorrect use of a time zone abbreviation:
+ * 1. They might reference tzname[0] before calling tzset (explicitly
+ * or implicitly).
+ * 2. They might reference tzname[1] before calling tzset (explicitly
+ * or implicitly).
+ * 3. They might reference tzname[1] after setting to a time zone
+ * in which Daylight Saving Time is never observed.
+ * 4. They might reference tzname[0] after setting to a time zone
+ * in which Standard Time is never observed.
+ * 5. They might reference tm.tm_zone after calling offtime.
+ * What's best to do in the above cases is open to debate;
+ * for now, we just set things up so that in any of the five cases
+ * WILDABBR is used. Another possibility: initialize tzname[0] to the
+ * string "tzname[0] used before set", and similarly for the other cases.
+ * And another: initialize tzname[0] to "ERA", with an explanation in the
+ * manual page of what this "time zone abbreviation" means (doing this so
+ * that tzname[0] has the "normal" length of three characters).
+ */
+#define WILDABBR " "
+#endif /* !defined WILDABBR */
+
+static const char wildabbr[] = WILDABBR;
+
+static const char gmt[] = "GMT";
+
+/*
+ * The DST rules to use if a POSIX TZ string has no rules.
+ * Default to US rules as of 2017-05-07.
+ * POSIX does not specify the default DST rules;
+ * for historical reasons, US rules are a common default.
+ */
+#define TZDEFRULESTRING ",M3.2.0,M11.1.0"
+
+/* structs ttinfo, lsinfo, state have been moved to pgtz.h */
+
+enum r_type
+{
+ JULIAN_DAY, /* Jn = Julian day */
+ DAY_OF_YEAR, /* n = day of year */
+ MONTH_NTH_DAY_OF_WEEK /* Mm.n.d = month, week, day of week */
+};
+
+struct rule
+{
+ enum r_type r_type; /* type of rule */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ int32 r_time; /* transition time of rule */
+};
+
+/*
+ * Prototypes for static functions.
+ */
+
+static struct pg_tm *gmtsub(pg_time_t const *, int32, struct pg_tm *);
+static bool increment_overflow(int *, int);
+static bool increment_overflow_time(pg_time_t *, int32);
+static int64 leapcorr(struct state const *, pg_time_t);
+static struct pg_tm *timesub(pg_time_t const *, int32, struct state const *,
+ struct pg_tm *);
+static bool typesequiv(struct state const *, int, int);
+
+
+/*
+ * Section 4.12.3 of X3.159-1989 requires that
+ * Except for the strftime function, these functions [asctime,
+ * ctime, gmtime, localtime] return values in one of two static
+ * objects: a broken-down time structure and an array of char.
+ * Thanks to Paul Eggert for noting this.
+ */
+
+static struct pg_tm tm;
+
+/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */
+static void
+init_ttinfo(struct ttinfo *s, int32 utoff, bool isdst, int desigidx)
+{
+ s->tt_utoff = utoff;
+ s->tt_isdst = isdst;
+ s->tt_desigidx = desigidx;
+ s->tt_ttisstd = false;
+ s->tt_ttisut = false;
+}
+
+static int32
+detzcode(const char *const codep)
+{
+ int32 result;
+ int i;
+ int32 one = 1;
+ int32 halfmaxval = one << (32 - 2);
+ int32 maxval = halfmaxval - 1 + halfmaxval;
+ int32 minval = -1 - maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80)
+ {
+ /*
+ * Do two's-complement negation even on non-two's-complement machines.
+ * If the result would be minval - 1, return minval.
+ */
+ result -= !TWOS_COMPLEMENT(int32) && result != 0;
+ result += minval;
+ }
+ return result;
+}
+
+static int64
+detzcode64(const char *const codep)
+{
+ uint64 result;
+ int i;
+ int64 one = 1;
+ int64 halfmaxval = one << (64 - 2);
+ int64 maxval = halfmaxval - 1 + halfmaxval;
+ int64 minval = -TWOS_COMPLEMENT(int64) - maxval;
+
+ result = codep[0] & 0x7f;
+ for (i = 1; i < 8; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+
+ if (codep[0] & 0x80)
+ {
+ /*
+ * Do two's-complement negation even on non-two's-complement machines.
+ * If the result would be minval - 1, return minval.
+ */
+ result -= !TWOS_COMPLEMENT(int64) && result != 0;
+ result += minval;
+ }
+ return result;
+}
+
+static bool
+differ_by_repeat(const pg_time_t t1, const pg_time_t t0)
+{
+ if (TYPE_BIT(pg_time_t) - TYPE_SIGNED(pg_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)
+ + 4 * TZ_MAX_TIMES];
+};
+
+/* Local storage needed for 'tzloadbody'. */
+union local_storage
+{
+ /* The results of analyzing the file's contents after it is opened. */
+ struct file_analysis
+ {
+ /* The input buffer. */
+ union input_buffer u;
+
+ /* A temporary state used for parsing a TZ string in the file. */
+ struct state st;
+ } u;
+
+ /* We don't need the "fullname" member */
+};
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ * format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
+ * success, an errno value on failure.
+ * PG: If "canonname" is not NULL, then on success the canonical spelling of
+ * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
+ */
+static int
+tzloadbody(char const *name, char *canonname, struct state *sp, bool doextend,
+ union local_storage *lsp)
+{
+ int i;
+ int fid;
+ int stored;
+ ssize_t nread;
+ union input_buffer *up = &lsp->u.u;
+ int tzheadsize = sizeof(struct tzhead);
+
+ sp->goback = sp->goahead = false;
+
+ if (!name)
+ {
+ name = TZDEFAULT;
+ if (!name)
+ return EINVAL;
+ }
+
+ if (name[0] == ':')
+ ++name;
+
+ fid = pg_open_tzfile(name, canonname);
+ if (fid < 0)
+ return ENOENT; /* pg_open_tzfile may not set errno */
+
+ nread = read(fid, up->buf, sizeof up->buf);
+ if (nread < tzheadsize)
+ {
+ int err = nread < 0 ? errno : EINVAL;
+
+ close(fid);
+ return err;
+ }
+ if (close(fid) < 0)
+ return errno;
+ for (stored = 4; stored <= 8; stored *= 2)
+ {
+ int32 ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int32 ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+ int64 prevtr = 0;
+ int32 prevcorr = 0;
+ int32 leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ int32 timecnt = detzcode(up->tzhead.tzh_timecnt);
+ int32 typecnt = detzcode(up->tzhead.tzh_typecnt);
+ int32 charcnt = detzcode(up->tzhead.tzh_charcnt);
+ char const *p = up->buf + tzheadsize;
+
+ /*
+ * Although tzfile(5) currently requires typecnt to be nonzero,
+ * support future formats that may allow zero typecnt in files that
+ * have a TZ string and no transitions.
+ */
+ if (!(0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
+ && 0 <= typecnt && typecnt < TZ_MAX_TYPES
+ && 0 <= timecnt && timecnt < TZ_MAX_TIMES
+ && 0 <= charcnt && charcnt < TZ_MAX_CHARS
+ && (ttisstdcnt == typecnt || ttisstdcnt == 0)
+ && (ttisutcnt == typecnt || ttisutcnt == 0)))
+ return EINVAL;
+ if (nread
+ < (tzheadsize /* struct tzhead */
+ + timecnt * stored /* ats */
+ + timecnt /* types */
+ + typecnt * 6 /* ttinfos */
+ + charcnt /* chars */
+ + leapcnt * (stored + 4) /* lsinfos */
+ + ttisstdcnt /* ttisstds */
+ + ttisutcnt)) /* ttisuts */
+ return EINVAL;
+ sp->leapcnt = leapcnt;
+ sp->timecnt = timecnt;
+ sp->typecnt = typecnt;
+ sp->charcnt = charcnt;
+
+ /*
+ * Read transitions, discarding those out of pg_time_t range. But
+ * pretend the last transition before TIME_T_MIN occurred at
+ * TIME_T_MIN.
+ */
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i)
+ {
+ int64 at
+ = stored == 4 ? detzcode(p) : detzcode64(p);
+
+ sp->types[i] = at <= TIME_T_MAX;
+ if (sp->types[i])
+ {
+ pg_time_t attime
+ = ((TYPE_SIGNED(pg_time_t) ? at < TIME_T_MIN : at < 0)
+ ? TIME_T_MIN : at);
+
+ if (timecnt && attime <= sp->ats[timecnt - 1])
+ {
+ if (attime < sp->ats[timecnt - 1])
+ return EINVAL;
+ sp->types[i - 1] = 0;
+ timecnt--;
+ }
+ sp->ats[timecnt++] = attime;
+ }
+ p += stored;
+ }
+
+ timecnt = 0;
+ for (i = 0; i < sp->timecnt; ++i)
+ {
+ unsigned char typ = *p++;
+
+ if (sp->typecnt <= typ)
+ return EINVAL;
+ if (sp->types[i])
+ sp->types[timecnt++] = typ;
+ }
+ sp->timecnt = timecnt;
+ for (i = 0; i < sp->typecnt; ++i)
+ {
+ struct ttinfo *ttisp;
+ unsigned char isdst,
+ desigidx;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_utoff = detzcode(p);
+ p += 4;
+ isdst = *p++;
+ if (!(isdst < 2))
+ return EINVAL;
+ ttisp->tt_isdst = isdst;
+ desigidx = *p++;
+ if (!(desigidx < sp->charcnt))
+ return EINVAL;
+ ttisp->tt_desigidx = desigidx;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+
+ /* Read leap seconds, discarding those out of pg_time_t range. */
+ leapcnt = 0;
+ for (i = 0; i < sp->leapcnt; ++i)
+ {
+ int64 tr = stored == 4 ? detzcode(p) : detzcode64(p);
+ int32 corr = detzcode(p + stored);
+
+ p += stored + 4;
+ /* Leap seconds cannot occur before the Epoch. */
+ if (tr < 0)
+ 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 correction must
+ * differ from the previous one's by 1 second.
+ */
+ if (tr - prevtr < 28 * SECSPERDAY - 1
+ || (corr != prevcorr - 1 && corr != prevcorr + 1))
+ return EINVAL;
+ sp->lsis[leapcnt].ls_trans = prevtr = tr;
+ sp->lsis[leapcnt].ls_corr = prevcorr = corr;
+ leapcnt++;
+ }
+ }
+ sp->leapcnt = leapcnt;
+
+ for (i = 0; i < sp->typecnt; ++i)
+ {
+ struct ttinfo *ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = false;
+ else
+ {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisstd = *p++;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i)
+ {
+ struct ttinfo *ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisutcnt == 0)
+ ttisp->tt_ttisut = false;
+ else
+ {
+ if (*p != true && *p != false)
+ return EINVAL;
+ ttisp->tt_ttisut = *p++;
+ }
+ }
+
+ /*
+ * If this is an old file, we're done.
+ */
+ if (up->tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - up->buf;
+ memmove(up->buf, p, nread);
+ }
+ if (doextend && nread > 2 &&
+ up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES)
+ {
+ struct state *ts = &lsp->u.st;
+
+ up->buf[nread - 1] = '\0';
+ if (tzparse(&up->buf[1], ts, false))
+ {
+ /*
+ * Attempt to reuse existing abbreviations. Without this,
+ * America/Anchorage would be right on the edge after 2037 when
+ * TZ_MAX_CHARS is 50, as sp->charcnt equals 40 (for LMT AST AWT
+ * APT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for
+ * AKST AKDT). Reusing means sp->charcnt can stay 40 in this
+ * example.
+ */
+ int gotabbr = 0;
+ int charcnt = sp->charcnt;
+
+ for (i = 0; i < ts->typecnt; i++)
+ {
+ char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx;
+ int j;
+
+ for (j = 0; j < charcnt; j++)
+ if (strcmp(sp->chars + j, tsabbr) == 0)
+ {
+ ts->ttis[i].tt_desigidx = j;
+ gotabbr++;
+ break;
+ }
+ if (!(j < charcnt))
+ {
+ int tsabbrlen = strlen(tsabbr);
+
+ if (j + tsabbrlen < TZ_MAX_CHARS)
+ {
+ strcpy(sp->chars + j, tsabbr);
+ charcnt = j + tsabbrlen + 1;
+ ts->ttis[i].tt_desigidx = j;
+ gotabbr++;
+ }
+ }
+ }
+ if (gotabbr == ts->typecnt)
+ {
+ sp->charcnt = charcnt;
+
+ /*
+ * Ignore any trailing, no-op transitions generated by zic as
+ * they don't help here and can run afoul of bugs in zic 2016j
+ * or earlier.
+ */
+ while (1 < sp->timecnt
+ && (sp->types[sp->timecnt - 1]
+ == sp->types[sp->timecnt - 2]))
+ sp->timecnt--;
+
+ for (i = 0; i < ts->timecnt; 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]);
+ 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];
+ }
+ }
+ }
+ if (sp->typecnt == 0)
+ return EINVAL;
+ if (sp->timecnt > 1)
+ {
+ for (i = 1; i < sp->timecnt; ++i)
+ if (typesequiv(sp, sp->types[i], sp->types[0]) &&
+ differ_by_repeat(sp->ats[i], sp->ats[0]))
+ {
+ sp->goback = true;
+ break;
+ }
+ for (i = sp->timecnt - 2; i >= 0; --i)
+ if (typesequiv(sp, sp->types[sp->timecnt - 1],
+ sp->types[i]) &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[i]))
+ {
+ sp->goahead = true;
+ break;
+ }
+ }
+
+ /*
+ * Infer sp->defaulttype from the data. Although this default type is
+ * always zero for data from recent tzdb releases, things are trickier for
+ * data from tzdb 2018e or earlier.
+ *
+ * The first set of heuristics work around bugs in 32-bit data generated
+ * by tzdb 2013c or earlier. The workaround is for zones like
+ * Australia/Macquarie where timestamps before the first transition have a
+ * time type that is not the earliest standard-time type. See:
+ * https://mm.icann.org/pipermail/tz/2013-May/019368.html
+ */
+
+ /*
+ * If type 0 is unused in transitions, it's the type to use for early
+ * times.
+ */
+ for (i = 0; i < sp->timecnt; ++i)
+ if (sp->types[i] == 0)
+ break;
+ i = i < sp->timecnt ? -1 : 0;
+
+ /*
+ * Absent the above, if there are transition times and the first
+ * transition is to a daylight time find the standard type less than and
+ * closest to the type of the first transition.
+ */
+ if (i < 0 && sp->timecnt > 0 && sp->ttis[sp->types[0]].tt_isdst)
+ {
+ i = sp->types[0];
+ while (--i >= 0)
+ if (!sp->ttis[i].tt_isdst)
+ break;
+ }
+
+ /*
+ * The next heuristics are for data generated by tzdb 2018e or earlier,
+ * for zones like EST5EDT where the first transition is to DST.
+ */
+
+ /*
+ * If no result yet, find the first standard type. If there is none, punt
+ * to type zero.
+ */
+ if (i < 0)
+ {
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt)
+ {
+ i = 0;
+ break;
+ }
+ }
+
+ /*
+ * A simple 'sp->defaulttype = 0;' would suffice here if we didn't have to
+ * worry about 2018e-or-earlier data. Even simpler would be to remove the
+ * defaulttype member and just use 0 in its place.
+ */
+ sp->defaulttype = i;
+
+ return 0;
+}
+
+/* Load tz data from the file named NAME into *SP. Read extended
+ * format if DOEXTEND. Return 0 on success, an errno value on failure.
+ * PG: If "canonname" is not NULL, then on success the canonical spelling of
+ * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
+ */
+int
+tzload(const char *name, char *canonname, struct state *sp, bool doextend)
+{
+ union local_storage *lsp = malloc(sizeof *lsp);
+
+ if (!lsp)
+ return errno;
+ else
+ {
+ int err = tzloadbody(name, canonname, sp, doextend, lsp);
+
+ free(lsp);
+ return err;
+ }
+}
+
+static bool
+typesequiv(const struct state *sp, int a, int b)
+{
+ bool result;
+
+ if (sp == NULL ||
+ a < 0 || a >= sp->typecnt ||
+ b < 0 || b >= sp->typecnt)
+ result = false;
+ else
+ {
+ const struct ttinfo *ap = &sp->ttis[a];
+ const struct ttinfo *bp = &sp->ttis[b];
+
+ result = (ap->tt_utoff == bp->tt_utoff
+ && ap->tt_isdst == bp->tt_isdst
+ && ap->tt_ttisstd == bp->tt_ttisstd
+ && ap->tt_ttisut == bp->tt_ttisut
+ && (strcmp(&sp->chars[ap->tt_desigidx],
+ &sp->chars[bp->tt_desigidx])
+ == 0));
+ }
+ return result;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+ * Given a pointer into a timezone string, scan until a character that is not
+ * a valid character in a time zone abbreviation is found.
+ * Return a pointer to that character.
+ */
+
+static const char *
+getzname(const char *strp)
+{
+ char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*
+ * Given a pointer into an extended timezone string, scan until the ending
+ * delimiter of the time zone abbreviation is located.
+ * Return a pointer to the delimiter.
+ *
+ * As with getzname above, the legal character set is actually quite
+ * restricted, with other characters producing undefined results.
+ * We don't do any checking here; checking is done later in common-case code.
+ */
+
+static const char *
+getqzname(const char *strp, const int delim)
+{
+ int c;
+
+ while ((c = *strp) != '\0' && c != delim)
+ ++strp;
+ return strp;
+}
+
+/*
+ * Given a pointer into a timezone string, extract a number from that string.
+ * Check that the number is within a specified range; if it is not, return
+ * NULL.
+ * Otherwise, return a pointer to the first character not part of the number.
+ */
+
+static const char *
+getnum(const char *strp, int *const nump, const int min, const int max)
+{
+ char c;
+ int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do
+ {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*
+ * Given a pointer into a timezone string, extract a number of seconds,
+ * in hh[:mm[:ss]] form, from the string.
+ * If any error occurs, return NULL.
+ * Otherwise, return a pointer to the first character not part of the number
+ * of seconds.
+ */
+
+static const char *
+getsecs(const char *strp, int32 *const secsp)
+{
+ int num;
+
+ /*
+ * 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ * "M10.4.6/26", which does not conform to Posix, but which specifies the
+ * equivalent of "02:00 on the first Sunday on or after 23 Oct".
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (int32) SECSPERHOUR;
+ if (*strp == ':')
+ {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':')
+ {
+ ++strp;
+ /* 'SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*
+ * Given a pointer into a timezone string, extract an offset, in
+ * [+-]hh[:mm[:ss]] form, from the string.
+ * If any error occurs, return NULL.
+ * Otherwise, return a pointer to the first character not part of the time.
+ */
+
+static const char *
+getoffset(const char *strp, int32 *const offsetp)
+{
+ bool neg = false;
+
+ if (*strp == '-')
+ {
+ neg = true;
+ ++strp;
+ }
+ else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*
+ * Given a pointer into a timezone string, extract a rule in the form
+ * date[/time]. See POSIX section 8 for the format of "date" and "time".
+ * If a valid rule is not found, return NULL.
+ * Otherwise, return a pointer to the first character not part of the rule.
+ */
+
+static const char *
+getrule(const char *strp, struct rule *const rulep)
+{
+ if (*strp == 'J')
+ {
+ /*
+ * Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ }
+ else if (*strp == 'M')
+ {
+ /*
+ * Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ }
+ else if (is_digit(*strp))
+ {
+ /*
+ * Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ }
+ else
+ return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/')
+ {
+ /*
+ * Time specified.
+ */
+ ++strp;
+ strp = getoffset(strp, &rulep->r_time);
+ }
+ else
+ rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*
+ * Given a year, a rule, and the offset from UT at the time that rule takes
+ * effect, calculate the year-relative time that rule takes effect.
+ */
+
+static int32
+transtime(const int year, const struct rule *const rulep,
+ const int32 offset)
+{
+ bool leapyear;
+ int32 value;
+ int i;
+ int d,
+ m1,
+ yy0,
+ yy1,
+ yy2,
+ dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type)
+ {
+
+ case JULIAN_DAY:
+
+ /*
+ * Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ * years. In non-leap years, or if the day number is 59 or less,
+ * just add SECSPERDAY times the day number-1 to the time of
+ * January 1, midnight, to get the day.
+ */
+ value = (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+
+ /*
+ * n - day of year. Just add SECSPERDAY times the day number to
+ * the time of January 1, midnight, to get the day.
+ */
+ value = rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+
+ /*
+ * Mm.n.d - nth "dth day" of month m.
+ */
+
+ /*
+ * Use Zeller's Congruence to get day-of-week of first day of
+ * month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ * "dow" is the day-of-week of the first day of the month. Get the
+ * day-of-month (zero-origin) of the first "dow" day of the month.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i)
+ {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[(int) leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ * "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value = d * SECSPERDAY;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[(int) leapyear][i] * SECSPERDAY;
+ break;
+ }
+
+ /*
+ * "value" is the year-relative time of 00:00:00 UT on the day in
+ * question. To get the year-relative time of the specified local time on
+ * that day, add the transition time and the current offset from UT.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*
+ * Given a POSIX section 8-style TZ string, fill in the rule tables as
+ * appropriate.
+ * Returns true on success, false on failure.
+ */
+bool
+tzparse(const char *name, struct state *sp, bool lastditch)
+{
+ const char *stdname;
+ const char *dstname = NULL;
+ size_t stdlen;
+ size_t dstlen;
+ size_t charcnt;
+ int32 stdoffset;
+ int32 dstoffset;
+ char *cp;
+ bool load_ok;
+
+ stdname = name;
+ if (lastditch)
+ {
+ /* Unlike IANA, don't assume name is exactly "GMT" */
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ stdoffset = 0;
+ }
+ 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 (*name == '\0') /* we allow empty STD abbrev, unlike IANA */
+ return false;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return false;
+ }
+ charcnt = stdlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+
+ /*
+ * The IANA code always tries to tzload(TZDEFRULES) here. We do not want
+ * to do that; it would be bad news in the lastditch case, where we can't
+ * assume pg_open_tzfile() is sane yet. Moreover, if we did load it and
+ * it contains leap-second-dependent info, that would cause problems too.
+ * Finally, IANA has deprecated the TZDEFRULES feature, so it presumably
+ * will die at some point. Desupporting it now seems like good
+ * future-proofing.
+ */
+ load_ok = false;
+ sp->goback = sp->goahead = false; /* simulate failed tzload() */
+ sp->leapcnt = 0; /* intentionally assume no leap seconds */
+
+ if (*name != '\0')
+ {
+ if (*name == '<')
+ {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return false;
+ dstlen = name - dstname;
+ name++;
+ }
+ else
+ {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST abbr. */
+ }
+ if (!dstlen)
+ return false;
+ charcnt += dstlen + 1;
+ if (sizeof sp->chars < charcnt)
+ return false;
+ if (*name != '\0' && *name != ',' && *name != ';')
+ {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return false;
+ }
+ else
+ dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && !load_ok)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';')
+ {
+ struct rule start;
+ struct rule end;
+ int year;
+ int yearlim;
+ int timecnt;
+ pg_time_t janfirst;
+ int32 janoffset = 0;
+ int yearbeg;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return false;
+ if (*name++ != ',')
+ return false;
+ if ((name = getrule(name, &end)) == NULL)
+ return false;
+ if (*name != '\0')
+ return false;
+ sp->typecnt = 2; /* standard time and DST */
+
+ /*
+ * Two transitions per year, from EPOCH_YEAR forward.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
+ sp->defaulttype = 0;
+ timecnt = 0;
+ janfirst = 0;
+ yearbeg = EPOCH_YEAR;
+
+ do
+ {
+ int32 yearsecs
+ = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY;
+
+ yearbeg--;
+ if (increment_overflow_time(&janfirst, -yearsecs))
+ {
+ janoffset = -yearsecs;
+ break;
+ }
+ } while (EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg);
+
+ yearlim = yearbeg + YEARSPERREPEAT + 1;
+ for (year = yearbeg; year < yearlim; year++)
+ {
+ int32
+ starttime = transtime(year, &start, stdoffset),
+ endtime = transtime(year, &end, dstoffset);
+ int32
+ yearsecs = (year_lengths[isleap(year)]
+ * SECSPERDAY);
+ bool reversed = endtime < starttime;
+
+ if (reversed)
+ {
+ int32 swap = starttime;
+
+ starttime = endtime;
+ endtime = swap;
+ }
+ if (reversed
+ || (starttime < endtime
+ && (endtime - starttime
+ < (yearsecs
+ + (stdoffset - dstoffset)))))
+ {
+ if (TZ_MAX_TIMES - 2 < timecnt)
+ break;
+ sp->ats[timecnt] = janfirst;
+ if (!increment_overflow_time
+ (&sp->ats[timecnt],
+ janoffset + starttime))
+ sp->types[timecnt++] = !reversed;
+ sp->ats[timecnt] = janfirst;
+ if (!increment_overflow_time
+ (&sp->ats[timecnt],
+ janoffset + endtime))
+ {
+ sp->types[timecnt++] = reversed;
+ yearlim = year + YEARSPERREPEAT + 1;
+ }
+ }
+ if (increment_overflow_time
+ (&janfirst, janoffset + yearsecs))
+ break;
+ janoffset = 0;
+ }
+ sp->timecnt = timecnt;
+ if (!timecnt)
+ {
+ sp->ttis[0] = sp->ttis[1];
+ sp->typecnt = 1; /* Perpetual DST. */
+ }
+ else if (YEARSPERREPEAT < year - yearbeg)
+ sp->goback = sp->goahead = true;
+ }
+ else
+ {
+ int32 theirstdoffset;
+ int32 theirdstoffset;
+ int32 theiroffset;
+ bool isdst;
+ int i;
+ int j;
+
+ if (*name != '\0')
+ return false;
+
+ /*
+ * Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i)
+ {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst)
+ {
+ theirstdoffset =
+ -sp->ttis[j].tt_utoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i)
+ {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst)
+ {
+ theirdstoffset =
+ -sp->ttis[j].tt_utoff;
+ break;
+ }
+ }
+
+ /*
+ * Initially we're assumed to be in standard time.
+ */
+ isdst = false;
+ theiroffset = theirstdoffset;
+
+ /*
+ * Now juggle transition times and types tracking offsets as you
+ * do.
+ */
+ for (i = 0; i < sp->timecnt; ++i)
+ {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisut)
+ {
+ /* No adjustment to transition time */
+ }
+ else
+ {
+ /*
+ * If daylight saving time is in effect, and the
+ * transition time was not specified as standard time, add
+ * the daylight saving time offset to the transition time;
+ * otherwise, add the standard time offset to the
+ * transition time.
+ */
+ /*
+ * Transitions from DST to DDST will effectively disappear
+ * since POSIX provides for only one DST offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd)
+ {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ }
+ else
+ {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_utoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else
+ theirstdoffset = theiroffset;
+ }
+
+ /*
+ * Finally, fill in ttis.
+ */
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1);
+ sp->typecnt = 2;
+ sp->defaulttype = 0;
+ }
+ }
+ else
+ {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0);
+ sp->defaulttype = 0;
+ }
+ sp->charcnt = charcnt;
+ cp = sp->chars;
+ memcpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0)
+ {
+ memcpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return true;
+}
+
+static void
+gmtload(struct state *const sp)
+{
+ if (tzload(gmt, NULL, sp, true) != 0)
+ tzparse(gmt, sp, true);
+}
+
+
+/*
+ * The easy way to behave "as if no library function calls" localtime
+ * is to not call it, so we drop its guts into "localsub", which can be
+ * freely called. (And no, the PANS doesn't require the above behavior,
+ * but it *is* desirable.)
+ */
+static struct pg_tm *
+localsub(struct state const *sp, pg_time_t const *timep,
+ struct pg_tm *const tmp)
+{
+ const struct ttinfo *ttisp;
+ int i;
+ struct pg_tm *result;
+ const pg_time_t t = *timep;
+
+ if (sp == NULL)
+ return gmtsub(timep, 0, tmp);
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1]))
+ {
+ pg_time_t newt = t;
+ pg_time_t seconds;
+ pg_time_t years;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else
+ seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT;
+ seconds = years * AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ newt += seconds;
+ else
+ newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(sp, &newt, tmp);
+ if (result)
+ {
+ int64 newy;
+
+ newy = result->tm_year;
+ if (t < sp->ats[0])
+ newy -= years;
+ else
+ newy += years;
+ if (!(INT_MIN <= newy && newy <= INT_MAX))
+ return NULL;
+ result->tm_year = newy;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t < sp->ats[0])
+ {
+ i = sp->defaulttype;
+ }
+ else
+ {
+ int lo = 1;
+ int hi = sp->timecnt;
+
+ while (lo < hi)
+ {
+ int mid = (lo + hi) >> 1;
+
+ if (t < sp->ats[mid])
+ hi = mid;
+ else
+ lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
+ }
+ ttisp = &sp->ttis[i];
+
+ /*
+ * To get (wrong) behavior that's compatible with System V Release 2.0
+ * you'd replace the statement below with t += ttisp->tt_utoff;
+ * timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_utoff, sp, tmp);
+ if (result)
+ {
+ result->tm_isdst = ttisp->tt_isdst;
+ result->tm_zone = unconstify(char *, &sp->chars[ttisp->tt_desigidx]);
+ }
+ return result;
+}
+
+
+struct pg_tm *
+pg_localtime(const pg_time_t *timep, const pg_tz *tz)
+{
+ return localsub(&tz->state, timep, &tm);
+}
+
+
+/*
+ * gmtsub is to gmtime as localsub is to localtime.
+ *
+ * Except we have a private "struct state" for GMT, so no sp is passed in.
+ */
+
+static struct pg_tm *
+gmtsub(pg_time_t const *timep, int32 offset,
+ struct pg_tm *tmp)
+{
+ struct pg_tm *result;
+
+ /* GMT timezone state data is kept here */
+ static struct state *gmtptr = NULL;
+
+ if (gmtptr == NULL)
+ {
+ /* Allocate on first use */
+ gmtptr = (struct state *) malloc(sizeof(struct state));
+ if (gmtptr == NULL)
+ return NULL; /* errno should be set by malloc */
+ gmtload(gmtptr);
+ }
+
+ result = timesub(timep, offset, gmtptr, tmp);
+
+ /*
+ * Could get fancy here and deliver something such as "+xx" or "-xx" if
+ * offset is non-zero, but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->tm_zone = wildabbr;
+ else
+ tmp->tm_zone = gmtptr->chars;
+
+ return result;
+}
+
+struct pg_tm *
+pg_gmtime(const pg_time_t *timep)
+{
+ return gmtsub(timep, 0, &tm);
+}
+
+/*
+ * Return the number of leap years through the end of the given year
+ * where, to make the math easy, the answer for year zero is defined as zero.
+ */
+
+static int
+leaps_thru_end_of_nonneg(int y)
+{
+ return y / 4 - y / 100 + y / 400;
+}
+
+static int
+leaps_thru_end_of(const int y)
+{
+ return (y < 0
+ ? -1 - leaps_thru_end_of_nonneg(-1 - y)
+ : leaps_thru_end_of_nonneg(y));
+}
+
+static struct pg_tm *
+timesub(const pg_time_t *timep, int32 offset,
+ const struct state *sp, struct pg_tm *tmp)
+{
+ const struct lsinfo *lp;
+ pg_time_t tdays;
+ int idays; /* unsigned would be so 2003 */
+ int64 rem;
+ int y;
+ const int *ip;
+ int64 corr;
+ bool hit;
+ int i;
+
+ 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);
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = *timep / SECSPERDAY;
+ rem = *timep % SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)])
+ {
+ int newy;
+ pg_time_t tdelta;
+ int idelta;
+ int leapdays;
+
+ tdelta = tdays / DAYSPERLYEAR;
+ if (!((!TYPE_SIGNED(pg_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 -= ((pg_time_t) newy - y) * DAYSPERNYEAR;
+ tdays -= 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;
+ }
+ 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 %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+
+ /*
+ * A positive leap second requires a special representation. This uses
+ * "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
+ tmp->tm_gmtoff = offset;
+ return tmp;
+
+out_of_range:
+ errno = EOVERFLOW;
+ return NULL;
+}
+
+/*
+ * Normalize logic courtesy Paul Eggert.
+ */
+
+static bool
+increment_overflow(int *ip, int j)
+{
+ int const i = *ip;
+
+ /*----------
+ * If i >= 0 there can only be overflow if i + j > INT_MAX
+ * or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow.
+ * If i < 0 there can only be overflow if i + j < INT_MIN
+ * or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow.
+ *----------
+ */
+ if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i))
+ return true;
+ *ip += j;
+ return false;
+}
+
+static bool
+increment_overflow_time(pg_time_t *tp, int32 j)
+{
+ /*----------
+ * This is like
+ * 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...',
+ * except that it does the right thing even if *tp + j would overflow.
+ *----------
+ */
+ if (!(j < 0
+ ? (TYPE_SIGNED(pg_time_t) ? TIME_T_MIN - j <= *tp : -1 - j < *tp)
+ : *tp <= TIME_T_MAX - j))
+ return true;
+ *tp += j;
+ return false;
+}
+
+static int64
+leapcorr(struct state const *sp, pg_time_t t)
+{
+ struct lsinfo const *lp;
+ int i;
+
+ i = sp->leapcnt;
+ while (--i >= 0)
+ {
+ lp = &sp->lsis[i];
+ if (t >= lp->ls_trans)
+ return lp->ls_corr;
+ }
+ return 0;
+}
+
+/*
+ * Find the next DST transition time in the given zone after the given time
+ *
+ * *timep and *tz are input arguments, the other parameters are output values.
+ *
+ * When the function result is 1, *boundary is set to the pg_time_t
+ * representation of the next DST transition time after *timep,
+ * *before_gmtoff and *before_isdst are set to the GMT offset and isdst
+ * state prevailing just before that boundary (in particular, the state
+ * prevailing at *timep), and *after_gmtoff and *after_isdst are set to
+ * the state prevailing just after that boundary.
+ *
+ * When the function result is 0, there is no known DST transition
+ * after *timep, but *before_gmtoff and *before_isdst indicate the GMT
+ * offset and isdst state prevailing at *timep. (This would occur in
+ * DST-less time zones, or if a zone has permanently ceased using DST.)
+ *
+ * A function result of -1 indicates failure (this case does not actually
+ * occur in our current implementation).
+ */
+int
+pg_next_dst_boundary(const pg_time_t *timep,
+ long int *before_gmtoff,
+ int *before_isdst,
+ pg_time_t *boundary,
+ long int *after_gmtoff,
+ int *after_isdst,
+ const pg_tz *tz)
+{
+ const struct state *sp;
+ const struct ttinfo *ttisp;
+ int i;
+ int j;
+ const pg_time_t t = *timep;
+
+ sp = &tz->state;
+ if (sp->timecnt == 0)
+ {
+ /* non-DST zone, use lowest-numbered standard type */
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt)
+ {
+ i = 0;
+ break;
+ }
+ ttisp = &sp->ttis[i];
+ *before_gmtoff = ttisp->tt_utoff;
+ *before_isdst = ttisp->tt_isdst;
+ return 0;
+ }
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1]))
+ {
+ /* For values outside the transition table, extrapolate */
+ pg_time_t newt = t;
+ pg_time_t seconds;
+ pg_time_t tcycles;
+ int64 icycles;
+ int result;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else
+ seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return -1;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ newt += seconds;
+ else
+ newt -= seconds;
+ if (newt < sp->ats[0] ||
+ newt > sp->ats[sp->timecnt - 1])
+ return -1; /* "cannot happen" */
+
+ result = pg_next_dst_boundary(&newt, before_gmtoff,
+ before_isdst,
+ boundary,
+ after_gmtoff,
+ after_isdst,
+ tz);
+ if (t < sp->ats[0])
+ *boundary -= seconds;
+ else
+ *boundary += seconds;
+ return result;
+ }
+
+ if (t >= sp->ats[sp->timecnt - 1])
+ {
+ /* No known transition > t, so use last known segment's type */
+ i = sp->types[sp->timecnt - 1];
+ ttisp = &sp->ttis[i];
+ *before_gmtoff = ttisp->tt_utoff;
+ *before_isdst = ttisp->tt_isdst;
+ return 0;
+ }
+ if (t < sp->ats[0])
+ {
+ /* For "before", use lowest-numbered standard type */
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt)
+ {
+ i = 0;
+ break;
+ }
+ ttisp = &sp->ttis[i];
+ *before_gmtoff = ttisp->tt_utoff;
+ *before_isdst = ttisp->tt_isdst;
+ *boundary = sp->ats[0];
+ /* And for "after", use the first segment's type */
+ i = sp->types[0];
+ ttisp = &sp->ttis[i];
+ *after_gmtoff = ttisp->tt_utoff;
+ *after_isdst = ttisp->tt_isdst;
+ return 1;
+ }
+ /* Else search to find the boundary following t */
+ {
+ int lo = 1;
+ int hi = sp->timecnt - 1;
+
+ while (lo < hi)
+ {
+ int mid = (lo + hi) >> 1;
+
+ if (t < sp->ats[mid])
+ hi = mid;
+ else
+ lo = mid + 1;
+ }
+ i = lo;
+ }
+ j = sp->types[i - 1];
+ ttisp = &sp->ttis[j];
+ *before_gmtoff = ttisp->tt_utoff;
+ *before_isdst = ttisp->tt_isdst;
+ *boundary = sp->ats[i];
+ j = sp->types[i];
+ ttisp = &sp->ttis[j];
+ *after_gmtoff = ttisp->tt_utoff;
+ *after_isdst = ttisp->tt_isdst;
+ return 1;
+}
+
+/*
+ * Identify a timezone abbreviation's meaning in the given zone
+ *
+ * Determine the GMT offset and DST flag associated with the abbreviation.
+ * This is generally used only when the abbreviation has actually changed
+ * meaning over time; therefore, we also take a UTC cutoff time, and return
+ * the meaning in use at or most recently before that time, or the meaning
+ * in first use after that time if the abbrev was never used before that.
+ *
+ * On success, returns true and sets *gmtoff and *isdst. If the abbreviation
+ * was never used at all in this zone, returns false.
+ *
+ * Note: abbrev is matched case-sensitively; it should be all-upper-case.
+ */
+bool
+pg_interpret_timezone_abbrev(const char *abbrev,
+ const pg_time_t *timep,
+ long int *gmtoff,
+ int *isdst,
+ const pg_tz *tz)
+{
+ const struct state *sp;
+ const char *abbrs;
+ const struct ttinfo *ttisp;
+ int abbrind;
+ int cutoff;
+ int i;
+ const pg_time_t t = *timep;
+
+ sp = &tz->state;
+
+ /*
+ * Locate the abbreviation in the zone's abbreviation list. We assume
+ * there are not duplicates in the list.
+ */
+ abbrs = sp->chars;
+ abbrind = 0;
+ while (abbrind < sp->charcnt)
+ {
+ if (strcmp(abbrev, abbrs + abbrind) == 0)
+ break;
+ while (abbrs[abbrind] != '\0')
+ abbrind++;
+ abbrind++;
+ }
+ if (abbrind >= sp->charcnt)
+ return false; /* not there! */
+
+ /*
+ * Unlike pg_next_dst_boundary, we needn't sweat about extrapolation
+ * (goback/goahead zones). Finding the newest or oldest meaning of the
+ * abbreviation should get us what we want, since extrapolation would just
+ * be repeating the newest or oldest meanings.
+ *
+ * Use binary search to locate the first transition > cutoff time.
+ */
+ {
+ int lo = 0;
+ int hi = sp->timecnt;
+
+ while (lo < hi)
+ {
+ int mid = (lo + hi) >> 1;
+
+ if (t < sp->ats[mid])
+ hi = mid;
+ else
+ lo = mid + 1;
+ }
+ cutoff = lo;
+ }
+
+ /*
+ * Scan backwards to find the latest interval using the given abbrev
+ * before the cutoff time.
+ */
+ for (i = cutoff - 1; i >= 0; i--)
+ {
+ ttisp = &sp->ttis[sp->types[i]];
+ if (ttisp->tt_desigidx == abbrind)
+ {
+ *gmtoff = ttisp->tt_utoff;
+ *isdst = ttisp->tt_isdst;
+ return true;
+ }
+ }
+
+ /*
+ * Not there, so scan forwards to find the first one after.
+ */
+ for (i = cutoff; i < sp->timecnt; i++)
+ {
+ ttisp = &sp->ttis[sp->types[i]];
+ if (ttisp->tt_desigidx == abbrind)
+ {
+ *gmtoff = ttisp->tt_utoff;
+ *isdst = ttisp->tt_isdst;
+ return true;
+ }
+ }
+
+ return false; /* hm, not actually used in any interval? */
+}
+
+/*
+ * If the given timezone uses only one GMT offset, store that offset
+ * into *gmtoff and return true, else return false.
+ */
+bool
+pg_get_timezone_offset(const pg_tz *tz, long int *gmtoff)
+{
+ /*
+ * The zone could have more than one ttinfo, if it's historically used
+ * more than one abbreviation. We return true as long as they all have
+ * the same gmtoff.
+ */
+ const struct state *sp;
+ int i;
+
+ sp = &tz->state;
+ for (i = 1; i < sp->typecnt; i++)
+ {
+ if (sp->ttis[i].tt_utoff != sp->ttis[0].tt_utoff)
+ return false;
+ }
+ *gmtoff = sp->ttis[0].tt_utoff;
+ return true;
+}
+
+/*
+ * Return the name of the current timezone
+ */
+const char *
+pg_get_timezone_name(pg_tz *tz)
+{
+ if (tz)
+ return tz->TZname;
+ return NULL;
+}
+
+/*
+ * Check whether timezone is acceptable.
+ *
+ * What we are doing here is checking for leap-second-aware timekeeping.
+ * We need to reject such TZ settings because they'll wreak havoc with our
+ * date/time arithmetic.
+ */
+bool
+pg_tz_acceptable(pg_tz *tz)
+{
+ struct pg_tm *tt;
+ pg_time_t time2000;
+
+ /*
+ * To detect leap-second timekeeping, run pg_localtime for what should be
+ * GMT midnight, 2000-01-01. Insist that the tm_sec value be zero; any
+ * other result has to be due to leap seconds.
+ */
+ time2000 = (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
+ tt = pg_localtime(&time2000, tz);
+ if (!tt || tt->tm_sec != 0)
+ return false;
+
+ return true;
+}
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
new file mode 100644
index 0000000..82f14ee
--- /dev/null
+++ b/src/timezone/pgtz.c
@@ -0,0 +1,502 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtz.c
+ * Timezone Library Integration Functions
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/timezone/pgtz.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "datatype/timestamp.h"
+#include "miscadmin.h"
+#include "pgtz.h"
+#include "storage/fd.h"
+#include "utils/hsearch.h"
+
+
+/* Current session timezone (controlled by TimeZone GUC) */
+pg_tz *session_timezone = NULL;
+
+/* Current log timezone (controlled by log_timezone GUC) */
+pg_tz *log_timezone = NULL;
+
+
+static bool scan_directory_ci(const char *dirname,
+ const char *fname, int fnamelen,
+ char *canonname, int canonnamelen);
+
+
+/*
+ * Return full pathname of timezone data directory
+ */
+static const char *
+pg_TZDIR(void)
+{
+#ifndef SYSTEMTZDIR
+ /* normal case: timezone stuff is under our share dir */
+ static bool done_tzdir = false;
+ static char tzdir[MAXPGPATH];
+
+ if (done_tzdir)
+ return tzdir;
+
+ get_share_path(my_exec_path, tzdir);
+ strlcpy(tzdir + strlen(tzdir), "/timezone", MAXPGPATH - strlen(tzdir));
+
+ done_tzdir = true;
+ return tzdir;
+#else
+ /* we're configured to use system's timezone database */
+ return SYSTEMTZDIR;
+#endif
+}
+
+
+/*
+ * Given a timezone name, open() the timezone data file. Return the
+ * file descriptor if successful, -1 if not.
+ *
+ * The input name is searched for case-insensitively (we assume that the
+ * timezone database does not contain case-equivalent names).
+ *
+ * If "canonname" is not NULL, then on success the canonical spelling of the
+ * given name is stored there (the buffer must be > TZ_STRLEN_MAX bytes!).
+ */
+int
+pg_open_tzfile(const char *name, char *canonname)
+{
+ const char *fname;
+ char fullname[MAXPGPATH];
+ int fullnamelen;
+ int orignamelen;
+
+ /* Initialize fullname with base name of tzdata directory */
+ strlcpy(fullname, pg_TZDIR(), sizeof(fullname));
+ orignamelen = fullnamelen = strlen(fullname);
+
+ if (fullnamelen + 1 + strlen(name) >= MAXPGPATH)
+ return -1; /* not gonna fit */
+
+ /*
+ * If the caller doesn't need the canonical spelling, first just try to
+ * open the name as-is. This can be expected to succeed if the given name
+ * is already case-correct, or if the filesystem is case-insensitive; and
+ * we don't need to distinguish those situations if we aren't tasked with
+ * reporting the canonical spelling.
+ */
+ if (canonname == NULL)
+ {
+ int result;
+
+ fullname[fullnamelen] = '/';
+ /* test above ensured this will fit: */
+ strcpy(fullname + fullnamelen + 1, name);
+ result = open(fullname, O_RDONLY | PG_BINARY, 0);
+ if (result >= 0)
+ return result;
+ /* If that didn't work, fall through to do it the hard way */
+ fullname[fullnamelen] = '\0';
+ }
+
+ /*
+ * Loop to split the given name into directory levels; for each level,
+ * search using scan_directory_ci().
+ */
+ fname = name;
+ for (;;)
+ {
+ const char *slashptr;
+ int fnamelen;
+
+ slashptr = strchr(fname, '/');
+ if (slashptr)
+ fnamelen = slashptr - fname;
+ else
+ fnamelen = strlen(fname);
+ if (!scan_directory_ci(fullname, fname, fnamelen,
+ fullname + fullnamelen + 1,
+ MAXPGPATH - fullnamelen - 1))
+ return -1;
+ fullname[fullnamelen++] = '/';
+ fullnamelen += strlen(fullname + fullnamelen);
+ if (slashptr)
+ fname = slashptr + 1;
+ else
+ break;
+ }
+
+ if (canonname)
+ strlcpy(canonname, fullname + orignamelen + 1, TZ_STRLEN_MAX + 1);
+
+ return open(fullname, O_RDONLY | PG_BINARY, 0);
+}
+
+
+/*
+ * Scan specified directory for a case-insensitive match to fname
+ * (of length fnamelen --- fname may not be null terminated!). If found,
+ * copy the actual filename into canonname and return true.
+ */
+static bool
+scan_directory_ci(const char *dirname, const char *fname, int fnamelen,
+ char *canonname, int canonnamelen)
+{
+ bool found = false;
+ DIR *dirdesc;
+ struct dirent *direntry;
+
+ dirdesc = AllocateDir(dirname);
+
+ while ((direntry = ReadDirExtended(dirdesc, dirname, LOG)) != NULL)
+ {
+ /*
+ * Ignore . and .., plus any other "hidden" files. This is a security
+ * measure to prevent access to files outside the timezone directory.
+ */
+ if (direntry->d_name[0] == '.')
+ continue;
+
+ if (strlen(direntry->d_name) == fnamelen &&
+ pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0)
+ {
+ /* Found our match */
+ strlcpy(canonname, direntry->d_name, canonnamelen);
+ found = true;
+ break;
+ }
+ }
+
+ FreeDir(dirdesc);
+
+ return found;
+}
+
+
+/*
+ * We keep loaded timezones in a hashtable so we don't have to
+ * load and parse the TZ definition file every time one is selected.
+ * Because we want timezone names to be found case-insensitively,
+ * the hash key is the uppercased name of the zone.
+ */
+typedef struct
+{
+ /* tznameupper contains the all-upper-case name of the timezone */
+ char tznameupper[TZ_STRLEN_MAX + 1];
+ pg_tz tz;
+} pg_tz_cache;
+
+static HTAB *timezone_cache = NULL;
+
+
+static bool
+init_timezone_hashtable(void)
+{
+ HASHCTL hash_ctl;
+
+ hash_ctl.keysize = TZ_STRLEN_MAX + 1;
+ hash_ctl.entrysize = sizeof(pg_tz_cache);
+
+ timezone_cache = hash_create("Timezones",
+ 4,
+ &hash_ctl,
+ HASH_ELEM | HASH_STRINGS);
+ if (!timezone_cache)
+ return false;
+
+ return true;
+}
+
+/*
+ * Load a timezone from file or from cache.
+ * Does not verify that the timezone is acceptable!
+ *
+ * "GMT" is always interpreted as the tzparse() definition, without attempting
+ * to load a definition from the filesystem. This has a number of benefits:
+ * 1. It's guaranteed to succeed, so we don't have the failure mode wherein
+ * the bootstrap default timezone setting doesn't work (as could happen if
+ * the OS attempts to supply a leap-second-aware version of "GMT").
+ * 2. Because we aren't accessing the filesystem, we can safely initialize
+ * the "GMT" zone definition before my_exec_path is known.
+ * 3. It's quick enough that we don't waste much time when the bootstrap
+ * default timezone setting is later overridden from postgresql.conf.
+ */
+pg_tz *
+pg_tzset(const char *name)
+{
+ pg_tz_cache *tzp;
+ struct state tzstate;
+ char uppername[TZ_STRLEN_MAX + 1];
+ char canonname[TZ_STRLEN_MAX + 1];
+ char *p;
+
+ if (strlen(name) > TZ_STRLEN_MAX)
+ return NULL; /* not going to fit */
+
+ if (!timezone_cache)
+ if (!init_timezone_hashtable())
+ return NULL;
+
+ /*
+ * Upcase the given name to perform a case-insensitive hashtable search.
+ * (We could alternatively downcase it, but we prefer upcase so that we
+ * can get consistently upcased results from tzparse() in case the name is
+ * a POSIX-style timezone spec.)
+ */
+ p = uppername;
+ while (*name)
+ *p++ = pg_toupper((unsigned char) *name++);
+ *p = '\0';
+
+ tzp = (pg_tz_cache *) hash_search(timezone_cache,
+ uppername,
+ HASH_FIND,
+ NULL);
+ if (tzp)
+ {
+ /* Timezone found in cache, nothing more to do */
+ return &tzp->tz;
+ }
+
+ /*
+ * "GMT" is always sent to tzparse(), as per discussion above.
+ */
+ if (strcmp(uppername, "GMT") == 0)
+ {
+ if (!tzparse(uppername, &tzstate, true))
+ {
+ /* This really, really should not happen ... */
+ elog(ERROR, "could not initialize GMT time zone");
+ }
+ /* Use uppercase name as canonical */
+ strcpy(canonname, uppername);
+ }
+ else if (tzload(uppername, canonname, &tzstate, true) != 0)
+ {
+ if (uppername[0] == ':' || !tzparse(uppername, &tzstate, false))
+ {
+ /* Unknown timezone. Fail our call instead of loading GMT! */
+ return NULL;
+ }
+ /* For POSIX timezone specs, use uppercase name as canonical */
+ strcpy(canonname, uppername);
+ }
+
+ /* Save timezone in the cache */
+ tzp = (pg_tz_cache *) hash_search(timezone_cache,
+ uppername,
+ HASH_ENTER,
+ NULL);
+
+ /* hash_search already copied uppername into the hash key */
+ strcpy(tzp->tz.TZname, canonname);
+ memcpy(&tzp->tz.state, &tzstate, sizeof(tzstate));
+
+ return &tzp->tz;
+}
+
+/*
+ * Load a fixed-GMT-offset timezone.
+ * This is used for SQL-spec SET TIME ZONE INTERVAL 'foo' cases.
+ * It's otherwise equivalent to pg_tzset().
+ *
+ * The GMT offset is specified in seconds, positive values meaning west of
+ * Greenwich (ie, POSIX not ISO sign convention). However, we use ISO
+ * sign convention in the displayable abbreviation for the zone.
+ *
+ * Caution: this can fail (return NULL) if the specified offset is outside
+ * the range allowed by the zic library.
+ */
+pg_tz *
+pg_tzset_offset(long gmtoffset)
+{
+ long absoffset = (gmtoffset < 0) ? -gmtoffset : gmtoffset;
+ char offsetstr[64];
+ char tzname[128];
+
+ snprintf(offsetstr, sizeof(offsetstr),
+ "%02ld", absoffset / SECS_PER_HOUR);
+ absoffset %= SECS_PER_HOUR;
+ if (absoffset != 0)
+ {
+ snprintf(offsetstr + strlen(offsetstr),
+ sizeof(offsetstr) - strlen(offsetstr),
+ ":%02ld", absoffset / SECS_PER_MINUTE);
+ absoffset %= SECS_PER_MINUTE;
+ if (absoffset != 0)
+ snprintf(offsetstr + strlen(offsetstr),
+ sizeof(offsetstr) - strlen(offsetstr),
+ ":%02ld", absoffset);
+ }
+ if (gmtoffset > 0)
+ snprintf(tzname, sizeof(tzname), "<-%s>+%s",
+ offsetstr, offsetstr);
+ else
+ snprintf(tzname, sizeof(tzname), "<+%s>-%s",
+ offsetstr, offsetstr);
+
+ return pg_tzset(tzname);
+}
+
+
+/*
+ * Initialize timezone library
+ *
+ * This is called before GUC variable initialization begins. Its purpose
+ * is to ensure that log_timezone has a valid value before any logging GUC
+ * variables could become set to values that require elog.c to provide
+ * timestamps (e.g., log_line_prefix). We may as well initialize
+ * session_timezone to something valid, too.
+ */
+void
+pg_timezone_initialize(void)
+{
+ /*
+ * We may not yet know where PGSHAREDIR is (in particular this is true in
+ * an EXEC_BACKEND subprocess). So use "GMT", which pg_tzset forces to be
+ * interpreted without reference to the filesystem. This corresponds to
+ * the bootstrap default for these variables in guc.c, although in
+ * principle it could be different.
+ */
+ session_timezone = pg_tzset("GMT");
+ log_timezone = session_timezone;
+}
+
+
+/*
+ * Functions to enumerate available timezones
+ *
+ * Note that pg_tzenumerate_next() will return a pointer into the pg_tzenum
+ * structure, so the data is only valid up to the next call.
+ *
+ * All data is allocated using palloc in the current context.
+ */
+#define MAX_TZDIR_DEPTH 10
+
+struct pg_tzenum
+{
+ int baselen;
+ int depth;
+ DIR *dirdesc[MAX_TZDIR_DEPTH];
+ char *dirname[MAX_TZDIR_DEPTH];
+ struct pg_tz tz;
+};
+
+/* typedef pg_tzenum is declared in pgtime.h */
+
+pg_tzenum *
+pg_tzenumerate_start(void)
+{
+ pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum));
+ char *startdir = pstrdup(pg_TZDIR());
+
+ ret->baselen = strlen(startdir) + 1;
+ ret->depth = 0;
+ ret->dirname[0] = startdir;
+ ret->dirdesc[0] = AllocateDir(startdir);
+ if (!ret->dirdesc[0])
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m", startdir)));
+ return ret;
+}
+
+void
+pg_tzenumerate_end(pg_tzenum *dir)
+{
+ while (dir->depth >= 0)
+ {
+ FreeDir(dir->dirdesc[dir->depth]);
+ pfree(dir->dirname[dir->depth]);
+ dir->depth--;
+ }
+ pfree(dir);
+}
+
+pg_tz *
+pg_tzenumerate_next(pg_tzenum *dir)
+{
+ while (dir->depth >= 0)
+ {
+ struct dirent *direntry;
+ char fullname[MAXPGPATH * 2];
+ struct stat statbuf;
+
+ direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]);
+
+ if (!direntry)
+ {
+ /* End of this directory */
+ FreeDir(dir->dirdesc[dir->depth]);
+ pfree(dir->dirname[dir->depth]);
+ dir->depth--;
+ continue;
+ }
+
+ if (direntry->d_name[0] == '.')
+ continue;
+
+ snprintf(fullname, sizeof(fullname), "%s/%s",
+ dir->dirname[dir->depth], direntry->d_name);
+ if (stat(fullname, &statbuf) != 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat \"%s\": %m", fullname)));
+
+ if (S_ISDIR(statbuf.st_mode))
+ {
+ /* Step into the subdirectory */
+ if (dir->depth >= MAX_TZDIR_DEPTH - 1)
+ ereport(ERROR,
+ (errmsg_internal("timezone directory stack overflow")));
+ dir->depth++;
+ dir->dirname[dir->depth] = pstrdup(fullname);
+ dir->dirdesc[dir->depth] = AllocateDir(fullname);
+ if (!dir->dirdesc[dir->depth])
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open directory \"%s\": %m",
+ fullname)));
+
+ /* Start over reading in the new directory */
+ continue;
+ }
+
+ /*
+ * Load this timezone using tzload() not pg_tzset(), so we don't fill
+ * the cache. Also, don't ask for the canonical spelling: we already
+ * know it, and pg_open_tzfile's way of finding it out is pretty
+ * inefficient.
+ */
+ if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0)
+ {
+ /* Zone could not be loaded, ignore it */
+ continue;
+ }
+
+ if (!pg_tz_acceptable(&dir->tz))
+ {
+ /* Ignore leap-second zones */
+ continue;
+ }
+
+ /* OK, return the canonical zone name spelling. */
+ strlcpy(dir->tz.TZname, fullname + dir->baselen,
+ sizeof(dir->tz.TZname));
+
+ /* Timezone loaded OK. */
+ return &dir->tz;
+ }
+
+ /* Nothing more found */
+ return NULL;
+}
diff --git a/src/timezone/pgtz.h b/src/timezone/pgtz.h
new file mode 100644
index 0000000..ca01722
--- /dev/null
+++ b/src/timezone/pgtz.h
@@ -0,0 +1,81 @@
+/*-------------------------------------------------------------------------
+ *
+ * pgtz.h
+ * Timezone Library Integration Functions
+ *
+ * Note: this file contains only definitions that are private to the
+ * timezone library. Public definitions are in pgtime.h.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/timezone/pgtz.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _PGTZ_H
+#define _PGTZ_H
+
+#include "pgtime.h"
+#include "tzfile.h"
+
+
+#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+struct ttinfo
+{ /* time type information */
+ int32 tt_utoff; /* UT offset in seconds */
+ bool tt_isdst; /* used to set tm_isdst */
+ int tt_desigidx; /* abbreviation list index */
+ bool tt_ttisstd; /* transition is std time */
+ bool tt_ttisut; /* transition is UT */
+};
+
+struct lsinfo
+{ /* leap second information */
+ pg_time_t ls_trans; /* transition time */
+ int64 ls_corr; /* correction to apply */
+};
+
+struct state
+{
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ bool goback;
+ bool goahead;
+ pg_time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, 4 /* sizeof gmt */ ),
+ (2 * (TZ_STRLEN_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+
+ /*
+ * The time type to use for early times or if no transitions. It is always
+ * zero for recent tzdb releases. It might be nonzero for data from tzdb
+ * 2018e or earlier.
+ */
+ int defaulttype;
+};
+
+
+struct pg_tz
+{
+ /* TZname contains the canonically-cased name of the timezone */
+ char TZname[TZ_STRLEN_MAX + 1];
+ struct state state;
+};
+
+
+/* in pgtz.c */
+extern int pg_open_tzfile(const char *name, char *canonname);
+
+/* in localtime.c */
+extern int tzload(const char *name, char *canonname, struct state *sp,
+ bool doextend);
+extern bool tzparse(const char *name, struct state *sp, bool lastditch);
+
+#endif /* _PGTZ_H */
diff --git a/src/timezone/private.h b/src/timezone/private.h
new file mode 100644
index 0000000..533e3d9
--- /dev/null
+++ b/src/timezone/private.h
@@ -0,0 +1,163 @@
+/* Private header for tzdb code. */
+
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+ * This file is in the public domain, so clarified as of
+ * 1996-06-05 by Arthur David Olson.
+ *
+ * IDENTIFICATION
+ * src/timezone/private.h
+ */
+
+/*
+ * This header is for use ONLY with the time conversion code.
+ * There is no guarantee that it will remain unchanged,
+ * or that it will remain at all.
+ * Do NOT copy it to any system include directory.
+ * Thank you!
+ */
+
+#include <limits.h> /* for CHAR_BIT et al. */
+#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
+#include <unistd.h> /* for F_OK and R_OK */
+
+#include "pgtime.h"
+
+/* This string was in the Factory zone through version 2016f. */
+#define GRANDPARENTED "Local time zone must be set--see zic manual page"
+
+/*
+ * IANA has a bunch of HAVE_FOO #defines here, but in PG we want pretty
+ * much all of that to be done by PG's configure script.
+ */
+
+#ifndef ENOTSUP
+#define ENOTSUP EINVAL
+#endif
+#ifndef EOVERFLOW
+#define EOVERFLOW EINVAL
+#endif
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/* PG doesn't currently rely on <inttypes.h>, so work around strtoimax() */
+#undef strtoimax
+#ifdef HAVE_STRTOLL
+#define strtoimax strtoll
+#else
+#define strtoimax strtol
+#endif
+
+
+/*
+ * Finally, some convenience items.
+ */
+
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
+
+/*
+ * Max and min values of the integer type T, of which only the bottom
+ * B bits are used, and where the highest-order used bit is considered
+ * to be a sign bit if T is signed.
+ */
+#define MAXVAL(t, b) \
+ ((t) (((t) 1 << ((b) - 1 - TYPE_SIGNED(t))) \
+ - 1 + ((t) 1 << ((b) - 1 - TYPE_SIGNED(t)))))
+#define MINVAL(t, b) \
+ ((t) (TYPE_SIGNED(t) ? - TWOS_COMPLEMENT(t) - MAXVAL(t, b) : 0))
+
+/* The extreme time values, assuming no padding. */
+#define TIME_T_MIN MINVAL(pg_time_t, TYPE_BIT(pg_time_t))
+#define TIME_T_MAX MAXVAL(pg_time_t, TYPE_BIT(pg_time_t))
+
+/*
+ * 302 / 1000 is log10(2.0) rounded up.
+ * Subtract one for the sign bit if the type is signed;
+ * add one for integer division truncation;
+ * add one more for a minus sign if the type is signed.
+ */
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+ 1 + TYPE_SIGNED(type))
+
+/*
+ * INITIALIZE(x)
+ */
+#define INITIALIZE(x) ((x) = 0)
+
+#undef _
+#define _(msgid) (msgid)
+
+/* 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
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((int32) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+ * Since everything in isleap is modulo 400 (or a factor of 400), we know that
+ * isleap(y) == isleap(y % 400)
+ * and so
+ * isleap(a + b) == isleap((a + b) % 400)
+ * or
+ * isleap(a + b) == isleap(a % 400 + b % 400)
+ * This is true even if % means modulo rather than Fortran remainder
+ * (which is allowed by C89 but not by C99 or later).
+ * We use this to avoid addition overflow problems.
+ */
+
+#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 \
+ ((int64) YEARSPERREPEAT * (int64) AVGSECSPERYEAR)
+#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
+
+#endif /* !defined PRIVATE_H */
diff --git a/src/timezone/strftime.c b/src/timezone/strftime.c
new file mode 100644
index 0000000..dd6c7db
--- /dev/null
+++ b/src/timezone/strftime.c
@@ -0,0 +1,571 @@
+/* Convert a broken-down timestamp to a string. */
+
+/*
+ * Copyright 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Based on the UCB version with the copyright notice appearing above.
+ *
+ * This is ANSIish only when "multibyte character == plain character".
+ *
+ * IDENTIFICATION
+ * src/timezone/strftime.c
+ */
+
+#include "postgres.h"
+
+#include <fcntl.h>
+
+#include "private.h"
+
+
+struct lc_time_T
+{
+ const char *mon[MONSPERYEAR];
+ const char *month[MONSPERYEAR];
+ const char *wday[DAYSPERWEEK];
+ const char *weekday[DAYSPERWEEK];
+ const char *X_fmt;
+ const char *x_fmt;
+ const char *c_fmt;
+ const char *am;
+ const char *pm;
+ const char *date_fmt;
+};
+
+#define Locale (&C_time_locale)
+
+static const struct lc_time_T C_time_locale = {
+ {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ }, {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ }, {
+ "Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"
+ }, {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ },
+
+ /* X_fmt */
+ "%H:%M:%S",
+
+ /*
+ * x_fmt
+ *
+ * C99 and later require this format. Using just numbers (as here) makes
+ * Quakers happier; it's also compatible with SVR4.
+ */
+ "%m/%d/%y",
+
+ /*
+ * c_fmt
+ *
+ * C99 and later require this format. Previously this code used "%D %X",
+ * but we now conform to C99. Note that "%a %b %d %H:%M:%S %Y" is used by
+ * Solaris 2.3.
+ */
+ "%a %b %e %T %Y",
+
+ /* am */
+ "AM",
+
+ /* pm */
+ "PM",
+
+ /* date_fmt */
+ "%a %b %e %H:%M:%S %Z %Y"
+};
+
+enum warn
+{
+ IN_NONE, IN_SOME, IN_THIS, IN_ALL
+};
+
+static char *_add(const char *, char *, const char *);
+static char *_conv(int, const char *, char *, const char *);
+static char *_fmt(const char *, const struct pg_tm *, char *, const char *,
+ enum warn *);
+static char *_yconv(int, int, bool, bool, char *, char const *);
+
+
+/*
+ * Convert timestamp t to string s, a caller-allocated buffer of size maxsize,
+ * using the given format pattern.
+ *
+ * See also timestamptz_to_str.
+ */
+size_t
+pg_strftime(char *s, size_t maxsize, const char *format, const struct pg_tm *t)
+{
+ char *p;
+ int saved_errno = errno;
+ enum warn warn = IN_NONE;
+
+ p = _fmt(format, t, s, s + maxsize, &warn);
+ if (!p)
+ {
+ errno = EOVERFLOW;
+ return 0;
+ }
+ if (p == s + maxsize)
+ {
+ errno = ERANGE;
+ return 0;
+ }
+ *p = '\0';
+ errno = saved_errno;
+ return p - s;
+}
+
+static char *
+_fmt(const char *format, const struct pg_tm *t, char *pt,
+ const char *ptlim, enum warn *warnp)
+{
+ for (; *format; ++format)
+ {
+ if (*format == '%')
+ {
+ label:
+ switch (*++format)
+ {
+ case '\0':
+ --format;
+ break;
+ case 'A':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : Locale->weekday[t->tm_wday],
+ pt, ptlim);
+ continue;
+ case 'a':
+ pt = _add((t->tm_wday < 0 ||
+ t->tm_wday >= DAYSPERWEEK) ?
+ "?" : Locale->wday[t->tm_wday],
+ pt, ptlim);
+ continue;
+ case 'B':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->month[t->tm_mon],
+ pt, ptlim);
+ continue;
+ case 'b':
+ case 'h':
+ pt = _add((t->tm_mon < 0 ||
+ t->tm_mon >= MONSPERYEAR) ?
+ "?" : Locale->mon[t->tm_mon],
+ pt, ptlim);
+ continue;
+ case 'C':
+
+ /*
+ * %C used to do a... _fmt("%a %b %e %X %Y", t);
+ * ...whereas now POSIX 1003.2 calls for something
+ * completely different. (ado, 1993-05-24)
+ */
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ true, false, pt, ptlim);
+ continue;
+ case 'c':
+ {
+ enum warn warn2 = IN_SOME;
+
+ pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
+ if (warn2 == IN_ALL)
+ warn2 = IN_THIS;
+ if (warn2 > *warnp)
+ *warnp = warn2;
+ }
+ continue;
+ case 'D':
+ pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
+ continue;
+ case 'd':
+ pt = _conv(t->tm_mday, "%02d", pt, ptlim);
+ continue;
+ case 'E':
+ case 'O':
+
+ /*
+ * Locale modifiers of C99 and later. The sequences %Ec
+ * %EC %Ex %EX %Ey %EY %Od %oe %OH %OI %Om %OM %OS %Ou %OU
+ * %OV %Ow %OW %Oy are supposed to provide alternative
+ * representations.
+ */
+ goto label;
+ case 'e':
+ pt = _conv(t->tm_mday, "%2d", pt, ptlim);
+ continue;
+ case 'F':
+ pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
+ continue;
+ case 'H':
+ pt = _conv(t->tm_hour, "%02d", pt, ptlim);
+ continue;
+ case 'I':
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ "%02d", pt, ptlim);
+ continue;
+ case 'j':
+ pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
+ continue;
+ case 'k':
+
+ /*
+ * This used to be... _conv(t->tm_hour % 12 ? t->tm_hour %
+ * 12 : 12, 2, ' '); ...and has been changed to the below
+ * to match SunOS 4.1.1 and Arnold Robbins' strftime
+ * version 3.0. That is, "%k" and "%l" have been swapped.
+ * (ado, 1993-05-24)
+ */
+ pt = _conv(t->tm_hour, "%2d", pt, ptlim);
+ continue;
+#ifdef KITCHEN_SINK
+ case 'K':
+
+ /*
+ * After all this time, still unclaimed!
+ */
+ pt = _add("kitchen sink", pt, ptlim);
+ continue;
+#endif /* defined KITCHEN_SINK */
+ case 'l':
+
+ /*
+ * This used to be... _conv(t->tm_hour, 2, ' '); ...and
+ * has been changed to the below to match SunOS 4.1.1 and
+ * Arnold Robbin's strftime version 3.0. That is, "%k" and
+ * "%l" have been swapped. (ado, 1993-05-24)
+ */
+ pt = _conv((t->tm_hour % 12) ?
+ (t->tm_hour % 12) : 12,
+ "%2d", pt, ptlim);
+ continue;
+ case 'M':
+ pt = _conv(t->tm_min, "%02d", pt, ptlim);
+ continue;
+ case 'm':
+ pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
+ continue;
+ case 'n':
+ pt = _add("\n", pt, ptlim);
+ continue;
+ case 'p':
+ pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
+ Locale->pm :
+ Locale->am,
+ pt, ptlim);
+ continue;
+ case 'R':
+ pt = _fmt("%H:%M", t, pt, ptlim, warnp);
+ continue;
+ case 'r':
+ pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
+ continue;
+ case 'S':
+ pt = _conv(t->tm_sec, "%02d", pt, ptlim);
+ continue;
+ case 'T':
+ pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
+ continue;
+ case 't':
+ pt = _add("\t", pt, ptlim);
+ continue;
+ case 'U':
+ pt = _conv((t->tm_yday + DAYSPERWEEK -
+ t->tm_wday) / DAYSPERWEEK,
+ "%02d", pt, ptlim);
+ continue;
+ case 'u':
+
+ /*
+ * From Arnold Robbins' strftime version 3.0: "ISO 8601:
+ * Weekday as a decimal number [1 (Monday) - 7]" (ado,
+ * 1993-05-24)
+ */
+ pt = _conv((t->tm_wday == 0) ?
+ DAYSPERWEEK : t->tm_wday,
+ "%d", pt, ptlim);
+ continue;
+ case 'V': /* ISO 8601 week number */
+ case 'G': /* ISO 8601 year (four digits) */
+ case 'g': /* ISO 8601 year (two digits) */
+/*
+ * From Arnold Robbins' strftime version 3.0: "the week number of the
+ * year (the first Monday as the first day of week 1) as a decimal number
+ * (01-53)."
+ * (ado, 1993-05-24)
+ *
+ * From <https://www.cl.cam.ac.uk/~mgk25/iso-time.html> by Markus Kuhn:
+ * "Week 01 of a year is per definition the first week which has the
+ * Thursday in this year, which is equivalent to the week which contains
+ * the fourth day of January. In other words, the first week of a new year
+ * is the week which has the majority of its days in the new year. Week 01
+ * might also contain days from the previous year and the week before week
+ * 01 of a year is the last week (52 or 53) of the previous year even if
+ * it contains days from the new year. A week starts with Monday (day 1)
+ * and ends with Sunday (day 7). For example, the first week of the year
+ * 1997 lasts from 1996-12-30 to 1997-01-05..."
+ * (ado, 1996-01-02)
+ */
+ {
+ int year;
+ int base;
+ int yday;
+ int wday;
+ int w;
+
+ year = t->tm_year;
+ base = TM_YEAR_BASE;
+ yday = t->tm_yday;
+ wday = t->tm_wday;
+ for (;;)
+ {
+ int len;
+ int bot;
+ int top;
+
+ len = isleap_sum(year, base) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+
+ /*
+ * What yday (-3 ... 3) does the ISO year begin
+ * on?
+ */
+ bot = ((yday + 11 - wday) %
+ DAYSPERWEEK) - 3;
+
+ /*
+ * What yday does the NEXT ISO year begin on?
+ */
+ top = bot -
+ (len % DAYSPERWEEK);
+ if (top < -3)
+ top += DAYSPERWEEK;
+ top += len;
+ if (yday >= top)
+ {
+ ++base;
+ w = 1;
+ break;
+ }
+ if (yday >= bot)
+ {
+ w = 1 + ((yday - bot) /
+ DAYSPERWEEK);
+ break;
+ }
+ --base;
+ yday += isleap_sum(year, base) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+ }
+ if (*format == 'V')
+ pt = _conv(w, "%02d",
+ pt, ptlim);
+ else if (*format == 'g')
+ {
+ *warnp = IN_ALL;
+ pt = _yconv(year, base,
+ false, true,
+ pt, ptlim);
+ }
+ else
+ pt = _yconv(year, base,
+ true, true,
+ pt, ptlim);
+ }
+ continue;
+ case 'v':
+
+ /*
+ * From Arnold Robbins' strftime version 3.0: "date as
+ * dd-bbb-YYYY" (ado, 1993-05-24)
+ */
+ pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
+ continue;
+ case 'W':
+ pt = _conv((t->tm_yday + DAYSPERWEEK -
+ (t->tm_wday ?
+ (t->tm_wday - 1) :
+ (DAYSPERWEEK - 1))) / DAYSPERWEEK,
+ "%02d", pt, ptlim);
+ continue;
+ case 'w':
+ pt = _conv(t->tm_wday, "%d", pt, ptlim);
+ continue;
+ case 'X':
+ pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
+ continue;
+ case 'x':
+ {
+ enum warn warn2 = IN_SOME;
+
+ pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
+ if (warn2 == IN_ALL)
+ warn2 = IN_THIS;
+ if (warn2 > *warnp)
+ *warnp = warn2;
+ }
+ continue;
+ case 'y':
+ *warnp = IN_ALL;
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ false, true,
+ pt, ptlim);
+ continue;
+ case 'Y':
+ pt = _yconv(t->tm_year, TM_YEAR_BASE,
+ true, true,
+ pt, ptlim);
+ continue;
+ case 'Z':
+ if (t->tm_zone != NULL)
+ pt = _add(t->tm_zone, pt, ptlim);
+
+ /*
+ * C99 and later say that %Z must be replaced by the empty
+ * string if the time zone abbreviation is not
+ * determinable.
+ */
+ continue;
+ case 'z':
+ {
+ long diff;
+ char const *sign;
+ bool negative;
+
+ if (t->tm_isdst < 0)
+ continue;
+ diff = t->tm_gmtoff;
+ negative = diff < 0;
+ if (diff == 0)
+ {
+ if (t->tm_zone != NULL)
+ negative = t->tm_zone[0] == '-';
+ }
+ if (negative)
+ {
+ sign = "-";
+ diff = -diff;
+ }
+ else
+ sign = "+";
+ pt = _add(sign, pt, ptlim);
+ diff /= SECSPERMIN;
+ diff = (diff / MINSPERHOUR) * 100 +
+ (diff % MINSPERHOUR);
+ pt = _conv(diff, "%04d", pt, ptlim);
+ }
+ continue;
+ case '+':
+ pt = _fmt(Locale->date_fmt, t, pt, ptlim,
+ warnp);
+ continue;
+ case '%':
+
+ /*
+ * X311J/88-090 (4.12.3.5): if conversion char is
+ * undefined, behavior is undefined. Print out the
+ * character itself as printf(3) also does.
+ */
+ default:
+ break;
+ }
+ }
+ if (pt == ptlim)
+ break;
+ *pt++ = *format;
+ }
+ return pt;
+}
+
+static char *
+_conv(int n, const char *format, char *pt, const char *ptlim)
+{
+ char buf[INT_STRLEN_MAXIMUM(int) + 1];
+
+ sprintf(buf, format, n);
+ return _add(buf, pt, ptlim);
+}
+
+static char *
+_add(const char *str, char *pt, const char *ptlim)
+{
+ while (pt < ptlim && (*pt = *str++) != '\0')
+ ++pt;
+ return pt;
+}
+
+/*
+ * POSIX and the C Standard are unclear or inconsistent about
+ * what %C and %y do if the year is negative or exceeds 9999.
+ * Use the convention that %C concatenated with %y yields the
+ * same output as %Y, and that %Y contains at least 4 bytes,
+ * with more only if necessary.
+ */
+
+static char *
+_yconv(int a, int b, bool convert_top, bool convert_yy,
+ char *pt, const char *ptlim)
+{
+ int lead;
+ int trail;
+
+#define DIVISOR 100
+ trail = a % DIVISOR + b % DIVISOR;
+ lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
+ trail %= DIVISOR;
+ if (trail < 0 && lead > 0)
+ {
+ trail += DIVISOR;
+ --lead;
+ }
+ else if (lead < 0 && trail > 0)
+ {
+ trail -= DIVISOR;
+ ++lead;
+ }
+ if (convert_top)
+ {
+ if (lead == 0 && trail < 0)
+ pt = _add("-0", pt, ptlim);
+ else
+ pt = _conv(lead, "%02d", pt, ptlim);
+ }
+ if (convert_yy)
+ pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
+ return pt;
+}
diff --git a/src/timezone/tzfile.h b/src/timezone/tzfile.h
new file mode 100644
index 0000000..8f3eb6b
--- /dev/null
+++ b/src/timezone/tzfile.h
@@ -0,0 +1,110 @@
+/* Layout and location of TZif files. */
+
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+ * This file is in the public domain, so clarified as of
+ * 1996-06-05 by Arthur David Olson.
+ *
+ * IDENTIFICATION
+ * src/timezone/tzfile.h
+ */
+
+/*
+ * This header is for use ONLY with the time conversion code.
+ * There is no guarantee that it will remain unchanged,
+ * or that it will remain at all.
+ * Do NOT copy it to any system include directory.
+ * Thank you!
+ */
+
+/*
+ * Information about time zone files.
+ */
+
+#define TZDEFAULT "/etc/localtime"
+#define TZDEFRULES "posixrules"
+
+
+/* See Internet RFC 8536 for more details about the following format. */
+
+/*
+ * Each file begins with. . .
+ */
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead
+{
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ char tzh_reserved[15]; /* reserved; must be zero */
+ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+ * . . .followed by. . .
+ *
+ * tzh_timecnt (char [4])s coded transition times a la time(2)
+ * tzh_timecnt (unsigned char)s types of local time starting at above
+ * tzh_typecnt repetitions of
+ * one (char [4]) coded UT offset in seconds
+ * one (unsigned char) used to set tm_isdst
+ * one (unsigned char) that's an abbreviation list index
+ * tzh_charcnt (char)s '\0'-terminated zone abbreviations
+ * tzh_leapcnt repetitions of
+ * one (char [4]) coded leap second transition times
+ * one (char [4]) total correction after above
+ * tzh_ttisstdcnt (char)s indexed by type; if 1, transition
+ * time is standard time, if 0,
+ * transition time is local (wall clock)
+ * time; if absent, transition times are
+ * assumed to be local time
+ * tzh_ttisutcnt (char)s indexed by type; if 1, transition
+ * time is UT, if 0, transition time is
+ * local time; if absent, transition
+ * times are assumed to be local time.
+ * When this is 1, the corresponding
+ * std/wall indicator must also be 1.
+ */
+
+/*
+ * If tzh_version is '2' or greater, the above is followed by a second instance
+ * of tzhead and a second instance of the data in which each coded transition
+ * time uses 8 rather than 4 chars,
+ * then a POSIX-TZ-environment-variable-style string for use in handling
+ * instants after the last transition time stored in the file
+ * (with nothing between the newlines if there is no POSIX representation for
+ * such instants).
+ *
+ * If tz_version is '3' or greater, the above is extended as follows.
+ * First, the POSIX TZ string's hour offset may range from -167
+ * through 167 as compared to the POSIX-required 0 through 24.
+ * Second, its DST start time may be January 1 at 00:00 and its stop
+ * time December 31 at 24:00 plus the difference between DST and
+ * standard time, indicating DST all year.
+ */
+
+/*
+ * In the current implementation, "tzset()" refuses to deal with files that
+ * exceed any of the limits below.
+ */
+
+#define TZ_MAX_TIMES 2000
+
+/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+
+#endif /* !defined TZFILE_H */
diff --git a/src/timezone/tznames/Africa.txt b/src/timezone/tznames/Africa.txt
new file mode 100644
index 0000000..2ea08a6
--- /dev/null
+++ b/src/timezone/tznames/Africa.txt
@@ -0,0 +1,176 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Africa.txt
+#
+
+CAT 7200 # Central Africa Time
+ # (Africa/Blantyre)
+ # (Africa/Bujumbura)
+ # (Africa/Gaborone)
+ # (Africa/Harare)
+ # (Africa/Kigali)
+ # (Africa/Lubumbashi)
+ # (Africa/Lusaka)
+ # (Africa/Maputo)
+CEST 7200 D # Central Europe Summer Time
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+CET 3600 # Central Europe Time
+ # (Africa/Algiers)
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+EAT 10800 # East Africa Time
+ # (Africa/Addis_Ababa)
+ # (Africa/Asmera)
+ # (Africa/Dar_es_Salaam)
+ # (Africa/Djibouti)
+ # (Africa/Kampala)
+ # (Africa/Khartoum)
+ # (Africa/Mogadishu)
+ # (Africa/Nairobi)
+ # (Indian/Antananarivo)
+ # (Indian/Comoro)
+ # (Indian/Mayotte)
+EEST 10800 D # East-Egypt Summer Time
+ # Eastern Europe Summer Time
+ # (Africa/Cairo)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EET 7200 # East-Egypt Time
+ # Eastern Europe Time
+ # (Africa/Cairo)
+ # (Africa/Tripoli)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+GMT 0 # Greenwich Mean Time
+ # (Africa/Abidjan)
+ # (Africa/Bamako)
+ # (Africa/Banjul)
+ # (Africa/Bissau)
+ # (Africa/Conakry)
+ # (Africa/Dakar)
+ # (Africa/Lome)
+ # (Africa/Monrovia)
+ # (Africa/Nouakchott)
+ # (Africa/Ouagadougou)
+ # (Africa/Sao_Tome)
+ # (America/Danmarkshavn)
+ # (Atlantic/Reykjavik)
+ # (Atlantic/St_Helena)
+ # (Etc/GMT)
+ # (Europe/Dublin)
+ # (Europe/London)
+# CONFLICT! SAST is not unique
+# Other timezones:
+# - SAST South Australian Standard Time (not in IANA database)
+SAST 7200 # South Africa Standard Time
+ # (Africa/Johannesburg)
+WAST 7200 D # West Africa Summer Time (obsolete)
+WAT 3600 # West Africa Time
+ # (Africa/Bangui)
+ # (Africa/Brazzaville)
+ # (Africa/Douala)
+ # (Africa/Kinshasa)
+ # (Africa/Lagos)
+ # (Africa/Libreville)
+ # (Africa/Luanda)
+ # (Africa/Malabo)
+ # (Africa/Ndjamena)
+ # (Africa/Niamey)
+ # (Africa/Porto-Novo)
+ # (Africa/Windhoek)
+WEST 3600 D # Western Europe Summer Time
+ # (Africa/Casablanca)
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
+WET 0 # Western Europe Time
+ # (Africa/Casablanca)
+ # (Africa/El_Aaiun)
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
diff --git a/src/timezone/tznames/America.txt b/src/timezone/tznames/America.txt
new file mode 100644
index 0000000..2594c37
--- /dev/null
+++ b/src/timezone/tznames/America.txt
@@ -0,0 +1,257 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/America.txt
+#
+
+# Acre time is sometimes called Acre Standard Time (AST) which leads to a
+# conflict with AST (see below at AST)
+ACT -18000 # Acre Time (obsolete)
+# CONFLICT! ACST is not unique
+# Other timezones:
+# - ACST: Australian Central Standard Time
+ACST -14400 D # Acre Summer Time (obsolete, not in IANA database)
+ADT -10800 D # Atlantic Daylight Time
+ # (America/Glace_Bay)
+ # (America/Goose_Bay)
+ # (America/Halifax)
+ # (America/Thule)
+ # (Atlantic/Bermuda)
+AKDT -28800 D # Alaska Daylight Time
+ # (America/Anchorage)
+ # (America/Juneau)
+ # (America/Nome)
+ # (America/Yakutat)
+AKST -32400 # Alaska Standard Time
+ # (America/Anchorage)
+ # (America/Juneau)
+ # (America/Nome)
+ # (America/Yakutat)
+# CONFLICT! AMST is not unique
+# Other timezones:
+# - AMST: Armenia Summer Time (Asia)
+AMST -10800 D # Amazon Summer Time (obsolete)
+# CONFLICT! AMT is not unique
+# Other timezones:
+# - AMT: Armenia Time (Asia)
+AMT -14400 # Amazon Time (obsolete)
+ART America/Argentina/Buenos_Aires # Argentina Time (obsolete)
+ARST America/Argentina/Buenos_Aires # Argentina Summer Time (obsolete)
+# CONFLICT! AST is not unique
+# Other timezones:
+# - AST: Arabic Standard Time (Asia)
+# - AST: Al Manamah Standard Time (Asia) same offset as Arabia Standard Time
+# - AST/ACT: Acre Standard Time (America) listed as ACT
+# - AST: Anguilla Standard Time (America) same offset
+# - AST: Antigua Standard Time (America) same offset
+# - AST: Antilles Standard Time (America) same offset
+AST -14400 # Atlantic Standard Time
+ # (America/Anguilla)
+ # (America/Antigua)
+ # (America/Aruba)
+ # (America/Curacao)
+ # (America/Dominica)
+ # (America/Glace_Bay)
+ # (America/Goose_Bay)
+ # (America/Grenada)
+ # (America/Guadeloupe)
+ # (America/Halifax)
+ # (America/Martinique)
+ # (America/Montserrat)
+ # (America/Port_of_Spain)
+ # (America/Puerto_Rico)
+ # (America/Santo_Domingo)
+ # (America/St_Kitts)
+ # (America/St_Lucia)
+ # (America/St_Thomas)
+ # (America/St_Vincent)
+ # (America/Thule)
+ # (America/Tortola)
+ # (Atlantic/Bermuda)
+BOT -14400 # Bolivia Time (obsolete)
+BRA -10800 # Brazil Time (not in IANA database)
+BRST -7200 D # Brasil Summer Time (obsolete)
+BRT -10800 # Brasil Time (obsolete)
+# CONFLICT! CDT is not unique
+# Other timezones:
+# - CDT: Central Daylight Time (America)
+# - CDT: Mexico Central Daylight Time (America)
+# - CDT: Canada Central Daylight Time (America)
+CDT -14400 D # Cuba Central Daylight Time
+ # (America/Havana)
+# CONFLICT! CDT is not unique
+# Other timezones:
+# - CDT: Mexico Central Daylight Time (America)
+# - CDT: Cuba Central Daylight Time (America)
+# - CDT: Canada Central Daylight Time (America)
+CDT -18000 D # Central Daylight Time
+ # (America/Chicago)
+ # (America/Menominee)
+ # (America/Merida)
+ # (America/Mexico_City)
+ # (America/Monterrey)
+ # (America/North_Dakota/Center)
+ # (America/Rainy_River)
+ # (America/Rankin_Inlet)
+ # (America/Winnipeg)
+CLST -10800 D # Chile Summer Time (obsolete)
+CLT America/Santiago # Chile Time (obsolete)
+COT -18000 # Columbia Time (obsolete)
+# CONFLICT! CST is not unique
+# Other timezones:
+# - CST: Central Standard Time (Australia)
+# - CST: Central Standard Time (America)
+# - CST: China Standard Time (Asia)
+CST -18000 # Cuba Central Standard Time (America)
+ # (America/Havana)
+# CONFLICT! CST is not unique
+# Other timezones:
+# - CST: Central Standard Time (Australia)
+# - CST: China Standard Time (Asia)
+# - CST: Cuba Central Standard Time (America)
+CST -21600 # Central Standard Time (America)
+ # (America/Chicago)
+ # (America/Menominee)
+ # (America/Merida)
+ # (America/Mexico_City)
+ # (America/Monterrey)
+ # (America/North_Dakota/Center)
+ # (America/Rainy_River)
+ # (America/Rankin_Inlet)
+ # (America/Regina)
+ # (America/Swift_Current)
+ # (America/Winnipeg)
+ECT -18000 # Ecuador Time (obsolete)
+EDT -14400 D # Eastern Daylight Saving Time
+ # (America/Detroit)
+ # (America/Grand_Turk)
+ # (America/Indiana/Indianapolis)
+ # (America/Indiana/Knox)
+ # (America/Indiana/Marengo)
+ # (America/Indiana/Vevay)
+ # (America/Iqaluit)
+ # (America/Kentucky/Louisville)
+ # (America/Kentucky/Monticello)
+ # (America/Montreal)
+ # (America/Nassau)
+ # (America/New_York)
+ # (America/Nipigon)
+ # (America/Pangnirtung)
+ # (America/Thunder_Bay)
+ # (America/Toronto)
+EGST 0 D # East Greenland Summer Time (obsolete)
+EGT -3600 # East Greenland Time (Svalbard & Jan Mayen) (obsolete)
+# CONFLICT! EST is not unique
+# Other timezones:
+# - EST: Eastern Standard Time (Australia)
+EST -18000 # Eastern Standard Time (America)
+ # (America/Cancun)
+ # (America/Cayman)
+ # (America/Coral_Harbour)
+ # (America/Detroit)
+ # (America/Grand_Turk)
+ # (America/Indiana/Indianapolis)
+ # (America/Indiana/Knox)
+ # (America/Indiana/Marengo)
+ # (America/Indiana/Vevay)
+ # (America/Iqaluit)
+ # (America/Jamaica)
+ # (America/Kentucky/Louisville)
+ # (America/Kentucky/Monticello)
+ # (America/Montreal)
+ # (America/Nassau)
+ # (America/New_York)
+ # (America/Nipigon)
+ # (America/Panama)
+ # (America/Pangnirtung)
+ # (America/Thunder_Bay)
+ # (America/Toronto)
+FNT -7200 # Fernando de Noronha Time (obsolete)
+FNST -3600 D # Fernando de Noronha Summer Time (obsolete)
+GFT -10800 # French Guiana Time (obsolete)
+GMT 0 # Greenwich Mean Time
+ # (Africa/Abidjan)
+ # (Africa/Bamako)
+ # (Africa/Banjul)
+ # (Africa/Bissau)
+ # (Africa/Conakry)
+ # (Africa/Dakar)
+ # (Africa/Lome)
+ # (Africa/Monrovia)
+ # (Africa/Nouakchott)
+ # (Africa/Ouagadougou)
+ # (Africa/Sao_Tome)
+ # (America/Danmarkshavn)
+ # (Atlantic/Reykjavik)
+ # (Atlantic/St_Helena)
+ # (Etc/GMT)
+ # (Europe/Dublin)
+ # (Europe/London)
+GYT America/Guyana # Guyana Time (obsolete)
+HADT -32400 D # Hawaii-Aleutian Daylight Time (obsolete abbreviation)
+ # (America/Adak)
+HAST -36000 # Hawaii-Aleutian Standard Time (obsolete abbreviation)
+ # (America/Adak)
+HDT -32400 D # Hawaiian-Aleutian Daylight Time
+ # (America/Adak)
+MDT -21600 D # Mexico Mountain Daylight Time
+ # Mountain Daylight Time
+ # (America/Boise)
+ # (America/Cambridge_Bay)
+ # (America/Chihuahua)
+ # (America/Denver)
+ # (America/Edmonton)
+ # (America/Inuvik)
+ # (America/Mazatlan)
+ # (America/Yellowknife)
+MST -25200 # Mexico Mountain Standard Time
+ # Mountain Standard Time
+ # (America/Boise)
+ # (America/Cambridge_Bay)
+ # (America/Chihuahua)
+ # (America/Dawson_Creek)
+ # (America/Denver)
+ # (America/Edmonton)
+ # (America/Hermosillo)
+ # (America/Inuvik)
+ # (America/Mazatlan)
+ # (America/Phoenix)
+ # (America/Yellowknife)
+NDT -9000 D # Newfoundland Daylight Time
+ # (America/St_Johns)
+# CONFLICT! NFT is not unique
+# Other timezones:
+# - NFT: Norfolk Time (Pacific)
+NFT -12600 # Newfoundland Time (not in IANA database)
+NST -12600 # Newfoundland Standard Time
+ # (America/St_Johns)
+PDT -25200 D # Pacific Daylight Time
+ # (America/Dawson)
+ # (America/Los_Angeles)
+ # (America/Tijuana)
+ # (America/Vancouver)
+ # (America/Whitehorse)
+PET -18000 # Peru Time (obsolete)
+PMDT -7200 D # Pierre & Miquelon Daylight Time (obsolete)
+PMST -10800 # Pierre & Miquelon Standard Time (obsolete)
+# CONFLICT! PST is not unique
+# Other timezones:
+# - PST: Philippine Standard Time
+PST -28800 # Pacific Standard Time
+ # (America/Dawson)
+ # (America/Los_Angeles)
+ # (America/Tijuana)
+ # (America/Vancouver)
+ # (America/Whitehorse)
+ # (Pacific/Pitcairn)
+PYST -10800 D # Paraguay Summer Time (obsolete)
+PYT America/Asuncion # Paraguay Time (obsolete)
+SRT America/Paramaribo # Suriname Time (obsolete)
+UYST -7200 D # Uruguay Summer Time (obsolete)
+UYT -10800 # Uruguay Time (obsolete)
+VET America/Caracas # Venezuela Time (obsolete)
+WGST -7200 D # Western Greenland Summer Time (obsolete)
+WGT -10800 # West Greenland Time (obsolete)
diff --git a/src/timezone/tznames/Antarctica.txt b/src/timezone/tznames/Antarctica.txt
new file mode 100644
index 0000000..413b928
--- /dev/null
+++ b/src/timezone/tznames/Antarctica.txt
@@ -0,0 +1,27 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Antarctica.txt
+#
+
+AWST 28800 # Australian Western Standard Time
+ # (Antarctica/Casey)
+ # (Australia/Perth)
+CLST -10800 D # Chile Summer Time (obsolete)
+CLT America/Santiago # Chile Time (obsolete)
+DAVT Antarctica/Davis # Davis Time (Antarctica) (obsolete)
+DDUT 36000 # Dumont-d'Urville Time (Antarctica) (obsolete)
+MAWT Antarctica/Mawson # Mawson Time (Antarctica) (obsolete)
+MIST 39600 # Macquarie Island Time (obsolete)
+NZDT 46800 D # New Zealand Daylight Time
+ # (Antarctica/McMurdo)
+ # (Pacific/Auckland)
+NZST 43200 # New Zealand Standard Time
+ # (Antarctica/McMurdo)
+ # (Pacific/Auckland)
+ROTT -10800 # Rothera Time (obsolete)
+SYOT 10800 # Syowa Time (obsolete)
+VOST 21600 # Vostok time (obsolete)
diff --git a/src/timezone/tznames/Asia.txt b/src/timezone/tznames/Asia.txt
new file mode 100644
index 0000000..1133339
--- /dev/null
+++ b/src/timezone/tznames/Asia.txt
@@ -0,0 +1,190 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Asia.txt
+#
+
+AFT 16200 # Afghanistan Time (obsolete)
+ALMST 25200 D # Alma-Ata Summer Time (obsolete)
+ALMT 21600 # Alma-Ata Time (obsolete)
+# CONFLICT! AMST is not unique
+# Other timezones:
+# - AMST: Amazon Summer Time (America)
+AMST Asia/Yerevan # Armenia Summer Time (obsolete)
+# CONFLICT! AMT is not unique
+# Other timezones:
+# - AMT: Amazon Time (America)
+AMT Asia/Yerevan # Armenia Time (obsolete)
+ANAST Asia/Anadyr # Anadyr Summer Time (obsolete)
+ANAT Asia/Anadyr # Anadyr Time (obsolete)
+AQTST Asia/Aqtau # Aqtau Summer Time (obsolete)
+AQTT Asia/Aqtau # Aqtau Time (obsolete)
+# CONFLICT! AST is not unique
+# Other timezones:
+# - AST: Atlantic Standard Time (America)
+# - AST/ACT: Acre Standard Time (America) listed as ACT
+# - AST: Anguilla Standard Time (America) same offset as Atlantic Standard Time
+# - AST: Antigua Standard Time (America) same offset as Atlantic Standard Time
+# - AST: Antilles Standard Time (America) same offset as Atlantic Standard Time
+# - AST: Al Manamah Standard Time (Asia) same offset as Arabia Standard Time
+AST 10800 # Arabia Standard Time (obsolete)
+AZST Asia/Baku # Azerbaijan Summer Time (obsolete)
+AZT Asia/Baku # Azerbaijan Time (obsolete)
+BDT 21600 # Bangladesh Time (obsolete)
+BNT 28800 # Brunei Darussalam Time (obsolete)
+BORT 28800 # Borneo Time (Indonesia) (not in IANA database)
+BTT 21600 # Bhutan Time (obsolete)
+CCT 28800 # China Coastal Time (not in IANA database)
+CHOST Asia/Choibalsan # Choibalsan Summer Time (obsolete)
+CHOT Asia/Choibalsan # Choibalsan Time (obsolete)
+CIT 28800 # Central Indonesia Time (obsolete, WITA is now preferred)
+# CONFLICT! CST is not unique
+# Other timezones:
+# - CST: Central Standard Time (Australia)
+# - CST: Central Standard Time (America)
+# - CST: Cuba Central Standard Time (America)
+CST 28800 # China Standard Time
+ # (Asia/Macau)
+ # (Asia/Shanghai)
+ # (Asia/Taipei)
+EEST 10800 D # East-Egypt Summer Time
+ # Eastern Europe Summer Time
+ # (Africa/Cairo)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EET 7200 # East-Egypt Time
+ # Eastern Europe Time
+ # (Africa/Cairo)
+ # (Africa/Tripoli)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EIT 32400 # East Indonesia Time (obsolete, WIT is now preferred)
+GEST Asia/Tbilisi # Georgia Summer Time (obsolete)
+GET Asia/Tbilisi # Georgia Time (obsolete)
+# CONFLICT! GST is not unique
+# Other timezones:
+# - GST: South Georgia Time (Atlantic)
+GST 14400 # Gulf Standard Time (obsolete)
+HKT 28800 # Hong Kong Time
+ # (Asia/Hong_Kong)
+HOVST 28800 D # Hovd Summer Time (obsolete)
+HOVT Asia/Hovd # Hovd Time (obsolete)
+ICT 25200 # Indochina Time (obsolete)
+IDT 10800 D # Israel Daylight Time
+ # (Asia/Jerusalem)
+IRDT Asia/Tehran # Iran Daylight Time (obsolete)
+IRKST Asia/Irkutsk # Irkutsk Summer Time (obsolete)
+IRKT Asia/Irkutsk # Irkutsk Time (obsolete)
+IRST Asia/Tehran # Iran Standard Time (obsolete)
+IRT 12600 # Iran Time (not in IANA database)
+# CONFLICT! IST is not unique
+# Other timezones:
+# - IST: Irish Standard Time (Europe)
+# - IST: Israel Standard Time (Asia)
+IST 19800 # Indian Standard Time
+ # (Asia/Calcutta)
+# CONFLICT! IST is not unique
+# Other timezones:
+# - IST: Irish Standard Time (Europe)
+# - IST: Indian Standard Time (Asia)
+IST 7200 # Israel Standard Time
+ # (Asia/Jerusalem)
+JAYT 32400 # Jayapura Time (Indonesia) (not in IANA database)
+JST 32400 # Japan Standard Time
+ # (Asia/Tokyo)
+KDT 36000 D # Korean Daylight Time (not in IANA database)
+KGST 21600 D # Kyrgyzstan Summer Time (obsolete)
+KGT Asia/Bishkek # Kyrgyzstan Time (obsolete)
+KRAST Asia/Krasnoyarsk # Krasnoyarsk Summer Time (obsolete)
+KRAT Asia/Krasnoyarsk # Krasnoyarsk Time (obsolete)
+KST Asia/Pyongyang # Korean Standard Time
+ # (Asia/Pyongyang)
+KST 32400 # Korean Standard Time
+ # (Asia/Seoul)
+LKT Asia/Colombo # Lanka Time (obsolete)
+MAGST Asia/Magadan # Magadan Summer Time (obsolete)
+MAGT Asia/Magadan # Magadan Time (obsolete)
+MMT 23400 # Myanmar Time (obsolete)
+MYT 28800 # Malaysia Time (obsolete)
+NOVST Asia/Novosibirsk # Novosibirsk Summer Time (obsolete)
+NOVT Asia/Novosibirsk # Novosibirsk Time (obsolete)
+NPT 20700 # Nepal Time (obsolete)
+OMSST Asia/Omsk # Omsk Summer Time (obsolete)
+OMST Asia/Omsk # Omsk Time (obsolete)
+ORAT Asia/Oral # Oral Time (obsolete)
+PETST Asia/Kamchatka # Petropavlovsk-Kamchatski Summer Time (obsolete)
+PETT Asia/Kamchatka # Petropavlovsk-Kamchatski Time (obsolete)
+PHT 28800 # Philippine Time (obsolete)
+PKT 18000 # Pakistan Time
+ # (Asia/Karachi)
+PKST 21600 D # Pakistan Summer Time
+ # (Asia/Karachi)
+# CONFLICT! PST is not unique
+# Other timezones:
+# - PST: Pacific Standard Time (America)
+PST 28800 # Philippine Standard Time
+QYZT 21600 # Kizilorda Time (obsolete)
+SAKST Asia/Sakhalin # Sakhalin Summer Time (obsolete)
+SAKT Asia/Sakhalin # Sakhalin Time (obsolete)
+SGT Asia/Singapore # Singapore Time (obsolete)
+SRET 39600 # Srednekolymsk Time (obsolete)
+TJT 18000 # Tajikistan Time (obsolete)
+TLT 32400 # East Timor Time (obsolete)
+TMT Asia/Ashgabat # Turkmenistan Time (obsolete)
+ULAST 32400 D # Ulan Bator Summer Time (obsolete)
+ULAT Asia/Ulaanbaatar # Ulan Bator Time (obsolete)
+UZST 21600 D # Uzbekistan Summer Time (obsolete)
+UZT 18000 # Uzbekistan Time (obsolete)
+VLAST Asia/Vladivostok # Vladivostok Summer Time (obsolete)
+VLAT Asia/Vladivostok # Vladivostok Time (obsolete)
+WIB 25200 # Waktu Indonesia Barat
+ # (Asia/Jakarta)
+ # (Asia/Pontianak)
+WIT 32400 # Waktu Indonesia Timur (caution: this used to mean 25200)
+ # (Asia/Jayapura)
+WITA 28800 # Waktu Indonesia Tengah
+ # (Asia/Makassar)
+XJT 21600 # Xinjiang Time (obsolete)
+YAKST Asia/Yakutsk # Yakutsk Summer Time (obsolete)
+YAKT Asia/Yakutsk # Yakutsk Time (obsolete)
+YEKST 21600 D # Yekaterinburg Summer Time (obsolete)
+YEKT Asia/Yekaterinburg # Yekaterinburg Time (obsolete)
diff --git a/src/timezone/tznames/Atlantic.txt b/src/timezone/tznames/Atlantic.txt
new file mode 100644
index 0000000..4e036cd
--- /dev/null
+++ b/src/timezone/tznames/Atlantic.txt
@@ -0,0 +1,85 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Atlantic.txt
+#
+
+ADT -10800 D # Atlantic Daylight Time
+ # (America/Glace_Bay)
+ # (America/Goose_Bay)
+ # (America/Halifax)
+ # (America/Thule)
+ # (Atlantic/Bermuda)
+# CONFLICT! AST is not unique
+# Other timezones:
+# - AST: Arabic Standard Time (Asia)
+# - AST: Al Manamah Standard Time (Asia) same offset as Arabia Standard Time
+# - AST/ACT: Acre Standard Time (America) listed as ACT
+# - AST: Anguilla Standard Time (America) same offset
+# - AST: Antigua Standard Time (America) same offset
+# - AST: Antilles Standard Time (America) same offset
+AST -14400 # Atlantic Standard Time
+ # (America/Anguilla)
+ # (America/Antigua)
+ # (America/Aruba)
+ # (America/Curacao)
+ # (America/Dominica)
+ # (America/Glace_Bay)
+ # (America/Goose_Bay)
+ # (America/Grenada)
+ # (America/Guadeloupe)
+ # (America/Halifax)
+ # (America/Martinique)
+ # (America/Montserrat)
+ # (America/Port_of_Spain)
+ # (America/Puerto_Rico)
+ # (America/Santo_Domingo)
+ # (America/St_Kitts)
+ # (America/St_Lucia)
+ # (America/St_Thomas)
+ # (America/St_Vincent)
+ # (America/Thule)
+ # (America/Tortola)
+ # (Atlantic/Bermuda)
+AZOST 0 D # Azores Summer Time (obsolete)
+AZOT -3600 # Azores Time (obsolete)
+CVT Atlantic/Cape_Verde # Cape Verde Time (obsolete)
+FKST Atlantic/Stanley # Falkland Islands Summer/Standard Time (obsolete)
+FKT Atlantic/Stanley # Falkland Islands Time (obsolete)
+GMT 0 # Greenwich Mean Time
+ # (Africa/Abidjan)
+ # (Africa/Bamako)
+ # (Africa/Banjul)
+ # (Africa/Bissau)
+ # (Africa/Conakry)
+ # (Africa/Dakar)
+ # (Africa/Lome)
+ # (Africa/Monrovia)
+ # (Africa/Nouakchott)
+ # (Africa/Ouagadougou)
+ # (Africa/Sao_Tome)
+ # (America/Danmarkshavn)
+ # (Atlantic/Reykjavik)
+ # (Atlantic/St_Helena)
+ # (Etc/GMT)
+ # (Europe/Dublin)
+ # (Europe/London)
+# CONFLICT! GST is not unique
+# Other timezones:
+# - GST: Gulf Standard Time (Asia)
+GST -7200 # South Georgia Time (Atlantic) (obsolete)
+WEST 3600 D # Western Europe Summer Time
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
+WET 0 # Western Europe Time
+ # (Africa/Casablanca)
+ # (Africa/El_Aaiun)
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
diff --git a/src/timezone/tznames/Australia b/src/timezone/tznames/Australia
new file mode 100644
index 0000000..7216e06
--- /dev/null
+++ b/src/timezone/tznames/Australia
@@ -0,0 +1,27 @@
+# Time zone configuration file for set "Australia"
+
+# The abbreviations set up by this file are no longer in widespread use,
+# and should be avoided when possible. Use this file if you need backwards
+# compatibility with old applications or data.
+
+# In order to use this file, you need to set the run-time parameter
+# timezone_abbreviations to 'Australia'. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Australia
+
+
+# include the default set
+@INCLUDE Default
+
+# most timezones are already defined in the default set. With the OVERRIDE
+# option, PostgreSQL will use the new definitions instead of throwing an error
+# in case of a conflict.
+@OVERRIDE
+
+CST 34200 # Central Standard Time (not in IANA database)
+EAST 36000 # East Australian Standard Time (not in IANA database)
+EST 36000 # Eastern Standard Time (not in IANA database)
+SAST 34200 # South Australian Standard Time (not in IANA database)
+SAT 34200 # South Australian Standard Time (not in IANA database)
+WST 28800 # Western Standard Time (not in IANA database)
diff --git a/src/timezone/tznames/Australia.txt b/src/timezone/tznames/Australia.txt
new file mode 100644
index 0000000..da90866
--- /dev/null
+++ b/src/timezone/tznames/Australia.txt
@@ -0,0 +1,71 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Australia.txt
+#
+
+ACSST 37800 D # Australian Central Summer Standard Time (not in IANA database)
+ACDT 37800 D # Australian Central Daylight Time
+ # (Australia/Adelaide)
+ # (Australia/Broken_Hill)
+ # (Australia/Darwin)
+ACST 34200 # Australian Central Standard Time
+ # (Australia/Adelaide)
+ # (Australia/Broken_Hill)
+ # (Australia/Darwin)
+ACWST 31500 # Australian Central Western Standard Time (obsolete)
+AESST 39600 D # Australian Eastern Summer Standard Time (not in IANA database)
+AEDT 39600 D # Australian Eastern Daylight Time
+ # (Australia/Brisbane)
+ # (Australia/Currie)
+ # (Australia/Hobart)
+ # (Australia/Lindeman)
+ # (Australia/Melbourne)
+ # (Australia/Sydney)
+AEST 36000 # Australian Eastern Standard Time
+ # (Australia/Brisbane)
+ # (Australia/Currie)
+ # (Australia/Hobart)
+ # (Australia/Lindeman)
+ # (Australia/Melbourne)
+ # (Australia/Sydney)
+AWSST 32400 D # Australia Western Summer Standard Time (not in IANA database)
+AWST 28800 # Australian Western Standard Time
+ # (Australia/Perth)
+CADT 37800 D # Central Australia Daylight-Saving Time (not in IANA database)
+CAST 34200 # Central Australia Standard Time (not in IANA database)
+# CONFLICT! CST is not unique
+# Other timezones:
+# - CST: Central Standard Time (America)
+# - CST: China Standard Time (Asia)
+# - CST: Cuba Central Standard Time (America)
+CST 34200 # Central Standard Time (not in IANA database)
+CWST 31500 # Central Western Standard Time (not in IANA database)
+# CONFLICT! EAST is not unique
+# Other timezones:
+# - EAST: Easter Island Time (Chile) (Pacific)
+EAST 36000 # East Australian Standard Time (not in IANA database)
+# CONFLICT! EST is not unique
+# Other timezones:
+# - EST: Eastern Standard Time (America)
+EST 36000 # Eastern Standard Time (not in IANA database)
+LHDT Australia/Lord_Howe # Lord Howe Daylight Time (obsolete)
+LHST 37800 # Lord Howe Standard Time (obsolete)
+LIGT 36000 # Melbourne, Australia (not in IANA database)
+NZT 43200 # New Zealand Time (not in IANA database)
+SADT 37800 D # South Australian Daylight-Saving Time (not in IANA database)
+# CONFLICT! SAST is not unique
+# Other timezones:
+# - SAST South Africa Standard Time
+SAST 34200 # South Australian Standard Time (not in IANA database)
+SAT 34200 # South Australian Standard Time (not in IANA database)
+WADT 28800 D # West Australian Daylight-Saving Time (not in IANA database)
+WAST 25200 # West Australian Standard Time (not in IANA database)
+WDT 32400 D # West Australian Daylight-Saving Time (not in IANA database)
+# CONFLICT! WST is not unique
+# Other timezones:
+# - WST: West Samoa Time
+WST 28800 # Western Standard Time (not in IANA database)
diff --git a/src/timezone/tznames/Default b/src/timezone/tznames/Default
new file mode 100644
index 0000000..8a4dc59
--- /dev/null
+++ b/src/timezone/tznames/Default
@@ -0,0 +1,632 @@
+# Time zone configuration file for set "Default"
+
+# In order to use this file, you need to set the run-time parameter
+# timezone_abbreviations to 'Default'. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Default
+
+
+#################### AFRICA ####################
+
+EAT 10800 # East Africa Time
+ # (Africa/Addis_Ababa)
+ # (Africa/Asmera)
+ # (Africa/Dar_es_Salaam)
+ # (Africa/Djibouti)
+ # (Africa/Kampala)
+ # (Africa/Khartoum)
+ # (Africa/Mogadishu)
+ # (Africa/Nairobi)
+ # (Indian/Antananarivo)
+ # (Indian/Comoro)
+ # (Indian/Mayotte)
+SAST 7200 # South Africa Standard Time
+ # (Africa/Johannesburg)
+WAT 3600 # West Africa Time
+ # (Africa/Bangui)
+ # (Africa/Brazzaville)
+ # (Africa/Douala)
+ # (Africa/Kinshasa)
+ # (Africa/Lagos)
+ # (Africa/Libreville)
+ # (Africa/Luanda)
+ # (Africa/Malabo)
+ # (Africa/Ndjamena)
+ # (Africa/Niamey)
+ # (Africa/Porto-Novo)
+ # (Africa/Windhoek)
+
+#################### AMERICA ####################
+
+# Acre time is sometimes called Acre Standard Time (AST) which leads to a
+# conflict with AST (see below at AST)
+ACT -18000 # Acre Time (obsolete)
+AKDT -28800 D # Alaska Daylight Time
+ # (America/Anchorage)
+ # (America/Juneau)
+ # (America/Nome)
+ # (America/Yakutat)
+AKST -32400 # Alaska Standard Time
+ # (America/Anchorage)
+ # (America/Juneau)
+ # (America/Nome)
+ # (America/Yakutat)
+ART America/Argentina/Buenos_Aires # Argentina Time (obsolete)
+ARST America/Argentina/Buenos_Aires # Argentina Summer Time (obsolete)
+BOT -14400 # Bolivia Time (obsolete)
+BRA -10800 # Brazil Time (not in IANA database)
+BRST -7200 D # Brasil Summer Time (obsolete)
+BRT -10800 # Brasil Time (obsolete)
+COT -18000 # Columbia Time (obsolete)
+# CONFLICT! CDT is not unique
+# Other timezones:
+# - CDT: Mexico Central Daylight Time (America)
+# - CDT: Cuba Central Daylight Time (America)
+# - CDT: Canada Central Daylight Time (America)
+CDT -18000 D # Central Daylight Time
+ # (America/Chicago)
+ # (America/Menominee)
+ # (America/Merida)
+ # (America/Mexico_City)
+ # (America/Monterrey)
+ # (America/North_Dakota/Center)
+ # (America/Rainy_River)
+ # (America/Rankin_Inlet)
+ # (America/Winnipeg)
+CLST -10800 D # Chile Summer Time (obsolete)
+CLT America/Santiago # Chile Time (obsolete)
+# CONFLICT! CST is not unique
+# Other timezones:
+# - CST: Central Standard Time (Australia)
+# - CST: China Standard Time (Asia)
+# - CST: Cuba Central Standard Time (America)
+CST -21600 # Central Standard Time (America)
+ # (America/Chicago)
+ # (America/Menominee)
+ # (America/Merida)
+ # (America/Mexico_City)
+ # (America/Monterrey)
+ # (America/North_Dakota/Center)
+ # (America/Rainy_River)
+ # (America/Rankin_Inlet)
+ # (America/Regina)
+ # (America/Swift_Current)
+ # (America/Winnipeg)
+EDT -14400 D # Eastern Daylight Saving Time
+ # (America/Detroit)
+ # (America/Grand_Turk)
+ # (America/Indiana/Indianapolis)
+ # (America/Indiana/Knox)
+ # (America/Indiana/Marengo)
+ # (America/Indiana/Vevay)
+ # (America/Iqaluit)
+ # (America/Kentucky/Louisville)
+ # (America/Kentucky/Monticello)
+ # (America/Montreal)
+ # (America/Nassau)
+ # (America/New_York)
+ # (America/Nipigon)
+ # (America/Pangnirtung)
+ # (America/Thunder_Bay)
+ # (America/Toronto)
+EGST 0 D # East Greenland Summer Time (obsolete)
+EGT -3600 # East Greenland Time (Svalbard & Jan Mayen) (obsolete)
+# CONFLICT! EST is not unique
+# Other timezones:
+# - EST: Eastern Standard Time (Australia)
+EST -18000 # Eastern Standard Time (America)
+ # (America/Cancun)
+ # (America/Cayman)
+ # (America/Coral_Harbour)
+ # (America/Detroit)
+ # (America/Grand_Turk)
+ # (America/Indiana/Indianapolis)
+ # (America/Indiana/Knox)
+ # (America/Indiana/Marengo)
+ # (America/Indiana/Vevay)
+ # (America/Iqaluit)
+ # (America/Jamaica)
+ # (America/Kentucky/Louisville)
+ # (America/Kentucky/Monticello)
+ # (America/Montreal)
+ # (America/Nassau)
+ # (America/New_York)
+ # (America/Nipigon)
+ # (America/Panama)
+ # (America/Pangnirtung)
+ # (America/Thunder_Bay)
+ # (America/Toronto)
+FNT -7200 # Fernando de Noronha Time (obsolete)
+FNST -3600 D # Fernando de Noronha Summer Time (obsolete)
+GFT -10800 # French Guiana Time (obsolete)
+GYT America/Guyana # Guyana Time (obsolete)
+MDT -21600 D # Mexico Mountain Daylight Time
+ # Mountain Daylight Time
+ # (America/Boise)
+ # (America/Cambridge_Bay)
+ # (America/Chihuahua)
+ # (America/Denver)
+ # (America/Edmonton)
+ # (America/Inuvik)
+ # (America/Mazatlan)
+ # (America/Yellowknife)
+MST -25200 # Mexico Mountain Standard Time
+ # Mountain Standard Time
+ # (America/Boise)
+ # (America/Cambridge_Bay)
+ # (America/Chihuahua)
+ # (America/Dawson_Creek)
+ # (America/Denver)
+ # (America/Edmonton)
+ # (America/Hermosillo)
+ # (America/Inuvik)
+ # (America/Mazatlan)
+ # (America/Phoenix)
+ # (America/Yellowknife)
+NDT -9000 D # Newfoundland Daylight Time
+ # (America/St_Johns)
+# CONFLICT! NFT is not unique
+# Other timezones:
+# - NFT: Norfolk Time (Pacific)
+NFT -12600 # Newfoundland Time (not in IANA database)
+NST -12600 # Newfoundland Standard Time
+ # (America/St_Johns)
+PET -18000 # Peru Time (obsolete)
+PDT -25200 D # Pacific Daylight Time
+ # (America/Dawson)
+ # (America/Los_Angeles)
+ # (America/Tijuana)
+ # (America/Vancouver)
+ # (America/Whitehorse)
+PMDT -7200 D # Pierre & Miquelon Daylight Time (obsolete)
+PMST -10800 # Pierre & Miquelon Standard Time (obsolete)
+# CONFLICT! PST is not unique
+# Other timezones:
+# - PST: Philippine Standard Time
+PST -28800 # Pacific Standard Time
+ # (America/Dawson)
+ # (America/Los_Angeles)
+ # (America/Tijuana)
+ # (America/Vancouver)
+ # (America/Whitehorse)
+ # (Pacific/Pitcairn)
+PYST -10800 D # Paraguay Summer Time (obsolete)
+PYT America/Asuncion # Paraguay Time (obsolete)
+UYST -7200 D # Uruguay Summer Time (obsolete)
+UYT -10800 # Uruguay Time (obsolete)
+VET America/Caracas # Venezuela Time (obsolete)
+WGST -7200 D # Western Greenland Summer Time (obsolete)
+WGT -10800 # West Greenland Time (obsolete)
+
+#################### ANTARCTICA ####################
+
+DAVT Antarctica/Davis # Davis Time (Antarctica) (obsolete)
+DDUT 36000 # Dumont-d'Urville Time (Antarctica) (obsolete)
+MAWT Antarctica/Mawson # Mawson Time (Antarctica) (obsolete)
+
+#################### ASIA ####################
+
+AFT 16200 # Afghanistan Time (obsolete)
+ALMT 21600 # Alma-Ata Time (obsolete)
+ALMST 25200 D # Alma-Ata Summer Time (obsolete)
+# CONFLICT! AMST is not unique
+# Other timezones:
+# - AMST: Amazon Summer Time (America)
+AMST Asia/Yerevan # Armenia Summer Time (obsolete)
+# CONFLICT! AMT is not unique
+# Other timezones:
+# - AMT: Armenia Time (Asia)
+AMT -14400 # Amazon Time (obsolete)
+ANAST Asia/Anadyr # Anadyr Summer Time (obsolete)
+ANAT Asia/Anadyr # Anadyr Time (obsolete)
+AZST Asia/Baku # Azerbaijan Summer Time (obsolete)
+AZT Asia/Baku # Azerbaijan Time (obsolete)
+BDT 21600 # Bangladesh Time (obsolete)
+BNT 28800 # Brunei Darussalam Time (obsolete)
+BORT 28800 # Borneo Time (Indonesia) (not in IANA database)
+BTT 21600 # Bhutan Time (obsolete)
+CCT 28800 # China Coastal Time (not in IANA database)
+GEST Asia/Tbilisi # Georgia Summer Time (obsolete)
+GET Asia/Tbilisi # Georgia Time (obsolete)
+HKT 28800 # Hong Kong Time
+ # (Asia/Hong_Kong)
+ICT 25200 # Indochina Time (obsolete)
+IDT 10800 D # Israel Daylight Time
+ # (Asia/Jerusalem)
+IRKST Asia/Irkutsk # Irkutsk Summer Time (obsolete)
+IRKT Asia/Irkutsk # Irkutsk Time (obsolete)
+IRT 12600 # Iran Time (not in IANA database)
+# CONFLICT! IST is not unique
+# Other timezones:
+# - IST: Irish Standard Time (Europe)
+# - IST: Indian Standard Time (Asia)
+IST 7200 # Israel Standard Time
+ # (Asia/Jerusalem)
+JAYT 32400 # Jayapura Time (Indonesia) (not in IANA database)
+JST 32400 # Japan Standard Time
+ # (Asia/Tokyo)
+KDT 36000 D # Korean Daylight Time (not in IANA database)
+KGST 21600 D # Kyrgyzstan Summer Time (obsolete)
+KGT Asia/Bishkek # Kyrgyzstan Time (obsolete)
+KRAST Asia/Krasnoyarsk # Krasnoyarsk Summer Time (obsolete)
+KRAT Asia/Krasnoyarsk # Krasnoyarsk Time (obsolete)
+KST 32400 # Korean Standard Time
+ # (Asia/Seoul)
+LKT Asia/Colombo # Lanka Time (obsolete)
+MAGST Asia/Magadan # Magadan Summer Time (obsolete)
+MAGT Asia/Magadan # Magadan Time (obsolete)
+MMT 23400 # Myanmar Time (obsolete)
+MYT 28800 # Malaysia Time (obsolete)
+NOVST Asia/Novosibirsk # Novosibirsk Summer Time (obsolete)
+NOVT Asia/Novosibirsk # Novosibirsk Time (obsolete)
+NPT 20700 # Nepal Time (obsolete)
+OMSST Asia/Omsk # Omsk Summer Time (obsolete)
+OMST Asia/Omsk # Omsk Time (obsolete)
+PETST Asia/Kamchatka # Petropavlovsk-Kamchatski Summer Time (obsolete)
+PETT Asia/Kamchatka # Petropavlovsk-Kamchatski Time (obsolete)
+PHT 28800 # Philippine Time (obsolete)
+PKT 18000 # Pakistan Time
+ # (Asia/Karachi)
+PKST 21600 D # Pakistan Summer Time
+ # (Asia/Karachi)
+SGT Asia/Singapore # Singapore Time (obsolete)
+TJT 18000 # Tajikistan Time (obsolete)
+TMT Asia/Ashgabat # Turkmenistan Time (obsolete)
+ULAST 32400 D # Ulan Bator Summer Time (obsolete)
+ULAT Asia/Ulaanbaatar # Ulan Bator Time (obsolete)
+UZST 21600 D # Uzbekistan Summer Time (obsolete)
+UZT 18000 # Uzbekistan Time (obsolete)
+VLAST Asia/Vladivostok # Vladivostok Summer Time (obsolete)
+VLAT Asia/Vladivostok # Vladivostok Time (obsolete)
+XJT 21600 # Xinjiang Time (obsolete)
+YAKST Asia/Yakutsk # Yakutsk Summer Time (obsolete)
+YAKT Asia/Yakutsk # Yakutsk Time (obsolete)
+YEKST 21600 D # Yekaterinburg Summer Time (obsolete)
+YEKT Asia/Yekaterinburg # Yekaterinburg Time (obsolete)
+
+#################### ATLANTIC ####################
+
+ADT -10800 D # Atlantic Daylight Time
+ # (America/Glace_Bay)
+ # (America/Goose_Bay)
+ # (America/Halifax)
+ # (America/Thule)
+ # (Atlantic/Bermuda)
+# CONFLICT! AST is not unique
+# Other timezones:
+# - AST: Arabic Standard Time (Asia)
+# - AST: Al Manamah Standard Time (Asia) same offset as Arabia Standard Time
+# - AST/ACT: Acre Standard Time (America) listed as ACT
+# - AST: Anguilla Standard Time (America) same offset
+# - AST: Antigua Standard Time (America) same offset
+# - AST: Antilles Standard Time (America) same offset
+AST -14400 # Atlantic Standard Time
+ # (America/Anguilla)
+ # (America/Antigua)
+ # (America/Aruba)
+ # (America/Curacao)
+ # (America/Dominica)
+ # (America/Glace_Bay)
+ # (America/Goose_Bay)
+ # (America/Grenada)
+ # (America/Guadeloupe)
+ # (America/Halifax)
+ # (America/Martinique)
+ # (America/Montserrat)
+ # (America/Port_of_Spain)
+ # (America/Puerto_Rico)
+ # (America/Santo_Domingo)
+ # (America/St_Kitts)
+ # (America/St_Lucia)
+ # (America/St_Thomas)
+ # (America/St_Vincent)
+ # (America/Thule)
+ # (America/Tortola)
+ # (Atlantic/Bermuda)
+AZOST 0 D # Azores Summer Time (obsolete)
+AZOT -3600 # Azores Time (obsolete)
+FKST Atlantic/Stanley # Falkland Islands Summer/Standard Time (obsolete)
+FKT Atlantic/Stanley # Falkland Islands Time (obsolete)
+
+#################### AUSTRALIA ####################
+
+ACSST 37800 D # Australian Central Summer Standard Time (not in IANA database)
+ACDT 37800 D # Australian Central Daylight Time
+ # (Australia/Adelaide)
+ # (Australia/Broken_Hill)
+ # (Australia/Darwin)
+ACST 34200 # Australian Central Standard Time
+ # (Australia/Adelaide)
+ # (Australia/Broken_Hill)
+ # (Australia/Darwin)
+ACWST 31500 # Australian Central Western Standard Time (obsolete)
+AESST 39600 D # Australian Eastern Summer Standard Time (not in IANA database)
+AEDT 39600 D # Australian Eastern Daylight Time
+ # (Australia/Brisbane)
+ # (Australia/Currie)
+ # (Australia/Hobart)
+ # (Australia/Lindeman)
+ # (Australia/Melbourne)
+ # (Australia/Sydney)
+AEST 36000 # Australian Eastern Standard Time
+ # (Australia/Brisbane)
+ # (Australia/Currie)
+ # (Australia/Hobart)
+ # (Australia/Lindeman)
+ # (Australia/Melbourne)
+ # (Australia/Sydney)
+AWSST 32400 D # Australia Western Summer Standard Time (not in IANA database)
+AWST 28800 # Australian Western Standard Time
+ # (Australia/Perth)
+CADT 37800 D # Central Australia Daylight-Saving Time (not in IANA database)
+CAST 34200 # Central Australia Standard Time (not in IANA database)
+LHDT Australia/Lord_Howe # Lord Howe Daylight Time (obsolete)
+LHST 37800 # Lord Howe Standard Time (obsolete)
+LIGT 36000 # Melbourne, Australia (not in IANA database)
+NZT 43200 # New Zealand Time (not in IANA database)
+SADT 37800 D # South Australian Daylight-Saving Time (not in IANA database)
+WADT 28800 D # West Australian Daylight-Saving Time (not in IANA database)
+WAST 25200 # West Australian Standard Time (not in IANA database)
+WDT 32400 D # West Australian Daylight-Saving Time (not in IANA database)
+
+#################### ETC ####################
+
+GMT 0 # Greenwich Mean Time
+ # (Africa/Abidjan)
+ # (Africa/Bamako)
+ # (Africa/Banjul)
+ # (Africa/Bissau)
+ # (Africa/Conakry)
+ # (Africa/Dakar)
+ # (Africa/Lome)
+ # (Africa/Monrovia)
+ # (Africa/Nouakchott)
+ # (Africa/Ouagadougou)
+ # (Africa/Sao_Tome)
+ # (America/Danmarkshavn)
+ # (Atlantic/Reykjavik)
+ # (Atlantic/St_Helena)
+ # (Etc/GMT)
+ # (Europe/Dublin)
+ # (Europe/London)
+UCT 0 # Universal Coordinated Time
+ # (Etc/UCT)
+UT 0 # Universal Time (not in IANA database)
+UTC 0 # Coordinated Universal Time
+Z 0 # Zulu
+ZULU 0 # Zulu
+
+#################### EUROPE ####################
+
+# CONFLICT! BST is not unique
+# Other timezones:
+# - BST: Bougainville Standard Time (Papua New Guinea)
+BST 3600 D # British Summer Time
+ # (Europe/London)
+BDST 7200 D # British Double Summer Time
+CEST 7200 D # Central Europe Summer Time
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+CET 3600 # Central Europe Time
+ # (Africa/Algiers)
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+CETDST 7200 D # Central Europe Summer Time
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+EEST 10800 D # East-Egypt Summertime
+ # Eastern Europe Summer Time
+ # (Africa/Cairo)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EET 7200 # East-Egypt Time
+ # Eastern Europe Time
+ # (Africa/Cairo)
+ # (Africa/Tripoli)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EETDST 10800 D # East-Egypt Summertime
+ # Eastern Europe Summer Time
+ # (Africa/Cairo)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+FET 10800 # Further-eastern European Time (obsolete)
+ # (Europe/Kaliningrad)
+ # (Europe/Minsk)
+MEST 7200 D # Middle Europe Summer Time
+ # (MET)
+MESZ 7200 D # Mitteleuropaeische Sommerzeit (German)
+ # (attested in IANA comments though not their code)
+MET 3600 # Middle Europe Time
+ # (MET)
+METDST 7200 D # Middle Europe Summer Time (not in IANA database)
+MEZ 3600 # Mitteleuropaeische Zeit (German)
+ # (attested in IANA comments though not their code)
+MSD 14400 D # Moscow Daylight Time (obsolete)
+MSK Europe/Moscow # Moscow Time
+ # (Europe/Moscow)
+ # (Europe/Volgograd)
+VOLT Europe/Volgograd # Volgograd Time (obsolete)
+WET 0 # Western Europe Time
+ # (Africa/Casablanca)
+ # (Africa/El_Aaiun)
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
+WETDST 3600 D # Western Europe Summer Time
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
+
+#################### INDIAN ####################
+
+CXT 25200 # Christmas Island Time (Indian Ocean) (obsolete)
+IOT Indian/Chagos # British Indian Ocean Territory (Chagos) (obsolete)
+MUT 14400 # Mauritius Island Time (obsolete)
+MUST 18000 D # Mauritius Island Summer Time (obsolete)
+MVT 18000 # Maldives Island Time (obsolete)
+RET 14400 # Reunion Time (obsolete)
+SCT 14400 # Seychelles Time (obsolete)
+TFT 18000 # Kerguelen Time (obsolete)
+
+#################### PACIFIC ####################
+
+CHADT 49500 D # Chatham Daylight Time (New Zealand) (obsolete)
+CHAST 45900 # Chatham Standard Time (New Zealand) (obsolete)
+CHUT 36000 # Chuuk Time (obsolete)
+CKT Pacific/Rarotonga # Cook Islands Time (obsolete)
+EASST Pacific/Easter # Easter Island Summer Time (obsolete)
+EAST Pacific/Easter # Easter Island Time (Chile) (obsolete)
+FJST 46800 D # Fiji Summer Time (caution: this used to mean -46800) (obsolete)
+FJT 43200 # Fiji Time (caution: this used to mean -43200) (obsolete)
+GALT -21600 # Galapagos Time (obsolete)
+GAMT -32400 # Gambier Time (obsolete)
+GILT 43200 # Gilbert Islands Time (obsolete)
+HST -36000 # Hawaiian Standard Time
+ # (Pacific/Honolulu)
+ # (Pacific/Johnston)
+KOST Pacific/Kosrae # Kosrae Time (obsolete)
+LINT Pacific/Kiritimati # Line Islands Time (Kiribati) (obsolete)
+MART -34200 # Marquesas Time (obsolete)
+MHT 43200 # Kwajalein Time (obsolete)
+MPT 36000 # North Mariana Islands Time (not in IANA database)
+NUT Pacific/Niue # Niue Time (obsolete)
+NZDT 46800 D # New Zealand Daylight Time
+ # (Antarctica/McMurdo)
+ # (Pacific/Auckland)
+NZST 43200 # New Zealand Standard Time
+ # (Antarctica/McMurdo)
+ # (Pacific/Auckland)
+PGT 36000 # Papua New Guinea Time (obsolete)
+PHOT Pacific/Enderbury # Phoenix Islands Time (Kiribati) (obsolete)
+PONT 39600 # Ponape Time (Micronesia) (obsolete)
+PWT 32400 # Palau Time (obsolete)
+TAHT -36000 # Tahiti Time (obsolete)
+TKT Pacific/Fakaofo # Tokelau Time (obsolete)
+TOT 46800 # Tonga Time (obsolete)
+TRUT 36000 # Truk Time (obsolete)
+TVT 43200 # Tuvalu Time (obsolete)
+VUT 39600 # Vanuata Time (obsolete)
+WAKT 43200 # Wake Time (obsolete)
+WFT 43200 # Wallis and Futuna Time (obsolete)
+YAPT 36000 # Yap Time (Micronesia) (not in IANA database)
diff --git a/src/timezone/tznames/Etc.txt b/src/timezone/tznames/Etc.txt
new file mode 100644
index 0000000..aa48404
--- /dev/null
+++ b/src/timezone/tznames/Etc.txt
@@ -0,0 +1,34 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Etc.txt
+#
+
+GMT 0 # Greenwich Mean Time
+ # (Africa/Abidjan)
+ # (Africa/Bamako)
+ # (Africa/Banjul)
+ # (Africa/Bissau)
+ # (Africa/Conakry)
+ # (Africa/Dakar)
+ # (Africa/Lome)
+ # (Africa/Monrovia)
+ # (Africa/Nouakchott)
+ # (Africa/Ouagadougou)
+ # (Africa/Sao_Tome)
+ # (America/Danmarkshavn)
+ # (Atlantic/Reykjavik)
+ # (Atlantic/St_Helena)
+ # (Etc/GMT)
+ # (Europe/Dublin)
+ # (Europe/London)
+UCT 0 # Universal Coordinated Time
+ # (Etc/UCT)
+UT 0 # Universal Time (not in IANA database)
+UTC 0 # Coordinated Universal Time
+ # (Etc/UTC)
+Z 0 # Zulu
+ZULU 0 # Zulu
diff --git a/src/timezone/tznames/Europe.txt b/src/timezone/tznames/Europe.txt
new file mode 100644
index 0000000..2e762b9
--- /dev/null
+++ b/src/timezone/tznames/Europe.txt
@@ -0,0 +1,219 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Europe.txt
+#
+
+# CONFLICT! BST is not unique
+# Other timezones:
+# - BST: Bougainville Standard Time (Papua New Guinea)
+BST 3600 D # British Summer Time
+ # (Europe/London)
+BDST 7200 D # British Double Summer Time
+CEST 7200 D # Central Europe Summer Time
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+CET 3600 # Central Europe Time
+ # (Africa/Algiers)
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+CETDST 7200 D # Central Europe Summer Time
+ # (Africa/Ceuta)
+ # (Europe/Amsterdam)
+ # (Europe/Andorra)
+ # (Europe/Belgrade)
+ # (Europe/Berlin)
+ # (Europe/Brussels)
+ # (Europe/Budapest)
+ # (Europe/Copenhagen)
+ # (Europe/Gibraltar)
+ # (Europe/Luxembourg)
+ # (Europe/Madrid)
+ # (Europe/Malta)
+ # (Europe/Monaco)
+ # (Europe/Oslo)
+ # (Europe/Paris)
+ # (Europe/Prague)
+ # (Europe/Rome)
+ # (Europe/Stockholm)
+ # (Europe/Tirane)
+ # (Europe/Vaduz)
+ # (Europe/Vienna)
+ # (Europe/Warsaw)
+ # (Europe/Zurich)
+EEST 10800 D # East-Egypt Summertime
+ # Eastern Europe Summer Time
+ # (Africa/Cairo)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EET 7200 # East-Egypt Time
+ # Eastern Europe Time
+ # (Africa/Cairo)
+ # (Africa/Tripoli)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+EETDST 10800 D # East-Egypt Summertime
+ # Eastern Europe Summer Time
+ # (Africa/Cairo)
+ # (Asia/Amman)
+ # (Asia/Beirut)
+ # (Asia/Damascus)
+ # (Asia/Gaza)
+ # (Asia/Nicosia)
+ # (Europe/Athens)
+ # (Europe/Bucharest)
+ # (Europe/Chisinau)
+ # (Europe/Helsinki)
+ # (Europe/Istanbul)
+ # (Europe/Kaliningrad)
+ # (Europe/Kiev)
+ # (Europe/Minsk)
+ # (Europe/Riga)
+ # (Europe/Simferopol)
+ # (Europe/Sofia)
+ # (Europe/Tallinn)
+ # (Europe/Uzhgorod)
+ # (Europe/Vilnius)
+ # (Europe/Zaporozhye)
+FET 10800 # Further-eastern European Time (obsolete)
+ # (Europe/Kaliningrad)
+ # (Europe/Minsk)
+GMT 0 # Greenwich Mean Time
+ # (Africa/Abidjan)
+ # (Africa/Bamako)
+ # (Africa/Banjul)
+ # (Africa/Bissau)
+ # (Africa/Conakry)
+ # (Africa/Dakar)
+ # (Africa/Lome)
+ # (Africa/Monrovia)
+ # (Africa/Nouakchott)
+ # (Africa/Ouagadougou)
+ # (Africa/Sao_Tome)
+ # (America/Danmarkshavn)
+ # (Atlantic/Reykjavik)
+ # (Atlantic/St_Helena)
+ # (Etc/GMT)
+ # (Europe/Dublin)
+ # (Europe/London)
+# CONFLICT! IST is not unique
+# Other timezones:
+# - IST: Indian Standard Time (Asia)
+# - IST: Israel Standard Time (Asia)
+IST 3600 # Irish Standard Time
+ # (Europe/Dublin)
+MEST 7200 D # Middle Europe Summer Time
+ # (MET)
+MESZ 7200 D # Mitteleuropaeische Sommerzeit (German)
+ # (attested in IANA comments though not their code)
+MET 3600 # Middle Europe Time
+ # (MET)
+METDST 7200 D # Middle Europe Summer Time (not in IANA database)
+MEZ 3600 # Mitteleuropaeische Zeit (German)
+ # (attested in IANA comments though not their code)
+MSD 14400 D # Moscow Daylight Time (obsolete)
+MSK Europe/Moscow # Moscow Time
+ # (Europe/Moscow)
+ # (Europe/Volgograd)
+SAMST Europe/Samara # Samara Summer Time (obsolete)
+SAMT Europe/Samara # Samara Time (obsolete)
+VOLT Europe/Volgograd # Volgograd Time (obsolete)
+WEST 3600 D # Western Europe Summer Time
+ # (Africa/Casablanca)
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
+WET 0 # Western Europe Time
+ # (Africa/Casablanca)
+ # (Africa/El_Aaiun)
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
+WETDST 3600 D # Western Europe Summer Time
+ # (Atlantic/Canary)
+ # (Atlantic/Faeroe)
+ # (Atlantic/Madeira)
+ # (Europe/Lisbon)
diff --git a/src/timezone/tznames/India b/src/timezone/tznames/India
new file mode 100644
index 0000000..85830e9
--- /dev/null
+++ b/src/timezone/tznames/India
@@ -0,0 +1,19 @@
+# Time zone configuration file for set "India"
+
+# In order to use this file, you need to set the run-time parameter
+# timezone_abbreviations to 'India'. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/India
+
+
+# include the default set
+@INCLUDE Default
+
+# most timezones are already defined in the default set. With the OVERRIDE
+# option, PostgreSQL will use the new definitions instead of throwing an error
+# in case of a conflict.
+@OVERRIDE
+
+IST 19800 # Indian Standard Time
+ # (Asia/Calcutta)
diff --git a/src/timezone/tznames/Indian.txt b/src/timezone/tznames/Indian.txt
new file mode 100644
index 0000000..8e6fe60
--- /dev/null
+++ b/src/timezone/tznames/Indian.txt
@@ -0,0 +1,30 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Indian.txt
+#
+
+CCT 23400 # Cocos Islands Time (Indian Ocean) (obsolete)
+CXT 25200 # Christmas Island Time (Indian Ocean) (obsolete)
+EAT 10800 # East Africa Time
+ # (Africa/Addis_Ababa)
+ # (Africa/Asmera)
+ # (Africa/Dar_es_Salaam)
+ # (Africa/Djibouti)
+ # (Africa/Kampala)
+ # (Africa/Khartoum)
+ # (Africa/Mogadishu)
+ # (Africa/Nairobi)
+ # (Indian/Antananarivo)
+ # (Indian/Comoro)
+ # (Indian/Mayotte)
+IOT Indian/Chagos # British Indian Ocean Territory (Chagos) (obsolete)
+MUT 14400 # Mauritius Island Time (obsolete)
+MUST 18000 D # Mauritius Island Summer Time (obsolete)
+MVT 18000 # Maldives Island Time (obsolete)
+RET 14400 # Reunion Time (obsolete)
+SCT 14400 # Seychelles Time (obsolete)
+TFT 18000 # Kerguelen Time (obsolete)
diff --git a/src/timezone/tznames/Makefile b/src/timezone/tznames/Makefile
new file mode 100644
index 0000000..e80bf53
--- /dev/null
+++ b/src/timezone/tznames/Makefile
@@ -0,0 +1,30 @@
+#-------------------------------------------------------------------------
+#
+# Makefile
+# Makefile for the timezone names
+
+# IDENTIFICATION
+# src/timezone/tznames/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/timezone/tznames
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+TZNAMES_TEMPLATES = Africa.txt America.txt Antarctica.txt Asia.txt \
+ Atlantic.txt Australia.txt Etc.txt Europe.txt Indian.txt Pacific.txt
+TZNAMES_TEMPLATES_FILES = $(TZNAMES_TEMPLATES:%=$(srcdir)/%)
+
+TZNAMES_SETS = Default Australia India
+TZNAMES_SETS_FILES = $(TZNAMES_SETS:%=$(srcdir)/%)
+
+install: installdirs
+ $(INSTALL_DATA) $(TZNAMES_TEMPLATES_FILES) '$(DESTDIR)$(datadir)/timezonesets'
+ $(INSTALL_DATA) $(TZNAMES_SETS_FILES) '$(DESTDIR)$(datadir)/timezonesets'
+
+installdirs:
+ $(MKDIR_P) '$(DESTDIR)$(datadir)/timezonesets'
+
+uninstall:
+ rm -rf '$(DESTDIR)$(datadir)/timezonesets'
diff --git a/src/timezone/tznames/Pacific.txt b/src/timezone/tznames/Pacific.txt
new file mode 100644
index 0000000..c30008c
--- /dev/null
+++ b/src/timezone/tznames/Pacific.txt
@@ -0,0 +1,84 @@
+#
+# NOTE:
+# This file is NOT loaded by the PostgreSQL database. It just serves as
+# a template for timezones you could need. See the `Date/Time Support'
+# appendix in the PostgreSQL documentation for more information.
+#
+# src/timezone/tznames/Pacific.txt
+#
+
+# CONFLICT! BST is not unique
+# Other timezones:
+# - BST: British Summer Time
+BST 39600 # Bougainville Standard Time (Papua New Guinea) (obsolete)
+CHADT 49500 D # Chatham Daylight Time (New Zealand) (obsolete)
+CHAST 45900 # Chatham Standard Time (New Zealand) (obsolete)
+ChST 36000 # Chamorro Standard Time (lower case "h" is as in IANA database)
+ # (Pacific/Guam)
+ # (Pacific/Saipan)
+CHUT 36000 # Chuuk Time (obsolete)
+CKT Pacific/Rarotonga # Cook Islands Time (obsolete)
+EASST Pacific/Easter # Easter Island Summer Time (obsolete)
+# CONFLICT! EAST is not unique
+# Other timezones:
+# - EAST: East Australian Standard Time (Australia)
+EAST Pacific/Easter # Easter Island Time (Chile) (obsolete)
+FJST 46800 D # Fiji Summer Time (caution: this used to mean -46800) (obsolete)
+FJT 43200 # Fiji Time (caution: this used to mean -43200) (obsolete)
+GALT -21600 # Galapagos Time (obsolete)
+GAMT -32400 # Gambier Time (obsolete)
+GILT 43200 # Gilbert Islands Time (obsolete)
+HST -36000 # Hawaiian Standard Time
+ # (Pacific/Honolulu)
+ # (Pacific/Johnston)
+KOST Pacific/Kosrae # Kosrae Time (obsolete)
+LINT Pacific/Kiritimati # Line Islands Time (Kiribati) (obsolete)
+MART -34200 # Marquesas Time (obsolete)
+MHT 43200 # Kwajalein Time (obsolete)
+MPT 36000 # North Mariana Islands Time (not in IANA database)
+NCT 39600 # New Caledonia Time (obsolete)
+# CONFLICT! NFT is not unique
+# Other timezones:
+# - NFT: Newfoundland Time (America)
+NFT Pacific/Norfolk # Norfolk Time (obsolete)
+NRT Pacific/Nauru # Nauru Time (obsolete)
+NUT Pacific/Niue # Niue Time (obsolete)
+NZDT 46800 D # New Zealand Daylight Time
+ # (Antarctica/McMurdo)
+ # (Pacific/Auckland)
+NZST 43200 # New Zealand Standard Time
+ # (Antarctica/McMurdo)
+ # (Pacific/Auckland)
+PGT 36000 # Papua New Guinea Time (obsolete)
+PHOT Pacific/Enderbury # Phoenix Islands Time (Kiribati) (obsolete)
+PONT 39600 # Ponape Time (Micronesia) (obsolete)
+# CONFLICT! PST is not unique
+# Other timezones:
+# - PST: Philippine Standard Time
+PST -28800 # Pacific Standard Time
+ # (America/Dawson)
+ # (America/Los_Angeles)
+ # (America/Tijuana)
+ # (America/Vancouver)
+ # (America/Whitehorse)
+ # (Pacific/Pitcairn)
+PWT 32400 # Palau Time (obsolete)
+SBT 39600 # Solomon Islands Time (obsolete)
+SST -39600 # South Sumatran Time
+ # (Pacific/Midway)
+ # (Pacific/Pago_Pago)
+TAHT -36000 # Tahiti Time (obsolete)
+TKT Pacific/Fakaofo # Tokelau Time (obsolete)
+TOT 46800 # Tonga Time (obsolete)
+TRUT 36000 # Truk Time (obsolete)
+TVT 43200 # Tuvalu Time (obsolete)
+VUT 39600 # Vanuata Time (obsolete)
+WAKT 43200 # Wake Time (obsolete)
+WFT 43200 # Wallis and Futuna Time (obsolete)
+WSDT 50400 D # West Samoa Daylight Time (obsolete)
+WSST 46800 # West Samoa Standard Time (obsolete)
+# CONFLICT! WST is not unique
+# Other timezones:
+# - WST: Western Standard Time (Australia)
+WST 46800 # West Samoa Time (caution: this used to mean -39600) (not in IANA database)
+YAPT 36000 # Yap Time (Micronesia) (not in IANA database)
diff --git a/src/timezone/tznames/README b/src/timezone/tznames/README
new file mode 100644
index 0000000..6d355e4
--- /dev/null
+++ b/src/timezone/tznames/README
@@ -0,0 +1,40 @@
+src/timezone/tznames/README
+
+tznames
+=======
+
+This directory contains files with timezone sets for PostgreSQL. The problem
+is that time zone abbreviations are not unique throughout the world and you
+might find out that a time zone abbreviation in the `Default' set collides
+with the one you wanted to use. This can be fixed by selecting a timezone
+set that defines the abbreviation the way you want it. There might already
+be a file here that serves your needs. If not, you can create your own.
+
+In order to use one of these files, you need to set
+
+ timezone_abbreviations = 'xyz'
+
+in any of the usual ways for setting a parameter, where xyz is the filename
+that contains the desired time zone abbreviations.
+
+If you do not find an appropriate set of abbreviations for your geographic
+location supplied here, please report this to <pgsql-hackers@lists.postgresql.org>.
+Your set of time zone abbreviations can then be included in future releases.
+For the time being you can always add your own set.
+
+Typically a custom abbreviation set is made by including the `Default' set
+and then adding or overriding abbreviations as necessary. For examples,
+see the `Australia' and `India' files.
+
+The files named Africa.txt, etc, are not intended to be used directly as
+time zone abbreviation files. They contain reference definitions of time zone
+abbreviations that can be copied into a custom abbreviation file as needed.
+These files contain most of the time zone abbreviations that were shown
+in the IANA timezone database circa 2010.
+
+However, it turns out that many of these abbreviations had simply been
+invented by the IANA timezone group, and do not have currency in real-world
+use. The IANA group have changed their policy about that, and now prefer to
+use numeric UTC offsets whenever there's not an abbreviation with known
+real-world popularity. A lot of these abbreviations therefore no longer
+appear in the IANA data, and so are marked "obsolete" in these data files.
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
new file mode 100644
index 0000000..0ea6ead
--- /dev/null
+++ b/src/timezone/zic.c
@@ -0,0 +1,4013 @@
+/* Compile .zi time zone data into TZif binary files. */
+
+/*
+ * This file is in the public domain, so clarified as of
+ * 2006-07-17 by Arthur David Olson.
+ *
+ * IDENTIFICATION
+ * src/timezone/zic.c
+ */
+
+#include "postgres_fe.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "pg_getopt.h"
+
+#include "private.h"
+#include "tzfile.h"
+
+#define ZIC_VERSION_PRE_2013 '2'
+#define ZIC_VERSION '3'
+
+typedef int64 zic_t;
+#define ZIC_MIN PG_INT64_MIN
+#define ZIC_MAX PG_INT64_MAX
+
+#ifndef ZIC_MAX_ABBR_LEN_WO_WARN
+#define ZIC_MAX_ABBR_LEN_WO_WARN 6
+#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
+
+#ifndef WIN32
+#ifdef S_IRUSR
+#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+#else
+#define MKDIR_UMASK 0755
+#endif
+#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
+
+/* The maximum ptrdiff_t value, for pre-C99 platforms. */
+#ifndef PTRDIFF_MAX
+static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t));
+#endif
+
+/*
+ * The type for line numbers. In Postgres, use %d to format them; upstream
+ * uses PRIdMAX but we prefer not to rely on that, not least because it
+ * results in platform-dependent strings to be translated.
+ */
+typedef int lineno_t;
+
+struct rule
+{
+ const char *r_filename;
+ lineno_t r_linenum;
+ const char *r_name;
+
+ zic_t r_loyear; /* for example, 1986 */
+ zic_t r_hiyear; /* for example, 1986 */
+ bool r_lowasnum;
+ bool r_hiwasnum;
+
+ int r_month; /* 0..11 */
+
+ int r_dycode; /* see below */
+ int r_dayofmonth;
+ int r_wday;
+
+ zic_t r_tod; /* time from midnight */
+ bool r_todisstd; /* is r_tod standard time? */
+ bool r_todisut; /* is r_tod UT? */
+ bool r_isdst; /* is this daylight saving time? */
+ zic_t r_save; /* offset from standard time */
+ const char *r_abbrvar; /* variable part of abbreviation */
+
+ bool r_todo; /* a rule to do (used in outzone) */
+ zic_t r_temp; /* used in outzone */
+};
+
+/*
+ * r_dycode r_dayofmonth r_wday
+ */
+
+#define DC_DOM 0 /* 1..31 */ /* unused */
+#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
+#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
+
+struct zone
+{
+ const char *z_filename;
+ lineno_t z_linenum;
+
+ const char *z_name;
+ zic_t z_stdoff;
+ char *z_rule;
+ const char *z_format;
+ char z_format_specifier;
+
+ bool z_isdst;
+ zic_t z_save;
+
+ struct rule *z_rules;
+ ptrdiff_t z_nrules;
+
+ struct rule z_untilrule;
+ zic_t z_untiltime;
+};
+
+extern int link(const char *target, const char *linkname);
+#ifndef AT_SYMLINK_FOLLOW
+#define linkat(targetdir, target, linknamedir, linkname, flag) \
+ (itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname))
+#endif
+
+static void memory_exhausted(const char *msg) pg_attribute_noreturn();
+static void verror(const char *string, va_list args) pg_attribute_printf(1, 0);
+static void error(const char *string,...) pg_attribute_printf(1, 2);
+static void warning(const char *string,...) pg_attribute_printf(1, 2);
+static void usage(FILE *stream, int status) pg_attribute_noreturn();
+static void addtt(zic_t starttime, int type);
+static int addtype(zic_t, char const *, bool, bool, bool);
+static void leapadd(zic_t, int, int);
+static void adjleap(void);
+static void associate(void);
+static void dolink(const char *, const char *, bool);
+static char **getfields(char *buf);
+static zic_t gethms(const char *string, const char *errstring);
+static zic_t getsave(char *, bool *);
+static void inexpires(char **, int);
+static void infile(const char *filename);
+static void inleap(char **fields, int nfields);
+static void inlink(char **fields, int nfields);
+static void inrule(char **fields, int nfields);
+static 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);
+static void mkdirs(char const *, bool);
+static void newabbr(const char *abbr);
+static zic_t oadd(zic_t t1, zic_t t2);
+static void outzone(const struct zone *zp, ptrdiff_t ntzones);
+static zic_t rpytime(const struct rule *rp, zic_t wantedy);
+static void rulesub(struct rule *rp,
+ const char *loyearp, const char *hiyearp,
+ const char *typep, const char *monthp,
+ const char *dayp, const char *timep);
+static zic_t tadd(zic_t t1, zic_t t2);
+
+/* Bound on length of what %z can expand to. */
+enum
+{
+PERCENT_Z_LEN_BOUND = sizeof "+995959" - 1};
+
+/* If true, work around a bug in Qt 5.6.1 and earlier, which mishandles
+ TZif files whose POSIX-TZ-style strings contain '<'; see
+ QTBUG-53071 <https://bugreports.qt.io/browse/QTBUG-53071>. This
+ workaround will no longer be needed when Qt 5.6.1 and earlier are
+ obsolete, say in the year 2021. */
+#ifndef WORK_AROUND_QTBUG_53071
+enum
+{
+WORK_AROUND_QTBUG_53071 = true};
+#endif
+
+static int charcnt;
+static bool errors;
+static bool warnings;
+static const char *filename;
+static int leapcnt;
+static bool leapseen;
+static zic_t leapminyear;
+static zic_t leapmaxyear;
+static lineno_t linenum;
+static int max_abbrvar_len = PERCENT_Z_LEN_BOUND;
+static int max_format_len;
+static zic_t max_year;
+static zic_t min_year;
+static bool noise;
+static bool print_abbrevs;
+static zic_t print_cutoff;
+static const char *rfilename;
+static lineno_t rlinenum;
+static const char *progname;
+static ptrdiff_t timecnt;
+static ptrdiff_t timecnt_alloc;
+static int typecnt;
+
+/*
+ * Line codes.
+ */
+
+#define LC_RULE 0
+#define LC_ZONE 1
+#define LC_LINK 2
+#define LC_LEAP 3
+#define LC_EXPIRES 4
+
+/*
+ * Which fields are which on a Zone line.
+ */
+
+#define ZF_NAME 1
+#define ZF_STDOFF 2
+#define ZF_RULE 3
+#define ZF_FORMAT 4
+#define ZF_TILYEAR 5
+#define ZF_TILMONTH 6
+#define ZF_TILDAY 7
+#define ZF_TILTIME 8
+#define ZONE_MINFIELDS 5
+#define ZONE_MAXFIELDS 9
+
+/*
+ * Which fields are which on a Zone continuation line.
+ */
+
+#define ZFC_STDOFF 0
+#define ZFC_RULE 1
+#define ZFC_FORMAT 2
+#define ZFC_TILYEAR 3
+#define ZFC_TILMONTH 4
+#define ZFC_TILDAY 5
+#define ZFC_TILTIME 6
+#define ZONEC_MINFIELDS 3
+#define ZONEC_MAXFIELDS 7
+
+/*
+ * Which files are which on a Rule line.
+ */
+
+#define RF_NAME 1
+#define RF_LOYEAR 2
+#define RF_HIYEAR 3
+#define RF_COMMAND 4
+#define RF_MONTH 5
+#define RF_DAY 6
+#define RF_TOD 7
+#define RF_SAVE 8
+#define RF_ABBRVAR 9
+#define RULE_FIELDS 10
+
+/*
+ * Which fields are which on a Link line.
+ */
+
+#define LF_TARGET 1
+#define LF_LINKNAME 2
+#define LINK_FIELDS 3
+
+/*
+ * Which fields are which on a Leap line.
+ */
+
+#define LP_YEAR 1
+#define LP_MONTH 2
+#define LP_DAY 3
+#define LP_TIME 4
+#define LP_CORR 5
+#define LP_ROLL 6
+#define LEAP_FIELDS 7
+
+/* Expires lines are like Leap lines, except without CORR and ROLL fields. */
+#define EXPIRES_FIELDS 5
+
+/*
+ * Year synonyms.
+ */
+
+#define YR_MINIMUM 0
+#define YR_MAXIMUM 1
+#define YR_ONLY 2
+
+static struct rule *rules;
+static ptrdiff_t nrules; /* number of rules */
+static ptrdiff_t nrules_alloc;
+
+static struct zone *zones;
+static ptrdiff_t nzones; /* number of zones */
+static ptrdiff_t nzones_alloc;
+
+struct link
+{
+ const char *l_filename;
+ lineno_t l_linenum;
+ const char *l_target;
+ const char *l_linkname;
+};
+
+static struct link *links;
+static ptrdiff_t nlinks;
+static ptrdiff_t nlinks_alloc;
+
+struct lookup
+{
+ const char *l_word;
+ const int l_value;
+};
+
+static struct lookup const *byword(const char *string,
+ const struct lookup *lp);
+
+static struct lookup const zi_line_codes[] = {
+ {"Rule", LC_RULE},
+ {"Zone", LC_ZONE},
+ {"Link", LC_LINK},
+ {NULL, 0}
+};
+static struct lookup const leap_line_codes[] = {
+ {"Leap", LC_LEAP},
+ {"Expires", LC_EXPIRES},
+ {NULL, 0}
+};
+
+static struct lookup const mon_names[] = {
+ {"January", TM_JANUARY},
+ {"February", TM_FEBRUARY},
+ {"March", TM_MARCH},
+ {"April", TM_APRIL},
+ {"May", TM_MAY},
+ {"June", TM_JUNE},
+ {"July", TM_JULY},
+ {"August", TM_AUGUST},
+ {"September", TM_SEPTEMBER},
+ {"October", TM_OCTOBER},
+ {"November", TM_NOVEMBER},
+ {"December", TM_DECEMBER},
+ {NULL, 0}
+};
+
+static struct lookup const wday_names[] = {
+ {"Sunday", TM_SUNDAY},
+ {"Monday", TM_MONDAY},
+ {"Tuesday", TM_TUESDAY},
+ {"Wednesday", TM_WEDNESDAY},
+ {"Thursday", TM_THURSDAY},
+ {"Friday", TM_FRIDAY},
+ {"Saturday", TM_SATURDAY},
+ {NULL, 0}
+};
+
+static struct lookup const lasts[] = {
+ {"last-Sunday", TM_SUNDAY},
+ {"last-Monday", TM_MONDAY},
+ {"last-Tuesday", TM_TUESDAY},
+ {"last-Wednesday", TM_WEDNESDAY},
+ {"last-Thursday", TM_THURSDAY},
+ {"last-Friday", TM_FRIDAY},
+ {"last-Saturday", TM_SATURDAY},
+ {NULL, 0}
+};
+
+static struct lookup const begin_years[] = {
+ {"minimum", YR_MINIMUM},
+ {"maximum", YR_MAXIMUM},
+ {NULL, 0}
+};
+
+static struct lookup const end_years[] = {
+ {"minimum", YR_MINIMUM},
+ {"maximum", YR_MAXIMUM},
+ {"only", YR_ONLY},
+ {NULL, 0}
+};
+
+static struct lookup const leap_types[] = {
+ {"Rolling", true},
+ {"Stationary", false},
+ {NULL, 0}
+};
+
+static const int len_months[2][MONSPERYEAR] = {
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static const int len_years[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+static struct attype
+{
+ zic_t at;
+ bool dontmerge;
+ unsigned char type;
+} *attypes;
+static zic_t utoffs[TZ_MAX_TYPES];
+static char isdsts[TZ_MAX_TYPES];
+static unsigned char desigidx[TZ_MAX_TYPES];
+static bool ttisstds[TZ_MAX_TYPES];
+static bool ttisuts[TZ_MAX_TYPES];
+static char chars[TZ_MAX_CHARS];
+static zic_t trans[TZ_MAX_LEAPS];
+static zic_t corr[TZ_MAX_LEAPS];
+static char roll[TZ_MAX_LEAPS];
+
+/*
+ * Memory allocation.
+ */
+
+static void
+memory_exhausted(const char *msg)
+{
+ fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
+ exit(EXIT_FAILURE);
+}
+
+static size_t
+size_product(size_t nitems, size_t itemsize)
+{
+ if (SIZE_MAX / itemsize < nitems)
+ memory_exhausted(_("size overflow"));
+ return nitems * itemsize;
+}
+
+static void *
+memcheck(void *ptr)
+{
+ if (ptr == NULL)
+ memory_exhausted(strerror(errno));
+ return ptr;
+}
+
+static void *
+emalloc(size_t size)
+{
+ return memcheck(malloc(size));
+}
+
+static void *
+erealloc(void *ptr, size_t size)
+{
+ return memcheck(realloc(ptr, size));
+}
+
+static char *
+ecpyalloc(char const *str)
+{
+ return memcheck(strdup(str));
+}
+
+static void *
+growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc)
+{
+ if (nitems < *nitems_alloc)
+ return ptr;
+ else
+ {
+ ptrdiff_t nitems_max = PTRDIFF_MAX - WORK_AROUND_QTBUG_53071;
+ ptrdiff_t amax = nitems_max < SIZE_MAX ? nitems_max : SIZE_MAX;
+
+ if ((amax - 1) / 3 * 2 < *nitems_alloc)
+ memory_exhausted(_("integer overflow"));
+ *nitems_alloc += (*nitems_alloc >> 1) + 1;
+ return erealloc(ptr, size_product(*nitems_alloc, itemsize));
+ }
+}
+
+/*
+ * Error handling.
+ */
+
+static void
+eats(char const *name, lineno_t num, char const *rname, lineno_t rnum)
+{
+ filename = name;
+ linenum = num;
+ rfilename = rname;
+ rlinenum = rnum;
+}
+
+static void
+eat(char const *name, lineno_t num)
+{
+ eats(name, num, NULL, -1);
+}
+
+static void
+verror(const char *string, va_list args)
+{
+ /*
+ * Match the format of "cc" to allow sh users to zic ... 2>&1 | error -t
+ * "*" -v on BSD systems.
+ */
+ if (filename)
+ fprintf(stderr, _("\"%s\", line %d: "), filename, linenum);
+ vfprintf(stderr, string, args);
+ if (rfilename != NULL)
+ fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+ rfilename, rlinenum);
+ fprintf(stderr, "\n");
+}
+
+static void
+error(const char *string,...)
+{
+ va_list args;
+
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+ errors = true;
+}
+
+static void
+warning(const char *string,...)
+{
+ va_list args;
+
+ fprintf(stderr, _("warning: "));
+ va_start(args, string);
+ verror(string, args);
+ va_end(args);
+ warnings = true;
+}
+
+static void
+close_file(FILE *stream, char const *dir, char const *name)
+{
+ char const *e = (ferror(stream) ? _("I/O error")
+ : fclose(stream) != 0 ? strerror(errno) : NULL);
+
+ if (e)
+ {
+ fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
+ dir ? dir : "", dir ? "/" : "",
+ name ? name : "", name ? ": " : "",
+ e);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void
+usage(FILE *stream, int status)
+{
+ fprintf(stream,
+ _("%s: usage is %s [ --version ] [ --help ] [ -v ] [ -P ] \\\n"
+ "\t[ -b {slim|fat} ] [ -d directory ] [ -l localtime ]"
+ " [ -L leapseconds ] \\\n"
+ "\t[ -p posixrules ] [ -r '[@lo][/@hi]' ] [ -t localtime-link ] \\\n"
+ "\t[ filename ... ]\n\n"
+ "Report bugs to %s.\n"),
+ progname, progname, PACKAGE_BUGREPORT);
+ if (status == EXIT_SUCCESS)
+ close_file(stream, NULL, NULL);
+ exit(status);
+}
+
+/* Change the working directory to DIR, possibly creating DIR and its
+ ancestors. After this is done, all files are accessed with names
+ relative to DIR. */
+static void
+change_directory(char const *dir)
+{
+ if (chdir(dir) != 0)
+ {
+ int chdir_errno = errno;
+
+ if (chdir_errno == ENOENT)
+ {
+ mkdirs(dir, false);
+ chdir_errno = chdir(dir) == 0 ? 0 : errno;
+ }
+ if (chdir_errno != 0)
+ {
+ fprintf(stderr, _("%s: Can't chdir to %s: %s\n"),
+ progname, dir, strerror(chdir_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+#define TIME_T_BITS_IN_FILE 64
+
+/* The minimum and maximum values representable in a TZif file. */
+static zic_t const min_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The minimum, and one less than the maximum, values specified by
+ the -r option. These default to MIN_TIME and MAX_TIME. */
+static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE);
+static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE);
+
+/* The time specified by 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
+timerange_option(char *timerange)
+{
+ int64 lo = min_time,
+ hi = max_time;
+ char *lo_end = timerange,
+ *hi_end;
+
+ if (*timerange == '@')
+ {
+ errno = 0;
+ lo = strtoimax(timerange + 1, &lo_end, 10);
+ if (lo_end == timerange + 1 || (lo == PG_INT64_MAX && errno == ERANGE))
+ return false;
+ }
+ hi_end = lo_end;
+ if (lo_end[0] == '/' && lo_end[1] == '@')
+ {
+ errno = 0;
+ hi = strtoimax(lo_end + 2, &hi_end, 10);
+ if (hi_end == lo_end + 2 || hi == PG_INT64_MIN)
+ return false;
+ hi -= !(hi == PG_INT64_MAX && errno == ERANGE);
+ }
+ if (*hi_end || hi < lo || max_time < lo || hi < min_time)
+ return false;
+ lo_time = lo < min_time ? min_time : lo;
+ hi_time = max_time < hi ? max_time : hi;
+ return true;
+}
+
+static const char *psxrules;
+static const char *lcltime;
+static const char *directory;
+static const char *leapsec;
+static const char *tzdefault;
+
+/* -1 if the TZif output file should be slim, 0 if default, 1 if the
+ output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT
+ determines the default. */
+static int bloat;
+
+static bool
+want_bloat(void)
+{
+ return 0 <= bloat;
+}
+
+#ifndef ZIC_BLOAT_DEFAULT
+#define ZIC_BLOAT_DEFAULT "slim"
+#endif
+
+int
+main(int argc, char **argv)
+{
+ int c,
+ k;
+ ptrdiff_t i,
+ j;
+ bool timerange_given = false;
+
+#ifndef WIN32
+ umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
+#endif
+ progname = argv[0];
+ if (TYPE_BIT(zic_t) < 64)
+ {
+ fprintf(stderr, "%s: %s\n", progname,
+ _("wild compilation-time specification of zic_t"));
+ return EXIT_FAILURE;
+ }
+ for (k = 1; k < argc; k++)
+ if (strcmp(argv[k], "--version") == 0)
+ {
+ printf("zic %s\n", PG_VERSION);
+ close_file(stdout, NULL, NULL);
+ return EXIT_SUCCESS;
+ }
+ else if (strcmp(argv[k], "--help") == 0)
+ {
+ usage(stdout, EXIT_SUCCESS);
+ }
+ while ((c = getopt(argc, argv, "b:d:l:L:p:Pr:st:vy:")) != EOF && c != -1)
+ switch (c)
+ {
+ default:
+ usage(stderr, EXIT_FAILURE);
+ case 'b':
+ if (strcmp(optarg, "slim") == 0)
+ {
+ if (0 < bloat)
+ error(_("incompatible -b options"));
+ bloat = -1;
+ }
+ else if (strcmp(optarg, "fat") == 0)
+ {
+ if (bloat < 0)
+ error(_("incompatible -b options"));
+ bloat = 1;
+ }
+ else
+ error(_("invalid option: -b '%s'"), optarg);
+ break;
+ case 'd':
+ if (directory == NULL)
+ directory = strdup(optarg);
+ else
+ {
+ fprintf(stderr,
+ _("%s: More than one -d option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'l':
+ if (lcltime == NULL)
+ lcltime = strdup(optarg);
+ else
+ {
+ fprintf(stderr,
+ _("%s: More than one -l option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'p':
+ if (psxrules == NULL)
+ psxrules = strdup(optarg);
+ else
+ {
+ fprintf(stderr,
+ _("%s: More than one -p option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 't':
+ if (tzdefault != NULL)
+ {
+ fprintf(stderr,
+ _("%s: More than one -t option"
+ " specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ tzdefault = optarg;
+ break;
+ case 'y':
+ warning(_("-y ignored"));
+ break;
+ case 'L':
+ if (leapsec == NULL)
+ leapsec = strdup(optarg);
+ else
+ {
+ fprintf(stderr,
+ _("%s: More than one -L option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'v':
+ noise = true;
+ break;
+ case 'P':
+ print_abbrevs = true;
+ print_cutoff = time(NULL);
+ break;
+ case 'r':
+ if (timerange_given)
+ {
+ fprintf(stderr,
+ _("%s: More than one -r option specified\n"),
+ progname);
+ return EXIT_FAILURE;
+ }
+ if (!timerange_option(optarg))
+ {
+ fprintf(stderr,
+ _("%s: invalid time range: %s\n"),
+ progname, optarg);
+ return EXIT_FAILURE;
+ }
+ timerange_given = true;
+ break;
+ case 's':
+ warning(_("-s ignored"));
+ break;
+ }
+ if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
+ usage(stderr, EXIT_FAILURE); /* usage message by request */
+ if (bloat == 0)
+ {
+ static char const bloat_default[] = ZIC_BLOAT_DEFAULT;
+
+ if (strcmp(bloat_default, "slim") == 0)
+ bloat = -1;
+ else if (strcmp(bloat_default, "fat") == 0)
+ bloat = 1;
+ else
+ abort(); /* Configuration error. */
+ }
+ if (directory == NULL)
+ directory = "data";
+ if (tzdefault == NULL)
+ tzdefault = TZDEFAULT;
+
+ if (optind < argc && leapsec != NULL)
+ {
+ infile(leapsec);
+ adjleap();
+ }
+
+ for (k = optind; k < argc; k++)
+ infile(argv[k]);
+ if (errors)
+ return EXIT_FAILURE;
+ associate();
+ change_directory(directory);
+ for (i = 0; i < nzones; i = j)
+ {
+ /*
+ * Find the next non-continuation zone entry.
+ */
+ for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
+ continue;
+ outzone(&zones[i], j - i);
+ }
+
+ /*
+ * Make links.
+ */
+ for (i = 0; i < nlinks; ++i)
+ {
+ eat(links[i].l_filename, links[i].l_linenum);
+ dolink(links[i].l_target, links[i].l_linkname, false);
+ if (noise)
+ for (j = 0; j < nlinks; ++j)
+ if (strcmp(links[i].l_linkname,
+ links[j].l_target) == 0)
+ warning(_("link to link"));
+ }
+ if (lcltime != NULL)
+ {
+ eat(_("command line"), 1);
+ dolink(lcltime, tzdefault, true);
+ }
+ if (psxrules != NULL)
+ {
+ eat(_("command line"), 1);
+ dolink(psxrules, TZDEFRULES, true);
+ }
+ if (warnings && (ferror(stderr) || fclose(stderr) != 0))
+ return EXIT_FAILURE;
+ return errors ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static bool
+componentcheck(char const *name, char const *component,
+ char const *component_end)
+{
+ enum
+ {
+ component_len_max = 14};
+ ptrdiff_t component_len = component_end - component;
+
+ if (component_len == 0)
+ {
+ if (!*name)
+ error(_("empty file name"));
+ else
+ error(_(component == name
+ ? "file name '%s' begins with '/'"
+ : *component_end
+ ? "file name '%s' contains '//'"
+ : "file name '%s' ends with '/'"),
+ name);
+ return false;
+ }
+ if (0 < component_len && component_len <= 2
+ && component[0] == '.' && component_end[-1] == '.')
+ {
+ int len = component_len;
+
+ error(_("file name '%s' contains '%.*s' component"),
+ name, len, component);
+ return false;
+ }
+ if (noise)
+ {
+ if (0 < component_len && component[0] == '-')
+ warning(_("file name '%s' component contains leading '-'"),
+ name);
+ if (component_len_max < component_len)
+ warning(_("file name '%s' contains overlength component"
+ " '%.*s...'"),
+ name, component_len_max, component);
+ }
+ return true;
+}
+
+static bool
+namecheck(const char *name)
+{
+ char const *cp;
+
+ /* Benign characters in a portable file name. */
+ static char const benign[] =
+ "-/_"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /*
+ * Non-control chars in the POSIX portable character set, excluding the
+ * benign characters.
+ */
+ static char const printable_and_not_benign[] =
+ " !\"#$%&'()*+,.0123456789:;<=>?@[\\]^`{|}~";
+
+ char const *component = name;
+
+ for (cp = name; *cp; cp++)
+ {
+ unsigned char c = *cp;
+
+ if (noise && !strchr(benign, c))
+ {
+ warning((strchr(printable_and_not_benign, c)
+ ? _("file name '%s' contains byte '%c'")
+ : _("file name '%s' contains byte '\\%o'")),
+ name, c);
+ }
+ if (c == '/')
+ {
+ if (!componentcheck(name, component, cp))
+ return false;
+ component = cp + 1;
+ }
+ }
+ return componentcheck(name, component, cp);
+}
+
+/*
+ * Create symlink contents suitable for symlinking FROM to TO, as a
+ * freshly allocated string. FROM should be a relative file name, and
+ * is relative to the global variable DIRECTORY. TO can be either
+ * relative or absolute.
+ */
+#ifdef HAVE_SYMLINK
+static char *
+relname(char const *target, char const *linkname)
+{
+ size_t i,
+ taillen,
+ dotdotetcsize;
+ size_t dir_len = 0,
+ dotdots = 0,
+ linksize = SIZE_MAX;
+ char const *f = target;
+ char *result = NULL;
+
+ if (*linkname == '/')
+ {
+ /* Make F absolute too. */
+ size_t len = strlen(directory);
+ bool needslash = len && directory[len - 1] != '/';
+
+ linksize = len + needslash + strlen(target) + 1;
+ f = result = emalloc(linksize);
+ strcpy(result, directory);
+ result[len] = '/';
+ strcpy(result + len + needslash, target);
+ }
+ for (i = 0; f[i] && f[i] == linkname[i]; i++)
+ if (f[i] == '/')
+ dir_len = i + 1;
+ for (; linkname[i]; i++)
+ dotdots += linkname[i] == '/' && linkname[i - 1] != '/';
+ taillen = strlen(f + dir_len);
+ dotdotetcsize = 3 * dotdots + taillen + 1;
+ if (dotdotetcsize <= linksize)
+ {
+ if (!result)
+ result = emalloc(dotdotetcsize);
+ for (i = 0; i < dotdots; i++)
+ memcpy(result + 3 * i, "../", 3);
+ memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
+ }
+ return result;
+}
+#endif /* HAVE_SYMLINK */
+
+/* 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;
+
+ /*
+ * 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);
+ }
+ 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);
+ }
+ if (link_errno != 0)
+ {
+#ifdef HAVE_SYMLINK
+ 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;
+ }
+ free(linkalloc);
+ if (symlink_errno == 0)
+ {
+ if (link_errno != ENOTSUP)
+ warning(_("symbolic link used because hard link failed: %s"),
+ strerror(link_errno));
+ }
+ else
+#endif /* HAVE_SYMLINK */
+ {
+ FILE *fp,
+ *tp;
+ int c;
+
+ fp = fopen(target, "rb");
+ if (!fp)
+ {
+ char const *e = strerror(errno);
+
+ fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
+ progname, directory, target, e);
+ exit(EXIT_FAILURE);
+ }
+ tp = 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);
+ }
+ while ((c = getc(fp)) != EOF)
+ putc(c, tp);
+ close_file(fp, directory, target);
+ close_file(tp, directory, linkname);
+ if (link_errno != ENOTSUP)
+ warning(_("copy used because hard link failed: %s"),
+ strerror(link_errno));
+#ifdef HAVE_SYMLINK
+ else if (symlink_errno != ENOTSUP)
+ warning(_("copy used because symbolic link failed: %s"),
+ strerror(symlink_errno));
+#endif
+ }
+ }
+}
+
+/* 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;
+}
+
+/* Return true if NAME is a symbolic link. */
+static bool
+itssymlink(char const *name)
+{
+#ifdef HAVE_SYMLINK
+ char c;
+
+ return 0 <= readlink(name, &c, 1);
+#else
+ return false;
+#endif
+}
+
+/*
+ * Associate sets of rules with zones.
+ */
+
+/*
+ * Sort by rule name.
+ */
+
+static int
+rcomp(const void *cp1, const void *cp2)
+{
+ return strcmp(((const struct rule *) cp1)->r_name,
+ ((const struct rule *) cp2)->r_name);
+}
+
+static void
+associate(void)
+{
+ struct zone *zp;
+ struct rule *rp;
+ ptrdiff_t i,
+ j,
+ base,
+ out;
+
+ if (nrules != 0)
+ {
+ qsort(rules, nrules, sizeof *rules, rcomp);
+ for (i = 0; i < nrules - 1; ++i)
+ {
+ if (strcmp(rules[i].r_name,
+ rules[i + 1].r_name) != 0)
+ continue;
+ if (strcmp(rules[i].r_filename,
+ rules[i + 1].r_filename) == 0)
+ continue;
+ eat(rules[i].r_filename, rules[i].r_linenum);
+ warning(_("same rule name in multiple files"));
+ eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
+ warning(_("same rule name in multiple files"));
+ for (j = i + 2; j < nrules; ++j)
+ {
+ if (strcmp(rules[i].r_name,
+ rules[j].r_name) != 0)
+ break;
+ if (strcmp(rules[i].r_filename,
+ rules[j].r_filename) == 0)
+ continue;
+ if (strcmp(rules[i + 1].r_filename,
+ rules[j].r_filename) == 0)
+ continue;
+ break;
+ }
+ i = j - 1;
+ }
+ }
+ for (i = 0; i < nzones; ++i)
+ {
+ zp = &zones[i];
+ zp->z_rules = NULL;
+ zp->z_nrules = 0;
+ }
+ for (base = 0; base < nrules; base = out)
+ {
+ rp = &rules[base];
+ for (out = base + 1; out < nrules; ++out)
+ if (strcmp(rp->r_name, rules[out].r_name) != 0)
+ break;
+ for (i = 0; i < nzones; ++i)
+ {
+ zp = &zones[i];
+ if (strcmp(zp->z_rule, rp->r_name) != 0)
+ continue;
+ zp->z_rules = rp;
+ zp->z_nrules = out - base;
+ }
+ }
+ for (i = 0; i < nzones; ++i)
+ {
+ zp = &zones[i];
+ if (zp->z_nrules == 0)
+ {
+ /*
+ * Maybe we have a local standard time offset.
+ */
+ eat(zp->z_filename, zp->z_linenum);
+ zp->z_save = getsave(zp->z_rule, &zp->z_isdst);
+
+ /*
+ * Note, though, that if there's no rule, a '%s' in the format is
+ * a bad thing.
+ */
+ if (zp->z_format_specifier == 's')
+ error("%s", _("%s in ruleless zone"));
+ }
+ }
+ if (errors)
+ exit(EXIT_FAILURE);
+}
+
+static void
+infile(const char *name)
+{
+ FILE *fp;
+ char **fields;
+ char *cp;
+ const struct lookup *lp;
+ int nfields;
+ bool wantcont;
+ lineno_t num;
+ char buf[BUFSIZ];
+
+ if (strcmp(name, "-") == 0)
+ {
+ name = _("standard input");
+ fp = stdin;
+ }
+ else if ((fp = fopen(name, "r")) == NULL)
+ {
+ const char *e = strerror(errno);
+
+ fprintf(stderr, _("%s: Cannot open %s: %s\n"),
+ progname, name, e);
+ exit(EXIT_FAILURE);
+ }
+ wantcont = false;
+ for (num = 1;; ++num)
+ {
+ eat(name, num);
+ if (fgets(buf, sizeof buf, fp) != buf)
+ break;
+ cp = strchr(buf, '\n');
+ if (cp == NULL)
+ {
+ error(_("line too long"));
+ exit(EXIT_FAILURE);
+ }
+ *cp = '\0';
+ fields = getfields(buf);
+ nfields = 0;
+ while (fields[nfields] != NULL)
+ {
+ static char nada;
+
+ if (strcmp(fields[nfields], "-") == 0)
+ fields[nfields] = &nada;
+ ++nfields;
+ }
+ if (nfields == 0)
+ {
+ if (name == leapsec && *buf == '#')
+ {
+ /*
+ * PG: INT64_FORMAT isn't portable for sscanf, so be content
+ * with scanning a "long". Once we are requiring C99 in all
+ * live branches, it'd be sensible to adopt upstream's
+ * practice of using the <inttypes.h> macros. But for now, we
+ * don't actually use this code, and it won't overflow before
+ * 2038 anyway.
+ */
+ long cl_tmp;
+
+ sscanf(buf, "#expires %ld", &cl_tmp);
+ comment_leapexpires = cl_tmp;
+ }
+ }
+ else if (wantcont)
+ {
+ wantcont = inzcont(fields, nfields);
+ }
+ else
+ {
+ struct lookup const *line_codes
+ = name == leapsec ? leap_line_codes : zi_line_codes;
+
+ lp = byword(fields[0], line_codes);
+ if (lp == NULL)
+ error(_("input line of unknown type"));
+ else
+ switch (lp->l_value)
+ {
+ case LC_RULE:
+ inrule(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_ZONE:
+ wantcont = inzone(fields, nfields);
+ break;
+ case LC_LINK:
+ inlink(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_LEAP:
+ inleap(fields, nfields);
+ wantcont = false;
+ break;
+ case LC_EXPIRES:
+ inexpires(fields, nfields);
+ wantcont = false;
+ break;
+ default: /* "cannot happen" */
+ fprintf(stderr,
+ _("%s: panic: Invalid l_value %d\n"),
+ progname, lp->l_value);
+ exit(EXIT_FAILURE);
+ }
+ }
+ free(fields);
+ }
+ close_file(fp, NULL, filename);
+ if (wantcont)
+ error(_("expected continuation line not found"));
+}
+
+/*
+ * Convert a string of one of the forms
+ * h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
+ * into a number of seconds.
+ * A null string maps to zero.
+ * Call error with errstring and return zero on errors.
+ */
+
+static zic_t
+gethms(char const *string, char const *errstring)
+{
+ /* PG: make hh be int not zic_t to avoid sscanf portability issues */
+ int hh;
+ int sign,
+ mm = 0,
+ ss = 0;
+ char hhx,
+ mmx,
+ ssx,
+ xr = '0',
+ xs;
+ int tenths = 0;
+ bool ok = true;
+
+ if (string == NULL || *string == '\0')
+ return 0;
+ if (*string == '-')
+ {
+ sign = -1;
+ ++string;
+ }
+ else
+ sign = 1;
+ switch (sscanf(string,
+ "%d%c%d%c%d%c%1d%*[0]%c%*[0123456789]%c",
+ &hh, &hhx, &mm, &mmx, &ss, &ssx, &tenths, &xr, &xs))
+ {
+ default:
+ ok = false;
+ break;
+ case 8:
+ ok = '0' <= xr && xr <= '9';
+ /* fallthrough */
+ case 7:
+ ok &= ssx == '.';
+ if (ok && noise)
+ warning(_("fractional seconds rejected by"
+ " pre-2018 versions of zic"));
+ /* fallthrough */
+ case 5:
+ ok &= mmx == ':';
+ /* fallthrough */
+ case 3:
+ ok &= hhx == ':';
+ /* fallthrough */
+ case 1:
+ break;
+ }
+ if (!ok)
+ {
+ error("%s", errstring);
+ return 0;
+ }
+ if (hh < 0 ||
+ mm < 0 || mm >= MINSPERHOUR ||
+ ss < 0 || ss > SECSPERMIN)
+ {
+ error("%s", errstring);
+ return 0;
+ }
+ /* Some compilers warn that this test is unsatisfiable for 32-bit ints */
+#if INT_MAX > PG_INT32_MAX
+ if (ZIC_MAX / SECSPERHOUR < hh)
+ {
+ error(_("time overflow"));
+ return 0;
+ }
+#endif
+ ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
+ if (noise && (hh > HOURSPERDAY ||
+ (hh == HOURSPERDAY && (mm != 0 || ss != 0))))
+ warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
+ return oadd(sign * (zic_t) hh * SECSPERHOUR,
+ sign * (mm * SECSPERMIN + ss));
+}
+
+static zic_t
+getsave(char *field, bool *isdst)
+{
+ int dst = -1;
+ zic_t save;
+ size_t fieldlen = strlen(field);
+
+ if (fieldlen != 0)
+ {
+ char *ep = field + fieldlen - 1;
+
+ switch (*ep)
+ {
+ case 'd':
+ dst = 1;
+ *ep = '\0';
+ break;
+ case 's':
+ dst = 0;
+ *ep = '\0';
+ break;
+ }
+ }
+ save = gethms(field, _("invalid saved time"));
+ *isdst = dst < 0 ? save != 0 : dst;
+ return save;
+}
+
+static void
+inrule(char **fields, int nfields)
+{
+ static struct rule r;
+
+ if (nfields != RULE_FIELDS)
+ {
+ error(_("wrong number of fields on Rule line"));
+ return;
+ }
+ switch (*fields[RF_NAME])
+ {
+ case '\0':
+ case ' ':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ case '+':
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
+ return;
+ }
+ r.r_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]);
+ r.r_name = ecpyalloc(fields[RF_NAME]);
+ r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+ if (max_abbrvar_len < strlen(r.r_abbrvar))
+ max_abbrvar_len = strlen(r.r_abbrvar);
+ rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
+ rules[nrules++] = r;
+}
+
+static bool
+inzone(char **fields, int nfields)
+{
+ ptrdiff_t i;
+
+ if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS)
+ {
+ error(_("wrong number of fields on Zone line"));
+ return false;
+ }
+ if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0)
+ {
+ error(
+ _("\"Zone %s\" line and -l option are mutually exclusive"),
+ tzdefault);
+ return false;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL)
+ {
+ error(
+ _("\"Zone %s\" line and -p option are mutually exclusive"),
+ TZDEFRULES);
+ return false;
+ }
+ for (i = 0; i < nzones; ++i)
+ if (zones[i].z_name != NULL &&
+ strcmp(zones[i].z_name, fields[ZF_NAME]) == 0)
+ {
+ error(_("duplicate zone name %s"
+ " (file \"%s\", line %d)"),
+ fields[ZF_NAME],
+ zones[i].z_filename,
+ zones[i].z_linenum);
+ return false;
+ }
+ return inzsub(fields, nfields, false);
+}
+
+static bool
+inzcont(char **fields, int nfields)
+{
+ if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS)
+ {
+ error(_("wrong number of fields on Zone continuation line"));
+ return false;
+ }
+ return inzsub(fields, nfields, true);
+}
+
+static bool
+inzsub(char **fields, int nfields, bool iscont)
+{
+ char *cp;
+ char *cp1;
+ static struct zone z;
+ int i_stdoff,
+ i_rule,
+ i_format;
+ int i_untilyear,
+ i_untilmonth;
+ int i_untilday,
+ i_untiltime;
+ bool hasuntil;
+
+ if (iscont)
+ {
+ i_stdoff = ZFC_STDOFF;
+ i_rule = ZFC_RULE;
+ i_format = ZFC_FORMAT;
+ i_untilyear = ZFC_TILYEAR;
+ i_untilmonth = ZFC_TILMONTH;
+ i_untilday = ZFC_TILDAY;
+ i_untiltime = ZFC_TILTIME;
+ z.z_name = NULL;
+ }
+ else if (!namecheck(fields[ZF_NAME]))
+ return false;
+ else
+ {
+ i_stdoff = ZF_STDOFF;
+ i_rule = ZF_RULE;
+ i_format = ZF_FORMAT;
+ i_untilyear = ZF_TILYEAR;
+ i_untilmonth = ZF_TILMONTH;
+ i_untilday = ZF_TILDAY;
+ i_untiltime = ZF_TILTIME;
+ z.z_name = ecpyalloc(fields[ZF_NAME]);
+ }
+ z.z_filename = filename;
+ z.z_linenum = linenum;
+ z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
+ if ((cp = strchr(fields[i_format], '%')) != NULL)
+ {
+ if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
+ || strchr(fields[i_format], '/'))
+ {
+ error(_("invalid abbreviation format"));
+ 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);
+ hasuntil = nfields > i_untilyear;
+ if (hasuntil)
+ {
+ z.z_untilrule.r_filename = filename;
+ z.z_untilrule.r_linenum = linenum;
+ rulesub(&z.z_untilrule,
+ fields[i_untilyear],
+ "only",
+ "",
+ (nfields > i_untilmonth) ?
+ fields[i_untilmonth] : "Jan",
+ (nfields > i_untilday) ? fields[i_untilday] : "1",
+ (nfields > i_untiltime) ? fields[i_untiltime] : "0");
+ z.z_untiltime = rpytime(&z.z_untilrule,
+ z.z_untilrule.r_loyear);
+ if (iscont && nzones > 0 &&
+ z.z_untiltime > min_time &&
+ z.z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime > min_time &&
+ zones[nzones - 1].z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime >= z.z_untiltime)
+ {
+ error(_("Zone continuation line end time is not after end time of previous line"));
+ return false;
+ }
+ }
+ zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
+ zones[nzones++] = z;
+
+ /*
+ * If there was an UNTIL field on this line, there's more information
+ * about the zone on the next line.
+ */
+ return hasuntil;
+}
+
+static zic_t
+getleapdatetime(char **fields, int nfields, bool expire_line)
+{
+ const char *cp;
+ const struct lookup *lp;
+ zic_t i,
+ j;
+
+ /* PG: make year be int not zic_t to avoid sscanf portability issues */
+ int year;
+ int month,
+ day;
+ zic_t dayoff,
+ tod;
+ zic_t t;
+ char xs;
+
+ dayoff = 0;
+ cp = fields[LP_YEAR];
+ if (sscanf(cp, "%d%c", &year, &xs) != 1)
+ {
+ /*
+ * Leapin' Lizards!
+ */
+ error(_("invalid leaping year"));
+ return -1;
+ }
+ if (!expire_line)
+ {
+ if (!leapseen || leapmaxyear < year)
+ leapmaxyear = year;
+ if (!leapseen || leapminyear > year)
+ leapminyear = year;
+ leapseen = true;
+ }
+ j = EPOCH_YEAR;
+ while (j != year)
+ {
+ if (year > j)
+ {
+ i = len_years[isleap(j)];
+ ++j;
+ }
+ else
+ {
+ --j;
+ i = -len_years[isleap(j)];
+ }
+ dayoff = oadd(dayoff, i);
+ }
+ if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL)
+ {
+ error(_("invalid month name"));
+ return -1;
+ }
+ month = lp->l_value;
+ j = TM_JANUARY;
+ while (j != month)
+ {
+ i = len_months[isleap(year)][j];
+ dayoff = oadd(dayoff, i);
+ ++j;
+ }
+ cp = fields[LP_DAY];
+ if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
+ day <= 0 || day > len_months[isleap(year)][month])
+ {
+ error(_("invalid day of month"));
+ return -1;
+ }
+ dayoff = oadd(dayoff, day - 1);
+ if (dayoff < min_time / SECSPERDAY)
+ {
+ error(_("time too small"));
+ return -1;
+ }
+ if (dayoff > max_time / SECSPERDAY)
+ {
+ error(_("time too large"));
+ return -1;
+ }
+ t = dayoff * SECSPERDAY;
+ tod = gethms(fields[LP_TIME], _("invalid time of day"));
+ t = tadd(t, tod);
+ if (t < 0)
+ error(_("leap second precedes Epoch"));
+ return t;
+}
+
+static void
+inleap(char **fields, int nfields)
+{
+ if (nfields != LEAP_FIELDS)
+ error(_("wrong number of fields on Leap line"));
+ else
+ {
+ zic_t t = getleapdatetime(fields, nfields, false);
+
+ if (0 <= t)
+ {
+ struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
+
+ if (!lp)
+ error(_("invalid Rolling/Stationary field on Leap line"));
+ else
+ {
+ int correction = 0;
+
+ if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
+ correction = -1;
+ else if (strcmp(fields[LP_CORR], "+") == 0)
+ correction = 1;
+ else
+ error(_("invalid CORRECTION field on Leap line"));
+ if (correction)
+ leapadd(t, correction, lp->l_value);
+ }
+ }
+ }
+}
+
+static void
+inexpires(char **fields, int nfields)
+{
+ if (nfields != EXPIRES_FIELDS)
+ error(_("wrong number of fields on Expires line"));
+ else if (0 <= leapexpires)
+ error(_("multiple Expires lines"));
+ else
+ leapexpires = getleapdatetime(fields, nfields, true);
+}
+
+static void
+inlink(char **fields, int nfields)
+{
+ struct link l;
+
+ if (nfields != LINK_FIELDS)
+ {
+ error(_("wrong number of fields on Link line"));
+ return;
+ }
+ if (*fields[LF_TARGET] == '\0')
+ {
+ error(_("blank TARGET field on Link line"));
+ return;
+ }
+ if (!namecheck(fields[LF_LINKNAME]))
+ return;
+ l.l_filename = filename;
+ l.l_linenum = linenum;
+ l.l_target = ecpyalloc(fields[LF_TARGET]);
+ l.l_linkname = ecpyalloc(fields[LF_LINKNAME]);
+ links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
+ links[nlinks++] = l;
+}
+
+static void
+rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
+ const char *typep, const char *monthp, const char *dayp,
+ const char *timep)
+{
+ const struct lookup *lp;
+ const char *cp;
+ char *dp;
+ char *ep;
+ char xs;
+
+ /* PG: year_tmp is to avoid sscanf portability issues */
+ int year_tmp;
+
+ if ((lp = byword(monthp, mon_names)) == NULL)
+ {
+ error(_("invalid month name"));
+ return;
+ }
+ rp->r_month = lp->l_value;
+ rp->r_todisstd = false;
+ rp->r_todisut = false;
+ dp = ecpyalloc(timep);
+ if (*dp != '\0')
+ {
+ ep = dp + strlen(dp) - 1;
+ switch (lowerit(*ep))
+ {
+ case 's': /* Standard */
+ rp->r_todisstd = true;
+ rp->r_todisut = false;
+ *ep = '\0';
+ break;
+ case 'w': /* Wall */
+ rp->r_todisstd = false;
+ rp->r_todisut = false;
+ *ep = '\0';
+ break;
+ case 'g': /* Greenwich */
+ case 'u': /* Universal */
+ case 'z': /* Zulu */
+ rp->r_todisstd = true;
+ rp->r_todisut = true;
+ *ep = '\0';
+ break;
+ }
+ }
+ rp->r_tod = gethms(dp, _("invalid time of day"));
+ free(dp);
+
+ /*
+ * Year work.
+ */
+ cp = loyearp;
+ lp = byword(cp, begin_years);
+ rp->r_lowasnum = lp == NULL;
+ if (!rp->r_lowasnum)
+ switch (lp->l_value)
+ {
+ case YR_MINIMUM:
+ rp->r_loyear = ZIC_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_loyear = ZIC_MAX;
+ break;
+ default: /* "cannot happen" */
+ fprintf(stderr,
+ _("%s: panic: Invalid l_value %d\n"),
+ progname, lp->l_value);
+ exit(EXIT_FAILURE);
+ }
+ else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
+ rp->r_loyear = year_tmp;
+ else
+ {
+ error(_("invalid starting year"));
+ return;
+ }
+ cp = hiyearp;
+ lp = byword(cp, end_years);
+ rp->r_hiwasnum = lp == NULL;
+ if (!rp->r_hiwasnum)
+ switch (lp->l_value)
+ {
+ case YR_MINIMUM:
+ rp->r_hiyear = ZIC_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_hiyear = ZIC_MAX;
+ break;
+ case YR_ONLY:
+ rp->r_hiyear = rp->r_loyear;
+ break;
+ default: /* "cannot happen" */
+ fprintf(stderr,
+ _("%s: panic: Invalid l_value %d\n"),
+ progname, lp->l_value);
+ exit(EXIT_FAILURE);
+ }
+ else if (sscanf(cp, "%d%c", &year_tmp, &xs) == 1)
+ rp->r_hiyear = year_tmp;
+ else
+ {
+ error(_("invalid ending year"));
+ return;
+ }
+ if (rp->r_loyear > rp->r_hiyear)
+ {
+ error(_("starting year greater than ending year"));
+ return;
+ }
+ if (*typep != '\0')
+ {
+ error(_("year type \"%s\" is unsupported; use \"-\" instead"),
+ typep);
+ return;
+ }
+
+ /*
+ * Day work. Accept things such as: 1 lastSunday last-Sunday
+ * (undocumented; warn about this) Sun<=20 Sun>=7
+ */
+ dp = ecpyalloc(dayp);
+ if ((lp = byword(dp, lasts)) != NULL)
+ {
+ rp->r_dycode = DC_DOWLEQ;
+ rp->r_wday = lp->l_value;
+ rp->r_dayofmonth = len_months[1][rp->r_month];
+ }
+ else
+ {
+ if ((ep = strchr(dp, '<')) != NULL)
+ rp->r_dycode = DC_DOWLEQ;
+ else if ((ep = strchr(dp, '>')) != NULL)
+ rp->r_dycode = DC_DOWGEQ;
+ else
+ {
+ ep = dp;
+ rp->r_dycode = DC_DOM;
+ }
+ if (rp->r_dycode != DC_DOM)
+ {
+ *ep++ = 0;
+ if (*ep++ != '=')
+ {
+ error(_("invalid day of month"));
+ free(dp);
+ return;
+ }
+ if ((lp = byword(dp, wday_names)) == NULL)
+ {
+ error(_("invalid weekday name"));
+ free(dp);
+ return;
+ }
+ rp->r_wday = lp->l_value;
+ }
+ if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
+ rp->r_dayofmonth <= 0 ||
+ (rp->r_dayofmonth > len_months[1][rp->r_month]))
+ {
+ error(_("invalid day of month"));
+ free(dp);
+ return;
+ }
+ }
+ free(dp);
+}
+
+static void
+convert(const int32 val, char *const buf)
+{
+ int i;
+ int shift;
+ unsigned char *const b = (unsigned char *) buf;
+
+ for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
+ b[i] = val >> shift;
+}
+
+static void
+convert64(const zic_t val, char *const buf)
+{
+ int i;
+ int shift;
+ unsigned char *const b = (unsigned char *) buf;
+
+ for (i = 0, shift = 56; i < 8; ++i, shift -= 8)
+ b[i] = val >> shift;
+}
+
+static void
+puttzcode(const int32 val, FILE *const fp)
+{
+ char buf[4];
+
+ convert(val, buf);
+ fwrite(buf, sizeof buf, 1, fp);
+}
+
+static void
+puttzcodepass(zic_t val, FILE *fp, int pass)
+{
+ if (pass == 1)
+ puttzcode(val, fp);
+ else
+ {
+ char buf[8];
+
+ convert64(val, buf);
+ fwrite(buf, sizeof buf, 1, fp);
+ }
+}
+
+static int
+atcomp(const void *avp, const void *bvp)
+{
+ 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 timerange
+{
+ int defaulttype;
+ ptrdiff_t base,
+ count;
+ int leapbase,
+ leapcount;
+};
+
+static struct timerange
+limitrange(struct timerange r, zic_t lo, zic_t hi,
+ zic_t const *ats, unsigned char const *types)
+{
+ 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)
+ {
+ r.leapcount--;
+ r.leapbase++;
+ }
+
+ if (hi < ZIC_MAX)
+ {
+ while (0 < r.count && hi + 1 < ats[r.base + r.count - 1])
+ r.count--;
+ while (0 < r.leapcount && hi + 1 < trans[r.leapbase + r.leapcount - 1])
+ r.leapcount--;
+ }
+
+ return r;
+}
+
+static void
+writezone(const char *const name, const char *const string, char version,
+ int defaulttype)
+{
+ FILE *fp;
+ ptrdiff_t i,
+ j;
+ 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;
+
+ /*
+ * Allocate the ATS and TYPES arrays via a single malloc, as this is a bit
+ * faster.
+ */
+ zic_t *ats = emalloc(MAXALIGN(size_product(nats, sizeof *ats + 1)));
+ void *typesptr = ats + nats;
+ unsigned char *types = typesptr;
+ struct timerange rangeall,
+ range32,
+ range64;
+
+ /*
+ * Sort.
+ */
+ if (timecnt > 1)
+ qsort(attypes, timecnt, sizeof *attypes, atcomp);
+
+ /*
+ * Optimize.
+ */
+ {
+ ptrdiff_t fromi,
+ toi;
+
+ toi = 0;
+ fromi = 0;
+ for (; fromi < timecnt; ++fromi)
+ {
+ if (toi != 0
+ && ((attypes[fromi].at
+ + utoffs[attypes[toi - 1].type])
+ <= (attypes[toi - 1].at
+ + utoffs[toi == 1 ? 0
+ : attypes[toi - 2].type])))
+ {
+ attypes[toi - 1].type =
+ attypes[fromi].type;
+ continue;
+ }
+ if (toi == 0
+ || attypes[fromi].dontmerge
+ || (utoffs[attypes[toi - 1].type]
+ != utoffs[attypes[fromi].type])
+ || (isdsts[attypes[toi - 1].type]
+ != isdsts[attypes[fromi].type])
+ || (desigidx[attypes[toi - 1].type]
+ != desigidx[attypes[fromi].type]))
+ attypes[toi++] = attypes[fromi];
+ }
+ timecnt = toi;
+ }
+
+ if (noise && timecnt > 1200)
+ {
+ if (timecnt > TZ_MAX_TIMES)
+ warning(_("reference clients mishandle"
+ " more than %d transition times"),
+ TZ_MAX_TIMES);
+ else
+ warning(_("pre-2014 clients may mishandle"
+ " more than 1200 transition times"));
+ }
+
+ /*
+ * Transfer.
+ */
+ for (i = 0; i < timecnt; ++i)
+ {
+ ats[i] = attypes[i].at;
+ types[i] = attypes[i].type;
+ }
+
+ /*
+ * Correct for leap seconds.
+ */
+ for (i = 0; i < timecnt; ++i)
+ {
+ j = leapcnt;
+ while (--j >= 0)
+ if (ats[i] > trans[j] - corr[j])
+ {
+ ats[i] = tadd(ats[i], corr[j]);
+ break;
+ }
+ }
+
+ /*
+ * Work around QTBUG-53071 for timestamps less than y2038_boundary - 1, by
+ * inserting a no-op transition at time y2038_boundary - 1. This works
+ * only for timestamps before the boundary, which should be good enough in
+ * practice as QTBUG-53071 should be long-dead by 2038. Do this after
+ * correcting for leap seconds, as the idea is to insert a transition just
+ * before 32-bit pg_time_t rolls around, and this occurs at a slightly
+ * different moment if transitions are leap-second corrected.
+ */
+ if (WORK_AROUND_QTBUG_53071 && timecnt != 0 && want_bloat()
+ && ats[timecnt - 1] < y2038_boundary - 1 && strchr(string, '<'))
+ {
+ ats[timecnt] = y2038_boundary - 1;
+ types[timecnt] = types[timecnt - 1];
+ timecnt++;
+ }
+
+ rangeall.defaulttype = defaulttype;
+ rangeall.base = rangeall.leapbase = 0;
+ rangeall.count = timecnt;
+ rangeall.leapcount = leapcnt;
+ range64 = limitrange(rangeall, lo_time, hi_time, ats, types);
+ range32 = limitrange(range64, PG_INT32_MIN, PG_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: Cannot 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;
+ }
+ if (!fp)
+ {
+ fprintf(stderr, _("%s: Cannot create %s/%s: %s\n"),
+ progname, directory, name, strerror(fopen_errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ for (pass = 1; pass <= 2; ++pass)
+ {
+ ptrdiff_t thistimei,
+ thistimecnt,
+ thistimelim;
+ int thisleapi,
+ thisleapcnt,
+ thisleaplim;
+ int currenttype,
+ thisdefaulttype;
+ bool locut,
+ hicut;
+ zic_t lo;
+ int old0;
+ char omittype[TZ_MAX_TYPES];
+ int typemap[TZ_MAX_TYPES];
+ int thistypecnt,
+ stdcnt,
+ utcnt;
+ char thischars[TZ_MAX_CHARS];
+ int thischarcnt;
+ bool toomanytimes;
+ int indmap[TZ_MAX_CHARS];
+
+ if (pass == 1)
+ {
+ /*
+ * Arguably the default time type in the 32-bit data should be
+ * range32.defaulttype, which is suited for timestamps just before
+ * PG_INT32_MIN. However, zic traditionally used the time type of
+ * the indefinite past instead. Internet RFC 8532 says readers
+ * should ignore 32-bit data, so this discrepancy matters only to
+ * obsolete readers where the traditional type might be more
+ * appropriate even if it's "wrong". So, use the historical zic
+ * value, unless -r specifies a low cutoff that excludes some
+ * 32-bit timestamps.
+ */
+ thisdefaulttype = (lo_time <= PG_INT32_MIN
+ ? range64.defaulttype
+ : range32.defaulttype);
+
+ thistimei = range32.base;
+ thistimecnt = range32.count;
+ toomanytimes = thistimecnt >> 31 >> 1 != 0;
+ thisleapi = range32.leapbase;
+ thisleapcnt = range32.leapcount;
+ locut = PG_INT32_MIN < lo_time;
+ hicut = hi_time < PG_INT32_MAX;
+ }
+ else
+ {
+ thisdefaulttype = range64.defaulttype;
+ thistimei = range64.base;
+ thistimecnt = range64.count;
+ toomanytimes = thistimecnt >> 31 >> 31 >> 2 != 0;
+ thisleapi = range64.leapbase;
+ thisleapcnt = range64.leapcount;
+ locut = min_time < lo_time;
+ 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;
+ }
+ memset(omittype, true, typecnt);
+ omittype[thisdefaulttype] = false;
+ for (i = thistimei; i < thistimelim; i++)
+ omittype[types[i]] = false;
+
+ /*
+ * Reorder types to make THISDEFAULTTYPE type 0. Use TYPEMAP to swap
+ * OLD0 and THISDEFAULTTYPE so that THISDEFAULTTYPE appears as type 0
+ * in the output instead of OLD0. TYPEMAP also omits unused types.
+ */
+ old0 = strlen(omittype);
+
+#ifndef LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH
+
+ /*
+ * For some pre-2011 systems: if the last-to-be-written standard (or
+ * daylight) type has an offset different from the most recently used
+ * offset, append an (unused) copy of the most recently used type (to
+ * help get global "altzone" and "timezone" variables set correctly).
+ */
+ if (want_bloat())
+ {
+ int mrudst,
+ mrustd,
+ hidst,
+ histd,
+ type;
+
+ hidst = histd = mrudst = mrustd = -1;
+ for (i = thistimei; i < thistimelim; ++i)
+ if (isdsts[types[i]])
+ mrudst = types[i];
+ else
+ mrustd = types[i];
+ for (i = old0; i < typecnt; i++)
+ {
+ int h = (i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i);
+
+ if (!omittype[h])
+ {
+ if (isdsts[h])
+ hidst = i;
+ else
+ histd = i;
+ }
+ }
+ if (hidst >= 0 && mrudst >= 0 && hidst != mrudst &&
+ utoffs[hidst] != utoffs[mrudst])
+ {
+ isdsts[mrudst] = -1;
+ type = addtype(utoffs[mrudst],
+ &chars[desigidx[mrudst]],
+ true,
+ ttisstds[mrudst],
+ ttisuts[mrudst]);
+ isdsts[mrudst] = 1;
+ omittype[type] = false;
+ }
+ if (histd >= 0 && mrustd >= 0 && histd != mrustd &&
+ utoffs[histd] != utoffs[mrustd])
+ {
+ isdsts[mrustd] = -1;
+ type = addtype(utoffs[mrustd],
+ &chars[desigidx[mrustd]],
+ false,
+ ttisstds[mrustd],
+ ttisuts[mrustd]);
+ isdsts[mrustd] = 0;
+ omittype[type] = false;
+ }
+ }
+#endif /* !defined
+ * LEAVE_SOME_PRE_2011_SYSTEMS_IN_THE_LURCH */
+ thistypecnt = 0;
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ typemap[i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i]
+ = thistypecnt++;
+
+ for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
+ indmap[i] = -1;
+ thischarcnt = stdcnt = utcnt = 0;
+ for (i = old0; i < typecnt; i++)
+ {
+ char *thisabbr;
+
+ if (omittype[i])
+ continue;
+ if (ttisstds[i])
+ stdcnt = thistypecnt;
+ if (ttisuts[i])
+ utcnt = thistypecnt;
+ if (indmap[desigidx[i]] >= 0)
+ continue;
+ thisabbr = &chars[desigidx[i]];
+ for (j = 0; j < thischarcnt; ++j)
+ if (strcmp(&thischars[j], thisabbr) == 0)
+ break;
+ if (j == thischarcnt)
+ {
+ strcpy(&thischars[thischarcnt], thisabbr);
+ thischarcnt += strlen(thisabbr) + 1;
+ }
+ indmap[desigidx[i]] = j;
+ }
+ if (pass == 1 && !want_bloat())
+ {
+ utcnt = stdcnt = thisleapcnt = 0;
+ thistimecnt = -(locut + hicut);
+ thistypecnt = thischarcnt = 1;
+ thistimelim = thistimei;
+ }
+#define DO(field) fwrite(tzh.field, sizeof tzh.field, 1, fp)
+ tzh = tzh0;
+ 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(thistypecnt, tzh.tzh_typecnt);
+ convert(thischarcnt, tzh.tzh_charcnt);
+ DO(tzh_magic);
+ DO(tzh_version);
+ DO(tzh_reserved);
+ DO(tzh_ttisutcnt);
+ DO(tzh_ttisstdcnt);
+ DO(tzh_leapcnt);
+ DO(tzh_timecnt);
+ DO(tzh_typecnt);
+ DO(tzh_charcnt);
+#undef DO
+ if (pass == 1 && !want_bloat())
+ {
+ /* Output a minimal data block with just one time type. */
+ puttzcode(0, fp); /* utoff */
+ putc(0, fp); /* dst */
+ putc(0, fp); /* index of abbreviation */
+ putc(0, fp); /* empty-string abbreviation */
+ continue;
+ }
+
+ /* PG: print current timezone abbreviations if requested */
+ if (print_abbrevs && pass == 2)
+ {
+ /* Print "type" data for periods ending after print_cutoff */
+ for (i = thistimei; i < thistimelim; ++i)
+ {
+ if (i == thistimelim - 1 || ats[i + 1] > print_cutoff)
+ {
+ unsigned char tm = types[i];
+ char *thisabbrev = &thischars[indmap[desigidx[tm]]];
+
+ fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
+ thisabbrev,
+ utoffs[tm],
+ isdsts[tm] ? "\tD" : "");
+ }
+ }
+ /* Print the default type if we have no transitions at all */
+ if (thistimei >= thistimelim)
+ {
+ unsigned char tm = defaulttype;
+ char *thisabbrev = &thischars[indmap[desigidx[tm]]];
+
+ fprintf(stdout, "%s\t" INT64_FORMAT "%s\n",
+ thisabbrev,
+ utoffs[tm],
+ isdsts[tm] ? "\tD" : "");
+ }
+ }
+
+ /*
+ * Output a LO_TIME transition if needed; see limitrange. But do not
+ * go below the minimum representable value for this pass.
+ */
+ lo = pass == 1 && lo_time < PG_INT32_MIN ? PG_INT32_MIN : lo_time;
+
+ if (locut)
+ puttzcodepass(lo, fp, pass);
+ for (i = thistimei; i < thistimelim; ++i)
+ {
+ zic_t at = ats[i] < lo ? lo : ats[i];
+
+ puttzcodepass(at, fp, pass);
+ }
+ if (hicut)
+ puttzcodepass(hi_time + 1, fp, pass);
+ currenttype = 0;
+ if (locut)
+ putc(currenttype, fp);
+ for (i = thistimei; i < thistimelim; ++i)
+ {
+ currenttype = typemap[types[i]];
+ putc(currenttype, fp);
+ }
+ if (hicut)
+ putc(currenttype, fp);
+
+ for (i = old0; i < typecnt; i++)
+ {
+ int h = (i == old0 ? thisdefaulttype
+ : i == thisdefaulttype ? old0 : i);
+
+ if (!omittype[h])
+ {
+ puttzcode(utoffs[h], fp);
+ putc(isdsts[h], fp);
+ putc(indmap[desigidx[h]], fp);
+ }
+ }
+ if (thischarcnt != 0)
+ fwrite(thischars, sizeof thischars[0],
+ thischarcnt, fp);
+ for (i = thisleapi; i < thisleaplim; ++i)
+ {
+ zic_t todo;
+
+ if (roll[i])
+ {
+ if (timecnt == 0 || trans[i] < ats[0])
+ {
+ j = 0;
+ while (isdsts[j])
+ if (++j >= typecnt)
+ {
+ j = 0;
+ break;
+ }
+ }
+ else
+ {
+ j = 1;
+ while (j < timecnt &&
+ trans[i] >= ats[j])
+ ++j;
+ j = types[j - 1];
+ }
+ todo = tadd(trans[i], -utoffs[j]);
+ }
+ else
+ todo = trans[i];
+ puttzcodepass(todo, fp, pass);
+ puttzcode(corr[i], fp);
+ }
+ if (stdcnt != 0)
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ putc(ttisstds[i], fp);
+ if (utcnt != 0)
+ for (i = old0; i < typecnt; i++)
+ if (!omittype[i])
+ putc(ttisuts[i], fp);
+ }
+ fprintf(fp, "\n%s\n", string);
+ close_file(fp, directory, name);
+ free(ats);
+}
+
+static char const *
+abbroffset(char *buf, zic_t offset)
+{
+ char sign = '+';
+ int seconds,
+ minutes;
+
+ if (offset < 0)
+ {
+ offset = -offset;
+ sign = '-';
+ }
+
+ seconds = offset % SECSPERMIN;
+ offset /= SECSPERMIN;
+ minutes = offset % MINSPERHOUR;
+ offset /= MINSPERHOUR;
+ if (100 <= offset)
+ {
+ error(_("%%z UT offset magnitude exceeds 99:59:59"));
+ return "%z";
+ }
+ else
+ {
+ char *p = buf;
+
+ *p++ = sign;
+ *p++ = '0' + offset / 10;
+ *p++ = '0' + offset % 10;
+ if (minutes | seconds)
+ {
+ *p++ = '0' + minutes / 10;
+ *p++ = '0' + minutes % 10;
+ if (seconds)
+ {
+ *p++ = '0' + seconds / 10;
+ *p++ = '0' + seconds % 10;
+ }
+ }
+ *p = '\0';
+ return buf;
+ }
+}
+
+static size_t
+doabbr(char *abbr, struct zone const *zp, char const *letters,
+ bool isdst, zic_t save, bool doquotes)
+{
+ char *cp;
+ char *slashp;
+ size_t len;
+ char const *format = zp->z_format;
+
+ slashp = strchr(format, '/');
+ if (slashp == NULL)
+ {
+ char letterbuf[PERCENT_Z_LEN_BOUND + 1];
+
+ if (zp->z_format_specifier == 'z')
+ letters = abbroffset(letterbuf, zp->z_stdoff + save);
+ else if (!letters)
+ letters = "%s";
+ sprintf(abbr, format, letters);
+ }
+ else if (isdst)
+ {
+ strcpy(abbr, slashp + 1);
+ }
+ else
+ {
+ memcpy(abbr, format, slashp - format);
+ abbr[slashp - format] = '\0';
+ }
+ len = strlen(abbr);
+ if (!doquotes)
+ return len;
+ for (cp = abbr; is_alpha(*cp); cp++)
+ continue;
+ if (len > 0 && *cp == '\0')
+ return len;
+ abbr[len + 2] = '\0';
+ abbr[len + 1] = '>';
+ memmove(abbr + 1, abbr, len);
+ abbr[0] = '<';
+ return len + 2;
+}
+
+static void
+updateminmax(const zic_t x)
+{
+ if (min_year > x)
+ min_year = x;
+ if (max_year < x)
+ max_year = x;
+}
+
+static int
+stringoffset(char *result, zic_t offset)
+{
+ int hours;
+ int minutes;
+ int seconds;
+ bool negative = offset < 0;
+ int len = negative;
+
+ if (negative)
+ {
+ offset = -offset;
+ result[0] = '-';
+ }
+ seconds = offset % SECSPERMIN;
+ offset /= SECSPERMIN;
+ minutes = offset % MINSPERHOUR;
+ offset /= MINSPERHOUR;
+ hours = offset;
+ if (hours >= HOURSPERDAY * DAYSPERWEEK)
+ {
+ result[0] = '\0';
+ return 0;
+ }
+ len += sprintf(result + len, "%d", hours);
+ if (minutes != 0 || seconds != 0)
+ {
+ len += sprintf(result + len, ":%02d", minutes);
+ if (seconds != 0)
+ len += sprintf(result + len, ":%02d", seconds);
+ }
+ return len;
+}
+
+static int
+stringrule(char *result, struct rule *const rp, zic_t save, zic_t stdoff)
+{
+ zic_t tod = rp->r_tod;
+ int compat = 0;
+
+ if (rp->r_dycode == DC_DOM)
+ {
+ int month,
+ total;
+
+ if (rp->r_dayofmonth == 29 && rp->r_month == TM_FEBRUARY)
+ return -1;
+ total = 0;
+ for (month = 0; month < rp->r_month; ++month)
+ total += len_months[0][month];
+ /* Omit the "J" in Jan and Feb, as that's shorter. */
+ if (rp->r_month <= 1)
+ result += sprintf(result, "%d", total + rp->r_dayofmonth - 1);
+ else
+ result += sprintf(result, "J%d", total + rp->r_dayofmonth);
+ }
+ else
+ {
+ int week;
+ int wday = rp->r_wday;
+ int wdayoff;
+
+ if (rp->r_dycode == DC_DOWGEQ)
+ {
+ wdayoff = (rp->r_dayofmonth - 1) % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
+ wday -= wdayoff;
+ tod += wdayoff * SECSPERDAY;
+ week = 1 + (rp->r_dayofmonth - 1) / DAYSPERWEEK;
+ }
+ else if (rp->r_dycode == DC_DOWLEQ)
+ {
+ if (rp->r_dayofmonth == len_months[1][rp->r_month])
+ week = 5;
+ else
+ {
+ wdayoff = rp->r_dayofmonth % DAYSPERWEEK;
+ if (wdayoff)
+ compat = 2013;
+ wday -= wdayoff;
+ tod += wdayoff * SECSPERDAY;
+ week = rp->r_dayofmonth / DAYSPERWEEK;
+ }
+ }
+ else
+ return -1; /* "cannot happen" */
+ if (wday < 0)
+ wday += DAYSPERWEEK;
+ result += sprintf(result, "M%d.%d.%d",
+ rp->r_month + 1, week, wday);
+ }
+ if (rp->r_todisut)
+ tod += stdoff;
+ if (rp->r_todisstd && !rp->r_isdst)
+ tod += save;
+ if (tod != 2 * SECSPERMIN * MINSPERHOUR)
+ {
+ *result++ = '/';
+ if (!stringoffset(result, tod))
+ return -1;
+ if (tod < 0)
+ {
+ if (compat < 2013)
+ compat = 2013;
+ }
+ else if (SECSPERDAY <= tod)
+ {
+ if (compat < 1994)
+ compat = 1994;
+ }
+ }
+ return compat;
+}
+
+static int
+rule_cmp(struct rule const *a, struct rule const *b)
+{
+ if (!a)
+ return -!!b;
+ if (!b)
+ return 1;
+ if (a->r_hiyear != b->r_hiyear)
+ return a->r_hiyear < b->r_hiyear ? -1 : 1;
+ if (a->r_month - b->r_month != 0)
+ return a->r_month - b->r_month;
+ return a->r_dayofmonth - b->r_dayofmonth;
+}
+
+static int
+stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
+{
+ const struct zone *zp;
+ struct rule *rp;
+ struct rule *stdrp;
+ struct rule *dstrp;
+ ptrdiff_t i;
+ const char *abbrvar;
+ int compat = 0;
+ int c;
+ size_t len;
+ int offsetlen;
+ struct rule stdr,
+ dstr;
+
+ result[0] = '\0';
+
+ /*
+ * Internet RFC 8536 section 5.1 says to use an empty TZ string if future
+ * timestamps are truncated.
+ */
+ if (hi_time < max_time)
+ return -1;
+
+ zp = zpfirst + zonecount - 1;
+ stdrp = dstrp = NULL;
+ for (i = 0; i < zp->z_nrules; ++i)
+ {
+ 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.
+ */
+ 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);
+ if (!offsetlen)
+ {
+ result[0] = '\0';
+ return -1;
+ }
+ len += offsetlen;
+ if (dstrp == NULL)
+ return compat;
+ len += doabbr(result + len, zp, 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));
+ if (!offsetlen)
+ {
+ result[0] = '\0';
+ return -1;
+ }
+ len += offsetlen;
+ }
+ result[len++] = ',';
+ c = stringrule(result + len, dstrp, dstrp->r_save, zp->z_stdoff);
+ if (c < 0)
+ {
+ result[0] = '\0';
+ return -1;
+ }
+ if (compat < c)
+ compat = c;
+ len += strlen(result + len);
+ result[len++] = ',';
+ c = stringrule(result + len, stdrp, dstrp->r_save, zp->z_stdoff);
+ if (c < 0)
+ {
+ result[0] = '\0';
+ return -1;
+ }
+ if (compat < c)
+ compat = c;
+ return compat;
+}
+
+static void
+outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
+{
+ const struct zone *zp;
+ struct rule *rp;
+ ptrdiff_t i,
+ j;
+ bool usestart,
+ useuntil;
+ zic_t starttime,
+ untiltime;
+ zic_t stdoff;
+ zic_t save;
+ zic_t year;
+ zic_t startoff;
+ bool startttisstd;
+ bool startttisut;
+ int type;
+ char *startbuf;
+ char *ab;
+ char *envvar;
+ int max_abbr_len;
+ int max_envvar_len;
+ bool prodstic; /* all rules are min to max */
+ int compat;
+ bool do_extend;
+ char version;
+ ptrdiff_t lastatmax = -1;
+ zic_t one = 1;
+ zic_t y2038_boundary = one << 31;
+ zic_t max_year0;
+ int defaulttype = -1;
+
+ max_abbr_len = 2 + max_format_len + max_abbrvar_len;
+ max_envvar_len = 2 * max_abbr_len + 5 * 9;
+ startbuf = emalloc(max_abbr_len + 1);
+ ab = emalloc(max_abbr_len + 1);
+ envvar = emalloc(max_envvar_len + 1);
+ INITIALIZE(untiltime);
+ INITIALIZE(starttime);
+
+ /*
+ * Now. . .finally. . .generate some useful data!
+ */
+ timecnt = 0;
+ typecnt = 0;
+ charcnt = 0;
+ prodstic = zonecount == 1;
+
+ /*
+ * Thanks to Earl Chew for noting the need to unconditionally initialize
+ * startttisstd.
+ */
+ startttisstd = false;
+ startttisut = false;
+ min_year = max_year = EPOCH_YEAR;
+ if (leapseen)
+ {
+ updateminmax(leapminyear);
+ updateminmax(leapmaxyear + (leapmaxyear < ZIC_MAX));
+ }
+ for (i = 0; i < zonecount; ++i)
+ {
+ zp = &zpfirst[i];
+ if (i < zonecount - 1)
+ updateminmax(zp->z_untilrule.r_loyear);
+ for (j = 0; j < zp->z_nrules; ++j)
+ {
+ rp = &zp->z_rules[j];
+ if (rp->r_lowasnum)
+ updateminmax(rp->r_loyear);
+ if (rp->r_hiwasnum)
+ updateminmax(rp->r_hiyear);
+ if (rp->r_lowasnum || rp->r_hiwasnum)
+ prodstic = false;
+ }
+ }
+
+ /*
+ * Generate lots of data if a rule can't cover all future times.
+ */
+ compat = stringzone(envvar, zpfirst, zonecount);
+ version = compat < 2013 ? ZIC_VERSION_PRE_2013 : ZIC_VERSION;
+ do_extend = compat < 0;
+ if (noise)
+ {
+ if (!*envvar)
+ warning("%s %s",
+ _("no POSIX environment variable for zone"),
+ zpfirst->z_name);
+ else if (compat != 0)
+ {
+ /*
+ * Circa-COMPAT clients, and earlier clients, might not work for
+ * this zone when given dates before 1970 or after 2038.
+ */
+ warning(_("%s: pre-%d clients may mishandle"
+ " distant timestamps"),
+ zpfirst->z_name, compat);
+ }
+ }
+ if (do_extend)
+ {
+ /*
+ * Search through a couple of extra years past the obvious 400, to
+ * avoid edge cases. For example, suppose a non-POSIX rule applies
+ * from 2012 onwards and has transitions in March and September, plus
+ * some one-off transitions in November 2013. If zic looked only at
+ * the last 400 years, it would set max_year=2413, with the intent
+ * that the 400 years 2014 through 2413 will be repeated. The last
+ * transition listed in the tzfile would be in 2413-09, less than 400
+ * years after the last one-off transition in 2013-11. Two years
+ * might be overkill, but with the kind of edge cases available we're
+ * not sure that one year would suffice.
+ */
+ enum
+ {
+ years_of_observations = YEARSPERREPEAT + 2};
+
+ if (min_year >= ZIC_MIN + years_of_observations)
+ min_year -= years_of_observations;
+ else
+ min_year = ZIC_MIN;
+ if (max_year <= ZIC_MAX - years_of_observations)
+ max_year += years_of_observations;
+ else
+ max_year = ZIC_MAX;
+
+ /*
+ * Regardless of any of the above, for a "proDSTic" zone which
+ * specifies that its rules always have and always will be in effect,
+ * we only need one cycle to define the zone.
+ */
+ if (prodstic)
+ {
+ min_year = 1900;
+ max_year = min_year + years_of_observations;
+ }
+ }
+ max_year0 = max_year;
+ if (want_bloat())
+ {
+ /*
+ * For the benefit of older systems, generate data from 1900 through
+ * 2038.
+ */
+ if (min_year > 1900)
+ min_year = 1900;
+ if (max_year < 2038)
+ max_year = 2038;
+ }
+
+ for (i = 0; i < zonecount; ++i)
+ {
+ struct rule *prevrp = NULL;
+
+ /*
+ * A guess that may well be corrected later.
+ */
+ save = 0;
+ zp = &zpfirst[i];
+ usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ useuntil = i < (zonecount - 1);
+ if (useuntil && zp->z_untiltime <= min_time)
+ continue;
+ stdoff = zp->z_stdoff;
+ eat(zp->z_filename, zp->z_linenum);
+ *startbuf = '\0';
+ startoff = zp->z_stdoff;
+ if (zp->z_nrules == 0)
+ {
+ save = zp->z_save;
+ doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
+ type = addtype(oadd(zp->z_stdoff, save),
+ startbuf, zp->z_isdst, startttisstd,
+ startttisut);
+ if (usestart)
+ {
+ addtt(starttime, type);
+ usestart = false;
+ }
+ else
+ defaulttype = type;
+ }
+ else
+ for (year = min_year; year <= max_year; ++year)
+ {
+ if (useuntil && year > zp->z_untilrule.r_hiyear)
+ break;
+
+ /*
+ * Mark which rules to do in the current year. For those to
+ * do, calculate rpytime(rp, year); The former TYPE field was
+ * also considered here.
+ */
+ for (j = 0; j < zp->z_nrules; ++j)
+ {
+ rp = &zp->z_rules[j];
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ rp->r_todo = year >= rp->r_loyear &&
+ year <= rp->r_hiyear;
+ if (rp->r_todo)
+ {
+ rp->r_temp = rpytime(rp, year);
+ rp->r_todo
+ = (rp->r_temp < y2038_boundary
+ || year <= max_year0);
+ }
+ }
+ for (;;)
+ {
+ ptrdiff_t k;
+ zic_t jtime,
+ ktime;
+ zic_t offset;
+
+ INITIALIZE(ktime);
+ if (useuntil)
+ {
+ /*
+ * Turn untiltime into UT assuming the current stdoff
+ * and save values.
+ */
+ untiltime = zp->z_untiltime;
+ if (!zp->z_untilrule.r_todisut)
+ untiltime = tadd(untiltime,
+ -stdoff);
+ if (!zp->z_untilrule.r_todisstd)
+ untiltime = tadd(untiltime,
+ -save);
+ }
+
+ /*
+ * Find the rule (of those to do, if any) that takes
+ * effect earliest in the year.
+ */
+ k = -1;
+ for (j = 0; j < zp->z_nrules; ++j)
+ {
+ rp = &zp->z_rules[j];
+ if (!rp->r_todo)
+ continue;
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ offset = rp->r_todisut ? 0 : stdoff;
+ if (!rp->r_todisstd)
+ offset = oadd(offset, save);
+ jtime = rp->r_temp;
+ if (jtime == min_time ||
+ jtime == max_time)
+ continue;
+ jtime = tadd(jtime, -offset);
+ if (k < 0 || jtime < ktime)
+ {
+ k = j;
+ ktime = jtime;
+ }
+ else if (jtime == ktime)
+ {
+ char const *dup_rules_msg =
+ _("two rules for same instant");
+
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ warning("%s", dup_rules_msg);
+ rp = &zp->z_rules[k];
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ error("%s", dup_rules_msg);
+ }
+ }
+ if (k < 0)
+ break; /* go on to next year */
+ rp = &zp->z_rules[k];
+ rp->r_todo = false;
+ if (useuntil && ktime >= untiltime)
+ break;
+ save = rp->r_save;
+ if (usestart && ktime == starttime)
+ usestart = false;
+ if (usestart)
+ {
+ if (ktime < starttime)
+ {
+ startoff = oadd(zp->z_stdoff,
+ save);
+ doabbr(startbuf, zp,
+ rp->r_abbrvar,
+ rp->r_isdst,
+ rp->r_save,
+ false);
+ continue;
+ }
+ if (*startbuf == '\0'
+ && startoff == oadd(zp->z_stdoff,
+ save))
+ {
+ doabbr(startbuf,
+ zp,
+ rp->r_abbrvar,
+ rp->r_isdst,
+ rp->r_save,
+ false);
+ }
+ }
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ doabbr(ab, zp, rp->r_abbrvar,
+ rp->r_isdst, rp->r_save, false);
+ offset = oadd(zp->z_stdoff, rp->r_save);
+ if (!want_bloat() && !useuntil && !do_extend
+ && prevrp
+ && rp->r_hiyear == ZIC_MAX
+ && prevrp->r_hiyear == ZIC_MAX)
+ break;
+ type = addtype(offset, ab, rp->r_isdst,
+ rp->r_todisstd, rp->r_todisut);
+ if (defaulttype < 0 && !rp->r_isdst)
+ defaulttype = type;
+ if (rp->r_hiyear == ZIC_MAX
+ && !(0 <= lastatmax
+ && ktime < attypes[lastatmax].at))
+ lastatmax = timecnt;
+ addtt(ktime, type);
+ prevrp = rp;
+ }
+ }
+ if (usestart)
+ {
+ if (*startbuf == '\0' &&
+ zp->z_format != NULL &&
+ strchr(zp->z_format, '%') == NULL &&
+ strchr(zp->z_format, '/') == NULL)
+ strcpy(startbuf, zp->z_format);
+ eat(zp->z_filename, zp->z_linenum);
+ if (*startbuf == '\0')
+ error(_("cannot determine time zone abbreviation to use just after until time"));
+ else
+ {
+ bool isdst = startoff != zp->z_stdoff;
+
+ type = addtype(startoff, startbuf, isdst,
+ startttisstd, startttisut);
+ if (defaulttype < 0 && !isdst)
+ defaulttype = type;
+ addtt(starttime, type);
+ }
+ }
+
+ /*
+ * Now we may get to set starttime for the next zone line.
+ */
+ if (useuntil)
+ {
+ startttisstd = zp->z_untilrule.r_todisstd;
+ startttisut = zp->z_untilrule.r_todisut;
+ starttime = zp->z_untiltime;
+ if (!startttisstd)
+ starttime = tadd(starttime, -save);
+ if (!startttisut)
+ starttime = tadd(starttime, -stdoff);
+ }
+ }
+ if (defaulttype < 0)
+ defaulttype = 0;
+ if (0 <= lastatmax)
+ attypes[lastatmax].dontmerge = true;
+ if (do_extend)
+ {
+ /*
+ * If we're extending the explicitly listed observations for 400 years
+ * because we can't fill the POSIX-TZ field, check whether we actually
+ * ended up explicitly listing observations through that period. If
+ * there aren't any near the end of the 400-year period, add a
+ * redundant one at the end of the final year, to make it clear that
+ * we are claiming to have definite knowledge of the lack of
+ * transitions up to that point.
+ */
+ struct rule xr;
+ struct attype *lastat;
+
+ xr.r_month = TM_JANUARY;
+ xr.r_dycode = DC_DOM;
+ xr.r_dayofmonth = 1;
+ xr.r_tod = 0;
+ for (lastat = attypes, i = 1; i < timecnt; i++)
+ if (attypes[i].at > lastat->at)
+ lastat = &attypes[i];
+ if (!lastat || lastat->at < rpytime(&xr, max_year - 1))
+ {
+ addtt(rpytime(&xr, max_year + 1),
+ lastat ? lastat->type : defaulttype);
+ attypes[timecnt - 1].dontmerge = true;
+ }
+ }
+ writezone(zpfirst->z_name, envvar, version, defaulttype);
+ free(startbuf);
+ free(ab);
+ free(envvar);
+}
+
+static void
+addtt(zic_t starttime, int type)
+{
+ attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
+ attypes[timecnt].at = starttime;
+ attypes[timecnt].dontmerge = false;
+ attypes[timecnt].type = type;
+ ++timecnt;
+}
+
+static int
+addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
+{
+ int i,
+ j;
+
+ if (!(-1L - 2147483647L <= utoff && utoff <= 2147483647L))
+ {
+ error(_("UT offset out of range"));
+ exit(EXIT_FAILURE);
+ }
+ if (!want_bloat())
+ ttisstd = ttisut = false;
+
+ for (j = 0; j < charcnt; ++j)
+ if (strcmp(&chars[j], abbr) == 0)
+ break;
+ if (j == charcnt)
+ newabbr(abbr);
+ else
+ {
+ /* If there's already an entry, return its index. */
+ for (i = 0; i < typecnt; i++)
+ if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
+ && ttisstd == ttisstds[i] && ttisut == ttisuts[i])
+ return i;
+ }
+
+ /*
+ * There isn't one; add a new one, unless there are already too many.
+ */
+ if (typecnt >= TZ_MAX_TYPES)
+ {
+ error(_("too many local time types"));
+ exit(EXIT_FAILURE);
+ }
+ i = typecnt++;
+ utoffs[i] = utoff;
+ isdsts[i] = isdst;
+ ttisstds[i] = ttisstd;
+ ttisuts[i] = ttisut;
+ desigidx[i] = j;
+ return i;
+}
+
+static void
+leapadd(zic_t t, int correction, int rolling)
+{
+ int i;
+
+ if (TZ_MAX_LEAPS <= leapcnt)
+ {
+ error(_("too many leap seconds"));
+ exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < leapcnt; ++i)
+ if (t <= trans[i])
+ break;
+ memmove(&trans[i + 1], &trans[i], (leapcnt - i) * sizeof *trans);
+ memmove(&corr[i + 1], &corr[i], (leapcnt - i) * sizeof *corr);
+ memmove(&roll[i + 1], &roll[i], (leapcnt - i) * sizeof *roll);
+ trans[i] = t;
+ corr[i] = correction;
+ roll[i] = rolling;
+ ++leapcnt;
+}
+
+static void
+adjleap(void)
+{
+ int i;
+ zic_t last = 0;
+ zic_t prevtrans = 0;
+
+ /*
+ * propagate leap seconds forward
+ */
+ for (i = 0; i < leapcnt; ++i)
+ {
+ if (trans[i] - prevtrans < 28 * SECSPERDAY)
+ {
+ error(_("Leap seconds too close together"));
+ exit(EXIT_FAILURE);
+ }
+ prevtrans = trans[i];
+ trans[i] = tadd(trans[i], last);
+ last = corr[i] += last;
+ }
+
+ if (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;
+ }
+}
+
+/* Is A a space character in the C locale? */
+static bool
+is_space(char a)
+{
+ switch (a)
+ {
+ default:
+ return false;
+ case ' ':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ return true;
+ }
+}
+
+/* Is A an alphabetic character in the C locale? */
+static bool
+is_alpha(char a)
+{
+ switch (a)
+ {
+ default:
+ return false;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ return true;
+ }
+}
+
+/* If A is an uppercase character in the C locale, return its lowercase
+ counterpart. Otherwise, return A. */
+static char
+lowerit(char a)
+{
+ switch (a)
+ {
+ default:
+ return a;
+ case 'A':
+ return 'a';
+ case 'B':
+ return 'b';
+ case 'C':
+ return 'c';
+ case 'D':
+ return 'd';
+ case 'E':
+ return 'e';
+ case 'F':
+ return 'f';
+ case 'G':
+ return 'g';
+ case 'H':
+ return 'h';
+ case 'I':
+ return 'i';
+ case 'J':
+ return 'j';
+ case 'K':
+ return 'k';
+ case 'L':
+ return 'l';
+ case 'M':
+ return 'm';
+ case 'N':
+ return 'n';
+ case 'O':
+ return 'o';
+ case 'P':
+ return 'p';
+ case 'Q':
+ return 'q';
+ case 'R':
+ return 'r';
+ case 'S':
+ return 's';
+ case 'T':
+ return 't';
+ case 'U':
+ return 'u';
+ case 'V':
+ return 'v';
+ case 'W':
+ return 'w';
+ case 'X':
+ return 'x';
+ case 'Y':
+ return 'y';
+ case 'Z':
+ return 'z';
+ }
+}
+
+/* case-insensitive equality */
+static bool
+ciequal(const char *ap, const char *bp)
+{
+ while (lowerit(*ap) == lowerit(*bp++))
+ if (*ap++ == '\0')
+ return true;
+ return false;
+}
+
+static bool
+itsabbr(const char *abbr, const char *word)
+{
+ if (lowerit(*abbr) != lowerit(*word))
+ return false;
+ ++word;
+ while (*++abbr != '\0')
+ do
+ {
+ if (*word == '\0')
+ return false;
+ } while (lowerit(*word++) != lowerit(*abbr));
+ return true;
+}
+
+/* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */
+
+static bool
+ciprefix(char const *abbr, char const *word)
+{
+ do
+ if (!*abbr)
+ return true;
+ while (lowerit(*abbr++) == lowerit(*word++));
+
+ return false;
+}
+
+static const struct lookup *
+byword(const char *word, const struct lookup *table)
+{
+ const struct lookup *foundlp;
+ const struct lookup *lp;
+
+ if (word == NULL || table == NULL)
+ return NULL;
+
+ /*
+ * If TABLE is LASTS and the word starts with "last" followed by a
+ * non-'-', skip the "last" and look in WDAY_NAMES instead. Warn about any
+ * usage of the undocumented prefix "last-".
+ */
+ if (table == lasts && ciprefix("last", word) && word[4])
+ {
+ if (word[4] == '-')
+ warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
+ word, word + 5);
+ else
+ {
+ word += 4;
+ table = wday_names;
+ }
+ }
+
+ /*
+ * Look for exact match.
+ */
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciequal(word, lp->l_word))
+ return lp;
+
+ /*
+ * Look for inexact match.
+ */
+ foundlp = NULL;
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciprefix(word, lp->l_word))
+ {
+ if (foundlp == NULL)
+ foundlp = lp;
+ else
+ return NULL; /* multiple inexact matches */
+ }
+
+ if (foundlp && noise)
+ {
+ /* Warn about any backward-compatibility issue with pre-2017c zic. */
+ bool pre_2017c_match = false;
+
+ for (lp = table; lp->l_word; lp++)
+ if (itsabbr(word, lp->l_word))
+ {
+ if (pre_2017c_match)
+ {
+ warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
+ break;
+ }
+ pre_2017c_match = true;
+ }
+ }
+
+ return foundlp;
+}
+
+static char **
+getfields(char *cp)
+{
+ char *dp;
+ char **array;
+ int nsubs;
+
+ if (cp == NULL)
+ return NULL;
+ array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
+ nsubs = 0;
+ for (;;)
+ {
+ while (is_space(*cp))
+ ++cp;
+ if (*cp == '\0' || *cp == '#')
+ break;
+ array[nsubs++] = dp = cp;
+ do
+ {
+ if ((*dp = *cp++) != '"')
+ ++dp;
+ else
+ while ((*dp = *cp++) != '"')
+ if (*dp != '\0')
+ ++dp;
+ else
+ {
+ error(_("Odd number of quotation marks"));
+ exit(EXIT_FAILURE);
+ }
+ } while (*cp && *cp != '#' && !is_space(*cp));
+ if (is_space(*cp))
+ ++cp;
+ *dp = '\0';
+ }
+ array[nsubs] = NULL;
+ return array;
+}
+
+static void
+time_overflow(void)
+{
+ error(_("time overflow"));
+ exit(EXIT_FAILURE);
+}
+
+static zic_t
+oadd(zic_t t1, zic_t t2)
+{
+ if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2)
+ time_overflow();
+ return t1 + t2;
+}
+
+static zic_t
+tadd(zic_t t1, zic_t t2)
+{
+ if (t1 < 0)
+ {
+ if (t2 < min_time - t1)
+ {
+ if (t1 != min_time)
+ time_overflow();
+ return min_time;
+ }
+ }
+ else
+ {
+ if (max_time - t1 < t2)
+ {
+ if (t1 != max_time)
+ time_overflow();
+ return max_time;
+ }
+ }
+ return t1 + t2;
+}
+
+/*
+ * Given a rule, and a year, compute the date (in seconds since January 1,
+ * 1970, 00:00 LOCAL time) in that year that the rule refers to.
+ */
+
+static zic_t
+rpytime(const struct rule *rp, zic_t wantedy)
+{
+ int m,
+ i;
+ zic_t dayoff; /* with a nod to Margaret O. */
+ zic_t t,
+ y;
+
+ 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;
+ }
+ while (wantedy != y)
+ {
+ if (wantedy > y)
+ {
+ i = len_years[isleap(y)];
+ ++y;
+ }
+ else
+ {
+ --y;
+ i = -len_years[isleap(y)];
+ }
+ dayoff = oadd(dayoff, i);
+ }
+ while (m != rp->r_month)
+ {
+ i = len_months[isleap(y)][m];
+ dayoff = oadd(dayoff, i);
+ ++m;
+ }
+ i = rp->r_dayofmonth;
+ if (m == TM_FEBRUARY && i == 29 && !isleap(y))
+ {
+ if (rp->r_dycode == DC_DOWLEQ)
+ --i;
+ else
+ {
+ error(_("use of 2/29 in non leap-year"));
+ exit(EXIT_FAILURE);
+ }
+ }
+ --i;
+ dayoff = oadd(dayoff, i);
+ if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ)
+ {
+ 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;
+ }
+ while (wday != rp->r_wday)
+ if (rp->r_dycode == DC_DOWGEQ)
+ {
+ dayoff = oadd(dayoff, 1);
+ if (++wday >= LDAYSPERWEEK)
+ wday = 0;
+ ++i;
+ }
+ else
+ {
+ dayoff = oadd(dayoff, -1);
+ if (--wday < 0)
+ wday = LDAYSPERWEEK - 1;
+ --i;
+ }
+ if (i < 0 || i >= len_months[isleap(y)][m])
+ {
+ if (noise)
+ warning(_("rule goes past start/end of month; \
+will not work with pre-2004 versions of zic"));
+ }
+ }
+ if (dayoff < min_time / SECSPERDAY)
+ return min_time;
+ if (dayoff > max_time / SECSPERDAY)
+ return max_time;
+ t = (zic_t) dayoff * SECSPERDAY;
+ return tadd(t, rp->r_tod);
+}
+
+static void
+newabbr(const char *string)
+{
+ int i;
+
+ if (strcmp(string, GRANDPARENTED) != 0)
+ {
+ const char *cp;
+ const char *mp;
+
+ cp = string;
+ mp = NULL;
+ while (is_alpha(*cp) || ('0' <= *cp && *cp <= '9')
+ || *cp == '-' || *cp == '+')
+ ++cp;
+ if (noise && cp - string < 3)
+ mp = _("time zone abbreviation has fewer than 3 characters");
+ if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
+ mp = _("time zone abbreviation has too many characters");
+ if (*cp != '\0')
+ mp = _("time zone abbreviation differs from POSIX standard");
+ if (mp != NULL)
+ warning("%s (%s)", mp, string);
+ }
+ i = strlen(string) + 1;
+ if (charcnt + i > TZ_MAX_CHARS)
+ {
+ error(_("too many, or too long, time zone abbreviations"));
+ exit(EXIT_FAILURE);
+ }
+ strcpy(&chars[charcnt], string);
+ charcnt += i;
+}
+
+/* Ensure that the directories of ARGNAME exist, by making any missing
+ ones. If ANCESTORS, do this only for ARGNAME's ancestors; otherwise,
+ do it for ARGNAME too. Exit with failure if there is trouble.
+ Do not consider an existing non-directory to be trouble. */
+static void
+mkdirs(char const *argname, bool ancestors)
+{
+ char *name;
+ char *cp;
+
+ cp = name = ecpyalloc(argname);
+
+ /*
+ * On MS-Windows systems, do not worry about drive letters or backslashes,
+ * as this should suffice in practice. Time zone names do not use drive
+ * letters and backslashes. If the -d option of zic does not name an
+ * already-existing directory, it can use slashes to separate the
+ * already-existing ancestor prefix from the to-be-created subdirectories.
+ */
+
+ /* Do not mkdir a root directory, as it must exist. */
+ while (*cp == '/')
+ cp++;
+
+ while (cp && ((cp = strchr(cp, '/')) || !ancestors))
+ {
+ if (cp)
+ *cp = '\0';
+
+ /*
+ * Try to create it. It's OK if creation fails because the directory
+ * already exists, perhaps because some other process just created it.
+ * For simplicity do not check first whether it already exists, as
+ * that is checked anyway if the mkdir fails.
+ */
+ if (mkdir(name, MKDIR_UMASK) != 0)
+ {
+ /*
+ * For speed, skip itsdir if errno == EEXIST. Since mkdirs is
+ * called only after open fails with ENOENT on a subfile, EEXIST
+ * implies itsdir here.
+ */
+ int err = errno;
+
+ if (err != EEXIST && !itsdir(name))
+ {
+ error(_("%s: Cannot create directory %s: %s"),
+ progname, name, strerror(err));
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (cp)
+ *cp++ = '/';
+ }
+ free(name);
+}
+
+
+#ifdef WIN32
+/*
+ * To run on win32
+ */
+int
+link(const char *oldpath, const char *newpath)
+{
+ if (!CopyFile(oldpath, newpath, false))
+ {
+ _dosmaperr(GetLastError());
+ return -1;
+ }
+ return 0;
+}
+#endif