diff --git a/.gitignore b/.gitignore index cf3b825..d50d176 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ ChangeLog check_* date leapseconds +tzdir.h tzselect version version.h diff --git a/Makefile b/Makefile index 6edc73c..4e45f93 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,25 @@ # Make and install tzdb code and data. - # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. +# Request POSIX conformance; this must be the first non-comment line. +.POSIX: +# On older platforms you may need to scrounge for a POSIX-conforming 'make'. +# For example, on Solaris 10 (2005), use /usr/sfw/bin/gmake or +# /usr/xpg4/bin/make, not /usr/ccs/bin/make. + +# To affect how this Makefile works, you can run a shell script like this: +# +# #!/bin/sh +# make CC='gcc -std=gnu11' "$@" +# +# This example script is appropriate for a pre-2017 GNU/Linux system +# where a non-default setting is needed to support this package's use of C99. +# +# Alternatively, you can simply edit this Makefile to tailor the following +# macro definitions. + +############################################################################### +# Start of macros that one plausibly might want to tailor. # Package name for the code distribution. PACKAGE= tzcode @@ -191,8 +209,9 @@ UTF8_LOCALE= en_US.utf8 # On some hosts, this should have -lintl unless CFLAGS has -DHAVE_GETTEXT=0. LDLIBS= -# Add the following to the end of the "CFLAGS=" line as needed to override -# defaults specified in the source code. "-DFOO" is equivalent to "-DFOO=1". +# Add the following to an uncommented "CFLAGS=" line as needed +# to override defaults specified in the source code or by the system. +# "-DFOO" is equivalent to "-DFOO=1". # -DDEPRECATE_TWO_DIGIT_YEARS for optional runtime warnings about strftime # formats that generate only the last two digits of year numbers # -DEPOCH_LOCAL if the 'time' function returns local time not UT @@ -234,11 +253,16 @@ LDLIBS= # -DHAVE_UNISTD_H=0 if does not work* # -DHAVE_UTMPX_H=0 if does not work* # -Dlocale_t=XXX if your system uses XXX instead of locale_t -# -DPORT_TO_C89 if tzcode should also run on C89 platforms+ +# -DPORT_TO_C89 if tzcode should also run on mostly-C89 platforms+ +# Typically it is better to use a later standard. For example, +# with GCC 4.9.4 (2016), prefer '-std=gnu11' to '-DPORT_TO_C89'. +# Even with -DPORT_TO_C89, the code needs at least one C99 +# feature (integers at least 64 bits wide) and maybe more. # -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers # with external linkage, e.g., applications cannot define 'localtime'. # -Dssize_t=long on hosts like MS-Windows that lack ssize_t # -DSUPPORT_C89 if the tzcode library should support C89 callers+ +# However, this might trigger latent bugs in C99-or-later callers. # -DSUPPRESS_TZDIR to not prepend TZDIR to file names; this has # security implications and is not recommended for general use # -DTHREAD_SAFE to make localtime.c thread-safe, as POSIX requires; @@ -270,11 +294,15 @@ LDLIBS= # -DZIC_MAX_ABBR_LEN_WO_WARN=3 # (or some other number) to set the maximum time zone abbreviation length # that zic will accept without a warning (the default is 6) +# -g to generate symbolic debugging info +# -Idir to include from directory 'dir' +# -O0 to disable optimization; other -O options to enable more optimization +# -Uname to remove any definition of the macro 'name' # $(GCC_DEBUG_FLAGS) if you are using recent GCC and want lots of checking # # * Options marked "*" can be omitted if your compiler is C23 compatible. # * Options marked "+" are obsolescent and are planned to be removed -# once the code assumes C99 or later. +# once the code assumes C99 or later, say in the year 2029. # # Select instrumentation via "make GCC_INSTRUMENT='whatever'". GCC_INSTRUMENT = \ @@ -353,9 +381,11 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ # functions to be added to the time conversion library. # "offtime" is like "gmtime" except that it accepts a second (long) argument # that gives an offset to add to the time_t when converting it. -# "timelocal" is equivalent to "mktime". +# I.e., "offtime" is like calling "localtime_rz" with a fixed-offset zone. +# "timelocal" is nearly equivalent to "mktime". # "timeoff" is like "timegm" except that it accepts a second (long) argument # that gives an offset to use when converting to a time_t. +# I.e., "timeoff" is like calling "mktime_z" with a fixed-offset zone. # "posix2time" and "time2posix" are described in an included manual page. # X3J11's work does not describe any of these functions. # These functions may well disappear in future releases of the time @@ -378,7 +408,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ # # NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put # out by the National Institute of Standards and Technology -# which claims to test C and Posix conformance. If you want to pass PCTS, add +# which claims to test C and POSIX conformance. If you want to pass PCTS, add # -DPCTS # to the end of the "CFLAGS=" line. # @@ -388,13 +418,21 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ # 53 as a week number (rather than 52 or 53) for January days before # January's first Monday when a "%V" format is used and January 1 # falls on a Friday, Saturday, or Sunday. +# +# POSIX says CFLAGS defaults to "-O 1". +# Uncomment the following line and edit its contents as needed. -CFLAGS= +#CFLAGS= -O 1 -# Linker flags. Default to $(LFLAGS) for backwards compatibility -# to release 2012h and earlier. -LDFLAGS= $(LFLAGS) +# The name of a POSIX-like library archiver, its flags, C compiler, +# linker flags, and 'make' utility. Ordinarily the defaults suffice. +# The commented-out values are the defaults specified by POSIX 202x/D3. +#AR = ar +#ARFLAGS = -rv +#CC = c17 +#LDFLAGS = +#MAKE = make # For leap seconds, this Makefile uses LEAPSECONDS='-L leapseconds' in # submake command lines. The default is no leap seconds. @@ -418,18 +456,18 @@ ZFLAGS= ZIC_INSTALL= $(ZIC) -d '$(DESTDIR)$(TZDIR)' $(LEAPSECONDS) -# The name of a Posix-compliant 'awk' on your system. +# The name of a POSIX-compliant 'awk' on your system. # mawk 1.3.3 and Solaris 10 /usr/bin/awk do not work. # Also, it is better (though not essential) if 'awk' supports UTF-8, # and unfortunately mawk and busybox awk do not support UTF-8. # Try AWK=gawk or AWK=nawk if your awk has the abovementioned problems. AWK= awk -# The full path name of a Posix-compliant shell, preferably one that supports +# The full path name of a POSIX-compliant shell, preferably one that supports # the Korn shell's 'select' statement as an extension. # These days, Bash is the most popular. # It should be OK to set this to /bin/sh, on platforms where /bin/sh -# lacks 'select' or doesn't completely conform to Posix, but /bin/bash +# lacks 'select' or doesn't completely conform to POSIX, but /bin/bash # is typically nicer if it works. KSHELL= /bin/bash @@ -503,17 +541,16 @@ GZIPFLAGS= -9n DIFF_TZS= diff -u$$(! diff -u -F'^TZ=' - - <>/dev/null >&0 2>&1 \ || echo ' -F^TZ=') -############################################################################### - -#MAKE= make +# ':' on typical hosts; 'ranlib' on the ancient hosts that still need ranlib. +RANLIB= : -cc= cc -CC= $(cc) -DTZDIR='"$(TZDIR)"' +# POSIX prohibits defining or using SHELL. However, csh users on systems +# that use the user shell for Makefile commands may need to define SHELL. +#SHELL= /bin/sh -AR= ar +# End of macros that one plausibly might want to tailor. +############################################################################### -# ':' on typical hosts; 'ranlib' on the ancient hosts that still need ranlib. -RANLIB= : TZCOBJS= zic.o TZDOBJS= zdump.o localtime.o asctime.o strftime.o @@ -543,7 +580,7 @@ YDATA= $(PRIMARY_YDATA) etcetera NDATA= factory TDATA_TO_CHECK= $(YDATA) $(NDATA) backward TDATA= $(YDATA) $(NDATA) $(BACKWARD) -ZONETABLES= zone1970.tab zone.tab +ZONETABLES= zone.tab zone1970.tab zonenow.tab TABDATA= iso3166.tab $(TZDATA_TEXT) $(ZONETABLES) LEAP_DEPS= leapseconds.awk leap-seconds.list TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \ @@ -551,7 +588,7 @@ TZDATA_ZI_DEPS= ziguard.awk zishrink.awk version $(TDATA) \ DSTDATA_ZI_DEPS= ziguard.awk $(TDATA) $(PACKRATDATA) $(PACKRATLIST) DATA= $(TDATA_TO_CHECK) backzone iso3166.tab leap-seconds.list \ leapseconds $(ZONETABLES) -AWK_SCRIPTS= checklinks.awk checktab.awk leapseconds.awk \ +AWK_SCRIPTS= checklinks.awk checknow.awk checktab.awk leapseconds.awk \ ziguard.awk zishrink.awk MISC= $(AWK_SCRIPTS) TZS_YEAR= 2050 @@ -572,7 +609,7 @@ VERSION_DEPS= \ calendars CONTRIBUTING LICENSE Makefile NEWS README SECURITY \ africa antarctica asctime.c asia australasia \ backward backzone \ - checklinks.awk checktab.awk \ + checklinks.awk checknow.awk checktab.awk \ date.1 date.c difftime.c \ etcetera europe factory iso3166.tab \ leap-seconds.list leapseconds.awk localtime.c \ @@ -582,12 +619,7 @@ VERSION_DEPS= \ tzfile.5 tzfile.h tzselect.8 tzselect.ksh \ workman.sh zdump.8 zdump.c zic.8 zic.c \ ziguard.awk zishrink.awk \ - zone.tab zone1970.tab - -# And for the benefit of csh users on systems that assume the user -# shell should be used to handle commands in Makefiles. . . - -SHELL= /bin/sh + zone.tab zone1970.tab zonenow.tab all: tzselect zic zdump libtz.a $(TABDATA) \ vanguard.zi main.zi rearguard.zi @@ -657,6 +689,16 @@ tzdata.zi: $(DATAFORM).zi version zishrink.awk $(DATAFORM).zi >$@.out mv $@.out $@ +tzdir.h: + printf '%s\n' >$@.out \ + '#ifndef TZDEFAULT' \ + '# define TZDEFAULT "$(TZDEFAULT)" /* default zone */' \ + '#endif' \ + '#ifndef TZDIR' \ + '# define TZDIR "$(TZDIR)" /* TZif directory */' \ + '#endif' + mv $@.out $@ + version.h: version VERSION=`cat version` && printf '%s\n' \ 'static char const PKGVERSION[]="($(PACKAGE)) ";' \ @@ -763,7 +805,7 @@ force_tzs: $(TZS_NEW) libtz.a: $(LIBOBJS) rm -f $@ - $(AR) -rc $@ $(LIBOBJS) + $(AR) $(ARFLAGS) $@ $(LIBOBJS) $(RANLIB) $@ date: $(DATEOBJS) @@ -771,26 +813,32 @@ date: $(DATEOBJS) tzselect: tzselect.ksh version VERSION=`cat version` && sed \ - -e 's|#!/bin/bash|#!$(KSHELL)|g' \ - -e 's|AWK=[^}]*|AWK='\''$(AWK)'\''|g' \ - -e 's|\(PKGVERSION\)=.*|\1='\''($(PACKAGE)) '\''|' \ - -e 's|\(REPORT_BUGS_TO\)=.*|\1=$(BUGEMAIL)|' \ - -e 's|TZDIR=[^}]*|TZDIR=$(TZDIR)|' \ - -e 's|\(TZVERSION\)=.*|\1='"$$VERSION"'|' \ - <$@.ksh >$@.out + -e "s'#!/bin/bash'#!"'$(KSHELL)'\' \ + -e s\''\(AWK\)=[^}]*'\''\1=\'\''$(AWK)\'\'\' \ + -e s\''\(PKGVERSION\)=.*'\''\1=\'\''($(PACKAGE)) \'\'\' \ + -e s\''\(REPORT_BUGS_TO\)=.*'\''\1=\'\''$(BUGEMAIL)\'\'\' \ + -e s\''\(TZDIR\)=[^}]*'\''\1=\'\''$(TZDIR)\'\'\' \ + -e s\''\(TZVERSION\)=.*'\''\1=\'"'$$VERSION\\''" \ + <$@.ksh >$@.out chmod +x $@.out mv $@.out $@ check: check_back check_mild check_mild: check_character_set check_white_space check_links \ - check_name_lengths check_slashed_abbrs check_sorted \ + check_name_lengths check_now \ + check_slashed_abbrs check_sorted \ check_tables check_web check_ziguard check_zishrink check_tzs +# True if UTF8_LOCALE does not work; +# otherwise, false but with LC_ALL set to $(UTF8_LOCALE). +UTF8_LOCALE_MISSING = \ + { test ! '$(UTF8_LOCALE)' \ + || ! printf 'A\304\200B\n' \ + | LC_ALL='$(UTF8_LOCALE)' grep -q '^A.B$$' >/dev/null 2>&1 \ + || { LC_ALL='$(UTF8_LOCALE)'; export LC_ALL; false; }; } + check_character_set: $(ENCHILADA) - test ! '$(UTF8_LOCALE)' || \ - ! printf 'A\304\200B\n' | \ - LC_ALL='$(UTF8_LOCALE)' grep -q '^A.B$$' >/dev/null 2>&1 || { \ - LC_ALL='$(UTF8_LOCALE)' && export LC_ALL && \ + $(UTF8_LOCALE_MISSING) || { \ sharp='#' && \ ! grep -Env $(SAFE_LINE) $(MANS) date.1 $(MANTXTS) \ $(MISC) $(SOURCES) $(WEB_PAGES) \ @@ -805,12 +853,12 @@ check_character_set: $(ENCHILADA) touch $@ check_white_space: $(ENCHILADA) + $(UTF8_LOCALE_MISSING) || { \ patfmt=' \t|[\f\r\v]' && pat=`printf "$$patfmt\\n"` && \ - ! grep -En "$$pat" \ - $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list) - ! grep -n '[$s]$$' \ - $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list) - touch $@ + ! grep -En "$$pat|[$s]\$$" \ + $$(ls $(ENCHILADA) | grep -Fvx leap-seconds.list); \ + } + touch $@ PRECEDES_FILE_NAME = ^(Zone|Link[$s]+[^$s]+)[$s]+ FILE_NAME_COMPONENT_TOO_LONG = $(PRECEDES_FILE_NAME)[^$s]*[^/$s]{15} @@ -851,7 +899,29 @@ check_links: checklinks.awk tzdata.zi -f checklinks.awk tzdata.zi touch $@ -check_tables: checktab.awk $(YDATA) backward $(ZONETABLES) +# Check timestamps from now through 28 years from now, to make sure +# that zonenow.tab contains all sequences of planned timestamps, +# without any duplicate sequences. In theory this might require +# 2800 years but that would take a long time to check. +CHECK_NOW_TIMESTAMP = `./date +%s` +CHECK_NOW_FUTURE_YEARS = 28 +CHECK_NOW_FUTURE_SECS = $(CHECK_NOW_FUTURE_YEARS) '*' 366 '*' 24 '*' 60 '*' 60 +check_now: checknow.awk date tzdata.zi zdump zic zone1970.tab zonenow.tab + rm -fr $@.dir + mkdir $@.dir + ./zic -d $@.dir tzdata.zi + now=$(CHECK_NOW_TIMESTAMP) && \ + future=`expr $(CHECK_NOW_FUTURE_SECS) + $$now` && \ + ./zdump -i -t $$now,$$future \ + $$(find $$PWD/$@.dir/????*/ -type f) \ + >$@.dir/zdump.tab + $(AWK) \ + -v zdump_table=$@.dir/zdump.tab \ + -f checknow.awk zonenow.tab + rm -fr $@.dir + touch $@ + +check_tables: checktab.awk $(YDATA) backward zone.tab zone1970.tab for tab in $(ZONETABLES); do \ test "$$tab" = zone.tab && links='$(BACKWARD)' || links=''; \ $(AWK) -f checktab.awk -v zone_table=$$tab $(YDATA) $$links \ @@ -911,10 +981,10 @@ check_zishrink_posix check_zishrink_right: \ touch $@ clean_misc: - rm -fr check_*.dir + rm -fr check_*.dir typecheck_*.dir rm -f *.o *.out $(TIME_T_ALTERNATIVES) \ check_* core typecheck_* \ - date tzselect version.h zdump zic libtz.a + date tzdir.h tzselect version.h zdump zic libtz.a clean: clean_misc rm -fr *.dir tzdb-*/ rm -f *.zi $(TZS_NEW) @@ -952,12 +1022,18 @@ $(MANTXTS): workman.sh # plus N if GNU ls and touch are available. SET_TIMESTAMP_N = sh -c '\ n=$$0 dest=$$1; shift; \ - touch -cmr `ls -t "$$@" | sed 1q` "$$dest" && \ + <"$$dest" && \ if test $$n != 0 && \ - lsout=`ls -n --time-style="+%s" "$$dest" 2>/dev/null`; then \ + lsout=`ls -nt --time-style="+%s" "$$@" 2>/dev/null`; then \ set x $$lsout && \ - touch -cmd @`expr $$7 + $$n` "$$dest"; \ - else :; fi' + timestamp=`expr $$7 + $$n` && \ + echo "+ touch -md @$$timestamp $$dest" && \ + touch -md @$$timestamp "$$dest"; \ + else \ + newest=`ls -t "$$@" | sed 1q` && \ + echo "+ touch -mr $$newest $$dest" && \ + touch -mr "$$newest" "$$dest"; \ + fi' # If DEST depends on A B C ... in this Makefile, callers should use # $(SET_TIMESTAMP_DEP) DEST A B C ..., for the benefit of any # downstream 'make' that considers equal timestamps to be out of date. @@ -982,8 +1058,12 @@ set-timestamps.out: $(EIGHT_YARDS) rm -f test.out && \ for file in $$files; do \ if git diff --quiet $$file; then \ - time=`git log -1 --format='tformat:%ct' $$file` && \ - touch -cmd @$$time $$file; \ + time=`TZ=UTC0 git log -1 \ + --format='tformat:%cd' \ + --date='format:%Y-%m-%dT%H:%M:%SZ' \ + $$file` && \ + echo "+ touch -md $$time $$file" && \ + touch -md $$time $$file; \ else \ echo >&2 "$$file: warning: does not match repository"; \ fi || exit; \ @@ -1008,7 +1088,8 @@ check_public: $(VERSION_DEPS) rm -fr public.dir mkdir public.dir ln $(VERSION_DEPS) public.dir - cd public.dir && $(MAKE) CFLAGS='$(GCC_DEBUG_FLAGS)' ALL + cd public.dir \ + && $(MAKE) CFLAGS='$(GCC_DEBUG_FLAGS)' TZDIR='$(TZDIR)' ALL for i in $(TDATA_TO_CHECK) public.dir/tzdata.zi \ public.dir/vanguard.zi public.dir/main.zi \ public.dir/rearguard.zi; \ @@ -1139,7 +1220,7 @@ tzdata$(VERSION)-rearguard.tar.gz: rearguard.zi set-timestamps.out sed '1s/$$/-rearguard/' $@.dir/version : The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier. $(CREATE_EMPTY) $@.dir/pacificnew - touch -cmr version $@.dir/version + touch -mr version $@.dir/version LC_ALL=C && export LC_ALL && \ (cd $@.dir && \ tar $(TARFLAGS) -cf - \ @@ -1163,7 +1244,7 @@ tzdata$(VERSION)-tailored.tar.gz: set-timestamps.out `test $(DATAFORM) = vanguard || echo pacificnew` (grep '^#' tzdata.zi && echo && cat $(DATAFORM).zi) \ >$@.dir/etcetera - touch -cmr tzdata.zi $@.dir/etcetera + touch -mr tzdata.zi $@.dir/etcetera sed -n \ -e '/^# *version *\(.*\)/h' \ -e '/^# *ddeps */H' \ @@ -1174,7 +1255,7 @@ tzdata$(VERSION)-tailored.tar.gz: set-timestamps.out -e 's/ /-/g' \ -e 'p' \ $@.dir/version - touch -cmr version $@.dir/version + touch -mr version $@.dir/version links= && \ for file in $(TZDATA_DIST); do \ test -f $@.dir/$$file || links="$$links $$file"; \ @@ -1226,10 +1307,10 @@ zonenames: tzdata.zi asctime.o: private.h tzfile.h date.o: private.h difftime.o: private.h -localtime.o: private.h tzfile.h +localtime.o: private.h tzfile.h tzdir.h strftime.o: private.h tzfile.h zdump.o: version.h -zic.o: private.h tzfile.h version.h +zic.o: private.h tzfile.h tzdir.h version.h .PHONY: ALL INSTALL all .PHONY: check check_mild check_time_t_alternatives diff --git a/NEWS b/NEWS index b54538a..031ba6a 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,109 @@ News for the tz database +Release 2023d - 2023-12-21 20:02:24 -0800 + + Briefly: + Ittoqqortoormiit, Greenland changes time zones on 2024-03-31. + Vostok, Antarctica changed time zones on 2023-12-18. + Casey, Antarctica changed time zones five times since 2020. + Code and data fixes for Palestine timestamps starting in 2072. + A new data file zonenow.tab for timestamps starting now. + + Changes to future timestamps + + Ittoqqortoormiit, Greenland (America/Scoresbysund) joins most of + the rest of Greenland's timekeeping practice on 2024-03-31, by + changing its time zone from -01/+00 to -02/-01 at the same moment + as the spring-forward transition. Its clocks will therefore not + spring forward as previously scheduled. The time zone change + reverts to its common practice before 1981. + + Fix predictions for DST transitions in Palestine in 2072-2075, + correcting a typo introduced in 2023a. + + Changes to past and future timestamps + + Vostok, Antarctica changed to +05 on 2023-12-18. It had been at + +07 (not +06) for years. (Thanks to Zakhary V. Akulov.) + + Change data for Casey, Antarctica to agree with timeanddate.com, + by adding five time zone changes since 2020. Casey is now at +08 + instead of +11. + + Changes to past tm_isdst flags + + Much of Greenland, represented by America/Nuuk, changed its + standard time from -03 to -02 on 2023-03-25, not on 2023-10-28. + This does not affect UTC offsets, only the tm_isdst flag. + (Thanks to Thomas M. Steenholdt.) + + New data file + + A new data file zonenow.tab helps configure applications that use + timestamps dated from now on. This simplifies configuration, + since users choose from a smaller Zone set. The file's format is + experimental and subject to change. + + Changes to code + + localtime.c no longer mishandles TZif files that contain a single + transition into a DST regime. Previously, it incorrectly assumed + DST was in effect before the transition too. (Thanks to Alois + Treindl for debugging help.) + + localtime.c's timeoff no longer collides with OpenBSD 7.4. + + The C code now uses _Generic only if __STDC_VERSION__ says the + compiler is C11 or later. + + tzselect now optionally reads zonenow.tab, to simplify when + configuring only for timestamps dated from now on. + + tzselect no longer creates temporary files. + + tzselect no longer mishandles the following: + + Spaces and most other special characters in BUGEMAIL, PACKAGE, + TZDIR, and VERSION. + + TZ strings when using mawk 1.4.3, which mishandles regular + expressions of the form /X{2,}/. + + ISO 6709 coordinates when using an awk that lacks the GNU + extension of newlines in -v option-arguments. + + Non UTF-8 locales when using an iconv command that lacks the GNU + //TRANSLIT extension. + + zic no longer mishandles data for Palestine after the year 2075. + Previously, it incorrectly omitted post-2075 transitions that are + predicted for just before and just after Ramadan. (Thanks to Ken + Murchison for debugging help.) + + zic now works again on Linux 2.6.16 and 2.6.17 (2006). + (Problem reported by Rune Torgersen.) + + Changes to build procedure + + The Makefile is now more compatible with POSIX: + * It no longer defines AR, CC, CFLAGS, LDFLAGS, and SHELL. + * It no longer uses its own 'cc' in place of CC. + * It now uses ARFLAGS, with default specified by POSIX. + * It does not use LFLAGS incompatibly with POSIX. + * It uses the special .POSIX target. + * It quotes special characters more carefully. + * It no longer mishandles builds in an ISO 8859 locale. + Due to the CC changes, TZDIR is now #defined in a file tzfile.h + built by 'make', not in a $(CC) -D option. Also, TZDEFAULT is + now treated like TZDIR as they have similar roles. + + Changes to commentary + + Limitations and hazards of the optional support for obsolescent + C89 platforms are documented better, along with a tentative + schedule for removing this support. + + Release 2023c - 2023-03-28 12:42:14 -0700 Changes to past and future timestamps @@ -76,11 +180,14 @@ Release 2023a - 2023-03-22 12:39:33 -0700 platform dependent and abbreviations were silently truncated to 16 bytes even when the limit was greater than 16. - The code by default is now designed for C99 or later. To build in - a C89 environment, compile with -DPORT_TO_C89. To support C89 - callers of the tzcode library, compile with -DSUPPORT_C89. The - two new macros are transitional aids planned to be removed in a - future version, when C99 or later will be required. + The code by default is now designed for C99 or later. To build on + a mostly-C89 platform, compile with -DPORT_TO_C89; this should + work on C89 platforms that also support C99 'long long' and + perhaps a few other extensions to C89. To support C89 callers of + tzcode's library, compile with -DSUPPORT_C89; however, this could + trigger latent bugs in C99-or-later callers. The two new macros + are transitional aids planned to be removed in a future version + (say, in 2029), when C99 or later will be required. The code now builds again on pre-C99 platforms, if you compile with -DPORT_TO_C89. This fixes a bug introduced in 2022f. @@ -723,6 +830,8 @@ Release 2021b - 2021-09-24 16:23:00 -0700 them, set the EXPIRES_LINE Makefile variable. If a TZif file uses this new feature it is marked with a new TZif version number 4, a format intended to be documented in a successor to RFC 8536. + The old-format "#expires" comments are now treated solely as + comments and have no effect on the TZif files. zic -L LEAPFILE -r @LO no longer generates an invalid TZif file that omits leap second information for the range LO..B when LO @@ -4302,7 +4411,7 @@ Release 2012j - 2012-11-12 18:34:49 -0800 now uses tz@iana.org rather than the old elsie address. zic -v now complains about abbreviations that are less than 3 - or more than 6 characters, as per Posix. Formerly, it checked + or more than 6 characters, as per POSIX. Formerly, it checked for abbreviations that were more than 3. 'make public' no longer puts its temporary directory under /tmp, @@ -4467,8 +4576,8 @@ Release data2011m - 2011-10-24 21:42:16 +0700 In particular, the typos in comments in the data (2011-11-17 should have been 2011-10-17 as Alan Barrett noted, and spelling of Tiraspol that Tim Parenti noted) have been fixed, and the change for Ukraine has been - made in all 4 Ukrainian zones, rather than just Kiev (again, thanks to - Tim Parenti, and also Denys Gavrysh) + made in all 4 Ukrainian zones, rather than just Europe/Kiev + (again, thanks to Tim Parenti, and also Denys Gavrysh). In addition, I added Europe/Tiraspol to zone.tab. diff --git a/README b/README index 145aacd..edabd2e 100644 --- a/README +++ b/README @@ -11,14 +11,17 @@ changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules. See or the -file tz-link.html for how to acquire the code and data. Once acquired, -read the comments in the file 'Makefile' and make any changes needed -to make things right for your system, especially if you are using some -platform other than GNU/Linux. Then run the following commands, -substituting your desired installation directory for "$HOME/tzdir": - - make TOPDIR=$HOME/tzdir install - $HOME/tzdir/usr/bin/zdump -v America/Los_Angeles +file tz-link.html for how to acquire the code and data. + +Once acquired, read the leading comments in the file "Makefile" +and make any changes needed to make things right for your system, +especially when using a platform other than current GNU/Linux. + +Then run the following commands, substituting your desired +installation directory for "$HOME/tzdir": + + make TOPDIR="$HOME/tzdir" install + "$HOME/tzdir/usr/bin/zdump" -v America/Los_Angeles See the file tz-how-to.html for examples of how to read the data files. diff --git a/africa b/africa index 6cf4e39..6fae18c 100644 --- a/africa +++ b/africa @@ -285,13 +285,6 @@ Rule Egypt 2007 only - Sep Thu>=1 24:00 0 - # reproduced by other (more accessible) sites[, e.g.,]... # http://elgornal.net/news/news.aspx?id=4699258 -# From Paul Eggert (2014-06-04): -# Sarah El Deeb and Lee Keath of AP report that the Egyptian government says -# the change is because of blackouts in Cairo, even though Ahram Online (cited -# above) says DST had no affect on electricity consumption. There is -# no information about when DST will end this fall. See: -# http://abcnews.go.com/International/wireStory/el-sissi-pushes-egyptians-line-23614833 - # From Steffen Thorsen (2015-04-08): # Egypt will start DST on midnight after Thursday, April 30, 2015. # This is based on a law (no 35) from May 15, 2014 saying it starts the last diff --git a/antarctica b/antarctica index fc603e9..763c272 100644 --- a/antarctica +++ b/antarctica @@ -80,6 +80,11 @@ # - 2018 Oct 7 4:00 - 2019 Mar 17 3:00 - 2019 Oct 4 3:00 - 2020 Mar 8 3:00 # and now - 2020 Oct 4 0:01 +# From Paul Eggert (2023-12-20): +# Transitions from 2021 on are taken from: +# https://www.timeanddate.com/time/zone/antarctica/casey +# retrieved at various dates. + # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone Antarctica/Casey 0 - -00 1969 8:00 - +08 2009 Oct 18 2:00 @@ -93,7 +98,12 @@ Zone Antarctica/Casey 0 - -00 1969 8:00 - +08 2019 Oct 4 3:00 11:00 - +11 2020 Mar 8 3:00 8:00 - +08 2020 Oct 4 0:01 - 11:00 - +11 + 11:00 - +11 2021 Mar 14 0:00 + 8:00 - +08 2021 Oct 3 0:01 + 11:00 - +11 2022 Mar 13 0:00 + 8:00 - +08 2022 Oct 2 0:01 + 11:00 - +11 2023 Mar 9 3:00 + 8:00 - +08 Zone Antarctica/Davis 0 - -00 1957 Jan 13 7:00 - +07 1964 Nov 0 - -00 1969 Feb @@ -240,7 +250,50 @@ Zone Antarctica/Troll 0 - -00 2005 Feb 12 # year-round from 1960/61 to 1992 # Vostok, since 1957-12-16, temporarily closed 1994-02/1994-11 -# See Asia/Urumqi. +# From Craig Mundell (1994-12-15): +# http://quest.arc.nasa.gov/antarctica/QA/computers/Directions,Time,ZIP +# Vostok, which is one of the Russian stations, is set on the same +# time as Moscow, Russia. +# +# From Lee Hotz (2001-03-08): +# I queried the folks at Columbia who spent the summer at Vostok and this is +# what they had to say about time there: +# "in the US Camp (East Camp) we have been on New Zealand (McMurdo) +# time, which is 12 hours ahead of GMT. The Russian Station Vostok was +# 6 hours behind that (although only 2 miles away, i.e. 6 hours ahead +# of GMT). This is a time zone I think two hours east of Moscow. The +# natural time zone is in between the two: 8 hours ahead of GMT." +# +# From Paul Eggert (2001-05-04): +# This seems to be hopelessly confusing, so I asked Lee Hotz about it +# in person. He said that some Antarctic locations set their local +# time so that noon is the warmest part of the day, and that this +# changes during the year and does not necessarily correspond to mean +# solar noon. So the Vostok time might have been whatever the clocks +# happened to be during their visit. So we still don't really know what time +# it is at Vostok. +# +# From Zakhary V. Akulov (2023-12-17 22:00:48 +0700): +# ... from December, 18, 2023 00:00 by my decision the local time of +# the Antarctic research base Vostok will correspond to UTC+5. +# (2023-12-19): We constantly interact with Progress base, with company who +# builds new wintering station, with sledge convoys, with aviation - they all +# use UTC+5. Besides, difference between Moscow time is just 2 hours now, not 4. +# (2023-12-19, in response to the question "Has local time at Vostok +# been UTC+6 ever since 1957, or has it changed before?"): No. At least +# since my antarctic career start, 10 years ago, Vostok base has UTC+7. +# (In response to a 2023-12-18 question "from 02:00 to 00:00 today"): This. +# +# From Paul Eggert (2023-12-18): +# For lack of better info, guess Vostok was at +07 from founding through today, +# except when closed. + +# Zone NAME STDOFF RULES FORMAT [UNTIL] +Zone Antarctica/Vostok 0 - -00 1957 Dec 16 + 7:00 - +07 1994 Feb + 0 - -00 1994 Nov + 7:00 - +07 2023 Dec 18 2:00 + 5:00 - +05 # S Africa - year-round bases # Marion Island, -4653+03752 diff --git a/asia b/asia index a29a4dc..04526c1 100644 --- a/asia +++ b/asia @@ -655,7 +655,6 @@ Zone Asia/Shanghai 8:05:43 - LMT 1901 8:00 PRC C%sT # Xinjiang time, used by many in western China; represented by Ürümqi / Ürümchi # / Wulumuqi. (Please use Asia/Shanghai if you prefer Beijing time.) -# Vostok base in Antarctica matches this since 1970. Zone Asia/Urumqi 5:50:20 - LMT 1928 6:00 - +06 @@ -3427,6 +3426,9 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # From Heba Hamad (2023-03-22): # ... summer time will begin in Palestine from Saturday 04-29-2023, # 02:00 AM by 60 minutes forward. +# From Heba Hemad (2023-10-09): +# ... winter time will begin in Palestine from Saturday 10-28-2023, +# 02:00 AM by 60 minutes back. # # From Paul Eggert (2023-03-22): # For now, guess that spring and fall transitions will normally @@ -3548,13 +3550,13 @@ Rule Palestine 2070 only - Oct 4 2:00 0 - Rule Palestine 2071 only - Sep 19 2:00 0 - Rule Palestine 2072 only - Sep 10 2:00 0 - Rule Palestine 2072 only - Oct 15 2:00 1:00 S +Rule Palestine 2072 max - Oct Sat<=30 2:00 0 - Rule Palestine 2073 only - Sep 2 2:00 0 - Rule Palestine 2073 only - Oct 7 2:00 1:00 S Rule Palestine 2074 only - Aug 18 2:00 0 - Rule Palestine 2074 only - Sep 29 2:00 1:00 S Rule Palestine 2075 only - Aug 10 2:00 0 - Rule Palestine 2075 only - Sep 14 2:00 1:00 S -Rule Palestine 2075 max - Oct Sat<=30 2:00 0 - Rule Palestine 2076 only - Jul 25 2:00 0 - Rule Palestine 2076 only - Sep 5 2:00 1:00 S Rule Palestine 2077 only - Jul 17 2:00 0 - diff --git a/australasia b/australasia index 0633a30..dc98c1e 100644 --- a/australasia +++ b/australasia @@ -391,8 +391,14 @@ Zone Antarctica/Macquarie 0 - -00 1899 Nov # Please note that there will not be any daylight savings time change # in Fiji for 2022-2023.... # https://www.facebook.com/FijianGovernment/posts/pfbid0mmWVTYmTibn66ybpFda75pDcf34SSpoSaskJW5gXwaKo5Sgc7273Q4fXWc6kQV6Hl + +# From Almaz Mingaleev (2023-10-06): +# Cabinet approved the suspension of Daylight Saving and appropriate +# legislative changes will be considered including the repeal of the +# Daylight Saving Act 1998 +# https://www.fiji.gov.fj/Media-Centre/Speeches/English/CABINET-DECISIONS-3-OCTOBER-2023 # -# From Paul Eggert (2022-10-27): +# From Paul Eggert (2023-10-06): # For now, assume DST is suspended indefinitely. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S diff --git a/backward b/backward index 421f2ec..65c711b 100644 --- a/backward +++ b/backward @@ -205,7 +205,6 @@ Link America/Puerto_Rico America/Tortola Link Pacific/Port_Moresby Antarctica/DumontDUrville Link Pacific/Auckland Antarctica/McMurdo Link Asia/Riyadh Antarctica/Syowa -Link Asia/Urumqi Antarctica/Vostok Link Europe/Berlin Arctic/Longyearbyen Link Asia/Riyadh Asia/Aden Link Asia/Qatar Asia/Bahrain diff --git a/backzone b/backzone index 44d81c2..f452503 100644 --- a/backzone +++ b/backzone @@ -963,35 +963,6 @@ Link Antarctica/McMurdo Antarctica/South_Pole Zone Antarctica/Syowa 0 - -00 1957 Jan 29 3:00 - +03 -# Vostok, Antarctica -# -# Vostok, since 1957-12-16, temporarily closed 1994-02/1994-11 -# From Craig Mundell (1994-12-15): -# http://quest.arc.nasa.gov/antarctica/QA/computers/Directions,Time,ZIP -# Vostok, which is one of the Russian stations, is set on the same -# time as Moscow, Russia. -# -# From Lee Hotz (2001-03-08): -# I queried the folks at Columbia who spent the summer at Vostok and this is -# what they had to say about time there: -# "in the US Camp (East Camp) we have been on New Zealand (McMurdo) -# time, which is 12 hours ahead of GMT. The Russian Station Vostok was -# 6 hours behind that (although only 2 miles away, i.e. 6 hours ahead -# of GMT). This is a time zone I think two hours east of Moscow. The -# natural time zone is in between the two: 8 hours ahead of GMT." -# -# From Paul Eggert (2001-05-04): -# This seems to be hopelessly confusing, so I asked Lee Hotz about it -# in person. He said that some Antarctic locations set their local -# time so that noon is the warmest part of the day, and that this -# changes during the year and does not necessarily correspond to mean -# solar noon. So the Vostok time might have been whatever the clocks -# happened to be during their visit. So we still don't really know what time -# it is at Vostok. But we'll guess +06. -# -Zone Antarctica/Vostok 0 - -00 1957 Dec 16 - 6:00 - +06 - # Yemen # Milne says 2:59:54 was the meridian of the saluting battery at Aden, # and that Yemen was at 1:55:56, the meridian of the Hagia Sophia. diff --git a/checknow.awk b/checknow.awk new file mode 100644 index 0000000..d722c03 --- /dev/null +++ b/checknow.awk @@ -0,0 +1,54 @@ +# Check zonenow.tab for consistency with primary data. + +# Contributed by Paul Eggert. This file is in the public domain. + +function record_zone(zone, data) { + if (zone) { + zone_data[zone] = data + zones[data] = zones[data] " " zone + } +} + +BEGIN { + while (getline >"/dev/stderr" status = 1 @@ -110,7 +110,7 @@ BEGIN { used_max_cc = cc } } - if (used_max <= 1 && comments) { + if (used_max <= 1 && comments && zone_table != "zonenow.tab") { printf "%s:%d: unnecessary comment '%s'\n", \ zone_table, i, comments \ >>"/dev/stderr" @@ -149,7 +149,8 @@ $1 ~ /^#/ { next } if ($3 ~ /%/) rulePercentUsed[$2] = 1 } if (tz && tz ~ /\// && tz !~ /^Etc\//) { - if (!tztab[tz] && FILENAME != "backward") { + if (!tztab[tz] && FILENAME != "backward" \ + && zone_table != "zonenow.tab") { printf "%s: no data for '%s'\n", zone_table, tz \ >>"/dev/stderr" status = 1 diff --git a/date.1 b/date.1 index e810721..01907bc 100644 --- a/date.1 +++ b/date.1 @@ -154,15 +154,11 @@ hexadecimal (leading 0x), preceded by an optional sign. .br /usr/lib/locale/\f2L\fP/LC_TIME description of time locale \f2L\fP .br -/usr/share/zoneinfo timezone information directory +/usr/share/zoneinfo timezone directory .br -/usr/share/zoneinfo/posixrules default DST rules (obsolete, - and can cause bugs if present) +/usr/share/zoneinfo/posixrules default DST rules (obsolete) .br /usr/share/zoneinfo/GMT for UTC leap seconds -.sp -If -.B /usr/share/zoneinfo/GMT -is absent, -UTC leap seconds are loaded from -.BR /usr/share/zoneinfo/posixrules . +.PP +If /usr/share/zoneinfo/GMT is absent, +UTC leap seconds are loaded from /usr/share/zoneinfo/GMT0 if present. diff --git a/europe b/europe index 3907c05..27f821e 100644 --- a/europe +++ b/europe @@ -1123,6 +1123,23 @@ Zone Atlantic/Faroe -0:27:04 - LMT 1908 Jan 11 # Tórshavn # 2. The shift *from* DST in 2023 happens as normal, but coincides with the # shift to UTC-02 normaltime (people will not change their clocks here). # 3. After this, DST is still observed, but as -02/-01 instead of -03/-02. +# +# From Múte Bourup Egede via Jógvan Svabo Samuelsen (2023-03-15): +# Greenland will not switch to Daylight Saving Time this year, 2023, +# because the standard time for Greenland will change from UTC -3 to UTC -2. +# However, Greenland will change to Daylight Saving Time again in 2024 +# and onwards. + +# From a contributor who wishes to remain anonymous for now (2023-10-29): +# https://www.dr.dk/nyheder/seneste/i-nat-skal-uret-stilles-en-time-tilbage-men-foerste-gang-sker-det-ikke-i-groenland +# with a link to that page: +# https://naalakkersuisut.gl/Nyheder/2023/10/2710_sommertid +# ... Ittoqqortoormiit joins the time of Nuuk at March 2024. +# What would mean that America/Scoresbysund would either be in -01 year round +# or in -02/-01 like America/Nuuk, but no longer in -01/+00. +# +# From Paul Eggert (2023-10-29): +# For now, assume it will be like America/Nuuk. # Rule NAME FROM TO - IN ON AT SAVE LETTER/S Rule Thule 1991 1992 - Mar lastSun 2:00 1:00 D @@ -1143,10 +1160,12 @@ Zone America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 Zone America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 # Ittoqqortoormiit -2:00 - -02 1980 Apr 6 2:00 -2:00 C-Eur -02/-01 1981 Mar 29 - -1:00 EU -01/+00 + -1:00 EU -01/+00 2024 Mar 31 + -2:00 EU -02/-01 Zone America/Nuuk -3:26:56 - LMT 1916 Jul 28 # Godthåb -3:00 - -03 1980 Apr 6 2:00 - -3:00 EU -03/-02 2023 Oct 29 1:00u + -3:00 EU -03/-02 2023 Mar 26 1:00u + -2:00 - -02 2023 Oct 29 1:00u -2:00 EU -02/-01 Zone America/Thule -4:35:08 - LMT 1916 Jul 28 # Pituffik -4:00 Thule A%sT @@ -3711,11 +3730,7 @@ Zone Europe/Istanbul 1:55:52 - LMT 1880 # and not at 3:00 as would have been under EU rules. # This is why I have set the change to EU rules into May 1996, # so that the change in March is stil covered by the Ukraine rule. -# The next change in October 1996 happened under EU rules.... -# TZ database holds three other zones for Ukraine.... I have not yet -# worked out the consequences for these three zones, as we (me and my -# US colleague David Cochrane) are still trying to get more -# information upon these local deviations from Kiev rules. +# The next change in October 1996 happened under EU rules. # # From Paul Eggert (2022-08-27): # For now, assume that Ukraine's zones all followed the same rules, diff --git a/iso3166.tab b/iso3166.tab index be3348d..402c015 100644 --- a/iso3166.tab +++ b/iso3166.tab @@ -3,17 +3,22 @@ # This file is in the public domain, so clarified as of # 2009-05-17 by Arthur David Olson. # -# From Paul Eggert (2022-11-18): +# From Paul Eggert (2023-09-06): # This file contains a table of two-letter country codes. Columns are # separated by a single tab. Lines beginning with '#' are comments. # All text uses UTF-8 encoding. The columns of the table are as follows: # # 1. ISO 3166-1 alpha-2 country code, current as of -# ISO 3166-1 N1087 (2022-09-02). See: Updates on ISO 3166-1 -# https://isotc.iso.org/livelink/livelink/Open/16944257 -# 2. The usual English name for the coded region, -# chosen so that alphabetic sorting of subsets produces helpful lists. -# This is not the same as the English name in the ISO 3166 tables. +# ISO/TC 46 N1108 (2023-04-05). See: ISO/TC 46 Documents +# https://www.iso.org/committee/48750.html?view=documents +# 2. The usual English name for the coded region. This sometimes +# departs from ISO-listed names, sometimes so that sorted subsets +# of names are useful (e.g., "Samoa (American)" and "Samoa +# (western)" rather than "American Samoa" and "Samoa"), +# sometimes to avoid confusion among non-experts (e.g., +# "Czech Republic" and "Turkey" rather than "Czechia" and "Türkiye"), +# and sometimes to omit needless detail or churn (e.g., "Netherlands" +# rather than "Netherlands (the)" or "Netherlands (Kingdom of the)"). # # The table is sorted by country code. # diff --git a/leap-seconds.list b/leap-seconds.list index 17e3a10..3fe9a12 100644 --- a/leap-seconds.list +++ b/leap-seconds.list @@ -204,10 +204,10 @@ # current -- the update time stamp, the data and the name of the file # will not change. # -# Updated through IERS Bulletin C65 -# File expires on: 28 December 2023 +# Updated through IERS Bulletin C66 +# File expires on: 28 June 2024 # -#@ 3912710400 +#@ 3928521600 # 2272060800 10 # 1 Jan 1972 2287785600 11 # 1 Jul 1972 @@ -252,4 +252,4 @@ # the hash line is also ignored in the # computation. # -#h e76a99dc 65f15cc7 e613e040 f5078b5e b23834fe +#h 16edd0f0 3666784f 37db6bdd e74ced87 59af48f1 diff --git a/localtime.c b/localtime.c index 818d58f..c2cebfd 100644 --- a/localtime.c +++ b/localtime.c @@ -15,6 +15,7 @@ #define LOCALTIME_IMPLEMENTATION #include "private.h" +#include "tzdir.h" #include "tzfile.h" #include @@ -111,6 +112,11 @@ enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 }; # define TZNAME_MAXIMUM 255 #endif +/* A representation of the contents of a TZif file. Ideally this + would have no size limits; the following sizes should suffice for + practical use. This struct should not be too large, as instances + are put on the stack and stacks are relatively small on some platforms. + See tzfile.h for more about the sizes. */ struct state { int leapcnt; int timecnt; @@ -153,8 +159,7 @@ static int_fast32_t leapcorr(struct state const *, time_t); static bool normalize_overflow32(int_fast32_t *, int *, int); static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, struct tm *); -static bool typesequiv(struct state const *, int, int); -static bool tzparse(char const *, struct state *, struct state *); +static bool tzparse(char const *, struct state *, struct state const *); #ifdef ALL_STATE static struct state * lclptr; @@ -369,7 +374,8 @@ union input_buffer { /* The first part of the buffer, interpreted as a header. */ struct tzhead tzhead; - /* The entire buffer. */ + /* The entire buffer. Ideally this would have no size limits; + the following should suffice for practical use. */ char buf[2 * sizeof(struct tzhead) + 2 * sizeof(struct state) + 4 * TZ_MAX_TIMES]; }; @@ -388,7 +394,12 @@ union local_storage { struct state st; } u; - /* The file name to be opened. */ + /* The name of the file to be opened. Ideally this would have no + size limits, to support arbitrarily long Zone names. + Limiting Zone names to 1024 bytes should suffice for practical use. + However, there is no need for this to be smaller than struct + file_analysis as that struct is allocated anyway, as the other + union member. */ char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)]; }; @@ -674,14 +685,18 @@ tzloadbody(char const *name, struct state *sp, bool doextend, == sp->types[sp->timecnt - 2])) sp->timecnt--; - for (i = 0; - i < ts->timecnt && sp->timecnt < TZ_MAX_TIMES; - i++) { + sp->goahead = ts->goahead; + + for (i = 0; i < ts->timecnt; i++) { time_t t = ts->ats[i]; if (increment_overflow_time(&t, leapcorr(sp, t)) || (0 < sp->timecnt && t <= sp->ats[sp->timecnt - 1])) continue; + if (TZ_MAX_TIMES <= sp->timecnt) { + sp->goahead = false; + break; + } sp->ats[sp->timecnt] = t; sp->types[sp->timecnt] = (sp->typecnt + ts->types[i]); @@ -694,28 +709,6 @@ tzloadbody(char const *name, struct state *sp, bool doextend, } if (sp->typecnt == 0) return EINVAL; - if (sp->timecnt > 1) { - if (sp->ats[0] <= TIME_T_MAX - SECSPERREPEAT) { - time_t repeatat = sp->ats[0] + SECSPERREPEAT; - int repeattype = sp->types[0]; - for (i = 1; i < sp->timecnt; ++i) - if (sp->ats[i] == repeatat - && typesequiv(sp, sp->types[i], repeattype)) { - sp->goback = true; - break; - } - } - if (TIME_T_MIN + SECSPERREPEAT <= sp->ats[sp->timecnt - 1]) { - time_t repeatat = sp->ats[sp->timecnt - 1] - SECSPERREPEAT; - int repeattype = sp->types[sp->timecnt - 1]; - for (i = sp->timecnt - 2; i >= 0; --i) - if (sp->ats[i] == repeatat - && typesequiv(sp, sp->types[i], repeattype)) { - sp->goahead = true; - break; - } - } - } /* Infer sp->defaulttype from the data. Although this default type is always zero for data from recent tzdb releases, @@ -792,31 +785,6 @@ tzload(char const *name, struct state *sp, bool doextend) #endif } -static bool -typesequiv(const struct state *sp, int a, int b) -{ - register bool result; - - if (sp == NULL || - a < 0 || a >= sp->typecnt || - b < 0 || b >= sp->typecnt) - result = false; - else { - /* Compare the relevant members of *AP and *BP. - Ignore tt_ttisstd and tt_ttisut, as they are - irrelevant now and counting them could cause - sp->goahead to mistakenly remain false. */ - register const struct ttinfo * ap = &sp->ttis[a]; - register const struct ttinfo * bp = &sp->ttis[b]; - result = (ap->tt_utoff == bp->tt_utoff - && ap->tt_isdst == bp->tt_isdst - && (strcmp(&sp->chars[ap->tt_desigidx], - &sp->chars[bp->tt_desigidx]) - == 0)); - } - return result; -} - static const int mon_lengths[2][MONSPERYEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } @@ -913,8 +881,8 @@ getsecs(register const char *strp, int_fast32_t *const secsp) int_fast32_t secsperhour = SECSPERHOUR; /* - ** 'HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like - ** "M10.4.6/26", which does not conform to Posix, + ** '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". */ @@ -1116,7 +1084,7 @@ transtime(const int year, register const struct rule *const rulep, */ static bool -tzparse(const char *name, struct state *sp, struct state *basep) +tzparse(const char *name, struct state *sp, struct state const *basep) { const char * stdname; const char * dstname; @@ -1159,6 +1127,7 @@ tzparse(const char *name, struct state *sp, struct state *basep) } if (0 < sp->leapcnt) leaplo = sp->lsis[sp->leapcnt - 1].ls_trans; + sp->goback = sp->goahead = false; if (*name != '\0') { if (*name == '<') { dstname = ++name; @@ -1206,7 +1175,6 @@ tzparse(const char *name, struct state *sp, struct state *basep) */ 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; @@ -1366,15 +1334,14 @@ tzparse(const char *name, struct state *sp, struct state *basep) 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->defaulttype = 0; sp->charcnt = charcnt; cp = sp->chars; memcpy(cp, stdname, stdlen); @@ -1703,6 +1670,9 @@ gmtime(const time_t *timep) #if STD_INSPIRED +/* This function is obsolescent and may disappear in future releases. + Callers can instead use localtime_rz with a fixed-offset zone. */ + struct tm * offtime(const time_t *timep, long offset) { @@ -2313,6 +2283,8 @@ mktime(struct tm *tmp) } #if STD_INSPIRED +/* This function is obsolescent and may disapper in future releases. + Callers can instead use mktime. */ time_t timelocal(struct tm *tmp) { @@ -2321,8 +2293,13 @@ timelocal(struct tm *tmp) return mktime(tmp); } #else +# ifndef timeoff +# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 . */ +# endif static #endif +/* This function is obsolescent and may disapper in future releases. + Callers can instead use mktime_z with a fixed-offset zone. */ time_t timeoff(struct tm *tmp, long offset) { diff --git a/newctime.3 b/newctime.3 index 05bb7de..3b54d4a 100644 --- a/newctime.3 +++ b/newctime.3 @@ -292,20 +292,16 @@ will continue to exist in this form in future releases of this code. .SH FILES .ta \w'/usr/share/zoneinfo/posixrules\0\0'u -/usr/share/zoneinfo timezone information directory +/etc/localtime local timezone file .br -/usr/share/zoneinfo/localtime local timezone file +/usr/share/zoneinfo timezone directory .br -/usr/share/zoneinfo/posixrules default DST rules (obsolete, - and can cause bugs if present) +/usr/share/zoneinfo/posixrules default DST rules (obsolete) .br /usr/share/zoneinfo/GMT for UTC leap seconds -.sp -If -.B /usr/share/zoneinfo/GMT -is absent, -UTC leap seconds are loaded from -.BR /usr/share/zoneinfo/posixrules . +.PP +If /usr/share/zoneinfo/GMT is absent, +UTC leap seconds are loaded from /usr/share/zoneinfo/GMT0 if present. .SH SEE ALSO getenv(3), newstrftime(3), diff --git a/newtzset.3 b/newtzset.3 index 78b6b6c..45ddbd2 100644 --- a/newtzset.3 +++ b/newtzset.3 @@ -115,7 +115,7 @@ it must have the following syntax (spaces inserted for clarity): .PP Where: .RS -.TP 15 +.TP .IR std " and " dst Three or more bytes that are the designation for the standard .RI ( std ) @@ -205,7 +205,7 @@ The format of .I date is one of the following: .RS -.TP 10 +.TP .BI J n The Julian day .I n @@ -238,7 +238,7 @@ first week in which the .IR d' th day occurs. Day zero is Sunday. .RE -.IP "" 15 +.IP The .I time has the same format as @@ -306,21 +306,31 @@ The abbreviations for standard and daylight saving time are and .q "\*-02". .PP -If no -.I rule -is present in -.BR TZ , -the rules specified -by the +If +.B TZ +specifies daylight saving time but does not specify a +.IR rule , +and the optional .BR tzfile (5)-format file .B posixrules -in the system time conversion information directory are used, with the -standard and daylight saving time offsets from UT replaced by those specified by -the +is present in the system time conversion information directory, the +rules in +.B posixrules +are used, with the +.B posixrules +standard and daylight saving time offsets from UT +replaced by those specified by the .I offset values in .BR TZ . +However, the +.B posixrules +file is obsolete: if it is present it is only for backward compatibility, +and it does not work reliably. +Therefore, applications that specify a +.B TZ +string with daylight saving time should specify rules explicitly. .PP For compatibility with System V Release 3.1, a semicolon .RB ( ; ) @@ -329,20 +339,16 @@ may be used to separate the from the rest of the specification. .SH FILES .ta \w'/usr/share/zoneinfo/posixrules\0\0'u -/usr/share/zoneinfo timezone information directory +/etc/localtime local timezone file .br -/usr/share/zoneinfo/localtime local timezone file +/usr/share/zoneinfo timezone directory .br -/usr/share/zoneinfo/posixrules default DST rules (obsolete, - and can cause bugs if present) +/usr/share/zoneinfo/posixrules default DST rules (obsolete) .br /usr/share/zoneinfo/GMT for UTC leap seconds -.sp -If -.B /usr/share/zoneinfo/GMT -is absent, -UTC leap seconds are loaded from -.BR /usr/share/zoneinfo/posixrules . +.PP +If /usr/share/zoneinfo/GMT is absent, +UTC leap seconds are loaded from /usr/share/zoneinfo/GMT0 if present. .SH SEE ALSO getenv(3), newctime(3), diff --git a/northamerica b/northamerica index d8e3601..8ac106a 100644 --- a/northamerica +++ b/northamerica @@ -1453,7 +1453,7 @@ Rule StJohns 1989 2006 - Apr Sun>=1 0:01 1:00 D Rule StJohns 2007 2011 - Mar Sun>=8 0:01 1:00 D Rule StJohns 2007 2010 - Nov Sun>=1 0:01 0 S # -# St John's has an apostrophe, but Posix file names can't have apostrophes. +# St John's has an apostrophe, but POSIX file names can't have apostrophes. # Zone NAME STDOFF RULES FORMAT [UNTIL] Zone America/St_Johns -3:30:52 - LMT 1884 -3:30:52 StJohns N%sT 1918 diff --git a/private.h b/private.h index 838ab2b..52e8b1f 100644 --- a/private.h +++ b/private.h @@ -18,11 +18,15 @@ */ /* PORT_TO_C89 means the code should work even if the underlying - compiler and library support only C89. SUPPORT_C89 means the + compiler and library support only C89 plus C99's 'long long' + and perhaps a few other extensions to C89. SUPPORT_C89 means the tzcode library should support C89 callers in addition to the usual - support for C99-and-later callers. These macros are obsolescent, + support for C99-and-later callers; however, C89 support can trigger + latent bugs in C99-and-later callers. These macros are obsolescent, and the plan is to remove them along with any code needed only when - they are nonzero. */ + they are nonzero. A good time to do that might be in the year 2029 + because RHEL 7 (whose GCC defaults to C89) extended life cycle + support (ELS) is scheduled to end on 2028-06-30. */ #ifndef PORT_TO_C89 # define PORT_TO_C89 0 #endif @@ -70,9 +74,7 @@ #endif #if !defined HAVE__GENERIC && defined __has_extension -# if __has_extension(c_generic_selections) -# define HAVE__GENERIC 1 -# else +# if !__has_extension(c_generic_selections) # define HAVE__GENERIC 0 # endif #endif diff --git a/southamerica b/southamerica index e55c5f0..19c7e45 100644 --- a/southamerica +++ b/southamerica @@ -1697,6 +1697,12 @@ Rule Para 2010 2012 - Apr Sun>=8 0:00 0 - # From Carlos Raúl Perasso (2014-02-28): # Decree 1264 can be found at: # http://www.presidencia.gov.py/archivos/documentos/DECRETO1264_ey9r8zai.pdf +# +# From Paul Eggert (2023-07-26): +# Transition dates are now set by Law No. 7115, not by presidential decree. +# https://www.abc.com.py/politica/2023/07/12/promulgacion-el-cambio-de-hora-sera-por-ley/ +# From Carlos Raúl Perasso (2023-07-27): +# http://silpy.congreso.gov.py/descarga/ley-144138 Rule Para 2013 max - Mar Sun>=22 0:00 0 - # Zone NAME STDOFF RULES FORMAT [UNTIL] diff --git a/time2posix.3 b/time2posix.3 index f48402b..6644060 100644 --- a/time2posix.3 +++ b/time2posix.3 @@ -100,7 +100,7 @@ and back from, the POSIX representation over the leap second inserted at the end of June, 1993. .nf -.ta \w'93/06/30 'u +\w'23:59:59 'u +\w'A+0 'u +\w'X=time2posix(T) 'u +.ta \w'93/06/30\0'u +\w'23:59:59\0'u +\w'A+0\0'u +\w'X=time2posix(T)\0'u DATE TIME T X=time2posix(T) posix2time(X) 93/06/30 23:59:59 A+0 B+0 A+0 93/06/30 23:59:60 A+1 B+1 A+1 or A+2 diff --git a/tz-art.html b/tz-art.html index c86c186..e3be5f2 100644 --- a/tz-art.html +++ b/tz-art.html @@ -22,9 +22,10 @@

Documentaries

Explaining The Mysteries Of Time Zones" (2017; 2:15) briefly says why France has more time zones than Russia.
  • -"Why Denmark is -.17 Seconds Behind The World" (2019; 6:29) explains why Denmark and -the United Kingdom don't exactly follow their own law about civil time. +"Why Denmark used to be +.04 seconds behind the world" (2019; 6:29) explains why the United Kingdom +— and, once, Denmark — haven't always exactly followed their own +laws about civil time.
  • "About Time" (1962; 59 minutes) is part of the Bell Science extravaganza, with Frank Baxter, Richard Deacon, and Les Tremayne. diff --git a/tz-link.html b/tz-link.html index 43190dd..84bea93 100644 --- a/tz-link.html +++ b/tz-link.html @@ -973,6 +973,9 @@

    Costs and benefits of time shifts

    section summarizes reviews and position statements based on scientific literature in the area.

    diff --git a/tzfile.5 b/tzfile.5 index 59d9f6b..5528028 100644 --- a/tzfile.5 +++ b/tzfile.5 @@ -26,23 +26,24 @@ a signed binary integer is represented using two's complement, and a boolean is represented by a one-byte binary integer that is either 0 (false) or 1 (true). The format begins with a 44-byte header containing the following fields: -.IP * 2 +.RS "\w' 'u" +.IP \(bu "\w'\(bu 'u" The magic four-byte ASCII sequence .q "TZif" identifies the file as a timezone information file. -.IP * +.IP \(bu A byte identifying the version of the file's format (as of 2021, either an ASCII NUL, .q "2", .q "3", or .q "4" ). -.IP * +.IP \(bu Fifteen bytes containing zeros reserved for future use. -.IP * +.IP \(bu Six four-byte integer values, in the following order: -.RS -.TP +.RS "\w' \(bu 'u" +.TP "\w' 'u" .B tzh_ttisutcnt The number of UT/local indicators stored in the file. (UT is Universal Time.) @@ -68,14 +69,15 @@ stored in the file. .PP The above header is followed by the following fields, whose lengths depend on the contents of the header: -.IP * 2 +.RS "\w' 'u" +.IP \(bu "\w'\(bu 'u" .B tzh_timecnt four-byte signed integer values sorted in ascending order. These values are written in network byte order. Each is used as a transition time (as returned by .BR time (2)) at which the rules for computing local time change. -.IP * +.IP \(bu .B tzh_timecnt one-byte unsigned integer values; each one but the last tells which of the different types of local time types @@ -85,20 +87,20 @@ and continuing up to but not including the next transition time. (The last time type is present only for consistency checking with the POSIX-style TZ string described below.) These values serve as indices into the next field. -.IP * +.IP \(bu .B tzh_typecnt .B ttinfo entries, each defined as follows: -.in +.5i +.in +2 .sp .nf -.ta .5i +\w'unsigned char\0\0'u +.ta \w'\0\0\0\0'u +\w'unsigned char\0'u struct ttinfo { int32_t tt_utoff; unsigned char tt_isdst; unsigned char tt_desigidx; }; -.in -.5i +.in .fi .sp Each structure is written as a four-byte signed integer value for @@ -132,7 +134,8 @@ Also, in realistic applications is in the range [\-89999, 93599] (i.e., more than \-25 hours and less than 26 hours); this allows easy support by implementations that already support the POSIX-required range [\-24:59:59, 25:59:59]. -.IP * +.RS "\w' 'u" +.IP \(bu "\w'\(bu 'u" .B tzh_charcnt bytes that represent time zone designations, which are null-terminated byte strings, each indexed by the @@ -140,7 +143,7 @@ which are null-terminated byte strings, each indexed by the values mentioned above. The byte strings can overlap if one is a suffix of the other. The encoding of these strings is not specified. -.IP * +.IP \(bu .B tzh_leapcnt pairs of four-byte values, written in network byte order; the first value of each pair gives the nonnegative time @@ -167,18 +170,19 @@ otherwise, for timestamps before the first occurrence time, the leap-second correction is zero if the first pair's correction is 1 or \-1, and is unspecified otherwise (which can happen only in files truncated at the start). -.IP * +.IP \(bu .B tzh_ttisstdcnt standard/wall indicators, each stored as a one-byte boolean; they tell whether the transition times associated with local time types were specified as standard time or local (wall clock) time. -.IP * +.IP \(bu .B tzh_ttisutcnt UT/local indicators, each stored as a one-byte boolean; they tell whether the transition times associated with local time types were specified as UT or local time. If a UT/local indicator is set, the corresponding standard/wall indicator must also be set. +.RE .PP The standard/wall and UT/local indicators were designed for transforming a TZif file's transition times into transitions appropriate @@ -312,15 +316,17 @@ This section documents common problems in reading or writing TZif files. Most of these are problems in generating TZif files for use by older readers. The goals of this section are: -.IP * 2 +.RS "\w' 'u" +.IP \(bu "\w'\(bu 'u" to help TZif writers output files that avoid common pitfalls in older or buggy TZif readers, -.IP * +.IP \(bu to help TZif readers avoid common pitfalls when reading files generated by future TZif writers, and -.IP * +.IP \(bu to help any future specification authors see what sort of problems arise when the TZif format is changed. +.RE .PP When new versions of the TZif format have been defined, a design goal has been that a reader can successfully use a TZif @@ -335,21 +341,22 @@ workarounds, as well as to document other common bugs in readers. .PP Interoperability problems with TZif include the following: -.IP * 2 +.RS "\w' 'u" +.IP \(bu "\w'\(bu 'u" Some readers examine only version 1 data. As a partial workaround, a writer can output as much version 1 data as possible. However, a reader should ignore version 1 data, and should use version 2+ data even if the reader's native timestamps have only 32 bits. -.IP * +.IP \(bu Some readers designed for version 2 might mishandle timestamps after a version 3 or higher file's last transition, because they cannot parse extensions to POSIX in the TZ-like string. As a partial workaround, a writer can output more transitions than necessary, so that only far-future timestamps are mishandled by version 2 readers. -.IP * +.IP \(bu Some readers designed for version 2 do not support permanent daylight saving time with transitions after 24:00 \(en e.g., a TZ string @@ -367,22 +374,22 @@ for the next time zone east \(en e.g., .q "AST4" for permanent Atlantic Standard Time (\-04). -.IP * +.IP \(bu Some readers designed for version 2 or 3, and that require strict conformance to RFC 8536, reject version 4 files whose leap second tables are truncated at the start or that end in expiration times. -.IP * +.IP \(bu Some readers ignore the footer, and instead predict future timestamps from the time type of the last transition. As a partial workaround, a writer can output more transitions than necessary. -.IP * +.IP \(bu Some readers do not use time type 0 for timestamps before the first transition, in that they infer a time type using a heuristic that does not always select time type 0. As a partial workaround, a writer can output a dummy (no-op) first transition at an early time. -.IP * +.IP \(bu Some readers mishandle timestamps before the first transition that has a timestamp not less than \-2**31. Readers that support only 32-bit timestamps are likely to be @@ -391,11 +398,11 @@ more prone to this problem, for example, when they process bits. As a partial workaround, a writer can output a dummy transition at timestamp \-2**31. -.IP * +.IP \(bu Some readers mishandle a transition if its timestamp has the minimum possible signed 64-bit value. Timestamps less than \-2**59 are not recommended. -.IP * +.IP \(bu Some readers mishandle POSIX-style TZ strings that contain .q "<" @@ -407,11 +414,11 @@ or .q ">" for time zone abbreviations containing only alphabetic characters. -.IP * +.IP \(bu Many readers mishandle time zone abbreviations that contain non-ASCII characters. These characters are not recommended. -.IP * +.IP \(bu Some readers may mishandle time zone abbreviations that contain fewer than 3 or more than 6 characters, or that contain ASCII characters other than alphanumerics, @@ -419,7 +426,7 @@ contain ASCII characters other than alphanumerics, and .q "+". These abbreviations are not recommended. -.IP * +.IP \(bu Some readers mishandle TZif files that specify daylight-saving time UT offsets that are less than the UT offsets for the corresponding standard time. @@ -435,7 +442,7 @@ thus swapping standard and daylight saving time. Although this workaround misidentifies which part of the year uses daylight saving time, it records UT offsets and time zone abbreviations correctly. -.IP * +.IP \(bu Some readers generate ambiguous timestamps for positive leap seconds that occur when the UTC offset is not a multiple of 60 seconds. For example, in a timezone with UTC offset +01:23:45 and with @@ -446,38 +453,41 @@ instead of mapping the latter to 01:23:46, and they will map 78796815 to This has not yet been a practical problem, since no civil authority has observed such UTC offsets since leap seconds were introduced in 1972. +.RE .PP Some interoperability problems are reader bugs that are listed here mostly as warnings to developers of readers. -.IP * 2 +.RS "\w' 'u" +.IP \(bu "\w'\(bu 'u" Some readers do not support negative timestamps. Developers of distributed applications should keep this in mind if they need to deal with pre-1970 data. -.IP * +.IP \(bu Some readers mishandle timestamps before the first transition that has a nonnegative timestamp. Readers that do not support negative timestamps are likely to be more prone to this problem. -.IP * +.IP \(bu Some readers mishandle time zone abbreviations like .q "\*-08" that contain .q "+", .q "\*-", or digits. -.IP * +.IP \(bu Some readers mishandle UT offsets that are out of the traditional range of \-12 through +12 hours, and so do not support locations like Kiritimati that are outside this range. -.IP * +.IP \(bu Some readers mishandle UT offsets in the range [\-3599, \-1] seconds from UT, because they integer-divide the offset by 3600 to get 0 and then display the hour part as .q "+00". -.IP * +.IP \(bu Some readers mishandle UT offsets that are not a multiple of one hour, or of 15 minutes, or of 1 minute. +.RE .SH SEE ALSO .BR time (2), .BR localtime (3), diff --git a/tzfile.h b/tzfile.h index 9cbdcff..21ae37c 100644 --- a/tzfile.h +++ b/tzfile.h @@ -21,14 +21,6 @@ ** Information about time zone files. */ -#ifndef TZDIR -# define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ -#endif /* !defined TZDIR */ - -#ifndef TZDEFAULT -# define TZDEFAULT "/etc/localtime" -#endif /* !defined TZDEFAULT */ - #ifndef TZDEFRULES # define TZDEFRULES "posixrules" #endif /* !defined TZDEFRULES */ diff --git a/tzselect.8 b/tzselect.8 index 4578090..ee03161 100644 --- a/tzselect.8 +++ b/tzselect.8 @@ -95,7 +95,7 @@ Output version information and exit. .SH "ENVIRONMENT VARIABLES" .TP \f3AWK\fP -Name of a Posix-compliant +Name of a POSIX-compliant .B awk program (default: .BR awk ). diff --git a/tzselect.ksh b/tzselect.ksh index 9a91acf..2073bcc 100644 --- a/tzselect.ksh +++ b/tzselect.ksh @@ -10,7 +10,7 @@ REPORT_BUGS_TO=tz@iana.org # Porting notes: # -# This script requires a Posix-like shell and prefers the extension of a +# This script requires a POSIX-like shell and prefers the extension of a # 'select' statement. The 'select' statement was introduced in the # Korn shell and is available in Bash and other shell implementations. # If your host lacks both Bash and the Korn shell, you can get their @@ -21,19 +21,24 @@ REPORT_BUGS_TO=tz@iana.org # MirBSD Korn Shell # # For portability to Solaris 10 /bin/sh (supported by Oracle through -# January 2024) this script avoids some POSIX features and common -# extensions, such as $(...) (which works sometimes but not others), -# $((...)), ! CMD, ${#ID}, ${ID##PAT}, ${ID%%PAT}, and $10. - +# January 2027) this script avoids some POSIX features and common +# extensions, such as $(...), $((...)), ! CMD, unquoted ^, ${#ID}, +# ${ID##PAT}, ${ID%%PAT}, and $10. Although some of these constructs +# work sometimes, it's simpler to avoid them entirely. # -# This script also uses several features of modern awk programs. -# If your host lacks awk, or has an old awk that does not conform to Posix, -# you can use either of the following free programs instead: +# This script also uses several features of POSIX awk. +# If your host lacks awk, or has an old awk that does not conform to POSIX, +# you can use any of the following free programs instead: # # Gawk (GNU awk) # mawk # nawk - +# +# Because 'awk "VAR=VALUE" ...' and 'awk -v "VAR=VALUE" ...' are not portable +# if VALUE contains \, ", or newline, awk scripts in this file use: +# awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE" +# The substr avoids problems when VALUE is of the form X=Y and would be +# misinterpreted as an assignment. # Specify default values for environment variables if they are unset. : ${AWK=awk} @@ -42,14 +47,14 @@ REPORT_BUGS_TO=tz@iana.org # Output one argument as-is to standard output, with trailing newline. # Safer than 'echo', which can mishandle '\' or leading '-'. say() { - printf '%s\n' "$1" + printf '%s\n' "$1" } -# Check for awk Posix compliance. -($AWK -v x=y 'BEGIN { exit 123 }') /dev/null 2>&1 +# Check for awk POSIX compliance. +($AWK -v x=y 'BEGIN { exit 123 }') <>/dev/null >&0 2>&0 [ $? = 123 ] || { - say >&2 "$0: Sorry, your '$AWK' program is not Posix compatible." - exit 1 + say >&2 "$0: Sorry, your '$AWK' program is not POSIX compatible." + exit 1 } coord= @@ -86,10 +91,10 @@ Report bugs to $REPORT_BUGS_TO." # available, falling back on a portable substitute otherwise. if case $BASH_VERSION in - ?*) : ;; + ?*) :;; '') # '; exit' should be redundant, but Dash doesn't properly fail without it. - (eval 'set --; select x; do break; done; exit') /dev/null + (eval 'set --; select x; do break; done; exit') <>/dev/null 2>&0 esac then # Do this inside 'eval', as otherwise the shell might exit when parsing it @@ -99,7 +104,7 @@ then select select_result do case $select_result in - "") echo >&2 "Please enter a number in range." ;; + "") echo >&2 "Please enter a number in range.";; ?*) break esac done || exit @@ -121,9 +126,9 @@ else do select_i=`expr $select_i + 1` printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word" - done ;; + done;; *[!0-9]*) - echo >&2 'Please enter a number in range.' ;; + echo >&2 'Please enter a number in range.';; *) if test 1 -le $select_i && test $select_i -le $#; then shift `expr $select_i - 1` @@ -142,56 +147,57 @@ fi while getopts c:n:t:-: opt do - case $opt$OPTARG in - c*) - coord=$OPTARG ;; - n*) - location_limit=$OPTARG ;; - t*) # Undocumented option, used for developer testing. - zonetabtype=$OPTARG ;; - -help) - exec echo "$usage" ;; - -version) - exec echo "tzselect $PKGVERSION$TZVERSION" ;; - -*) - say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1 ;; - *) - say >&2 "$0: try '$0 --help'"; exit 1 ;; - esac + case $opt$OPTARG in + c*) + coord=$OPTARG;; + n*) + location_limit=$OPTARG;; + t*) # Undocumented option, used for developer testing. + zonetabtype=$OPTARG;; + -help) + exec echo "$usage";; + -version) + exec echo "tzselect $PKGVERSION$TZVERSION";; + -*) + say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1;; + *) + say >&2 "$0: try '$0 --help'"; exit 1 + esac done shift `expr $OPTIND - 1` case $# in 0) ;; -*) say >&2 "$0: $1: unknown argument"; exit 1 ;; +*) say >&2 "$0: $1: unknown argument"; exit 1 esac -# Make sure the tables are readable. -TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab -TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab -for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE -do - <"$f" || { - say >&2 "$0: time zone files are not set up correctly" - exit 1 - } -done +# translit=true to try transliteration. +# This is false if U+12345 CUNEIFORM SIGN URU TIMES KI has length 1 +# which means awk (and presumably the shell) do not need transliteration. +if $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) == 1 }'; then + translit=true +else + translit=false +fi -# If the current locale does not support UTF-8, convert data to current -# locale's format if possible, as the shell aligns columns better that way. -# Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI. -$AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' || { - { tmp=`(mktemp -d) 2>/dev/null` || { - tmp=${TMPDIR-/tmp}/tzselect.$$ && - (umask 77 && mkdir -- "$tmp") - };} && - trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM && - (iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \ - 2>/dev/null && - TZ_COUNTRY_TABLE=$tmp/iso3166.tab && - iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab && - TZ_ZONE_TABLE=$tmp/$zonetabtype.tab +# Read into shell variable $1 the contents of file $2. +# Convert to the current locale's encoding if possible, +# as the shell aligns columns better that way. +# If GNU iconv's //TRANSLIT does not work, fall back on POSIXish iconv; +# if that does not work, fall back on 'cat'. +read_file() { + { $translit && { + eval "$1=\`(iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\"\`" || + eval "$1=\`(iconv -f UTF-8) 2>/dev/null <\"\$2\"\`" + }; } || + eval "$1=\`cat <\"\$2\"\`" || { + say >&2 "$0: time zone files are not set up correctly" + exit 1 + } } +read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab" +read_file TZ_ZONETABTYPE_TABLE "$TZDIR/$zonetabtype.tab" +TZ_ZONENOW_TABLE= newline=' ' @@ -199,61 +205,77 @@ IFS=$newline # Awk script to output a country list. output_country_list=' - BEGIN { FS = "\t" } - /^#$/ { next } - /^#[^@]/ { next } - { - commentary = $0 ~ /^#@/ - if (commentary) { - col1ccs = substr($1, 3) - conts = $2 - } else { - col1ccs = $1 - conts = $3 - } - ncc = split(col1ccs, cc, /,/) - ncont = split(conts, cont, /,/) - for (i = 1; i <= ncc; i++) { - elsewhere = commentary - for (ci = 1; ci <= ncont; ci++) { - if (cont[ci] ~ continent_re) { - if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i] - elsewhere = 0 - } + BEGIN { + continent_re = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (iline = 1; iline <= nlines; iline++) { + $0 = line[iline] + commentary = $0 ~ /^#@/ + if (commentary) { + if ($0 !~ /^#@/) + continue + col1ccs = substr($1, 3) + conts = $2 + } else { + col1ccs = $1 + conts = $3 } - if (elsewhere) { - for (i = 1; i <= ncc; i++) { - cc_elsewhere[cc[i]] = 1 + ncc = split(col1ccs, cc, /,/) + ncont = split(conts, cont, /,/) + for (i = 1; i <= ncc; i++) { + elsewhere = commentary + for (ci = 1; ci <= ncont; ci++) { + if (cont[ci] ~ continent_re) { + if (!cc_seen[cc[i]]++) + cc_list[++ccs] = cc[i] + elsewhere = 0 + } } + if (elsewhere) + for (i = 1; i <= ncc; i++) + cc_elsewhere[cc[i]] = 1 } } - } - END { - while (getline &2 'Please identify a location' \ - 'so that time zone rules can be set correctly.' + echo >&2 'Please identify a location' \ + 'so that time zone rules can be set correctly.' + + continent= + country= + country_result= + region= + time= + TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE - continent= - country= - region= + case $coord in + ?*) + continent=coord;; + '') - case $coord in - ?*) - continent=coord;; - '') + # Ask the user for continent or ocean. - # Ask the user for continent or ocean. + echo >&2 \ + 'Please select a continent, ocean, "coord", "TZ", "time", or "now".' - echo >&2 'Please select a continent, ocean, "coord", "TZ", or "time".' + quoted_continents=` + $AWK ' + function handle_entry(entry) { + entry = substr(entry, 1, index(entry, "/") - 1) + if (entry == "America") + entry = entry "s" + if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/) + entry = entry " Ocean" + printf "'\''%s'\''\n", entry + } + BEGIN { + TZ_ZONETABTYPE_TABLE = substr(ARGV[1], 2) + ARGV[1] = "" + FS = "\t" + nlines = split(TZ_ZONETABTYPE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^[^#]/) + handle_entry($3) + else if ($0 ~ /^#@/) { + ncont = split($2, cont, /,/) + for (ci = 1; ci <= ncont; ci++) + handle_entry(cont[ci]) + } + } + } + ' ="$TZ_ZONETABTYPE_TABLE" | + sort -u | + tr '\n' ' ' + echo '' + ` + + eval ' + doselect '"$quoted_continents"' \ + "coord - I want to use geographical coordinates." \ + "TZ - I want to specify the timezone using the POSIX TZ format." \ + "time - I know local time already." \ + "now - Like \"time\", but configure only for timestamps from now on." + continent=$select_result + case $continent in + Americas) continent=America;; + *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''` + esac + case $zonetabtype,$continent in + zonenow,*) ;; + *,now) + ${TZ_ZONENOW_TABLE:+:} read_file TZ_ZONENOW_TABLE "$TZDIR/zonenow.tab" + TZ_ZONE_TABLE=$TZ_ZONENOW_TABLE + esac + ' + esac - quoted_continents=` + case $continent in + TZ) + # Ask the user for a POSIX TZ string. Check that it conforms. + check_POSIX_TZ_string=' + BEGIN { + tz = substr(ARGV[1], 2) + ARGV[1] = "" + tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \ + "|[[:alpha:]][[:alpha:]][[:alpha:]]+)") + time = ("(2[0-4]|[0-1]?[0-9])" \ + "(:[0-5][0-9](:[0-5][0-9])?)?") + offset = "[-+]?" time + mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" + jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \ + "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])") + datetime = ",(" mdate "|" jdate ")(/" time ")?" + tzpattern = ("^(:.*|" tzname offset "(" tzname \ + "(" offset ")?(" datetime datetime ")?)?)$") + exit tz ~ tzpattern + } + ' + + while + echo >&2 'Please enter the desired value' \ + 'of the TZ environment variable.' + echo >&2 'For example, AEST-10 is abbreviated' \ + 'AEST and is 10 hours' + echo >&2 'ahead (east) of Greenwich,' \ + 'with no daylight saving time.' + read tz + $AWK "$check_POSIX_TZ_string" ="$tz" + do + say >&2 "'$tz' is not a conforming POSIX timezone string." + done + TZ_for_date=$tz;; + *) + case $continent in + coord) + case $coord in + '') + echo >&2 'Please enter coordinates' \ + 'in ISO 6709 notation.' + echo >&2 'For example, +4042-07403 stands for' + echo >&2 '40 degrees 42 minutes north,' \ + '74 degrees 3 minutes west.' + read coord + esac + distance_table=` + $AWK \ + "$output_distances_or_times" \ + ="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" | + sort -n | + sed "${location_limit}q" + ` + regions=` + $AWK ' + BEGIN { + distance_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(distance_table, line, /\n/) + for (nr = 1; nr <= nlines; nr++) { + nf = split(line[nr], f, /\t/) + print f[nf] + } + } + ' ="$distance_table" + ` + echo >&2 'Please select one of the following timezones,' + echo >&2 'listed roughly in increasing order' \ + "of distance from $coord". + doselect $regions + region=$select_result + tz=` + $AWK ' + BEGIN { + distance_table = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(distance_table, line, /\n/) + for (nr = 1; nr <= nlines; nr++) { + nf = split(line[nr], f, /\t/) + if (f[nf] == region) + print f[4] + } + } + ' ="$distance_table" ="$region" + `;; + *) + case $continent in + now|time) + minute_format='%a %b %d %H:%M' + old_minute=`TZ=UTC0 date +"$minute_format"` + for i in 1 2 3 + do + time_table_command=` + $AWK \ + -v output_times=1 \ + "$output_distances_or_times" \ + = = ="$TZ_ZONE_TABLE" + ` + time_table=`eval "$time_table_command"` + new_minute=`TZ=UTC0 date +"$minute_format"` + case $old_minute in + "$new_minute") break + esac + old_minute=$new_minute + done + echo >&2 "The system says Universal Time is $new_minute." + echo >&2 "Assuming that's correct, what is the local time?" + sorted_table=`say "$time_table" | sort -k2n -k2,5 -k1n` || { + say >&2 "$0: cannot sort time table" + exit 1 + } + eval doselect ` $AWK ' - function handle_entry(entry) { - entry = substr(entry, 1, index(entry, "/") - 1) - if (entry == "America") - entry = entry "s" - if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/) - entry = entry " Ocean" - printf "'\''%s'\''\n", entry + BEGIN { + sorted_table = substr(ARGV[1], 2) + ARGV[1] = "" + nlines = split(sorted_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + outline = $6 " " $7 " " $4 " " $5 + if (outline == oldline) + continue + oldline = outline + gsub(/'\''/, "&\\\\&&", outline) + printf "'\''%s'\''\n", outline + } } - BEGIN { FS = "\t" } - /^[^#]/ { - handle_entry($3) - } - /^#@/ { - ncont = split($2, cont, /,/) - for (ci = 1; ci <= ncont; ci++) { - handle_entry(cont[ci]) + ' ="$sorted_table" + ` + time=$select_result + continent_re='^' + zone_table=` + $AWK ' + BEGIN { + time = substr(ARGV[1], 2) + time_table = substr(ARGV[2], 2) + ARGV[1] = ARGV[2] = "" + nlines = split(time_table, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($6 " " $7 " " $4 " " $5 == time) { + sub(/[^\t]*\t/, "") + print + } } } - ' <"$TZ_ZONE_TABLE" | - sort -u | - tr '\n' ' ' - echo '' + ' ="$time" ="$time_table" + ` + countries=` + $AWK \ + "$output_country_list" \ + ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | + sort -f ` + ;; + *) + continent_re="^$continent/" + zone_table=$TZ_ZONE_TABLE + esac - eval ' - doselect '"$quoted_continents"' \ - "coord - I want to use geographical coordinates." \ - "TZ - I want to specify the timezone using the Posix TZ format." \ - "time - I know local time already." - continent=$select_result - case $continent in - Americas) continent=America;; - *" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''` - esac - ' - esac + # Get list of names of countries in the continent or ocean. + countries=` + $AWK \ + "$output_country_list" \ + ="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" | + sort -f + ` + # If all zone table entries have comments, and there are + # at most 22 entries, asked based on those comments. + # This fits the prompt onto old-fashioned 24-line screens. + regions=` + $AWK ' + BEGIN { + TZ_ZONE_TABLE = substr(ARGV[1], 2) + ARGV[1] = "" + FS = "\t" + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^[^#]/ && !missing_comment) { + if ($4) + comment[++inlines] = $4 + else + missing_comment = 1 + } + } + if (!missing_comment && inlines <= 22) + for (i = 1; i <= inlines; i++) + print comment[i] + } + ' ="$zone_table" + ` + + # If there's more than one country, ask the user which one. + case $countries in + *"$newline"*) + echo >&2 'Please select a country' \ + 'whose clocks agree with yours.' + doselect $countries + country_result=$select_result + country=$select_result;; + *) + country=$countries + esac - case $continent in - TZ) - # Ask the user for a Posix TZ string. Check that it conforms. - while - echo >&2 'Please enter the desired value' \ - 'of the TZ environment variable.' - echo >&2 'For example, AEST-10 is abbreviated' \ - 'AEST and is 10 hours' - echo >&2 'ahead (east) of Greenwich,' \ - 'with no daylight saving time.' - read TZ - $AWK -v TZ="$TZ" 'BEGIN { - tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})" - time = "(2[0-4]|[0-1]?[0-9])" \ - "(:[0-5][0-9](:[0-5][0-9])?)?" - offset = "[-+]?" time - mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]" - jdate = "((J[1-9]|[0-9]|J?[1-9][0-9]" \ - "|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])" - datetime = ",(" mdate "|" jdate ")(/" time ")?" - tzpattern = "^(:.*|" tzname offset "(" tzname \ - "(" offset ")?(" datetime datetime ")?)?)$" - if (TZ ~ tzpattern) exit 1 - exit 0 - }' - do - say >&2 "'$TZ' is not a conforming Posix timezone string." - done - TZ_for_date=$TZ;; - *) - case $continent in - coord) - case $coord in - '') - echo >&2 'Please enter coordinates' \ - 'in ISO 6709 notation.' - echo >&2 'For example, +4042-07403 stands for' - echo >&2 '40 degrees 42 minutes north,' \ - '74 degrees 3 minutes west.' - read coord;; - esac - distance_table=`$AWK \ - -v coord="$coord" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - "$output_distances_or_times" <"$TZ_ZONE_TABLE" | - sort -n | - sed "${location_limit}q" - ` - regions=`$AWK \ - -v distance_table="$distance_table" ' - BEGIN { - nlines = split(distance_table, line, /\n/) - for (nr = 1; nr <= nlines; nr++) { - nf = split(line[nr], f, /\t/) - print f[nf] - } - } - '` - echo >&2 'Please select one of the following timezones,' - echo >&2 'listed roughly in increasing order' \ - "of distance from $coord". - doselect $regions - region=$select_result - TZ=`$AWK \ - -v distance_table="$distance_table" \ - -v region="$region" ' - BEGIN { - nlines = split(distance_table, line, /\n/) - for (nr = 1; nr <= nlines; nr++) { - nf = split(line[nr], f, /\t/) - if (f[nf] == region) { - print f[4] - } - } - } - '` - ;; - *) - case $continent in - time) - minute_format='%a %b %d %H:%M' - old_minute=`TZ=UTC0 date +"$minute_format"` - for i in 1 2 3 - do - time_table_command=` - $AWK -v output_times=1 \ - "$output_distances_or_times" <"$TZ_ZONE_TABLE" - ` - time_table=`eval "$time_table_command"` - new_minute=`TZ=UTC0 date +"$minute_format"` - case $old_minute in - "$new_minute") break;; - esac - old_minute=$new_minute - done - echo >&2 "The system says Universal Time is $new_minute." - echo >&2 "Assuming that's correct, what is the local time?" - eval doselect ` - say "$time_table" | - sort -k2n -k2,5 -k1n | - $AWK '{ - line = $6 " " $7 " " $4 " " $5 - if (line == oldline) next - oldline = line - gsub(/'\''/, "&\\\\&&", line) - printf "'\''%s'\''\n", line - }' - ` - time=$select_result - zone_table=` - say "$time_table" | - $AWK -v time="$time" '{ - if ($6 " " $7 " " $4 " " $5 == time) { - sub(/[^\t]*\t/, "") - print - } - }' - ` - countries=` - say "$zone_table" | - $AWK \ - -v continent_re='' \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - "$output_country_list" | - sort -f - ` - ;; - *) - zone_table=file - # Get list of names of countries in the continent or ocean. - countries=`$AWK \ - -v continent_re="^$continent/" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - "$output_country_list" \ - <"$TZ_ZONE_TABLE" | sort -f - `;; - esac - - # If there's more than one country, ask the user which one. - case $countries in - *"$newline"*) - echo >&2 'Please select a country' \ - 'whose clocks agree with yours.' - doselect $countries - country_result=$select_result - country=$select_result;; - *) - country=$countries - esac - - - # Get list of timezones in the country. - regions=` - case $zone_table in - file) cat -- "$TZ_ZONE_TABLE";; - *) say "$zone_table";; - esac | - $AWK \ - -v country="$country" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - ' - BEGIN { - FS = "\t" - cc = country - while (getline &2 'Please select one of the following timezones.' - doselect $regions - region=$select_result - esac - - # Determine TZ from country and region. - TZ=` - case $zone_table in - file) cat -- "$TZ_ZONE_TABLE";; - *) say "$zone_table";; - esac | - $AWK \ - -v country="$country" \ - -v region="$region" \ - -v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \ - ' - BEGIN { - FS = "\t" - cc = country - while (getline &2 "$0: time zone files are not set up correctly" - exit 1 - } - esac + # Get list of timezones in the country. + regions=` + $AWK ' + BEGIN { + country = substr(ARGV[1], 2) + TZ_COUNTRY_TABLE = substr(ARGV[2], 2) + TZ_ZONE_TABLE = substr(ARGV[3], 2) + ARGV[1] = ARGV[2] = ARGV[3] = "" + FS = "\t" + cc = country + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 !~ /^#/ && country == $2) { + cc = $1 + break + } + } + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) + continue + if ($1 ~ cc) + print $4 + } + } + ' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table" + ` + + # If there's more than one region, ask the user which one. + case $regions in + *"$newline"*) + echo >&2 'Please select one of the following timezones.' + doselect $regions + region=$select_result + esac + + # Determine tz from country and region. + tz=` + $AWK ' + BEGIN { + country = substr(ARGV[1], 2) + region = substr(ARGV[2], 2) + TZ_COUNTRY_TABLE = substr(ARGV[3], 2) + TZ_ZONE_TABLE = substr(ARGV[4], 2) + ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = "" + FS = "\t" + cc = country + nlines = split(TZ_COUNTRY_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 !~ /^#/ && country == $2) { + cc = $1 + break + } + } + nlines = split(TZ_ZONE_TABLE, line, /\n/) + for (i = 1; i <= nlines; i++) { + $0 = line[i] + if ($0 ~ /^#/) + continue + if ($1 ~ cc && ($4 == region || !region)) + print $3 + } + } + ' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table" + ` + esac + + # Make sure the corresponding zoneinfo file exists. + TZ_for_date=$TZDIR/$tz + <"$TZ_for_date" || { + say >&2 "$0: time zone files are not set up correctly" + exit 1 + } + esac - # Use the proposed TZ to output the current date relative to UTC. - # Loop until they agree in seconds. - # Give up after 8 unsuccessful tries. - extra_info= - for i in 1 2 3 4 5 6 7 8 - do - TZdate=`LANG=C TZ="$TZ_for_date" date` - UTdate=`LANG=C TZ=UTC0 date` - TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'` - UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'` - case $TZsec in - $UTsec) - extra_info=" + # Use the proposed TZ to output the current date relative to UTC. + # Loop until they agree in seconds. + # Give up after 8 unsuccessful tries. + + extra_info= + for i in 1 2 3 4 5 6 7 8 + do + TZdate=`LANG=C TZ="$TZ_for_date" date` + UTdate=`LANG=C TZ=UTC0 date` + TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'` + UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'` + case $TZsec in + $UTsec) + extra_info=" Selected time is now: $TZdate. Universal Time is now: $UTdate." - break - esac - done - - - # Output TZ info and ask the user to confirm. - - echo >&2 "" - echo >&2 "Based on the following information:" - echo >&2 "" - case $time%$country_result%$region%$coord in - ?*%?*%?*%) - say >&2 " $time$newline $country_result$newline $region";; - ?*%?*%%|?*%%?*%) say >&2 " $time$newline $country_result$region";; - ?*%%%) say >&2 " $time";; - %?*%?*%) say >&2 " $country_result$newline $region";; - %?*%%) say >&2 " $country_result";; - %%?*%?*) say >&2 " coord $coord$newline $region";; - %%%?*) say >&2 " coord $coord";; - *) say >&2 " TZ='$TZ'" - esac - say >&2 "" - say >&2 "TZ='$TZ' will be used.$extra_info" - say >&2 "Is the above information OK?" - - doselect Yes No - ok=$select_result - case $ok in - Yes) break - esac + break + esac + done + + + # Output TZ info and ask the user to confirm. + + echo >&2 "" + echo >&2 "Based on the following information:" + echo >&2 "" + case $time%$country_result%$region%$coord in + ?*%?*%?*%) + say >&2 " $time$newline $country_result$newline $region";; + ?*%?*%%|?*%%?*%) say >&2 " $time$newline $country_result$region";; + ?*%%%) say >&2 " $time";; + %?*%?*%) say >&2 " $country_result$newline $region";; + %?*%%) say >&2 " $country_result";; + %%?*%?*) say >&2 " coord $coord$newline $region";; + %%%?*) say >&2 " coord $coord";; + *) say >&2 " TZ='$tz'" + esac + say >&2 "" + say >&2 "TZ='$tz' will be used.$extra_info" + say >&2 "Is the above information OK?" + + doselect Yes No + ok=$select_result + case $ok in + Yes) break + esac do coord= done case $SHELL in -*csh) file=.login line="setenv TZ '$TZ'";; -*) file=.profile line="TZ='$TZ'; export TZ" +*csh) file=.login line="setenv TZ '$tz'";; +*) file=.profile line="TZ='$tz'; export TZ" esac test -t 1 && say >&2 " @@ -690,4 +799,4 @@ to the file '$file' in your home directory; then log out and log in again. Here is that TZ value again, this time on standard output so that you can use the $0 command in shell scripts:" -say "$TZ" +say "$tz" diff --git a/zdump.8 b/zdump.8 index f77c0c7..c3f0bba 100644 --- a/zdump.8 +++ b/zdump.8 @@ -152,10 +152,9 @@ tabbed columns line up.) .nf .sp .if \n(.g .ft CR -.if t .in +.5i -.if n .in +2 +.in +2 .nr w \w'1896-01-13 'u+\n(.i -.ta \w'1896-01-13 'u +\w'12:01:26 'u +\w'-103126 'u +\w'HWT 'u +.ta \w'1896-01-13\0\0'u +\w'12:01:26\0\0'u +\w'-103126\0\0'u +\w'HWT\0\0'u TZ="Pacific/Honolulu" - - -103126 LMT 1896-01-13 12:01:26 -1030 HST diff --git a/zdump.c b/zdump.c index 6f9573e..7d99cc7 100644 --- a/zdump.c +++ b/zdump.c @@ -602,7 +602,7 @@ main(int argc, char *argv[]) if (!tz) { char const *e = strerror(errno); fprintf(stderr, _("%s: unknown timezone '%s': %s\n"), - progname, argv[1], e); + progname, argv[i], e); return EXIT_FAILURE; } if (now) { diff --git a/zic.8 b/zic.8 index c467efe..a958ddd 100644 --- a/zic.8 +++ b/zic.8 @@ -95,7 +95,7 @@ as local time. .B zic will act as if the input contained a link line of the form .sp -.ti +.5i +.ti +2 .ta \w'Link\0\0'u +\w'\fItimezone\fP\0\0'u Link \fItimezone\fP localtime .sp @@ -118,9 +118,15 @@ TZ strings like "EET\*-2EEST" that lack transition rules. .B zic will act as if the input contained a link line of the form .sp -.ti +.5i +.ti +2 Link \fItimezone\fP posixrules .sp +If +.I timezone +is +.q "\*-" +(the default), any already-existing link is removed. +.sp Unless .I timezone is .q "\*-" , @@ -131,12 +137,6 @@ and it should not be combined with if .IR timezone 's transitions are at standard time or Universal Time (UT) instead of local time. -.sp -If -.I timezone -is -.BR \*- , -any already-existing link is removed. .TP .BR "\*-r " "[\fB@\fP\fIlo\fP][\fB/@\fP\fIhi\fP]" Limit the applicability of output files @@ -330,19 +330,19 @@ abbreviation must be unambiguous in context. .PP A rule line has the form .nf -.ti +.5i +.ti +2 .ta \w'Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'\*-\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00w\0\0'u +\w'1:00d\0\0'u .sp Rule NAME FROM TO \*- IN ON AT SAVE LETTER/S .sp For example: -.ti +.5i +.ti +2 .sp Rule US 1967 1973 \*- Apr lastSun 2:00w 1:00d D .sp .fi The fields that make up a rule line are: -.TP "\w'LETTER/S'u" +.TP .B NAME Gives the name of the rule set that contains this line. The name must start with a character that is neither @@ -404,7 +404,7 @@ Month names may be abbreviated. Gives the day on which the rule takes effect. Recognized forms include: .nf -.in +.5i +.in +2 .sp .ta \w'Sun<=25\0\0'u 5 the fifth of the month @@ -413,7 +413,7 @@ lastMon the last Monday in the month Sun>=8 first Sunday on or after the eighth Sun<=25 last Sunday on or before the 25th .fi -.in -.5i +.in .sp A weekday name (e.g., .BR "Sunday" ) @@ -440,7 +440,7 @@ Gives the time of day at which the rule takes effect, relative to 00:00, the start of a calendar day. Recognized forms include: .nf -.in +.5i +.in +2 .sp .ta \w'00:19:32.13\0\0'u 2 time in hours @@ -454,7 +454,7 @@ Recognized forms include: \*-2:30 2.5 hours before 00:00 \*- equivalent to 0 .fi -.in -.5i +.in .sp Although .B zic @@ -532,18 +532,18 @@ the variable part is null. A zone line has the form .sp .nf -.ti +.5i +.ti +2 .ta \w'Zone\0\0'u +\w'Asia/Amman\0\0'u +\w'STDOFF\0\0'u +\w'Jordan\0\0'u +\w'FORMAT\0\0'u Zone NAME STDOFF RULES FORMAT [UNTIL] .sp For example: .sp -.ti +.5i +.ti +2 Zone Asia/Amman 2:00 Jordan EE%sT 2017 Oct 27 01:00 .sp .fi The fields that make up a zone line are: -.TP "\w'STDOFF'u" +.TP .B NAME The name of the timezone. This is the name used in creating the time conversion information file for the @@ -663,15 +663,15 @@ For example: .br .ne 7 .nf -.in +2m +.in +2 .ta \w'# Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'2006\0\0'u +\w'\*-\0\0'u +\w'Oct\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u .sp # Rule NAME FROM TO \*- IN ON AT SAVE LETTER/S Rule US 1967 2006 - Oct lastSun 2:00 0 S Rule US 1967 1973 - Apr lastSun 2:00 1:00 D -.ta \w'Zone\0\0America/Menominee\0\0'u +\w'STDOFF\0\0'u +\w'RULES\0\0'u +\w'FORMAT\0\0'u -# Zone\0\0NAME STDOFF RULES FORMAT [UNTIL] -Zone\0\0America/Menominee \*-5:00 \*- EST 1973 Apr 29 2:00 +.ta \w'# Zone\0\0'u +\w'America/Menominee\0\0'u +\w'STDOFF\0\0'u +\w'RULES\0\0'u +\w'FORMAT\0\0'u +# Zone NAME STDOFF RULES FORMAT [UNTIL] +Zone America/Menominee \*-5:00 \*- EST 1973 Apr 29 2:00 \*-6:00 US C%sT .sp .in @@ -687,13 +687,13 @@ interprets this more sensibly as a single transition from 02:00 CST (\*-05) to A link line has the form .sp .nf -.ti +.5i +.ti +2 .ta \w'Link\0\0'u +\w'Europe/Istanbul\0\0'u Link TARGET LINK-NAME .sp For example: .sp -.ti +.5i +.ti +2 Link Europe/Istanbul Asia/Istanbul .sp .fi @@ -717,7 +717,7 @@ For example: .sp .ne 3 .nf -.in +2m +.in +2 .ta \w'Zone\0\0'u +\w'Greenwich\0\0'u Link Greenwich G_M_T Link Etc/GMT Greenwich @@ -737,13 +737,13 @@ The file that describes leap seconds can have leap lines and an expiration line. Leap lines have the following form: .nf -.ti +.5i +.ti +2 .ta \w'Leap\0\0'u +\w'YEAR\0\0'u +\w'MONTH\0\0'u +\w'DAY\0\0'u +\w'HH:MM:SS\0\0'u +\w'CORR\0\0'u .sp Leap YEAR MONTH DAY HH:MM:SS CORR R/S .sp For example: -.ti +.5i +.ti +2 .sp Leap 2016 Dec 31 23:59:60 + S .sp @@ -791,13 +791,13 @@ option is used. .PP The expiration line, if present, has the form: .nf -.ti +.5i +.ti +2 .ta \w'Expires\0\0'u +\w'YEAR\0\0'u +\w'MONTH\0\0'u +\w'DAY\0\0'u .sp Expires YEAR MONTH DAY HH:MM:SS .sp For example: -.ti +.5i +.ti +2 .sp Expires 2020 Dec 28 00:00:00 .sp @@ -816,7 +816,7 @@ Here is an extended example of .B zic input, intended to illustrate many of its features. .nf -.in +2m +.in +2 .ta \w'# Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'\*-\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u .sp # Rule NAME FROM TO \*- IN ON AT SAVE LETTER/S diff --git a/zic.c b/zic.c index 5518051..b260851 100644 --- a/zic.c +++ b/zic.c @@ -14,6 +14,7 @@ #include "version.h" #include "private.h" +#include "tzdir.h" #include "tzfile.h" #include @@ -164,13 +165,8 @@ symlink(char const *target, char const *linkname) } #endif #ifndef AT_SYMLINK_FOLLOW -# if HAVE_LINK -# define linkat(targetdir, target, linknamedir, linkname, flag) \ - (itssymlink(target) ? (errno = ENOTSUP, -1) : link(target, linkname)) -# else # define linkat(targetdir, target, linknamedir, linkname, flag) \ (errno = ENOTSUP, -1) -# endif #endif static void addtt(zic_t starttime, int type); @@ -190,7 +186,7 @@ static void inrule(char ** fields, int nfields); static bool inzcont(char ** fields, int nfields); static bool inzone(char ** fields, int nfields); static bool inzsub(char **, int, bool); -static bool itssymlink(char const *); +static int itssymlink(char const *, int *); static bool is_alpha(char a); static char lowerit(char); static void mkdirs(char const *, bool); @@ -901,7 +897,8 @@ static zic_t const max_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t lo_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); static zic_t hi_time = MAXVAL(zic_t, TIME_T_BITS_IN_FILE); -/* The time specified by the -R option, defaulting to MIN_TIME. */ +/* The time specified by the -R option, defaulting to MIN_TIME; + or lo_time, whichever is greater. */ static zic_t redundant_time = MINVAL(zic_t, TIME_T_BITS_IN_FILE); /* The time specified by an Expires line, or negative if no such line. */ @@ -1025,7 +1022,8 @@ main(int argc, char **argv) directory = optarg; else { fprintf(stderr, -_("%s: More than one -d option specified\n"), + _("%s: More than one -d option" + " specified\n"), progname); return EXIT_FAILURE; } @@ -1035,7 +1033,8 @@ _("%s: More than one -d option specified\n"), lcltime = optarg; else { fprintf(stderr, -_("%s: More than one -l option specified\n"), + _("%s: More than one -l option" + " specified\n"), progname); return EXIT_FAILURE; } @@ -1045,7 +1044,8 @@ _("%s: More than one -l option specified\n"), psxrules = optarg; else { fprintf(stderr, -_("%s: More than one -p option specified\n"), + _("%s: More than one -p option" + " specified\n"), progname); return EXIT_FAILURE; } @@ -1068,7 +1068,8 @@ _("%s: More than one -p option specified\n"), leapsec = optarg; else { fprintf(stderr, -_("%s: More than one -L option specified\n"), + _("%s: More than one -L option" + " specified\n"), progname); return EXIT_FAILURE; } @@ -1079,13 +1080,14 @@ _("%s: More than one -L option specified\n"), case 'r': if (timerange_given) { fprintf(stderr, -_("%s: More than one -r option specified\n"), + _("%s: More than one -r option" + " specified\n"), progname); return EXIT_FAILURE; } if (! timerange_option(optarg)) { fprintf(stderr, -_("%s: invalid time range: %s\n"), + _("%s: invalid time range: %s\n"), progname, optarg); return EXIT_FAILURE; } @@ -1108,6 +1110,8 @@ _("%s: invalid time range: %s\n"), fprintf(stderr, _("%s: -R time exceeds -r cutoff\n"), progname); return EXIT_FAILURE; } + if (redundant_time < lo_time) + redundant_time = lo_time; if (bloat == 0) { static char const bloat_default[] = ZIC_BLOAT_DEFAULT; if (strcmp(bloat_default, "slim") == 0) @@ -1389,9 +1393,9 @@ rename_dest(char *tempname, char const *name) } } -/* 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 +/* Create symlink contents suitable for symlinking TARGET to LINKNAME, as a + freshly allocated string. TARGET should be a relative file name, and + is relative to the global variable DIRECTORY. LINKNAME can be either relative or absolute. */ static char * relname(char const *target, char const *linkname) @@ -1428,6 +1432,18 @@ relname(char const *target, char const *linkname) return result; } +/* Return true if A and B must have the same parent dir if A and B exist. + Return false if this is not necessarily true (though it might be true). + Keep it simple, and do not inspect the file system. */ +static bool +same_parent_dirs(char const *a, char const *b) +{ + for (; *a == *b; a++, b++) + if (!*a) + return true; + return ! (strchr(a, '/') || strchr(b, '/')); +} + static void dolink(char const *target, char const *linkname, bool staysymlink) { @@ -1435,6 +1451,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) int link_errno; char *tempname = NULL; char const *outname = linkname; + int targetissym = -2, linknameissym = -2; check_for_signal(); @@ -1456,13 +1473,32 @@ dolink(char const *target, char const *linkname, bool staysymlink) break; } link_errno = errno; + /* Linux 2.6.16 and 2.6.17 mishandle AT_SYMLINK_FOLLOW. */ + if (link_errno == EINVAL) + link_errno = ENOTSUP; +#if HAVE_LINK + /* If linkat is not supported, fall back on link(A, B). + However, skip this if A is a relative symlink + and A and B might not have the same parent directory. + On some platforms link(A, B) does not follow a symlink A, + and if A is relative it might misbehave elsewhere. */ + if (link_errno == ENOTSUP + && (same_parent_dirs(target, outname) + || 0 <= itssymlink(target, &targetissym))) { + if (link(target, outname) == 0) { + link_errno = 0; + break; + } + link_errno = errno; + } +#endif if (link_errno == EXDEV || link_errno == ENOTSUP) break; if (link_errno == EEXIST) { staysymlink &= !tempname; random_dirent(&outname, &tempname); - if (staysymlink && itssymlink(linkname)) + if (staysymlink && itssymlink(linkname, &linknameissym)) break; } else if (link_errno == ENOENT && !linkdirs_made) { mkdirs(linkname, true); @@ -1525,12 +1561,17 @@ dolink(char const *target, char const *linkname, bool staysymlink) rename_dest(tempname, linkname); } -/* Return true if NAME is a symbolic link. */ -static bool -itssymlink(char const *name) +/* Return 1 if NAME is an absolute symbolic link, -1 if it is relative, + 0 if it is not a symbolic link. If *CACHE is not -2, it is the + cached result of a previous call to this function with the same NAME. */ +static int +itssymlink(char const *name, int *cache) { - char c; - return 0 <= readlink(name, &c, 1); + if (*cache == -2) { + char c = '\0'; + *cache = readlink(name, &c, 1) < 0 ? 0 : c == '/' ? 1 : -1; + } + return *cache; } /* @@ -1843,16 +1884,14 @@ inzone(char **fields, int nfields) return false; } if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) { - error( -_("\"Zone %s\" line and -l option are mutually exclusive"), - tzdefault); - return false; + 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; + 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 && @@ -1944,10 +1983,9 @@ inzsub(char **fields, int nfields, bool iscont) 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; + error(_("Zone continuation line end time is" + " not after end time of previous line")); + return false; } } z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]); @@ -2948,6 +2986,10 @@ rule_cmp(struct rule const *a, struct rule const *b) return a->r_dayofmonth - b->r_dayofmonth; } +/* Store into RESULT a POSIX TZ string that represent the future + predictions for the zone ZPFIRST with ZONECOUNT entries. Return a + compatibility indicator (a TZDB release year) if successful, a + negative integer if no such TZ string exissts. */ static int stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount) { @@ -3087,7 +3129,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) register int compat; register bool do_extend; register char version; - ptrdiff_t lastatmax = -1; + zic_t nonTZlimtime = ZIC_MIN; + int nonTZlimtype = -1; zic_t max_year0; int defaulttype = -1; @@ -3203,7 +3246,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) unspecifiedtype = addtype(0, "-00", false, false, false); for (i = 0; i < zonecount; ++i) { - struct rule *prevrp = NULL; /* ** A guess that may well be corrected later. */ @@ -3213,8 +3255,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) bool useuntil = i < (zonecount - 1); zic_t stdoff = zp->z_stdoff; zic_t startoff = stdoff; - zic_t prevktime; - INITIALIZE(prevktime); if (useuntil && zp->z_untiltime <= min_time) continue; eat(zp->z_filenum, zp->z_linenum); @@ -3228,6 +3268,10 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) startttisut); if (usestart) { addtt(starttime, type); + if (useuntil && nonTZlimtime < starttime) { + nonTZlimtime = starttime; + nonTZlimtype = type; + } usestart = false; } else defaulttype = type; @@ -3355,23 +3399,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) doabbr(ab, zp, rp->r_abbrvar, rp->r_isdst, rp->r_save, false); offset = oadd(zp->z_stdoff, rp->r_save); - if (!want_bloat() && !useuntil && !do_extend - && prevrp && lo_time <= prevktime - && redundant_time <= ktime - && rp->r_hiyear == ZIC_MAX - && prevrp->r_hiyear == ZIC_MAX) - break; type = addtype(offset, ab, rp->r_isdst, rp->r_todisstd, rp->r_todisut); if (defaulttype < 0 && !rp->r_isdst) defaulttype = type; - if (rp->r_hiyear == ZIC_MAX - && ! (0 <= lastatmax - && ktime < attypes[lastatmax].at)) - lastatmax = timecnt; addtt(ktime, type); - prevrp = rp; - prevktime = ktime; + if (nonTZlimtime < ktime + && (useuntil || rp->r_hiyear != ZIC_MAX)) { + nonTZlimtime = ktime; + nonTZlimtype = type; + } } } } @@ -3382,7 +3419,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) isdst, save, false); eat(zp->z_filenum, zp->z_linenum); if (*startbuf == '\0') -error(_("can't determine time zone abbreviation to use just after until time")); + error(_("can't determine time zone abbreviation" + " to use just after until time")); else { int type = addtype(startoff, startbuf, isdst, startttisstd, startttisut); @@ -3406,8 +3444,34 @@ error(_("can't determine time zone abbreviation to use just after until time")); } if (defaulttype < 0) defaulttype = 0; - if (0 <= lastatmax) - attypes[lastatmax].dontmerge = true; + if (!do_extend && !want_bloat()) { + /* Keep trailing transitions that are no greater than this. */ + zic_t keep_at_max; + + /* The earliest transition into a time governed by the TZ string. */ + zic_t TZstarttime = ZIC_MAX; + for (i = 0; i < timecnt; i++) { + zic_t at = attypes[i].at; + if (nonTZlimtime < at && at < TZstarttime) + TZstarttime = at; + } + if (TZstarttime == ZIC_MAX) + TZstarttime = nonTZlimtime; + + /* Omit trailing transitions deducible from the TZ string, + and not needed for -r or -R. */ + keep_at_max = max(TZstarttime, redundant_time); + for (i = j = 0; i < timecnt; i++) + if (attypes[i].at <= keep_at_max) { + attypes[j].at = attypes[i].at; + attypes[j].dontmerge = (attypes[i].at == TZstarttime + && (nonTZlimtype != attypes[i].type + || strchr(envvar, ','))); + attypes[j].type = attypes[i].type; + j++; + } + timecnt = j; + } if (do_extend) { /* ** If we're extending the explicitly listed observations diff --git a/zone.tab b/zone.tab index dbcb617..3fa9306 100644 --- a/zone.tab +++ b/zone.tab @@ -48,7 +48,7 @@ AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) @@ -87,7 +87,7 @@ BN +0456+11455 Asia/Brunei BO -1630-06809 America/La_Paz BQ +120903-0681636 America/Kralendijk BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Para (east); Amapa +BR -0127-04829 America/Belem Para (east), Amapa BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) BR -0803-03454 America/Recife Pernambuco BR -0712-04812 America/Araguaina Tocantins @@ -107,21 +107,21 @@ BT +2728+08939 Asia/Thimphu BW -2439+02555 Africa/Gaborone BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) -CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE) +CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) -CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA +4339-07923 America/Toronto Eastern - ON & QC (most areas) CA +6344-06828 America/Iqaluit Eastern - NU (most areas) -CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) -CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +484531-0913718 America/Atikokan EST - ON (Atikokan), NU (Coral H) +CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +4906-11631 America/Creston MST - BC (Creston) @@ -207,8 +207,8 @@ HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest ID -0610+10648 Asia/Jakarta Java, Sumatra ID -0002+10920 Asia/Pontianak Borneo (west, central) -ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) -ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas +ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas IE +5320-00615 Europe/Dublin IL +314650+0351326 Asia/Jerusalem IM +5409-00428 Europe/Isle_of_Man @@ -355,7 +355,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea RW -0157+03004 Africa/Kigali @@ -418,7 +418,7 @@ US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) -US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +433649-1161209 America/Boise Mountain - ID (south), OR (east) US +332654-1120424 America/Phoenix MST - AZ (except Navajo) US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) diff --git a/zone1970.tab b/zone1970.tab index 1f1cecb..abd9489 100644 --- a/zone1970.tab +++ b/zone1970.tab @@ -37,7 +37,7 @@ #country- #codes coordinates TZ comments AD +4230+00131 Europe/Andorra -AE,OM,RE,SC,TF +2518+05518 Asia/Dubai Crozet, Scattered Is +AE,OM,RE,SC,TF +2518+05518 Asia/Dubai Crozet AF +3431+06912 Asia/Kabul AL +4120+01950 Europe/Tirane AM +4011+04430 Asia/Yerevan @@ -47,12 +47,13 @@ AQ -6736+06253 Antarctica/Mawson Mawson AQ -6448-06406 Antarctica/Palmer Palmer AQ -6734-06808 Antarctica/Rothera Rothera AQ -720041+0023206 Antarctica/Troll Troll +AQ -7824+10654 Antarctica/Vostok Vostok AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) AR -3124-06411 America/Argentina/Cordoba most areas: CB, CC, CN, ER, FM, MN, SE, SF AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) +AR -2828-06547 America/Argentina/Catamarca Catamarca (CT), Chubut (CH) AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) @@ -81,7 +82,7 @@ BG +4241+02319 Europe/Sofia BM +3217-06446 Atlantic/Bermuda BO -1630-06809 America/La_Paz BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Pará (east); Amapá +BR -0127-04829 America/Belem Pará (east), Amapá BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) BR -0803-03454 America/Recife Pernambuco BR -0712-04812 America/Araguaina Tocantins @@ -99,19 +100,19 @@ BR -0958-06748 America/Rio_Branco Acre BT +2728+08939 Asia/Thimphu BY +5354+02734 Europe/Minsk BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) -CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE +CA +4734-05243 America/St_Johns Newfoundland, Labrador (SE) +CA +4439-06336 America/Halifax Atlantic - NS (most areas), PE CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) CA +4606-06447 America/Moncton Atlantic - New Brunswick CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas) +CA,BS +4339-07923 America/Toronto Eastern - ON & QC (most areas) CA +6344-06828 America/Iqaluit Eastern - NU (most areas) -CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba +CA +4953-09709 America/Winnipeg Central - ON (west), Manitoba CA +744144-0944945 America/Resolute Central - NU (Resolute) CA +624900-0920459 America/Rankin_Inlet Central - NU (central) CA +5024-10439 America/Regina CST - SK (most areas) CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); NT (E); SK (W) +CA +5333-11328 America/Edmonton Mountain - AB, BC(E), NT(E), SK(W) CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) CA +682059-1334300 America/Inuvik Mountain - NT (west) CA +5546-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) @@ -126,7 +127,7 @@ CL -3327-07040 America/Santiago most of Chile CL -5309-07055 America/Punta_Arenas Region of Magallanes CL -2709-10926 Pacific/Easter Easter Island CN +3114+12128 Asia/Shanghai Beijing Time -CN,AQ +4348+08735 Asia/Urumqi Xinjiang Time, Vostok +CN +4348+08735 Asia/Urumqi Xinjiang Time CO +0436-07405 America/Bogota CR +0956-08405 America/Costa_Rica CU +2308-08222 America/Havana @@ -171,8 +172,8 @@ HT +1832-07220 America/Port-au-Prince HU +4730+01905 Europe/Budapest ID -0610+10648 Asia/Jakarta Java, Sumatra ID -0002+10920 Asia/Pontianak Borneo (west, central) -ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) -ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas +ID -0507+11924 Asia/Makassar Borneo (east, south), Sulawesi/Celebes, Bali, Nusa Tengarra, Timor (west) +ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya), Malukus/Moluccas IE +5320-00615 Europe/Dublin IL +314650+0351326 Asia/Jerusalem IN +2232+08822 Asia/Kolkata @@ -251,7 +252,7 @@ PK +2452+06703 Asia/Karachi PL +5215+02100 Europe/Warsaw PM +4703-05620 America/Miquelon PN -2504-13005 Pacific/Pitcairn -PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST +PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST - QC (Lower North Shore) PS +3130+03428 Asia/Gaza Gaza Strip PS +313200+0350542 Asia/Hebron West Bank PT +3843-00908 Europe/Lisbon Portugal (mainland) @@ -287,7 +288,7 @@ RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky RU +5934+15048 Asia/Magadan MSK+08 - Magadan RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); N Kuril Is +RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E), N Kuril Is RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea SA,AQ,KW,YE +2438+04643 Asia/Riyadh Syowa @@ -329,7 +330,7 @@ US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) US +394421-1045903 America/Denver Mountain (most areas) -US +433649-1161209 America/Boise Mountain - ID (south); OR (east) +US +433649-1161209 America/Boise Mountain - ID (south), OR (east) US,CA +332654-1120424 America/Phoenix MST - AZ (most areas), Creston BC US +340308-1181434 America/Los_Angeles Pacific US +611305-1495401 America/Anchorage Alaska (most areas) diff --git a/zonenow.tab b/zonenow.tab new file mode 100644 index 0000000..2dbe8f0 --- /dev/null +++ b/zonenow.tab @@ -0,0 +1,301 @@ +# tzdb timezone descriptions, for users who do not care about old timestamps +# +# This file is in the public domain. +# +# From Paul Eggert (2023-12-18): +# This file contains a table where each row stands for a timezone +# where civil timestamps are predicted to agree from now on. +# This file is like zone1970.tab (see zone1970.tab's coments), +# but with the following changes: +# +# 1. Each timezone corresponds to a set of clocks that are planned +# to agree from now on. This is a larger set of clocks than in +# zone1970.tab, where each timezone's clocks must agree from 1970 on. +# 2. The first column is irrelevant and ignored. +# 3. The table is sorted in a different way: +# first by standard time UTC offset; +# then, if DST is used, by daylight saving UTC offset; +# then by time zone abbreviation. +# 4. Every timezone has a nonempty comments column, with wording +# distinguishing the timezone only from other timezones with the +# same UTC offset at some point during the year. +# +# The format of this table is experimental, and may change in future versions. +# +# This table is intended as an aid for users, to help them select timezones +# appropriate for their practical needs. It is not intended to take or +# endorse any position on legal or territorial claims. +# +#XX coordinates TZ comments +# +# -11 - SST +XX -1416-17042 Pacific/Pago_Pago Midway; Samoa ("SST") +# +# -11 +XX -1901-16955 Pacific/Niue Niue +# +# -10 - HST +XX +211825-1575130 Pacific/Honolulu Hawaii ("HST") +# +# -10 +XX -1732-14934 Pacific/Tahiti Tahiti; Cook Islands +# +# -10/-09 - HST / HDT (North America DST) +XX +515248-1763929 America/Adak western Aleutians in Alaska ("HST/HDT") +# +# -09:30 +XX -0900-13930 Pacific/Marquesas Marquesas +# +# -09 +XX -2308-13457 Pacific/Gambier Gambier +# +# -09/-08 - AKST/AKDT (North America DST) +XX +611305-1495401 America/Anchorage most of Alaska ("AKST/AKDT") +# +# -08 +XX -2504-13005 Pacific/Pitcairn Pitcairn +# +# -08/-07 - PST/PDT (North America DST) +XX +340308-1181434 America/Los_Angeles Pacific ("PST/PDT") - US & Canada; Mexico near US border +# +# -07 - MST +XX +332654-1120424 America/Phoenix Mountain Standard ("MST") - Arizona; western Mexico; Yukon +# +# -07/-06 - MST/MDT (North America DST) +XX +394421-1045903 America/Denver Mountain ("MST/MDT") - US & Canada; Mexico near US border +# +# -06 +XX -0054-08936 Pacific/Galapagos Galápagos +# +# -06 - CST +XX +1924-09909 America/Mexico_City Central Standard ("CST") - Saskatchewan; central Mexico; Central America +# +# -06/-05 (Chile DST) +XX -2709-10926 Pacific/Easter Easter Island +# +# -06/-05 - CST/CDT (North America DST) +XX +415100-0873900 America/Chicago Central ("CST/CDT") - US & Canada; Mexico near US border +# +# -05 +XX -1203-07703 America/Lima eastern South America +# +# -05 - EST +XX +175805-0764736 America/Jamaica Eastern Standard ("EST") - Caymans; Jamaica; eastern Mexico; Panama +# +# -05/-04 - CST/CDT (Cuba DST) +XX +2308-08222 America/Havana Cuba +# +# -05/-04 - EST/EDT (North America DST) +XX +404251-0740023 America/New_York Eastern ("EST/EDT") - US & Canada +# +# -04 +XX +1030-06656 America/Caracas western South America +# +# -04 - AST +XX +1828-06954 America/Santo_Domingo Atlantic Standard ("AST") - eastern Caribbean +# +# -04/-03 (Chile DST) +XX -3327-07040 America/Santiago most of Chile +# +# -04/-03 (Paraguay DST) +XX -2516-05740 America/Asuncion Paraguay +# +# -04/-03 - AST/ADT (North America DST) +XX +4439-06336 America/Halifax Atlantic ("AST/ADT") - Canada; Bermuda +# +# -03:30/-02:30 - NST/NDT (North America DST) +XX +4734-05243 America/St_Johns Newfoundland ("NST/NDT") +# +# -03 +XX -2332-04637 America/Sao_Paulo eastern South America +# +# -03/-02 (North America DST) +XX +4703-05620 America/Miquelon St Pierre & Miquelon +# +# -02 +XX -0351-03225 America/Noronha Fernando de Noronha; South Georgia +# +# -02/-01 (EU DST) +XX +6411-05144 America/Nuuk most of Greenland +# +# -01 +XX +1455-02331 Atlantic/Cape_Verde Cape Verde +# +# -01/+00 (EU DST) +XX +3744-02540 Atlantic/Azores Azores +# -01/+00 (EU DST) until 2024-03-31; then -02/-01 (EU DST) +XX +7029-02158 America/Scoresbysund Ittoqqortoormiit +# +# +00 - GMT +XX +0519-00402 Africa/Abidjan far western Africa; Iceland ("GMT") +# +# +00/+01 - GMT/BST (EU DST) +XX +513030-0000731 Europe/London United Kingdom ("GMT/BST") +# +# +00/+01 - WET/WEST (EU DST) +XX +3843-00908 Europe/Lisbon western Europe ("WET/WEST") +# +# +00/+02 - Troll DST +XX -720041+0023206 Antarctica/Troll Troll Station in Antarctica +# +# +01 - CET +XX +3647+00303 Africa/Algiers Algeria, Tunisia ("CET") +# +# +01 - WAT +XX +0627+00324 Africa/Lagos western Africa ("WAT") +# +# +01/+00 - IST/GMT (EU DST in reverse) +XX +5320-00615 Europe/Dublin Ireland ("IST/GMT") +# +# +01/+00 - (Morocco DST) +XX +3339-00735 Africa/Casablanca Morocco +# +# +01/+02 - CET/CEST (EU DST) +XX +4852+00220 Europe/Paris central Europe ("CET/CEST") +# +# +02 - CAT +XX -2558+03235 Africa/Maputo central Africa ("CAT") +# +# +02 - EET +XX +3254+01311 Africa/Tripoli Libya; Kaliningrad ("EET") +# +# +02 - SAST +XX -2615+02800 Africa/Johannesburg southern Africa ("SAST") +# +# +02/+03 - EET/EEST (EU DST) +XX +3758+02343 Europe/Athens eastern Europe ("EET/EEST") +# +# +02/+03 - EET/EEST (Egypt DST) +XX +3003+03115 Africa/Cairo Egypt +# +# +02/+03 - EET/EEST (Lebanon DST) +XX +3353+03530 Asia/Beirut Lebanon +# +# +02/+03 - EET/EEST (Moldova DST) +XX +4700+02850 Europe/Chisinau Moldova +# +# +02/+03 - EET/EEST (Palestine DST) +XX +3130+03428 Asia/Gaza Palestine +# +# +02/+03 - IST/IDT (Israel DST) +XX +314650+0351326 Asia/Jerusalem Israel +# +# +03 +XX +4101+02858 Europe/Istanbul Near East; Belarus +# +# +03 - EAT +XX -0117+03649 Africa/Nairobi eastern Africa ("EAT") +# +# +03 - MSK +XX +554521+0373704 Europe/Moscow Moscow ("MSK") +# +# +03:30 +XX +3540+05126 Asia/Tehran Iran +# +# +04 +XX +2518+05518 Asia/Dubai Russia; Caucasus; Persian Gulf; Seychelles; Réunion +# +# +04:30 +XX +3431+06912 Asia/Kabul Afghanistan +# +# +05 +XX +4120+06918 Asia/Tashkent Russia; Tajikistan; Turkmenistan; Uzbekistan; Maldives +# +# +05 - PKT +XX +2452+06703 Asia/Karachi Pakistan ("PKT") +# +# +05:30 +XX +0656+07951 Asia/Colombo Sri Lanka +# +# +05:30 - IST +XX +2232+08822 Asia/Kolkata India ("IST") +# +# +05:45 +XX +2743+08519 Asia/Kathmandu Nepal +# +# +06 +XX +2343+09025 Asia/Dhaka Russia; Kyrgyzstan; Bhutan; Bangladesh; Chagos +# +# +06:30 +XX +1647+09610 Asia/Yangon Myanmar; Cocos +# +# +07 +XX +1345+10031 Asia/Bangkok Russia; Indochina; Christmas Island +# +# +07 - WIB +XX -0610+10648 Asia/Jakarta Indonesia ("WIB") +# +# +08 +XX +0117+10351 Asia/Singapore Russia; Brunei; Malaysia; Singapore +# +# +08 - AWST +XX -3157+11551 Australia/Perth Western Australia ("AWST") +# +# +08 - CST +XX +3114+12128 Asia/Shanghai China ("CST") +# +# +08 - HKT +XX +2217+11409 Asia/Hong_Kong Hong Kong ("HKT") +# +# +08 - PHT +XX +1435+12100 Asia/Manila Philippines ("PHT") +# +# +08 - WITA +XX -0507+11924 Asia/Makassar Indonesia ("WITA") +# +# +08:45 +XX -3143+12852 Australia/Eucla Eucla +# +# +09 +XX +5203+11328 Asia/Chita Russia; Palau; East Timor +# +# +09 - JST +XX +353916+1394441 Asia/Tokyo Japan ("JST") +# +# +09 - KST +XX +3733+12658 Asia/Seoul Korea ("KST") +# +# +09 - WIT +XX -0232+14042 Asia/Jayapura Indonesia ("WIT") +# +# +09:30 - ACST +XX -1228+13050 Australia/Darwin Northern Territory ("ACST") +# +# +09:30/+10:30 - ACST/ACDT (Australia DST) +XX -3455+13835 Australia/Adelaide South Australia ("ACST/ACDT") +# +# +10 +XX +4310+13156 Asia/Vladivostok Russia; Yap; Chuuk; Papua New Guinea; Dumont d'Urville +# +# +10 - AEST +XX -2728+15302 Australia/Brisbane Queensland ("AEST") +# +# +10 - ChST +XX +1328+14445 Pacific/Guam Mariana Islands ("ChST") +# +# +10/+11 - AEST/AEDT (Australia DST) +XX -3352+15113 Australia/Sydney southeast Australia ("AEST/AEDT") +# +# +10:30/+11 +XX -3133+15905 Australia/Lord_Howe Lord Howe Island +# +# +11 +XX -0613+15534 Pacific/Bougainville Russia; Kosrae; Bougainville; Solomons +# +# +11/+12 (Australia DST) +XX -2903+16758 Pacific/Norfolk Norfolk Island +# +# +12 +XX +5301+15839 Asia/Kamchatka Russia; Tuvalu; Fiji; etc. +# +# +12/+13 (New Zealand DST) +XX -3652+17446 Pacific/Auckland New Zealand ("NZST/NZDT") +# +# +12:45/+13:45 (Chatham DST) +XX -4357-17633 Pacific/Chatham Chatham Islands +# +# +13 +XX -210800-1751200 Pacific/Tongatapu Kanton; Tokelau; Samoa (western); Tonga +# +# +14 +XX +0152-15720 Pacific/Kiritimati Kiritimati