aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2024-02-13 18:30:52 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2024-02-13 18:30:52 +0000
commit378c74faf328dd07970e24ddaa2858531ff6cd08 (patch)
treec116ff274e76c285cb3af257a16a3232793e78ac
parentd5228e8957c2b6a9cbb2a73947640ce3c70097e8 (diff)
downloadsrc-vendor/tzcode.tar.gz
src-vendor/tzcode.zip
-rw-r--r--Makefile260
-rw-r--r--NEWS193
-rw-r--r--README19
-rw-r--r--date.114
-rw-r--r--date.1.txt15
-rw-r--r--localtime.c128
-rw-r--r--newctime.316
-rw-r--r--newctime.3.txt9
-rw-r--r--newstrftime.3152
-rw-r--r--newstrftime.3.txt169
-rw-r--r--newtzset.3206
-rw-r--r--newtzset.3.txt285
-rw-r--r--private.h44
-rw-r--r--strftime.c7
-rw-r--r--theory.html37
-rw-r--r--time2posix.32
-rw-r--r--tz-art.html419
-rw-r--r--tz-link.html118
-rw-r--r--tzfile.5114
-rw-r--r--tzfile.5.txt607
-rw-r--r--tzfile.h14
-rw-r--r--tzselect.82
-rw-r--r--tzselect.8.txt40
-rw-r--r--tzselect.ksh1006
-rw-r--r--version2
-rw-r--r--zdump.85
-rw-r--r--zdump.8.txt116
-rw-r--r--zdump.c2
-rw-r--r--zic.883
-rw-r--r--zic.8.txt609
-rw-r--r--zic.c239
31 files changed, 2783 insertions, 2149 deletions
diff --git a/Makefile b/Makefile
index 6edc73cc6ffb..d48354c72df4 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
@@ -35,7 +53,7 @@ DATAFORM= main
LOCALTIME= Factory
-# The POSIXRULES macro controls interpretation of POSIX-like TZ
+# The POSIXRULES macro controls interpretation of POSIX-2017.1-like TZ
# settings like TZ='EET-2EEST' that lack DST transition rules.
# If POSIXRULES is '-', no template is installed; this is the default.
# Any other value for POSIXRULES is obsolete and should not be relied on, as:
@@ -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 <unistd.h> does not work*
# -DHAVE_UTMPX_H=0 if <utmpx.h> does not work*
# -Dlocale_t=XXX if your system uses XXX instead of locale_t
-# -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;
@@ -250,7 +274,7 @@ LDLIBS=
# -DTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
# the default is system-supplied, typically "/usr/lib/locale"
# -DTZDEFRULESTRING=\",date/time,date/time\" to default to the specified
-# DST transitions for POSIX-style TZ strings lacking them,
+# DST transitions for POSIX.1-2017-style TZ strings lacking them,
# in the usual case where POSIXRULES is '-'. If not specified,
# TZDEFRULESTRING defaults to US rules for future DST transitions.
# This mishandles some past timestamps, as US DST rules have changed.
@@ -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 = \
@@ -312,9 +340,10 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
# guess TM_GMTOFF from other macros; define NO_TM_GMTOFF to suppress this.
# Similarly, if your system has a "zone abbreviation" field, define
# -DTM_ZONE=tm_zone
-# and define NO_TM_ZONE to suppress any guessing. Although these two fields
-# not required by POSIX, a future version of POSIX is planned to require them
-# and they are widely available on GNU/Linux and BSD systems.
+# and define NO_TM_ZONE to suppress any guessing.
+# Although these two fields are not required by POSIX.1-2017,
+# POSIX 202x/D4 requires them and they are widely available
+# on GNU/Linux and BSD systems.
#
# The next batch of options control support for external variables
# exported by tzcode. In practice these variables are less useful
@@ -324,7 +353,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
# # -DHAVE_TZNAME=0 # do not support "tzname"
# # -DHAVE_TZNAME=1 # support "tzname", which is defined by system library
# # -DHAVE_TZNAME=2 # support and define "tzname"
-# # to the "CFLAGS=" line. "tzname" is required by POSIX 1988 and later.
+# # to the "CFLAGS=" line. "tzname" is required by POSIX.1-1988 and later.
# # If not defined, the code attempts to guess HAVE_TZNAME from other macros.
# # Warning: unless time_tz is also defined, HAVE_TZNAME=1 can cause
# # crashes when combined with some platforms' standard libraries,
@@ -334,8 +363,8 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
# # -DUSG_COMPAT=0 # do not support
# # -DUSG_COMPAT=1 # support, and variables are defined by system library
# # -DUSG_COMPAT=2 # support and define variables
-# # to the "CFLAGS=" line; "timezone" and "daylight" are inspired by
-# # Unix Systems Group code and are required by POSIX 2008 (with XSI) and later.
+# # to the "CFLAGS=" line; "timezone" and "daylight" are inspired by Unix
+# # Systems Group code and are required by POSIX.1-2008 and later (with XSI).
# # If not defined, the code attempts to guess USG_COMPAT from other macros.
# #
# # To support the external variable "altzone", add
@@ -353,9 +382,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 +409,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,19 +419,33 @@ 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.1-202x/D4.
+#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.
LEAPSECONDS=
+# Where to fetch leap-seconds.list from.
+leaplist_URI = \
+ https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list
+# The file is generated by the IERS Earth Orientation Centre, in Paris.
+leaplist_TZ = Europe/Paris
+
# The zic command and its arguments.
zic= ./zic
@@ -418,22 +463,23 @@ 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
-# Name of curl <https://curl.haxx.se/>, used for HTML validation.
+# Name of curl <https://curl.haxx.se/>, used for HTML validation
+# and to fetch leap-seconds.list from upstream.
CURL= curl
# Name of GNU Privacy Guard <https://gnupg.org/>, used to sign distributions.
@@ -503,17 +549,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 +588,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 +596,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 +617,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 +627,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 +697,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)) ";' \
@@ -676,6 +726,28 @@ leapseconds: $(LEAP_DEPS)
-f leapseconds.awk leap-seconds.list >$@.out
mv $@.out $@
+# Awk script to extract a Git-style author from leap-seconds.list comments.
+EXTRACT_AUTHOR = \
+ author_line { sub(/^.[[:space:]]*/, ""); \
+ sub(/:[[:space:]]*/, " <"); \
+ printf "%s>\n", $$0; \
+ success = 1; \
+ exit \
+ } \
+ /Questions or comments to:/ { author_line = 1 } \
+ END { exit !success }
+
+# Fetch leap-seconds.list from upstream.
+fetch-leap-seconds.list:
+ $(CURL) -OR $(leaplist_URI)
+
+# Fetch leap-seconds.list from upstream and commit it to the local repository.
+commit-leap-seconds.list: fetch-leap-seconds.list
+ author=$$($(AWK) '$(EXTRACT_AUTHOR)' leap-seconds.list) && \
+ date=$$(TZ=$(leaplist_TZ) stat -c%y leap-seconds.list) && \
+ git commit --author="$$author" --date="$$date" -m'make $@' \
+ leap-seconds.list
+
# Arguments to pass to submakes of install_data.
# They can be overridden by later submake arguments.
INSTALLARGS = \
@@ -763,7 +835,7 @@ force_tzs: $(TZS_NEW)
libtz.a: $(LIBOBJS)
rm -f $@
- $(AR) -rc $@ $(LIBOBJS)
+ $(AR) $(ARFLAGS) $@ $(LIBOBJS)
$(RANLIB) $@
date: $(DATEOBJS)
@@ -771,26 +843,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 +883,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 +929,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 +1011,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 +1052,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 +1088,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 +1118,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 +1250,7 @@ tzdata$(VERSION)-rearguard.tar.gz: rearguard.zi set-timestamps.out
sed '1s/$$/-rearguard/' <version >$@.dir/version
: The dummy pacificnew pacifies TZUpdater 2.3.1 and earlier.
$(CREATE_EMPTY) $@.dir/pacificnew
- touch -cmr version $@.dir/version
+ touch -mr version $@.dir/version
LC_ALL=C && export LC_ALL && \
(cd $@.dir && \
tar $(TARFLAGS) -cf - \
@@ -1163,7 +1274,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 +1285,7 @@ tzdata$(VERSION)-tailored.tar.gz: set-timestamps.out
-e 's/ /-/g' \
-e 'p' \
<tzdata.zi >$@.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,15 +1337,16 @@ 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
.PHONY: check_web check_zishrink
-.PHONY: clean clean_misc dummy.zd force_tzs
+.PHONY: clean clean_misc commit-leap-seconds.list dummy.zd
+.PHONY: fetch-leap-seconds.list force_tzs
.PHONY: install install_data maintainer-clean names
.PHONY: posix_only posix_right public
.PHONY: rearguard_signatures rearguard_signatures_version
diff --git a/NEWS b/NEWS
index b54538aa4a82..d407342a50e6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,177 @@
News for the tz database
+Release 2024a - 2024-02-01 09:28:56 -0800
+
+ Briefly:
+ Kazakhstan unifies on UTC+5 beginning 2024-03-01.
+ Palestine springs forward a week later after Ramadan.
+ zic no longer pretends to support indefinite-past DST.
+ localtime no longer mishandles Ciudad Juárez in 2422.
+
+ Changes to future timestamps
+
+ Kazakhstan unifies on UTC+5. This affects Asia/Almaty and
+ Asia/Qostanay which together represent the eastern portion of the
+ country that will transition from UTC+6 on 2024-03-01 at 00:00 to
+ join the western portion. (Thanks to Zhanbolat Raimbekov.)
+
+ Palestine springs forward a week later than previously predicted
+ in 2024 and 2025. (Thanks to Heba Hamad.) Change spring-forward
+ predictions to the second Saturday after Ramadan, not the first;
+ this also affects other predictions starting in 2039.
+
+ Changes to past timestamps
+
+ Asia/Ho_Chi_Minh's 1955-07-01 transition occurred at 01:00
+ not 00:00. (Thanks to Đoàn Trần Công Danh.)
+
+ From 1947 through 1949, Toronto's transitions occurred at 02:00
+ not 00:00. (Thanks to Chris Walton.)
+
+ In 1911 Miquelon adopted standard time on June 15, not May 15.
+
+ Changes to code
+
+ The FROM and TO columns of Rule lines can no longer be "minimum"
+ or an abbreviation of "minimum", because TZif files do not support
+ DST rules that extend into the indefinite past - although these
+ rules were supported when TZif files had only 32-bit data, this
+ stopped working when 64-bit TZif files were introduced in 1995.
+ This should not be a problem for realistic data, since DST was
+ first used in the 20th century. As a transition aid, FROM columns
+ like "minimum" are now diagnosed and then treated as if they were
+ the year 1900; this should suffice for TZif files on old systems
+ with only 32-bit time_t, and it is more compatible with bugs in
+ 2023c-and-earlier localtime.c. (Problem reported by Yoshito
+ Umaoka.)
+
+ localtime and related functions no longer mishandle some
+ timestamps that occur about 400 years after a switch to a time
+ zone with a DST schedule. In 2023d data this problem was visible
+ for some timestamps in November 2422, November 2822, etc. in
+ America/Ciudad_Juarez. (Problem reported by Gilmore Davidson.)
+
+ strftime %s now uses tm_gmtoff if available. (Problem and draft
+ patch reported by Dag-Erling Smørgrav.)
+
+ Changes to build procedure
+
+ The leap-seconds.list file is now copied from the IERS instead of
+ from its downstream counterpart at NIST, as the IERS version is
+ now in the public domain too and tends to be more up-to-date.
+ (Thanks to Martin Burnicki for liaisoning with the IERS.)
+
+ Changes to documentation
+
+ The strftime man page documents which struct tm members affect
+ which conversion specs, and that tzset is called. (Problems
+ reported by Robert Elz and Steve Summit.)
+
+
+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 +248,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 +898,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 +4479,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 +4644,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 145aacd495b7..edabd2e0690f 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 <https://www.iana.org/time-zones/repository/tz-link.html> or the
-file tz-link.html for how to acquire the code and data. Once acquired,
-read the comments in the file 'Makefile' and make any changes needed
-to make things right for your system, especially if you are using some
-platform other than GNU/Linux. Then run the following commands,
-substituting your desired installation directory for "$HOME/tzdir":
-
- make TOPDIR=$HOME/tzdir install
- $HOME/tzdir/usr/bin/zdump -v America/Los_Angeles
+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/date.1 b/date.1
index e8107212364d..01907bc76e2c 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/date.1.txt b/date.1.txt
index f7b28373b278..0a145aec6e1c 100644
--- a/date.1.txt
+++ b/date.1.txt
@@ -89,20 +89,19 @@ DESCRIPTION
Use Universal Time when setting and showing the date and time.
-r seconds
- Output the date that corresponds to seconds past the epoch of
- 1970-01-01 00:00:00 UTC, where seconds should be an integer,
- either decimal, octal (leading 0), or hexadecimal (leading 0x),
+ Output the date that corresponds to seconds past the epoch of
+ 1970-01-01 00:00:00 UTC, where seconds should be an integer,
+ either decimal, octal (leading 0), or hexadecimal (leading 0x),
preceded by an optional sign.
FILES
/etc/localtime local timezone file
/usr/lib/locale/L/LC_TIME description of time locale L
- /usr/share/zoneinfo timezone information directory
- /usr/share/zoneinfo/posixrules default DST rules (obsolete,
- and can cause bugs if present)
+ /usr/share/zoneinfo timezone directory
+ /usr/share/zoneinfo/posixrules default DST rules (obsolete)
/usr/share/zoneinfo/GMT for UTC leap seconds
- If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
- /usr/share/zoneinfo/posixrules.
+ If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
+ /usr/share/zoneinfo/GMT0 if present.
Time Zone Database date(1)
diff --git a/localtime.c b/localtime.c
index 818d58f8e7be..940a04fd09ab 100644
--- a/localtime.c
+++ b/localtime.c
@@ -7,7 +7,7 @@
/*
** Leap second handling from Bradley White.
-** POSIX-style TZ environment variable handling from Guy Harris.
+** POSIX.1-1988 style TZ environment variable handling from Guy Harris.
*/
/*LINTLIBRARY*/
@@ -15,6 +15,7 @@
#define LOCALTIME_IMPLEMENTATION
#include "private.h"
+#include "tzdir.h"
#include "tzfile.h"
#include <fcntl.h>
@@ -105,12 +106,17 @@ static char const UNSPEC[] = "-00";
for ttunspecified to work without crashing. */
enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
-/* Limit to time zone abbreviation length in POSIX-style TZ strings.
+/* Limit to time zone abbreviation length in POSIX.1-2017-style TZ strings.
This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */
#ifndef TZNAME_MAXIMUM
# 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".
*/
@@ -967,7 +935,8 @@ getoffset(register const char *strp, int_fast32_t *const offsetp)
/*
** Given a pointer into a timezone string, extract a rule in the form
-** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** date[/time]. See POSIX Base Definitions section 8.3 variable TZ
+** for the format of "date" and "time".
** If a valid rule is not found, return NULL.
** Otherwise, return a pointer to the first character not part of the rule.
*/
@@ -1111,12 +1080,12 @@ transtime(const int year, register const struct rule *const rulep,
}
/*
-** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** Given a POSIX.1-2017-style TZ string, fill in the rule tables as
** appropriate.
*/
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 +1128,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 +1176,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;
@@ -1236,7 +1205,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
}
yearlim = yearbeg;
- if (increment_overflow(&yearlim, YEARSPERREPEAT + 1))
+ if (increment_overflow(&yearlim, years_of_observations))
yearlim = INT_MAX;
for (year = yearbeg; year < yearlim; year++) {
int_fast32_t
@@ -1273,7 +1242,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
if (endtime < leaplo) {
yearlim = year;
if (increment_overflow(&yearlim,
- YEARSPERREPEAT + 1))
+ years_of_observations))
yearlim = INT_MAX;
}
if (increment_overflow_time
@@ -1285,7 +1254,7 @@ tzparse(const char *name, struct state *sp, struct state *basep)
if (! timecnt) {
sp->ttis[0] = sp->ttis[1];
sp->typecnt = 1; /* Perpetual DST. */
- } else if (YEARSPERREPEAT < year - yearbeg)
+ } else if (years_of_observations <= year - yearbeg)
sp->goback = sp->goahead = true;
} else {
register int_fast32_t theirstdoffset;
@@ -1344,8 +1313,8 @@ tzparse(const char *name, struct state *sp, struct state *basep)
/*
** Transitions from DST to DDST
** will effectively disappear since
- ** POSIX provides for only one DST
- ** offset.
+ ** POSIX.1-2017 provides for only one
+ ** DST offset.
*/
if (isdst && !sp->ttis[j].tt_ttisstd) {
sp->ats[i] += dstoffset -
@@ -1366,15 +1335,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);
@@ -1516,7 +1484,8 @@ tzfree(timezone_t sp)
**
** If successful and SETNAME is nonzero,
** set the applicable parts of tzname, timezone and altzone;
-** however, it's OK to omit this step if the timezone is POSIX-compatible,
+** however, it's OK to omit this step
+** if the timezone is compatible with POSIX.1-2017
** since in that case tzset should have already done this step correctly.
** SETNAME's type is int_fast32_t for compatibility with gmtsub,
** but it is actually a boolean and its value should be 0 or 1.
@@ -1703,6 +1672,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 +2285,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)
{
@@ -2320,10 +2294,18 @@ timelocal(struct tm *tmp)
tmp->tm_isdst = -1; /* in case it wasn't initialized */
return mktime(tmp);
}
-#else
-static
#endif
-time_t
+
+#ifndef EXTERN_TIMEOFF
+# ifndef timeoff
+# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
+# endif
+# define EXTERN_TIMEOFF static
+#endif
+
+/* This function is obsolescent and may disapper in future releases.
+ Callers can instead use mktime_z with a fixed-offset zone. */
+EXTERN_TIMEOFF time_t
timeoff(struct tm *tmp, long offset)
{
if (tmp)
diff --git a/newctime.3 b/newctime.3
index 05bb7deaba51..3b54d4ad6ad2 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/newctime.3.txt b/newctime.3.txt
index 7a59bbe05e3b..01ec922f1461 100644
--- a/newctime.3.txt
+++ b/newctime.3.txt
@@ -143,14 +143,13 @@ DESCRIPTION
exist in this form in future releases of this code.
FILES
- /usr/share/zoneinfo timezone information directory
- /usr/share/zoneinfo/localtime local timezone file
- /usr/share/zoneinfo/posixrules default DST rules (obsolete,
- and can cause bugs if present)
+ /etc/localtime local timezone file
+ /usr/share/zoneinfo timezone directory
+ /usr/share/zoneinfo/posixrules default DST rules (obsolete)
/usr/share/zoneinfo/GMT for UTC leap seconds
If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
- /usr/share/zoneinfo/posixrules.
+ /usr/share/zoneinfo/GMT0 if present.
SEE ALSO
getenv(3), newstrftime(3), newtzset(3), time(2), tzfile(5)
diff --git a/newstrftime.3 b/newstrftime.3
index 432c3e889344..704318ea2693 100644
--- a/newstrftime.3
+++ b/newstrftime.3
@@ -64,7 +64,7 @@ strftime \- format date and time
The
.B strftime
function formats the information from
-.I timeptr
+.BI * timeptr
into the array pointed to by
.I buf
according to the string pointed to by
@@ -85,47 +85,94 @@ bytes are placed into the array.
.PP
Each conversion specification is replaced by the characters as
follows which are then copied into the array.
+The characters depend on the values of zero or more members of
+.BI * timeptr
+as specified by brackets in the description.
+If a bracketed member name is followed by
+.q + ,
+.B strftime
+can use the named member even though POSIX.1-2017 does not list it;
+if the name is followed by
+.q \*- ,
+.B strftime
+ignores the member even though POSIX.1-2017 lists it
+which means portable code should set it.
+For portability,
+.BI * timeptr
+should be initialized as if by a successful call to
+.BR gmtime ,
+.BR localtime ,
+.BR mktime ,
+.BR timegm ,
+or similar functions.
.TP
%A
is replaced by the locale's full weekday name.
+.RI [ tm_wday ]
.TP
%a
is replaced by the locale's abbreviated weekday name.
+.RI [ tm_wday ]
.TP
%B
is replaced by the locale's full month name.
+.RI [ tm_mon ]
.TP
%b or %h
is replaced by the locale's abbreviated month name.
+.RI [ tm_mon ]
.TP
%C
is replaced by the century (a year divided by 100 and truncated to an integer)
-as a decimal number [00,99].
+as a decimal number, with at least two digits by default.
+.RI [ tm_year ]
.TP
%c
is replaced by the locale's appropriate date and time representation.
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_mon ,
+.IR tm_mday ,
+.IR tm_wday ,
+.IR tm_hour ,
+.IR tm_min ,
+.IR tm_sec ,
+.IR tm_gmtoff +,
+.IR tm_zone +,
+.IR tm_isdst \*-].
.TP
%D
is equivalent to
.c %m/%d/%y .
+.RI [ tm_year ,
+.IR tm_mon ,
+.IR tm_mday ]
.TP
%d
is replaced by the day of the month as a decimal number [01,31].
+.RI [ tm_mday ]
.TP
%e
is replaced by the day of month as a decimal number [1,31];
single digits are preceded by a blank.
+.RI [ tm_mday ]
.TP
%F
is equivalent to
.c %Y-%m-%d
(the ISO 8601 date format).
+.RI [ tm_year ,
+.IR tm_mon ,
+.IR tm_mday ]
.TP
%G
is replaced by the ISO 8601 year with century as a decimal number.
See also the
.c %V
conversion specification.
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_wday ]
.TP
%g
is replaced by the ISO 8601 year without century as a decimal number [00,99].
@@ -134,29 +181,39 @@ This is the year that includes the greater part of the week.
See also the
.c %V
conversion specification.
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_wday ]
.TP
%H
is replaced by the hour (24-hour clock) as a decimal number [00,23].
+.RI [ tm_hour ]
.TP
%I
is replaced by the hour (12-hour clock) as a decimal number [01,12].
+.RI [ tm_hour ]
.TP
%j
is replaced by the day of the year as a decimal number [001,366].
+.RI [ tm_yday ]
.TP
%k
is replaced by the hour (24-hour clock) as a decimal number [0,23];
single digits are preceded by a blank.
+.RI [ tm_hour ]
.TP
%l
is replaced by the hour (12-hour clock) as a decimal number [1,12];
single digits are preceded by a blank.
+.RI [ tm_hour ]
.TP
%M
is replaced by the minute as a decimal number [00,59].
+.RI [ tm_min ]
.TP
%m
is replaced by the month as a decimal number [01,12].
+.RI [ tm_mon ]
.TP
%n
is replaced by a newline.
@@ -166,28 +223,58 @@ is replaced by the locale's equivalent of either
.q AM
or
.q PM .
+.RI [ tm_hour ]
.TP
%R
is replaced by the time in the format
.c %H:%M .
+.RI [ tm_hour ,
+.IR tm_min ]
.TP
%r
is replaced by the locale's representation of 12-hour clock time
using AM/PM notation.
+.RI [ tm_hour ,
+.IR tm_min ,
+.IR tm_sec ]
.TP
%S
is replaced by the second as a decimal number [00,60].
The range of
seconds is [00,60] instead of [00,59] to allow for the periodic occurrence
of leap seconds.
+.RI [ tm_sec ]
.TP
%s
is replaced by the number of seconds since the Epoch (see
.BR ctime (3)).
+Although %s is reliable in this implementation,
+it can have glitches on other platforms (notably platforms lacking
+.IR tm_gmtoff ),
+so portable code should format a
+.B time_t
+value directly via something like
+.B sprintf
+instead of via
+.B localtime
+followed by
+.B strftime
+with "%s".
+.RI [ tm_year ,
+.IR tm_mon ,
+.IR tm_mday ,
+.IR tm_hour ,
+.IR tm_min ,
+.IR tm_sec ,
+.IR tm_gmtoff +,
+.IR tm_isdst \*-].
.TP
%T
is replaced by the time in the format
.c %H:%M:%S .
+.RI [ tm_hour ,
+.IR tm_min ,
+.IR tm_sec ]
.TP
%t
is replaced by a tab.
@@ -195,10 +282,14 @@ is replaced by a tab.
%U
is replaced by the week number of the year (Sunday as the first day of
the week) as a decimal number [00,53].
+.RI [ tm_wday ,
+.IR tm_yday ,
+.IR tm_year \*-]
.TP
%u
is replaced by the weekday (Monday as the first day of the week)
as a decimal number [1,7].
+.RI [ tm_wday ]
.TP
%V
is replaced by the week number of the year (Monday as the first day of
@@ -208,30 +299,64 @@ it is week 53 of the previous year, and the next week is week 1.
The year is given by the
.c %G
conversion specification.
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_wday ]
.TP
%W
is replaced by the week number of the year (Monday as the first day of
the week) as a decimal number [00,53].
+.RI [ tm_yday ,
+.IR tm_wday ]
.TP
%w
is replaced by the weekday (Sunday as the first day of the week)
as a decimal number [0,6].
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_wday ]
.TP
%X
is replaced by the locale's appropriate time representation.
+.RI [ tm_year \*-,
+.IR tm_yday \*-,
+.IR tm_mon \*-,
+.IR tm_mday \*-,
+.IR tm_wday \*-,
+.IR tm_hour ,
+.IR tm_min ,
+.IR tm_sec ,
+.IR tm_gmtoff +,
+.IR tm_zone +,
+.IR tm_isdst \*-].
.TP
%x
is replaced by the locale's appropriate date representation.
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_mon ,
+.IR tm_mday ,
+.IR tm_wday ,
+.IR tm_hour \*-,
+.IR tm_min \*-,
+.IR tm_sec \*-,
+.IR tm_gmtoff \*-,
+.IR tm_zone \*-,
+.IR tm_isdst \*-].
.TP
%Y
is replaced by the year with century as a decimal number.
+.RI [ tm_year ]
.TP
%y
is replaced by the year without century as a decimal number [00,99].
+.RI [ tm_year ]
.TP
%Z
is replaced by the time zone abbreviation,
or by the empty string if this is not determinable.
+.RI [ tm_zone +,
+.IR tm_isdst \*-]
.TP
%z
is replaced by the offset from the Prime Meridian
@@ -244,6 +369,9 @@ but local time is indeterminate; by convention this is used for
locations while uninhabited, and corresponds to a zero offset when the
time zone abbreviation begins with
.q "\*-" .
+.RI [ tm_gmtoff +,
+.IR tm_zone +,
+.IR tm_isdst \*-]
.TP
%%
is replaced by a single %.
@@ -252,6 +380,26 @@ is replaced by a single %.
is replaced by the locale's date and time in
.BR date (1)
format.
+.RI [ tm_year ,
+.IR tm_yday ,
+.IR tm_mon ,
+.IR tm_mday ,
+.IR tm_wday ,
+.IR tm_hour ,
+.IR tm_min ,
+.IR tm_sec ,
+.IR tm_gmtoff ,
+.IR tm_zone ]
+.PP
+As a side effect,
+.B strftime
+also behaves as if
+.B tzset
+were called.
+This is for compatibility with older platforms, as required by POSIX;
+it is not needed for
+.BR tzset 's
+own use.
.SH "RETURN VALUE"
If the conversion is successful,
.B strftime
diff --git a/newstrftime.3.txt b/newstrftime.3.txt
index 9227a8ebc6e3..ee7ed3031188 100644
--- a/newstrftime.3.txt
+++ b/newstrftime.3.txt
@@ -12,7 +12,7 @@ SYNOPSIS
cc ... -ltz
DESCRIPTION
- The strftime function formats the information from timeptr into the
+ The strftime function formats the information from *timeptr into the
array pointed to by buf according to the string pointed to by format.
The format string consists of zero or more conversion specifications
@@ -23,141 +23,174 @@ DESCRIPTION
No more than maxsize bytes are placed into the array.
Each conversion specification is replaced by the characters as follows
- which are then copied into the array.
+ which are then copied into the array. The characters depend on the
+ values of zero or more members of *timeptr as specified by brackets in
+ the description. If a bracketed member name is followed by "+",
+ strftime can use the named member even though POSIX.1-2017 does not
+ list it; if the name is followed by "-", strftime ignores the member
+ even though POSIX.1-2017 lists it which means portable code should set
+ it. For portability, *timeptr should be initialized as if by a
+ successful call to gmtime, localtime, mktime, timegm, or similar
+ functions.
- %A is replaced by the locale's full weekday name.
+ %A is replaced by the locale's full weekday name. [tm_wday]
- %a is replaced by the locale's abbreviated weekday name.
+ %a is replaced by the locale's abbreviated weekday name. [tm_wday]
- %B is replaced by the locale's full month name.
+ %B is replaced by the locale's full month name. [tm_mon]
%b or %h
- is replaced by the locale's abbreviated month name.
+ is replaced by the locale's abbreviated month name. [tm_mon]
- %C is replaced by the century (a year divided by 100 and truncated
- to an integer) as a decimal number [00,99].
+ %C is replaced by the century (a year divided by 100 and truncated
+ to an integer) as a decimal number, with at least two digits by
+ default. [tm_year]
- %c is replaced by the locale's appropriate date and time
- representation.
+ %c is replaced by the locale's appropriate date and time
+ representation. [tm_year, tm_yday, tm_mon, tm_mday, tm_wday,
+ tm_hour, tm_min, tm_sec, tm_gmtoff+, tm_zone+, tm_isdst-].
- %D is equivalent to %m/%d/%y.
+ %D is equivalent to %m/%d/%y. [tm_year, tm_mon, tm_mday]
%d is replaced by the day of the month as a decimal number [01,31].
+ [tm_mday]
- %e is replaced by the day of month as a decimal number [1,31];
- single digits are preceded by a blank.
+ %e is replaced by the day of month as a decimal number [1,31];
+ single digits are preceded by a blank. [tm_mday]
- %F is equivalent to %Y-%m-%d (the ISO 8601 date format).
+ %F is equivalent to %Y-%m-%d (the ISO 8601 date format). [tm_year,
+ tm_mon, tm_mday]
- %G is replaced by the ISO 8601 year with century as a decimal
- number. See also the %V conversion specification.
+ %G is replaced by the ISO 8601 year with century as a decimal
+ number. See also the %V conversion specification. [tm_year,
+ tm_yday, tm_wday]
- %g is replaced by the ISO 8601 year without century as a decimal
+ %g is replaced by the ISO 8601 year without century as a decimal
number [00,99]. This is the year that includes the greater part
of the week. (Monday as the first day of a week). See also the
- %V conversion specification.
+ %V conversion specification. [tm_year, tm_yday, tm_wday]
- %H is replaced by the hour (24-hour clock) as a decimal number
- [00,23].
+ %H is replaced by the hour (24-hour clock) as a decimal number
+ [00,23]. [tm_hour]
- %I is replaced by the hour (12-hour clock) as a decimal number
- [01,12].
+ %I is replaced by the hour (12-hour clock) as a decimal number
+ [01,12]. [tm_hour]
- %j is replaced by the day of the year as a decimal number
- [001,366].
+ %j is replaced by the day of the year as a decimal number
+ [001,366]. [tm_yday]
- %k is replaced by the hour (24-hour clock) as a decimal number
- [0,23]; single digits are preceded by a blank.
+ %k is replaced by the hour (24-hour clock) as a decimal number
+ [0,23]; single digits are preceded by a blank. [tm_hour]
- %l is replaced by the hour (12-hour clock) as a decimal number
- [1,12]; single digits are preceded by a blank.
+ %l is replaced by the hour (12-hour clock) as a decimal number
+ [1,12]; single digits are preceded by a blank. [tm_hour]
- %M is replaced by the minute as a decimal number [00,59].
+ %M is replaced by the minute as a decimal number [00,59]. [tm_min]
- %m is replaced by the month as a decimal number [01,12].
+ %m is replaced by the month as a decimal number [01,12]. [tm_mon]
%n is replaced by a newline.
- %p is replaced by the locale's equivalent of either "AM" or "PM".
+ %p is replaced by the locale's equivalent of either "AM" or "PM".
+ [tm_hour]
- %R is replaced by the time in the format %H:%M.
+ %R is replaced by the time in the format %H:%M. [tm_hour, tm_min]
%r is replaced by the locale's representation of 12-hour clock time
- using AM/PM notation.
+ using AM/PM notation. [tm_hour, tm_min, tm_sec]
- %S is replaced by the second as a decimal number [00,60]. The
- range of seconds is [00,60] instead of [00,59] to allow for the
- periodic occurrence of leap seconds.
+ %S is replaced by the second as a decimal number [00,60]. The
+ range of seconds is [00,60] instead of [00,59] to allow for the
+ periodic occurrence of leap seconds. [tm_sec]
- %s is replaced by the number of seconds since the Epoch (see
- ctime(3)).
+ %s is replaced by the number of seconds since the Epoch (see
+ ctime(3)). Although %s is reliable in this implementation, it
+ can have glitches on other platforms (notably platforms lacking
+ tm_gmtoff), so portable code should format a time_t value
+ directly via something like sprintf instead of via localtime
+ followed by strftime with "%s". [tm_year, tm_mon, tm_mday,
+ tm_hour, tm_min, tm_sec, tm_gmtoff+, tm_isdst-].
- %T is replaced by the time in the format %H:%M:%S.
+ %T is replaced by the time in the format %H:%M:%S. [tm_hour,
+ tm_min, tm_sec]
%t is replaced by a tab.
- %U is replaced by the week number of the year (Sunday as the first
- day of the week) as a decimal number [00,53].
+ %U is replaced by the week number of the year (Sunday as the first
+ day of the week) as a decimal number [00,53]. [tm_wday,
+ tm_yday, tm_year-]
%u is replaced by the weekday (Monday as the first day of the week)
- as a decimal number [1,7].
+ as a decimal number [1,7]. [tm_wday]
- %V is replaced by the week number of the year (Monday as the first
- day of the week) as a decimal number [01,53]. If the week
+ %V is replaced by the week number of the year (Monday as the first
+ day of the week) as a decimal number [01,53]. If the week
containing January 1 has four or more days in the new year, then
- it is week 1; otherwise it is week 53 of the previous year, and
+ it is week 1; otherwise it is week 53 of the previous year, and
the next week is week 1. The year is given by the %G conversion
- specification.
+ specification. [tm_year, tm_yday, tm_wday]
- %W is replaced by the week number of the year (Monday as the first
- day of the week) as a decimal number [00,53].
+ %W is replaced by the week number of the year (Monday as the first
+ day of the week) as a decimal number [00,53]. [tm_yday,
+ tm_wday]
%w is replaced by the weekday (Sunday as the first day of the week)
- as a decimal number [0,6].
+ as a decimal number [0,6]. [tm_year, tm_yday, tm_wday]
- %X is replaced by the locale's appropriate time representation.
+ %X is replaced by the locale's appropriate time representation.
+ [tm_year-, tm_yday-, tm_mon-, tm_mday-, tm_wday-, tm_hour,
+ tm_min, tm_sec, tm_gmtoff+, tm_zone+, tm_isdst-].
- %x is replaced by the locale's appropriate date representation.
+ %x is replaced by the locale's appropriate date representation.
+ [tm_year, tm_yday, tm_mon, tm_mday, tm_wday, tm_hour-, tm_min-,
+ tm_sec-, tm_gmtoff-, tm_zone-, tm_isdst-].
- %Y is replaced by the year with century as a decimal number.
+ %Y is replaced by the year with century as a decimal number.
+ [tm_year]
- %y is replaced by the year without century as a decimal number
- [00,99].
+ %y is replaced by the year without century as a decimal number
+ [00,99]. [tm_year]
- %Z is replaced by the time zone abbreviation, or by the empty
- string if this is not determinable.
+ %Z is replaced by the time zone abbreviation, or by the empty
+ string if this is not determinable. [tm_zone+, tm_isdst-]
- %z is replaced by the offset from the Prime Meridian in the format
- +HHMM or -HHMM (ISO 8601) as appropriate, with positive values
+ %z is replaced by the offset from the Prime Meridian in the format
+ +HHMM or -HHMM (ISO 8601) as appropriate, with positive values
representing locations east of Greenwich, or by the empty string
if this is not determinable. The numeric time zone abbreviation
- -0000 is used when the time is Universal Time but local time is
- indeterminate; by convention this is used for locations while
+ -0000 is used when the time is Universal Time but local time is
+ indeterminate; by convention this is used for locations while
uninhabited, and corresponds to a zero offset when the time zone
- abbreviation begins with "-".
+ abbreviation begins with "-". [tm_gmtoff+, tm_zone+, tm_isdst-]
%% is replaced by a single %.
- %+ is replaced by the locale's date and time in date(1) format.
+ %+ is replaced by the locale's date and time in date(1) format.
+ [tm_year, tm_yday, tm_mon, tm_mday, tm_wday, tm_hour, tm_min,
+ tm_sec, tm_gmtoff, tm_zone]
+
+ As a side effect, strftime also behaves as if tzset were called. This
+ is for compatibility with older platforms, as required by POSIX; it is
+ not needed for tzset's own use.
RETURN VALUE
- If the conversion is successful, strftime returns the number of bytes
- placed into the array, not counting the terminating NUL; errno is
- unchanged if the returned value is zero. Otherwise, errno is set to
- indicate the error, zero is returned, and the array contents are
+ If the conversion is successful, strftime returns the number of bytes
+ placed into the array, not counting the terminating NUL; errno is
+ unchanged if the returned value is zero. Otherwise, errno is set to
+ indicate the error, zero is returned, and the array contents are
unspecified.
ERRORS
This function fails if:
[ERANGE]
- The total number of resulting bytes, including the terminating
+ The total number of resulting bytes, including the terminating
NUL character, is more than maxsize.
This function may fail if:
[EOVERFLOW]
- The format includes an %s conversion and the number of seconds
+ The format includes an %s conversion and the number of seconds
since the Epoch cannot be represented in a time_t.
SEE ALSO
diff --git a/newtzset.3 b/newtzset.3
index 78b6b6ce67c4..b1384f32df0b 100644
--- a/newtzset.3
+++ b/newtzset.3
@@ -9,9 +9,9 @@ tzset \- initialize time conversion information
.el .ds - \-
.B #include <time.h>
.PP
-.B timezone_t tzalloc(char const *TZ);
+.BI "timezone_t tzalloc(char const *" TZ );
.PP
-.B void tzfree(timezone_t tz);
+.BI "void tzfree(timezone_t " tz );
.PP
.B void tzset(void);
.PP
@@ -31,62 +31,30 @@ The
.B tzalloc
function
allocates and returns a timezone object described by
-.BR TZ .
-If
-.B TZ
-is not a valid timezone description, or if the object cannot be allocated,
-.B tzalloc
-returns a null pointer and sets
-.BR errno .
-.PP
-The
-.B tzfree
-function
-frees a timezone object
-.BR tz ,
-which should have been successfully allocated by
-.BR tzalloc .
-This invalidates any
-.B tm_zone
-pointers that
-.B tz
-was used to set.
-.PP
-The
-.B tzset
-function
-acts like
-.BR tzalloc(getenv("TZ")) ,
-except it saves any resulting timezone object into internal
-storage that is accessed by
-.BR localtime ,
-.BR localtime_r ,
-and
-.BR mktime .
-The anonymous shared timezone object is freed by the next call to
-.BR tzset .
-If the implied call to
-.B tzalloc
-fails,
-.B tzset
-falls back on Universal Time (UT).
+.IR TZ .
.PP
If
-.B TZ
-is null, the best available approximation to local (wall
+.I TZ
+is a null pointer,
+.B tzalloc
+uses the best available approximation to local (wall
clock) time, as specified by the
.BR tzfile (5)-format
file
.B localtime
-in the system time conversion information directory, is used.
+in the system time conversion information directory.
+.PP
If
-.B TZ
+.I TZ
is the empty string,
-UT is used, with the abbreviation "UTC"
+.B tzalloc
+uses Universal Time (UT), with the abbreviation "UTC"
and without leap second correction; please see
.BR newctime (3)
-for more about UT, UTC, and leap seconds. If
-.B TZ
+for more about UT, UTC, and leap seconds.
+.PP
+If
+.I TZ
is nonnull and nonempty:
.IP
if the value begins with a colon, it is used as a pathname of a file
@@ -98,24 +66,25 @@ and, if that file cannot be read, is used directly as a specification of
the time conversion information.
.PP
When
-.B TZ
-is used as a pathname, if it begins with a slash,
-it is used as an absolute pathname; otherwise,
-it is used as a pathname relative to a system time conversion information
+.I TZ
+contents are used as a pathname, a pathname beginning with
+.q "/"
+is used as-is; otherwise
+the pathname is relative to a system time conversion information
directory.
The file must be in the format specified in
.BR tzfile (5).
.PP
When
-.B TZ
+.I TZ
is used directly as a specification of the time conversion information,
-it must have the following syntax (spaces inserted for clarity):
+it must have the following syntax:
.IP
\fIstd\|offset\fR[\fIdst\fR[\fIoffset\fR][\fB,\fIrule\fR]]
.PP
Where:
.RS
-.TP 15
+.TP
.IR std " and " dst
Three or more bytes that are the designation for the standard
.RI ( std )
@@ -196,7 +165,7 @@ describes when the change back happens. Each
.I time
field describes when, in current local time, the change to the other
time is made.
-As an extension to POSIX, daylight saving is assumed to be in effect
+As an extension to POSIX.1-2017, daylight saving is assumed to be in effect
all year if it begins January 1 at 00:00 and ends December 31 at
24:00 plus the difference between daylight saving and standard time,
leaving no room for standard time in the calendar.
@@ -205,7 +174,7 @@ The format of
.I date
is one of the following:
.RS
-.TP 10
+.TP
.BI J n
The Julian day
.I n
@@ -238,16 +207,16 @@ 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
.I offset
-except that POSIX does not allow a leading sign (\c
+except that POSIX.1-2017 does not allow a leading sign (\c
.q "\*-"
or
.q "+" ).
-As an extension to POSIX, the hours part of
+As an extension to POSIX.1-2017, the hours part of
.I time
can range from \-167 through 167; this allows for unusual rules such
as
@@ -259,9 +228,9 @@ is not given, is
.RE
.LP
Here are some examples of
-.B TZ
+.I TZ
values that directly specify the timezone; they use some of the
-extensions to POSIX.
+extensions to POSIX.1-2017.
.TP
.B EST5
stands for US Eastern Standard
@@ -306,43 +275,114 @@ 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
+.I 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 .
+.IR 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, if a
+.I TZ
+string directly specifies a timezone with daylight saving time,
+it should specify the daylight saving rules explicitly.
.PP
For compatibility with System V Release 3.1, a semicolon
.RB ( ; )
may be used to separate the
.I rule
-from the rest of the specification.
+from the rest of the specification;
+this is an extension to POSIX.
+.PP
+The
+.B tzfree
+function
+frees a timezone object
+.IR tz ,
+which should have been successfully allocated by
+.BR tzalloc .
+This invalidates any
+.B tm_zone
+pointers that
+.I tz
+was used to set.
+.PP
+The
+.B tzset
+function
+acts like
+.BR tzalloc(getenv("TZ")) ,
+except it saves any resulting timezone object into internal
+storage that is accessed by
+.BR localtime ,
+.BR localtime_r ,
+and
+.BR mktime .
+The anonymous shared timezone object is freed by the next call to
+.BR tzset .
+If the implied call to
+.B getenv
+fails,
+.B tzset
+acts like
+.BR tzalloc(nullptr) ;
+if the implied call to
+.B tzalloc
+fails,
+.B tzset
+falls back on UT.
+.SH "RETURN VALUE"
+If successful, the
+.B tzalloc
+function returns a nonnull pointer to the newly allocated object.
+Otherwise, it returns a null pointer and sets
+.IR errno .
+.SH ERRORS
+.TP
+.B EOVERFLOW
+.I TZ
+directly specifies time conversion information,
+and contains an integer out of machine range
+or a time zone abbreviation that is too long for this platform.
+.PP
+The
+.B tzalloc
+function may also fail and set
+.I errno
+for any of the errors specified for the routines
+.BR access (2),
+.BR close (2),
+.BR malloc (3),
+.BR open (2),
+and
+.BR read (2).
.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/newtzset.3.txt b/newtzset.3.txt
index ee5c12563624..957a2577bbce 100644
--- a/newtzset.3.txt
+++ b/newtzset.3.txt
@@ -16,25 +16,17 @@ SYNOPSIS
DESCRIPTION
The tzalloc function allocates and returns a timezone object described
- by TZ. If TZ is not a valid timezone description, or if the object
- cannot be allocated, tzalloc returns a null pointer and sets errno.
+ by TZ.
- The tzfree function frees a timezone object tz, which should have been
- successfully allocated by tzalloc. This invalidates any tm_zone
- pointers that tz was used to set.
+ If TZ is a null pointer, tzalloc uses the best available approximation
+ to local (wall clock) time, as specified by the tzfile(5)-format file
+ localtime in the system time conversion information directory.
- The tzset function acts like tzalloc(getenv("TZ")), except it saves any
- resulting timezone object into internal storage that is accessed by
- localtime, localtime_r, and mktime. The anonymous shared timezone
- object is freed by the next call to tzset. If the implied call to
- tzalloc fails, tzset falls back on Universal Time (UT).
-
- If TZ is null, the best available approximation to local (wall clock)
- time, as specified by the tzfile(5)-format file localtime in the system
- time conversion information directory, is used. If TZ is the empty
- string, UT is used, with the abbreviation "UTC" and without leap second
- correction; please see newctime(3) for more about UT, UTC, and leap
- seconds. If TZ is nonnull and nonempty:
+ If TZ is the empty string, tzalloc uses Universal Time (UT), with the
+ abbreviation "UTC" and without leap second correction; please see
+ newctime(3) for more about UT, UTC, and leap seconds.
+
+ If TZ is nonnull and nonempty:
if the value begins with a colon, it is used as a pathname of a
file from which to read the time conversion information;
@@ -44,155 +36,176 @@ DESCRIPTION
information, and, if that file cannot be read, is used directly
as a specification of the time conversion information.
- When TZ is used as a pathname, if it begins with a slash, it is used as
- an absolute pathname; otherwise, it is used as a pathname relative to a
- system time conversion information directory. The file must be in the
- format specified in tzfile(5).
+ When TZ contents are used as a pathname, a pathname beginning with "/"
+ is used as-is; otherwise the pathname is relative to a system time
+ conversion information directory. The file must be in the format
+ specified in tzfile(5).
When TZ is used directly as a specification of the time conversion
- information, it must have the following syntax (spaces inserted for
- clarity):
+ information, it must have the following syntax:
stdoffset[dst[offset][,rule]]
Where:
- std and dst Three or more bytes that are the designation for
- the standard (std) or the alternative (dst, such
- as daylight saving time) time zone. Only std is
- required; if dst is missing, then daylight saving
- time does not apply in this locale. Upper- and
- lowercase letters are explicitly allowed. Any
- characters except a leading colon (:), digits,
- comma (,), ASCII minus (-), ASCII plus (+), and
- NUL bytes are allowed. Alternatively, a
- designation can be surrounded by angle brackets <
- and >; in this case, the designation can contain
- any characters other than > and NUL.
-
- offset Indicates the value one must add to the local
- time to arrive at Coordinated Universal Time.
- The offset has the form:
-
- hh[:mm[:ss]]
-
- The minutes (mm) and seconds (ss) are optional.
- The hour (hh) is required and may be a single
- digit. The offset following std is required. If
- no offset follows dst, daylight saving time is
- assumed to be one hour ahead of standard time.
- One or more digits may be used; the value is
- always interpreted as a decimal number. The hour
- must be between zero and 24, and the minutes (and
- seconds) - if present - between zero and 59. If
- preceded by a "-", the time zone shall be east of
- the Prime Meridian; otherwise it shall be west
- (which may be indicated by an optional preceding
- "+".
-
- rule Indicates when to change to and back from
- daylight saving time. The rule has the form:
-
- date/time,date/time
-
- where the first date describes when the change
- from standard to daylight saving time occurs and
- the second date describes when the change back
- happens. Each time field describes when, in
- current local time, the change to the other time
- is made. As an extension to POSIX, daylight
- saving is assumed to be in effect all year if it
- begins January 1 at 00:00 and ends December 31 at
- 24:00 plus the difference between daylight saving
- and standard time, leaving no room for standard
- time in the calendar.
-
- The format of date is one of the following:
-
- Jn The Julian day n (1 <= n <= 365). Leap
- days are not counted; that is, in all
- years - including leap years - February
- 28 is day 59 and March 1 is day 60. It
- is impossible to explicitly refer to
- the occasional February 29.
-
- n The zero-based Julian day
- (0 <= n <= 365). Leap days are
- counted, and it is possible to refer to
- February 29.
-
- Mm.n.d The d'th day (0 <= d <= 6) of week n of
- month m of the year (1 <= n <= 5,
- 1 <= m <= 12, where week 5 means "the
- last d day in month m" which may occur
- in either the fourth or the fifth
- week). Week 1 is the first week in
- which the d'th day occurs. Day zero is
- Sunday.
-
- The time has the same format as offset except
- that POSIX does not allow a leading sign ("-" or
- "+"). As an extension to POSIX, the hours part
- of time can range from -167 through 167; this
- allows for unusual rules such as "the Saturday
- before the first Sunday of March". The default,
- if time is not given, is 02:00:00.
+ std and dst
+ Three or more bytes that are the designation for the
+ standard (std) or the alternative (dst, such as daylight
+ saving time) time zone. Only std is required; if dst is
+ missing, then daylight saving time does not apply in this
+ locale. Upper- and lowercase letters are explicitly
+ allowed. Any characters except a leading colon (:),
+ digits, comma (,), ASCII minus (-), ASCII plus (+), and
+ NUL bytes are allowed. Alternatively, a designation can
+ be surrounded by angle brackets < and >; in this case,
+ the designation can contain any characters other than >
+ and NUL.
+
+ offset Indicates the value one must add to the local time to
+ arrive at Coordinated Universal Time. The offset has the
+ form:
+
+ hh[:mm[:ss]]
+
+ The minutes (mm) and seconds (ss) are optional. The hour
+ (hh) is required and may be a single digit. The offset
+ following std is required. If no offset follows dst,
+ daylight saving time is assumed to be one hour ahead of
+ standard time. One or more digits may be used; the value
+ is always interpreted as a decimal number. The hour must
+ be between zero and 24, and the minutes (and seconds) -
+ if present - between zero and 59. If preceded by a "-",
+ the time zone shall be east of the Prime Meridian;
+ otherwise it shall be west (which may be indicated by an
+ optional preceding "+".
+
+ rule Indicates when to change to and back from daylight saving
+ time. The rule has the form:
+
+ date/time,date/time
+
+ where the first date describes when the change from
+ standard to daylight saving time occurs and the second
+ date describes when the change back happens. Each time
+ field describes when, in current local time, the change
+ to the other time is made. As an extension to
+ POSIX.1-2017, daylight saving is assumed to be in effect
+ all year if it begins January 1 at 00:00 and ends
+ December 31 at 24:00 plus the difference between daylight
+ saving and standard time, leaving no room for standard
+ time in the calendar.
+
+ The format of date is one of the following:
+
+ Jn The Julian day n (1 <= n <= 365). Leap days are
+ not counted; that is, in all years - including
+ leap years - February 28 is day 59 and March 1 is
+ day 60. It is impossible to explicitly refer to
+ the occasional February 29.
+
+ n The zero-based Julian day (0 <= n <= 365). Leap
+ days are counted, and it is possible to refer to
+ February 29.
+
+ Mm.n.d The d'th day (0 <= d <= 6) of week n of month m of
+ the year (1 <= n <= 5, 1 <= m <= 12, where week 5
+ means "the last d day in month m" which may occur
+ in either the fourth or the fifth week). Week 1
+ is the first week in which the d'th day occurs.
+ Day zero is Sunday.
+
+ The time has the same format as offset except that
+ POSIX.1-2017 does not allow a leading sign ("-" or "+").
+ As an extension to POSIX.1-2017, the hours part of time
+ can range from -167 through 167; this allows for unusual
+ rules such as "the Saturday before the first Sunday of
+ March". The default, if time is not given, is 02:00:00.
Here are some examples of TZ values that directly specify the timezone;
- they use some of the extensions to POSIX.
+ they use some of the extensions to POSIX.1-2017.
- EST5 stands for US Eastern Standard Time (EST), 5 hours behind UT,
+ EST5 stands for US Eastern Standard Time (EST), 5 hours behind UT,
without daylight saving.
<+12>-12<+13>,M11.1.0,M1.2.1/147
stands for Fiji time, 12 hours ahead of UT, springing forward on
- November's first Sunday at 02:00, and falling back on January's
- second Monday at 147:00 (i.e., 03:00 on the first Sunday on or
- after January 14). The abbreviations for standard and daylight
+ November's first Sunday at 02:00, and falling back on January's
+ second Monday at 147:00 (i.e., 03:00 on the first Sunday on or
+ after January 14). The abbreviations for standard and daylight
saving time are "+12" and "+13".
IST-2IDT,M3.4.4/26,M10.5.0
- stands for Israel Standard Time (IST) and Israel Daylight Time
- (IDT), 2 hours ahead of UT, springing forward on March's fourth
- Thursday at 26:00 (i.e., 02:00 on the first Friday on or after
+ stands for Israel Standard Time (IST) and Israel Daylight Time
+ (IDT), 2 hours ahead of UT, springing forward on March's fourth
+ Thursday at 26:00 (i.e., 02:00 on the first Friday on or after
March 23), and falling back on October's last Sunday at 02:00.
<-04>4<-03>,J1/0,J365/25
- stands for permanent daylight saving time, 3 hours behind UT
- with abbreviation "-03". There is a dummy fall-back transition
- on December 31 at 25:00 daylight saving time (i.e., 24:00
- standard time, equivalent to January 1 at 00:00 standard time),
- and a simultaneous spring-forward transition on January 1 at
- 00:00 standard time, so daylight saving time is in effect all
+ stands for permanent daylight saving time, 3 hours behind UT
+ with abbreviation "-03". There is a dummy fall-back transition
+ on December 31 at 25:00 daylight saving time (i.e., 24:00
+ standard time, equivalent to January 1 at 00:00 standard time),
+ and a simultaneous spring-forward transition on January 1 at
+ 00:00 standard time, so daylight saving time is in effect all
year and the initial <-04> is a placeholder.
<-03>3<-02>,M3.5.0/-2,M10.5.0/-1
- stands for time in western Greenland, 3 hours behind UT, where
- clocks follow the EU rules of springing forward on March's last
- Sunday at 01:00 UT (-02:00 local time, i.e., 22:00 the previous
- day) and falling back on October's last Sunday at 01:00 UT
- (-01:00 local time, i.e., 23:00 the previous day). The
- abbreviations for standard and daylight saving time are "-03"
+ stands for time in western Greenland, 3 hours behind UT, where
+ clocks follow the EU rules of springing forward on March's last
+ Sunday at 01:00 UT (-02:00 local time, i.e., 22:00 the previous
+ day) and falling back on October's last Sunday at 01:00 UT
+ (-01:00 local time, i.e., 23:00 the previous day). The
+ abbreviations for standard and daylight saving time are "-03"
and "-02".
- If no rule is present in TZ, the rules specified by the
- tzfile(5)-format file 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 offset values
- in TZ.
+ If TZ specifies daylight saving time but does not specify a rule, and
+ the optional tzfile(5)-format file posixrules is present in the system
+ time conversion information directory, the rules in posixrules are
+ used, with the posixrules standard and daylight saving time offsets
+ from UT replaced by those specified by the offset values in TZ.
+ However, the posixrules file is obsolete: if it is present it is only
+ for backward compatibility, and it does not work reliably. Therefore,
+ if a TZ string directly specifies a timezone with daylight saving time,
+ it should specify the daylight saving rules explicitly.
+
+ For compatibility with System V Release 3.1, a semicolon (;) may be
+ used to separate the rule from the rest of the specification; this is
+ an extension to POSIX.
+
+ The tzfree function frees a timezone object tz, which should have been
+ successfully allocated by tzalloc. This invalidates any tm_zone
+ pointers that tz was used to set.
- For compatibility with System V Release 3.1, a semicolon (;) may be
- used to separate the rule from the rest of the specification.
+ The tzset function acts like tzalloc(getenv("TZ")), except it saves any
+ resulting timezone object into internal storage that is accessed by
+ localtime, localtime_r, and mktime. The anonymous shared timezone
+ object is freed by the next call to tzset. If the implied call to
+ getenv fails, tzset acts like tzalloc(nullptr); if the implied call to
+ tzalloc fails, tzset falls back on UT.
+
+RETURN VALUE
+ If successful, the tzalloc function returns a nonnull pointer to the
+ newly allocated object. Otherwise, it returns a null pointer and sets
+ errno.
+
+ERRORS
+ EOVERFLOW
+ TZ directly specifies time conversion information, and contains
+ an integer out of machine range or a time zone abbreviation that
+ is too long for this platform.
+
+ The tzalloc function may also fail and set errno for any of the errors
+ specified for the routines access(2), close(2), malloc(3), open(2), and
+ read(2).
FILES
- /usr/share/zoneinfo timezone information directory
- /usr/share/zoneinfo/localtime local timezone file
- /usr/share/zoneinfo/posixrules default DST rules (obsolete,
- and can cause bugs if present)
+ /etc/localtime local timezone file
+ /usr/share/zoneinfo timezone directory
+ /usr/share/zoneinfo/posixrules default DST rules (obsolete)
/usr/share/zoneinfo/GMT for UTC leap seconds
- If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
- /usr/share/zoneinfo/posixrules.
+ If /usr/share/zoneinfo/GMT is absent, UTC leap seconds are loaded from
+ /usr/share/zoneinfo/GMT0 if present.
SEE ALSO
getenv(3), newctime(3), newstrftime(3), time(2), tzfile(5)
diff --git a/private.h b/private.h
index 838ab2b2df8f..0dac6af4e3cb 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
@@ -754,7 +756,7 @@ struct tm *offtime(time_t const *, long);
time_t timelocal(struct tm *);
# endif
# if TZ_TIME_T || !defined timeoff
-time_t timeoff(struct tm *, long);
+# define EXTERN_TIMEOFF
# endif
# if TZ_TIME_T || !defined time2posix
time_t time2posix(time_t);
@@ -766,7 +768,8 @@ time_t posix2time(time_t);
/* Infer TM_ZONE on systems where this information is known, but suppress
guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
-#if (defined __GLIBC__ \
+#if (200809 < _POSIX_VERSION \
+ || defined __GLIBC__ \
|| defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
@@ -896,6 +899,19 @@ static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
# define UNINIT_TRAP 0
#endif
+/* localtime.c sometimes needs access to timeoff if it is not already public.
+ tz_private_timeoff should be used only by localtime.c. */
+#if (!defined EXTERN_TIMEOFF \
+ && defined TM_GMTOFF && (200809 < _POSIX_VERSION || ! UNINIT_TRAP))
+# ifndef timeoff
+# define timeoff tz_private_timeoff
+# endif
+# define EXTERN_TIMEOFF
+#endif
+#ifdef EXTERN_TIMEOFF
+time_t timeoff(struct tm *, long);
+#endif
+
#ifdef DEBUG
# undef unreachable
# define unreachable() abort()
@@ -955,6 +971,18 @@ enum {
#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
+/* How many years to generate (in zic.c) or search through (in localtime.c).
+ This is two years larger than the obvious 400, to avoid edge cases.
+ E.g., suppose a non-POSIX.1-2017 rule applies from 2012 on with transitions
+ in March and September, plus one-off transitions in November 2013.
+ If zic looked only at the last 400 years, it would set max_year=2413,
+ with the intent that the 400 years 2014 through 2413 will be repeated.
+ The last transition listed in the tzfile would be in 2413-09,
+ less than 400 years after the last one-off transition in 2013-11.
+ Two years is not overkill for localtime.c, as a one-year bump
+ would mishandle 2023d's America/Ciudad_Juarez for November 2422. */
+enum { years_of_observations = YEARSPERREPEAT + 2 };
+
enum {
TM_SUNDAY,
TM_MONDAY,
diff --git a/strftime.c b/strftime.c
index df169830f9ef..755d341fa6d9 100644
--- a/strftime.c
+++ b/strftime.c
@@ -327,11 +327,12 @@ label:
tm.tm_mday = t->tm_mday;
tm.tm_mon = t->tm_mon;
tm.tm_year = t->tm_year;
+#ifdef TM_GMTOFF
+ mkt = timeoff(&tm, t->TM_GMTOFF);
+#else
tm.tm_isdst = t->tm_isdst;
-#if defined TM_GMTOFF && ! UNINIT_TRAP
- tm.TM_GMTOFF = t->TM_GMTOFF;
-#endif
mkt = mktime(&tm);
+#endif
/* If mktime fails, %s expands to the
value of (time_t) -1 as a failure
marker; this is better in practice
diff --git a/theory.html b/theory.html
index 369c75433ff2..516d2a525111 100644
--- a/theory.html
+++ b/theory.html
@@ -95,7 +95,7 @@ Group Base Specifications Issue 7</a>, IEEE Std 1003.1-2017, 2018
Edition.
Because the database's scope encompasses real-world changes to civil
timekeeping, its model for describing time is more complex than the
-standard and daylight saving times supported by POSIX.
+standard and daylight saving times supported by POSIX.1-2017.
A <code><abbr>tz</abbr></code> timezone corresponds to a ruleset that can
have more than two changes per year, these changes need not merely
flip back and forth between two alternatives, and the rules themselves
@@ -187,7 +187,7 @@ in decreasing order of importance:
href="https://en.wikipedia.org/wiki/ASCII">ASCII</a> letters,
'<code>.</code>', '<code>-</code>' and '<code>_</code>'.
Do not use digits, as that might create an ambiguity with <a
- href="https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03">POSIX
+ href="https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03">POSIX.1-2017
<code>TZ</code> strings</a>.
A file name component must not exceed 14 characters or start with
'<code>-</code>'.
@@ -362,6 +362,11 @@ The backward-compatibility file <code>zone.tab</code> is similar
but conforms to the older-version guidelines related to <abbr>ISO</abbr> 3166-1;
it lists only one country code per entry and unlike <code>zone1970.tab</code>
it can list names defined in <code>backward</code>.
+Applications that process only timestamps from now on can instead use the file
+<code>zonenow.tab</code>, which partitions the world more coarsely,
+into regions where clocks agree now and in the predicted future;
+this file is smaller and simpler than <code>zone1970.tab</code>
+and <code>zone.tab</code>.
</p>
<p>
@@ -373,7 +378,7 @@ nowadays distributions typically use it
and no great weight should be attached to whether a link
is defined in <code>backward</code> or in some other file.
The source file <code>etcetera</code> defines names that may be useful
-on platforms that do not support POSIX-style <code>TZ</code> strings;
+on platforms that do not support POSIX.1-2017-style <code>TZ</code> strings;
no other source file other than <code>backward</code>
contains links to its zones.
One of <code>etcetera</code>'s names is <code>Etc/UTC</code>,
@@ -421,7 +426,7 @@ in decreasing order of importance:
expression <code>[-+[:alnum:]]{3,6}</code> should match the
abbreviation.
This guarantees that all abbreviations could have been specified by a
- POSIX <code>TZ</code> string.
+ POSIX.1-2017 <code>TZ</code> string.
</p>
</li>
<li>
@@ -765,12 +770,12 @@ href="https://www.dissentmagazine.org/blog/booked-a-global-history-of-time-vanes
calendar with 24-hour days. These divergences range from
relatively minor, such as Japanese bars giving times like "24:30" for the
wee hours of the morning, to more-significant differences such as <a
- href="https://www.pri.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time">the
+ href="https://theworld.org/stories/2015-01-30/if-you-have-meeting-ethiopia-you-better-double-check-time">the
east African practice of starting the day at dawn</a>, renumbering
the Western 06:00 to be 12:00. These practices are largely outside
the scope of the <code><abbr>tz</abbr></code> code and data, which
provide only limited support for date and time localization
- such as that required by POSIX.
+ such as that required by POSIX.1-2017.
If <abbr>DST</abbr> is not used a different time zone
can often do the trick; for example, in Kenya a <code>TZ</code> setting
like <code>&lt;-03&gt;3</code> or <code>America/Cayenne</code> starts
@@ -867,23 +872,23 @@ input is occasionally extended, and a platform may still be shipping
an older <code>zic</code>.
</p>
-<h3 id="POSIX">POSIX properties and limitations</h3>
+<h3 id="POSIX">POSIX.1-2017 properties and limitations</h3>
<ul>
<li>
<p>
- In POSIX, time display in a process is controlled by the
+ In POSIX.1-2017, time display in a process is controlled by the
environment variable <code>TZ</code>.
- Unfortunately, the POSIX
+ Unfortunately, the POSIX.1-2017
<code>TZ</code> string takes a form that is hard to describe and
is error-prone in practice.
- Also, POSIX <code>TZ</code> strings cannot deal with daylight
+ Also, POSIX.1-2017 <code>TZ</code> strings cannot deal with daylight
saving time rules not based on the Gregorian calendar (as in
Morocco), or with situations where more than two time zone
abbreviations or <abbr>UT</abbr> offsets are used in an area.
</p>
<p>
- The POSIX <code>TZ</code> string takes the following form:
+ The POSIX.1-2017 <code>TZ</code> string takes the following form:
</p>
<p>
@@ -950,7 +955,7 @@ an older <code>zic</code>.
</dl>
<p>
- Here is an example POSIX <code>TZ</code> string for New
+ Here is an example POSIX.1-2017 <code>TZ</code> string for New
Zealand after 2007.
It says that standard time (<abbr>NZST</abbr>) is 12 hours ahead
of <abbr>UT</abbr>, and that daylight saving time
@@ -961,7 +966,7 @@ an older <code>zic</code>.
<pre><code>TZ='NZST-12NZDT,M9.5.0,M4.1.0/3'</code></pre>
<p>
- This POSIX <code>TZ</code> string is hard to remember, and
+ This POSIX.1-2017 <code>TZ</code> string is hard to remember, and
mishandles some timestamps before 2008.
With this package you can use this instead:
</p>
@@ -999,7 +1004,7 @@ an older <code>zic</code>.
limit phone calls to off-peak hours.
</li>
<li>
- POSIX provides no convenient and efficient way to determine
+ POSIX.1-2017 provides no convenient and efficient way to determine
the <abbr>UT</abbr> offset and time zone abbreviation of arbitrary
timestamps, particularly for timezones
that do not fit into the POSIX model.
@@ -1026,14 +1031,14 @@ an older <code>zic</code>.
</li>
</ul>
-<h3 id="POSIX-extensions">Extensions to POSIX in the
+<h3 id="POSIX-extensions">Extensions to POSIX.1-2017 in the
<code><abbr>tz</abbr></code> code</h3>
<ul>
<li>
<p>
The <code>TZ</code> environment variable is used in generating
the name of a file from which time-related information is read
- (or is interpreted à la POSIX); <code>TZ</code> is no longer
+ (or is interpreted à la POSIX.1-2017); <code>TZ</code> is no longer
constrained to be a string containing abbreviations
and numeric data as described <a href="#POSIX">above</a>.
The file's format is <dfn><abbr>TZif</abbr></dfn>,
diff --git a/time2posix.3 b/time2posix.3
index f48402b91e55..6644060ad76d 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 c86c186c09f9..3ee1eb244731 100644
--- a/tz-art.html
+++ b/tz-art.html
@@ -22,9 +22,10 @@ into problems that programmers have with timekeeping.</li>
Explaining The Mysteries Of Time Zones</a>" (2017; 2:15)
briefly says why France has more time zones than Russia.
<li>
-"<a href="https://www.youtube.com/watch?v=yRz-Dl60Lfc">Why Denmark is
-.17 Seconds Behind The World</a>" (2019; 6:29) explains why Denmark and
-the United Kingdom don't exactly follow their own law about civil time.
+"<a href="https://www.youtube.com/watch?v=yRz-Dl60Lfc">Why Denmark used to be
+.04 seconds behind the world</a>" (2019; 6:29) explains why the United Kingdom
+&mdash; and, once, Denmark &mdash; haven't always exactly followed their own
+laws about civil time.
<li>
"About Time" (1962; 59 minutes) is part of the
Bell Science extravaganza, with Frank Baxter, Richard Deacon, and Les Tremayne.
@@ -209,7 +210,7 @@ Umberto Eco,
Island of the Day Before</em></a>
(<em>L'isola del giorno prima</em>), 1994.
"...the story of a 17th century Italian nobleman trapped near an island
-on the International Date Line. Time and time zones play an integral
+on the International Date Line. Time and time zones play an integral
part in the novel." (Paul Eggert, 2006-04-22)
</li>
<li>
@@ -229,94 +230,73 @@ year-round <abbr>DST</abbr> as a way of lessening wintertime despair.
</li>
</ul>
<h2>Music</h2>
-<p>
-Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:</p>
-<table>
-<tr><td>Artist</td><td>Karrin Allyson</td></tr>
-<tr><td>CD</td><td>I Didn't Know About You</td></tr>
-<tr><td>Copyright Date</td><td>1993</td></tr>
-<tr><td>Label</td><td>Concord Jazz, Inc.</td></tr>
-<tr><td>ID</td><td>CCD-4543</td></tr>
-<tr><td>Track Time</td><td>3:44</td></tr>
-<tr><td>Personnel</td><td>Karrin Allyson, vocal;
+<ul>
+<li>
+Recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:
+<ul>
+<li>
+Karrin Allyson, <em>I Didn't Know About You</em> (1993), track 11, 3:44.
+Concord Jazz CCD-4543.
+Karrin Allyson, vocal;
Russ Long, piano;
Gerald Spaits, bass;
-Todd Strait, drums</td></tr>
-<tr><td>Notes</td><td>CD notes "additional lyric by Karrin Allyson;
-arranged by Russ Long and Karrin Allyson"</td></tr>
-<tr><td>ADO Rating</td><td>1 star</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/i-didnt-know-about-you-mw0000618657">AMG Rating</a></td><td>4 stars</td></tr>
-<tr><td>Penguin Rating</td><td>3.5 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Kevin Mahogany</td></tr>
-<tr><td>CD</td><td>Double Rainbow</td></tr>
-<tr><td>Copyright Date</td><td>1993</td></tr>
-<tr><td>Label</td><td>Enja Records</td></tr>
-<tr><td>ID</td><td>ENJ-7097 2</td></tr>
-<tr><td>Track Time</td><td>6:27</td></tr>
-<tr><td>Personnel</td><td>Kevin Mahogany, vocal;
+Todd Strait, drums.
+CD notes "additional lyric by Karrin Allyson;
+arranged by Russ Long and Karrin Allyson".
+ADO &#x2605;,
+<a href="https://www.allmusic.com/album/i-didnt-know-about-you-mw0000618657">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;, Penguin &#x2605;&#x2605;&#x2605;&#x2BEA;.
+</li>
+<li>
+Kevin Mahogany, <em>Double Rainbow</em> (1993), track 3, 6:27. Enja ENJ-7097 2.
+Kevin Mahogany, vocal;
Kenny Barron, piano;
Ray Drummond, bass;
Ralph Moore, tenor saxophone;
-Lewis Nash, drums</td></tr>
-<tr><td>ADO Rating</td><td>1.5 stars</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/double-rainbow-mw0000620371">AMG Rating</a></td><td>3 stars</td></tr>
-<tr><td>Penguin Rating</td><td>3 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Joe Williams</td></tr>
-<tr><td>CD</td><td>Here's to Life</td></tr>
-<tr><td>Copyright Date</td><td>1994</td></tr>
-<tr><td>Label</td><td>Telarc International Corporation</td></tr>
-<tr><td>ID</td><td>CD-83357</td></tr>
-<tr><td>Track Time</td><td>3:58</td></tr>
-<tr><td>Personnel</td><td>Joe Williams, vocal
-The Robert Farnon [39 piece] Orchestra</td></tr>
-<tr><td>Notes</td><td>This CD is also available as part of a 3-CD package from
-Telarc, "Triple Play" (CD-83461)</td></tr>
-<tr><td>ADO Rating</td><td>black dot</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/heres-to-life-mw0000623648">AMG Rating</a></td><td>2 stars</td></tr>
-<tr><td>Penguin Rating</td><td>3 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Charles Fambrough</td></tr>
-<tr><td>CD</td><td>Keeper of the Spirit</td></tr>
-<tr><td>Copyright Date</td><td>1995</td></tr>
-<tr><td>Label</td><td>AudioQuest Music</td></tr>
-<tr><td>ID</td><td>AQ-CD1033</td></tr>
-<tr><td>Track Time</td><td>7:07</td></tr>
-<tr><td>Personnel</td><td>Charles Fambrough, bass;
+Lewis Nash, drums.
+ADO &#x2605;&#x2BEA;,
+<a href="https://www.allmusic.com/album/double-rainbow-mw0000620371">AMG</a>
+&#x2605;&#x2605;&#x2605;, Penguin &#x2605;&#x2605;&#x2605;.
+</li>
+<li>
+Joe Williams, <em>Here's to Life</em> (1994), track 7, 3:58.
+Telarc Jazz CD-83357.
+Joe Williams, vocal; The Robert Farnon [39 piece] Orchestra.
+Also in a 3-CD package "Triple Play", Telarc CD-83461.
+ADO &#x2022;,
+<a href="https://www.allmusic.com/album/heres-to-life-mw0000623648">AMG</a>
+&#x2605;&#x2605;, Penguin &#x2605;&#x2605;&#x2605;.
+</li>
+<li>
+Charles Fambrough, <em>Keeper of the Spirit</em> (1995), track 7, 7:07.
+AudioQuest AQ-CD1033.
+Charles Fambrough, bass;
Joel Levine, tenor recorder;
Edward Simon, piano;
Lenny White, drums;
-Marion Simon, percussion</td></tr>
-<tr><td>ADO Rating</td><td>2 stars</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/keeper-of-the-spirit-mw0000176559">AMG Rating</a></td><td>unrated</td></tr>
-<tr><td>Penguin Rating</td><td>3 stars</td></tr>
-</table>
-<hr>
-<p>Also of note:</p>
-<table>
-<tr><td>Artist</td><td>Holly Cole Trio</td></tr>
-<tr><td>CD</td><td>Blame It On My Youth</td></tr>
-<tr><td>Copyright Date</td><td>1992</td></tr>
-<tr><td>Label</td><td>Manhattan</td></tr>
-<tr><td>ID</td><td>CDP 7 97349 2</td></tr>
-<tr><td>Total Time</td><td>37:45</td></tr>
-<tr><td>Personnel</td><td>Holly Cole, voice;
+Marion Simon, percussion.
+ADO &#x2605;,
+<a href="https://www.allmusic.com/album/keeper-of-the-spirit-mw0000176559">AMG</a>
+unrated, Penguin &#x2605;&#x2605;&#x2605;.
+</ul>
+</li>
+<li>
+Holly Cole Trio, Blame It On My Youth (1992). Manhattan CDP 7 97349 2, 37:45.
+Holly Cole, voice;
Aaron Davis, piano;
-David Piltch, string bass</td></tr>
-<tr><td>Notes</td><td>Lyrical reference to "Eastern Standard Time" in
-Tom Waits' "Purple Avenue"</td></tr>
-<tr><td>ADO Rating</td><td>2.5 stars</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/blame-it-on-my-youth-mw0000274303">AMG Rating</a></td><td>3 stars</td></tr>
-<tr><td>Penguin Rating</td><td>unrated</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Milt Hinton</td></tr>
-<tr><td>CD</td><td>Old Man Time</td></tr>
-<tr><td>Copyright Date</td><td>1990</td></tr>
-<tr><td>Label</td><td>Chiaroscuro</td></tr>
-<tr><td>ID</td><td>CR(D) 310</td></tr>
-<tr><td>Total Time</td><td>149:38 (two CDs)</td></tr>
-<tr><td>Personnel</td><td>Milt Hinton, bass;
+David Piltch, string bass.
+Lyrical reference to "Eastern Standard Time" in
+Tom Waits's "Purple Avenue".
+ADO &#x2605;&#x2605;&#x2BEA;,
+<a href="https://www.allmusic.com/album/blame-it-on-my-youth-mw0000274303">AMG</a>
+&#x2605;&#x2605;&#x2605;, Penguin unrated.
+</li>
+<li>
+Milt Hinton,
+<a href="https://chiaroscurojazz.org/catalog/old-man-time-2-cd-set/"><em>Old
+Man Time</em></a> (1990).
+Chiaroscuro CR(D) 310, 149:38 (two CDs).
+Milt Hinton, bass;
Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet;
Al Grey, trombone;
Eddie Barefield, Joe Camel (Flip Phillips), Buddy Tate,
@@ -328,165 +308,129 @@ Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
drums;
Lionel Hampton, vibraphone;
Cab Calloway, Joe Williams, vocal;
-Buck Clayton, arrangements</td></tr>
-<tr><td>Notes</td><td>tunes include Old Man Time, Time After Time,
-Sometimes I'm Happy,
-A Hot Time in the Old Town Tonight,
-Four or Five Times, Now's the Time,
-Time on My Hands, This Time It's Us,
-and Good Time Charlie.
-<a href="http://www.chiaroscurojazz.com/album.php?C=310">Album info</a>
-is available.</td></tr>
-<tr><td>ADO Rating</td><td>3 stars</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/old-man-time-mw0000269353">AMG Rating</a></td><td>4.5 stars</td></tr>
-<tr><td>Penguin Rating</td><td>3 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Alan Broadbent</td></tr>
-<tr><td>CD</td><td>Pacific Standard Time</td></tr>
-<tr><td>Copyright Date</td><td>1995</td></tr>
-<tr><td>Label</td><td>Concord Jazz, Inc.</td></tr>
-<tr><td>ID</td><td>CCD-4664</td></tr>
-<tr><td>Total Time</td><td>62:42</td></tr>
-<tr><td>Personnel</td><td>Alan Broadbent, piano;
+Buck Clayton, arrangements.
+Tunes include "Old Man Time", "Time After Time",
+"Sometimes I'm Happy",
+"A Hot Time in the Old Town Tonight",
+"Four or Five Times", "Now's the Time",
+"Time on My Hands", "This Time It's Us",
+and "Good Time Charlie".
+ADO &#x2605;&#x2605;&#x2605;,
+<a href="https://www.allmusic.com/album/old-man-time-mw0000269353">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;&#x2BEA;, Penguin &#x2605;&#x2605;&#x2605;.
+</li>
+<li>
+Alan Broadbent, <em>Pacific Standard Time</em> (1995).
+Concord Jazz CCD-4664, 62:42.
+Alan Broadbent, piano;
Putter Smith, Bass;
-Frank Gibson, Jr., drums</td></tr>
-<tr><td>Notes</td><td>The CD cover features an analemma for equation-of-time fans</td></tr>
-<tr><td>ADO Rating</td><td>1 star</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/pacific-standard-time-mw0000645433">AMG Rating</a></td><td>4 stars</td></tr>
-<tr><td>Penguin Rating</td><td>3.5 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Anthony Braxton/Richard Teitelbaum</td></tr>
-<tr><td>CD</td><td>Silence/Time Zones</td></tr>
-<tr><td>Copyright Date</td><td>1996</td></tr>
-<tr><td>Label</td><td>Black Lion</td></tr>
-<tr><td>ID</td><td>BLCD 760221</td></tr>
-<tr><td>Total Time</td><td>72:58</td></tr>
-<tr><td>Personnel</td><td>Anthony Braxton, sopranino and alto saxophones,
+Frank Gibson, Jr., drums.
+The CD cover features an analemma for equation-of-time fans.
+ADO &#x2605;,
+<a href="https://www.allmusic.com/album/pacific-standard-time-mw0000645433">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;, Penguin &#x2605;&#x2605;&#x2605;&#x2BEA;.
+</li>
+<li>
+Anthony Braxton/Richard Teitelbaum, <em>Silence/Time Zones</em> (1996).
+Black Lion BLCD 760221, 72:58.
+Anthony Braxton, sopranino and alto saxophones,
contrebasse clarinet, miscellaneous instruments;
Leo Smith, trumpet and miscellaneous instruments;
Leroy Jenkins, violin and miscellaneous instruments;
-Richard Teitelbaum, modular moog and micromoog synthesizer</td></tr>
-<tr><td>ADO Rating</td><td>black dot</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/silence-time-zones-mw0000595735">AMG Rating</a></td><td>4 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Charles Gayle</td></tr>
-<tr><td>CD</td><td>Time Zones</td></tr>
-<tr><td>Copyright Date</td><td>2006</td></tr>
-<tr><td>Label</td><td>Tompkins Square</td></tr>
-<tr><td>ID</td><td>TSQ2839</td></tr>
-<tr><td>Total Time</td><td>49:06</td></tr>
-<tr><td>Personnel</td><td>Charles Gayle, piano</td></tr>
-<tr><td>ADO Rating</td><td>1 star</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/time-zones-mw0000349642">AMG Rating</a></td><td>4.5 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>The Get Up Kids</td></tr>
-<tr><td>CD</td><td>Eudora</td></tr>
-<tr><td>Copyright Date</td><td>2001</td></tr>
-<tr><td>Label</td><td>Vagrant</td></tr>
-<tr><td>ID</td><td>357</td></tr>
-<tr><td>Total Time</td><td>65:12</td></tr>
-<tr><td>Notes</td><td>Includes the song "Central Standard Time." Thanks to Colin Bowern for this information.</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/eudora-mw0000592063">AMG Rating</a></td><td>2.5 stars</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-
-<tr><td>Artist</td><td>Coldplay</td></tr>
-<tr><td>Song</td><td>Clocks</td></tr>
-<tr><td>Copyright Date</td><td>2003</td></tr>
-<tr><td>Label</td><td>Capitol Records</td></tr>
-<tr><td>ID</td><td>52608</td></tr>
-<tr><td>Total Time</td><td>4:13</td></tr>
-<tr><td>Notes</td><td>Won the 2004 Record of the Year honor at the
+Richard Teitelbaum, modular moog and micromoog synthesizer.
+ADO &#x2022;,
+<a href="https://www.allmusic.com/album/silence-time-zones-mw0000595735">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;.
+</li>
+<li>
+Charles Gayle, <em>Time Zones</em> (2006). Tompkins Square TSQ2839, 49:06.
+Charles Gayle, piano.
+ADO &#x2605;,
+<a href="https://www.allmusic.com/album/time-zones-mw0000349642">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;&#x2BEA;.
+</li>
+<li>
+The Get Up Kids, <em>Eudora</em> (2001). Vagrant 357, 65:12.
+Includes the song "Central Standard Time."
+Thanks to Colin Bowern for this information.
+<a href="https://www.allmusic.com/album/eudora-mw0000592063">AMG</a>
+&#x2605;&#x2605;&#x2BEA;.
+</li>
+<li>
+Coldplay, "Clocks" (2003).
+Capitol 52608, 4:13.
+Won the 2004 Record of the Year honor at the
Grammy Awards. Co-written and performed by Chris Martin,
great-great-grandson of <abbr>DST</abbr> inventor William Willett.
-The song's first line is "Lights go out and I can't be saved".</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-
-<tr><td>Artist</td><td>Jaime Guevara</td></tr>
-<tr><td>Song</td><td><a
+The song's first line is "Lights go out and I can't be saved".
+</li>
+<li>
+Jaime Guevara, "<a
href="https://www.youtube.com/watch?v=ZfN4Fe_A50U">Qu&eacute;
-hora es</a></td></tr>
-<tr><td>Date</td><td>1993</td></tr>
-<tr><td>Total Time</td><td>3:04</td></tr>
-<tr><td>Notes</td><td>The song protested "Sixto Hour" in Ecuador
+hora es</a>" (1993), 3:04.
+The song protested "Sixto Hour" in Ecuador
(1992&ndash;3). Its lyrics include "Amanec&iacute;a en mitad de la noche, los
guaguas iban a clase sin sol" ("It was dawning in the middle of the
night, the buses went to class without sun").
-<tr><td>&nbsp;</td><td></td></tr>
-
-<tr><td>Artist</td><td>Irving Kahal and Harry Richman</td></tr>
-<tr><td>Song</td><td>There Ought to be a Moonlight Saving Time</td></tr>
-<tr><td>Copyright Date</td><td>1931</td>
-<tr><td>Notes</td><td>This musical standard was a No. 1 hit for Guy Lombardo
+</li>
+<li>
+Irving Kahal and Harry Richman,
+"There Ought to be a Moonlight Saving Time" (1931).
+This musical standard was a No. 1 hit for Guy Lombardo
in 1931, and was also performed by Maurice Chevalier, Blossom Dearie
and many others. The phrase "Moonlight saving time" also appears in
the 1995 country song "Not Enough Hours in the Night" written by Aaron
Barker, Kim Williams and Rob Harbin and performed by Doug
-Supernaw.</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-
-<tr><td>Artist</td><td>The Microscopic Septet</td></tr>
-<tr><td>CD</td><td>Lobster Leaps In</td></tr>
-<tr><td>Copyright Date</td><td>2008</td></tr>
-<tr><td>Label</td><td>Cuneiform</td></tr>
-<tr><td>ID</td><td>272</td></tr>
-<tr><td>Total Time</td><td>73:05</td></tr>
-<tr><td>Notes</td><td>Includes the song "Twilight Time Zone."</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/lobster-leaps-in-mw0000794929">AMG Rating</a></td><td>3.5 stars</td></tr>
-<tr><td>ADO Rating</td><td>2 stars</td></tr>
-
-<tr><td>&nbsp;</td><td></td></tr>
-
-<tr><td>Artist</td><td>Bob Dylan</td></tr>
-<tr><td>CD</td><td>The Times They Are a-Changin'</td></tr>
-<tr><td>Copyright Date</td><td>1964</td></tr>
-<tr><td>Label</td><td>Columbia</td></tr>
-<tr><td>ID</td><td>CK-8905</td></tr>
-<tr><td>Total Time</td><td>45:36</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/the-times-they-a-changin-mw0000202344">AMG Rating</a></td><td>4.5 stars</td></tr>
-<tr><td>ADO Rating</td><td>1.5 stars</td></tr>
-<tr><td>Notes<td>The title song is also available on "Bob Dylan's Greatest Hits" and "The Essential Bob Dylan."</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-
-<tr><td>Artist</td><td>Luciana Souza</td></tr>
-<tr><td>CD</td><td>Tide</td></tr>
-<tr><td>Copyright Date</td><td>2009</td></tr>
-<tr><td>Label</td><td>Universal Jazz France</td></tr>
-<tr><td>ID</td><td>B0012688-02</td></tr>
-<tr><td>Total Time</td><td>42:31</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/tide-mw0000815692">AMG Rating</a></td><td>3.5 stars</td></tr>
-<tr><td>ADO Rating</td><td>2.5 stars</td></tr>
-<tr><td>Notes<td>Includes the song "Fire and Wood" with the lyric
+Supernaw.
+</li>
+<li>
+The Microscopic Septet, <em>Lobster Leaps In</em> (2008).
+Cuneiform 272, 73:05.
+Includes the song "Twilight Time Zone."
+ADO &#x2605;&#x2605;,
+<a href="https://www.allmusic.com/album/lobster-leaps-in-mw0000794929">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2BEA;.
+</li>
+<li>
+Bob Dylan, <em>The Times They Are a-Changin'</em> (1964).
+Columbia CK-8905, 45:36.
+ADO &#x2605;&#x2BEA;,
+<a href="https://www.allmusic.com/album/the-times-they-a-changin-mw0000202344">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;&#x2BEA;.
+The title song is also available on "Bob Dylan's Greatest Hits" and "The Essential Bob Dylan."
+</li>
+<li>
+Luciana Souza, <em>Tide</em> (2009). Universal Jazz France B0012688-02, 42:31.
+ADO &#x2605;&#x2605;&#x2BEA;,
+<a href="https://www.allmusic.com/album/tide-mw0000815692">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2BEA;.
+Includes the song "Fire and Wood" with the lyric
"The clocks were turned back you remember/Think it's still November."
-</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Ken Nordine</td></tr>
-<tr><td>CD</td><td>You're Getting Better: The Word Jazz Dot Masters</td></tr>
-<tr><td>Copyright Date</td><td>2005</td></tr>
-<tr><td>Label</td><td>Geffen</td></tr>
-<tr><td>ID</td><td>B0005171-02</td></tr>
-<tr><td>Total Time</td><td>156:22</td></tr>
-<tr><td>ADO Rating</td><td>1 star</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/youre-getting-better-the-word-jazz-dot-masters-mw0000736197">AMG Rating</a></td><td>4.5 stars</td></tr>
-<tr><td>Notes</td><td>Includes the piece "What Time Is It"
-("He knew what time it was everywhere...that counted").</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Chicago</td></tr>
-<tr><td>CD</td><td>Chicago Transit Authority</td></tr>
-<tr><td>Copyright Date</td><td>1969</td></tr>
-<tr><td>Label</td><td>Columbia</td></tr>
-<tr><td>ID</td><td>64409</td></tr>
-<tr><td>Total Time</td><td>1:16:20</td></tr>
-<tr><td><a href="https://www.allmusic.com/album/chicago-transit-authority-mw0000189364">AMG Rating</a></td><td>4 stars</td></tr>
-<tr><td>Notes</td><td>Includes the song "Does Anybody Really Know What Time It Is?"</td></tr>
-<tr><td>&nbsp;</td><td></td></tr>
-<tr><td>Artist</td><td>Emanuele Arciuli</td></tr>
-<tr><td>Composer</td><td>William Duckworth</td></tr>
-<tr><td>CD</td><td><a href="https://neumarecords.org/ols/products/william-duckworth-the-time-curve-preludes">The Time Curve Preludes</a></td></tr>
-<tr><td>Copyright Date</td><td>2023</td></tr>
-<tr><td>Label</td><td>Neuma</td></tr>
-<tr><td>Total Time</td><td>44:46</td></tr>
-<tr><td>Notes</td><td>The first work of postminimal music. Unlike minimalism, it does not assume that the listener has plenty of time.</td></tr>
-</table>
+</li>
+<li>
+Ken Nordine, <em>You're Getting Better: The Word Jazz Dot Masters</em> (2005).
+Geffen B0005171-02, 156:22.
+ADO &#x2605;,
+<a href="https://www.allmusic.com/album/youre-getting-better-the-word-jazz-dot-masters-mw0000736197">AMG</a>
+&#x2605;&#x2605;&#x2605;&#x2605;&#x2BEA;.
+Includes the piece "What Time Is It"
+("He knew what time it was everywhere...that counted").
+</li>
+<li>
+Chicago, <em>Chicago Transit Authority</em> (1969). Columbia 64409, 1:16:20.
+<a href="https://www.allmusic.com/album/chicago-transit-authority-mw0000189364">AMG</a> &#x2605;&#x2605;&#x2605;&#x2605;.
+Includes the song "Does Anybody Really Know What Time It Is?".
+</li>
+<li>
+Emanuele Arciuli,
+<a href="https://neumarecords.org/ols/products/william-duckworth-the-time-curve-preludes"><em>The Time Curve Preludes</em></a> (2023).
+Neuma 174, 44:46.
+The title piece, composed by
+<a href="https://en.wikipedia.org/wiki/William_Duckworth_(composer)">William
+Duckworth</a>, is the first work of postminimal music.
+Unlike minimalism, it does not assume that the listener has plenty of time.
+</li>
+</ul>
<h2>Comics</h2>
<ul>
<li>
@@ -506,7 +450,10 @@ along with the panels
"<a href="https://xkcd.com/2050/">6/6 Time</a>" (2018-09-24),
"<a href="https://xkcd.com/2092/">Consensus New Year</a>" (2018-12-31),
"<a href="https://xkcd.com/2266/">Leap Smearing</a>" (2020-02-10),
-and "<a href="https://xkcd.com/2594/">Consensus Time</a>" (2022-03-16).
+"<a href="https://xkcd.com/2594/">Consensus Time</a>" (2022-03-16),
+"<a href="https://xkcd.com/2846/">Daylight Saving Choice</a>" (2023-10-25),
+"<a href="https://xkcd.com/2854/">Date Line</a>" (2023-11-13),
+and "<a href="https://xkcd.com/2867/">DateTime</a>" (2023-12-13).
The related book <em>What If?</em> has an entry
"<a href="https://what-if.xkcd.com/26/">Leap Seconds</a>" (2012-12-31).
</li>
@@ -519,9 +466,13 @@ Before Swine</em> (2016-11-06)</a>.
Stonehenge is abandoned in <a
href="https://www.gocomics.com/nonsequitur/2017/03/12"><em>Non Sequitur</em>
(2017-03-12)</a>.
+</li>
<li>
-The boss freaks out in <a
-href="https://dilbert.com/strip/1998-03-14"><em>Dilbert</em> (1998-03-14)</a>.
+Caulfield proposes changing clocks just once a year in
+<a href="https://www.gocomics.com/frazz/2023/12/31"><em>Frazz</em>
+(2023-12-31)</a>, while Peter and Jason go multi-lingual and -zonal in
+<a href="https://www.gocomics.com/foxtrot/2023/12/31"><em>FoxTrot</em>
+(the same day)</a>.
</li>
<li>
Peppermint Patty: "What if the world comes to an end tonight, Marcie?"
@@ -605,10 +556,10 @@ entitled "The Kid," originally aired 1997-11-04)
</li>
<li>
"I put myself and my staff through this crazy, huge ordeal, all because
-I refused to go on at midnight, okay? And so I work, you know, and
-then I get this job at eleven, supposed to be a big deal. Then
+I refused to go on at midnight, okay? And so I work, you know, and
+then I get this job at eleven, supposed to be a big deal. Then
yesterday daylight [saving] time ended. Right now it's basically
-midnight." (Conan O'Brien on the 2010-11-08 premiere of <em>Conan</em>.)
+midnight." (Conan O'Brien on the 2010-11-08 premiere of <em>Conan</em>)
</li>
<li>
"The best method, I told folks, was to hang a large clock high on a
@@ -616,7 +567,7 @@ barn wall where all the cows could see it. If you have Holsteins, you
will need to use an analog clock." (Jerry Nelson, <a
href="http://www.agriculture.com/family/farm-humor/how-to-adjust-dairy-cows-to-daylight-savings-time">How
to adjust dairy cows to daylight saving time</a>", <em>Successful Farming</em>,
-2017-10-09.)
+2017-10-09)
</li>
<li>
"And now, driving to California, I find that I must enter a password
diff --git a/tz-link.html b/tz-link.html
index 43190dd7efcd..9fb57c9058a9 100644
--- a/tz-link.html
+++ b/tz-link.html
@@ -114,7 +114,7 @@ Indiana, which switched from central to eastern time in 1991
and switched back in 2006.
To use the database on an extended <a
href="https://en.wikipedia.org/wiki/POSIX"><abbr
-title="Portable Operating System Interface">POSIX</abbr></a>
+title="Portable Operating System Interface">POSIX</abbr>.1-2017</a>
implementation set the <code><abbr>TZ</abbr></code>
environment variable to the location's full name,
e.g., <code><abbr>TZ</abbr>="America/New_York"</code>.</p>
@@ -172,7 +172,7 @@ Since version 2022a, each release has been distributed in
ustar interchange format</a>, compressed as described above;
older releases use a nearly compatible format.
Since version 2016h, each release has contained a text file named
-"<samp>version</samp>" whose first (and currently only) line is the version.
+"<code>version</code>" whose first (and currently only) line is the version.
Older releases are <a href="https://ftp.iana.org/tz/releases/">archived</a>,
and are also available in an
<a href="ftp://ftp.iana.org/tz/releases/"><abbr
@@ -362,9 +362,6 @@ lets you see the <code><abbr>TZ</abbr></code> values directly.</li>
<li><a
href="https://www.convertit.com/Go/ConvertIt/World_Time/Current_Time.ASP">Current
Time in 1000 Places</a> uses descriptions of the values.</li>
-<li><a href="https://home.kpn.nl/vanadovv/time/TZworld.html">Complete
-timezone information for all countries</a>
-displays tables of <abbr>DST</abbr> rules.
<li><a href="https://www.timeanddate.com/worldclock/">The World Clock &ndash;
Worldwide</a> lets you sort zone names and convert times.</li>
<li><a href="https://24timezones.com">24TimeZones</a> has a world
@@ -511,12 +508,12 @@ It is freely available under the Apache License.</li>
<li>Many modern
<a href="https://en.wikipedia.org/wiki/JavaScript">JavaScript</a>
runtimes support <code><abbr>tz</abbr></code> natively via the
-<samp>timeZone</samp> option of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat"><samp>Intl.DateTimeFormat</samp></a>.
+<code>timeZone</code> option of <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat"><code>Intl.DateTimeFormat</code></a>.
This can be used as-is or with most of the following libraries,
-many of which also support runtimes lacking the <samp>timeZone</samp> option.
+many of which also support runtimes lacking the <code>timeZone</code> option.
<ul>
<li>The <a
-href="https://github.com/formatjs/date-time-format-timezone"><samp>Intl.DateTimeFormat</samp>
+href="https://github.com/formatjs/date-time-format-timezone"><code>Intl.DateTimeFormat</code>
timezone polyfill</a>
is freely available under a <abbr>BSD</abbr>-style license.</li>
<li>The <a href="https://date-fns.org/">date-fns</a>
@@ -529,7 +526,7 @@ the <a href="https://momentjs.com/docs/">now-legacy Moment.js</a> date
manipulation library.
It is freely available under the <abbr>MIT</abbr> license.</li>
<li><a href="https://moment.github.io/luxon/">Luxon</a> improves
-timezone support for the <samp>Intl</samp> API.
+timezone support for the <code>Intl</code> API.
It is freely available under the <abbr>MIT</abbr> license.</li>
<li><a href="https://momentjs.com/timezone/">Moment Timezone</a> is a
Moment.js plugin.
@@ -550,11 +547,11 @@ convert timestamps, view transition histories, and download code and data.
It is freely available under the <abbr>MIT</abbr> license.</li>
</ul>
The proposed <a
-href="https://github.com/tc39/proposal-temporal"><samp>Temporal</samp>
+href="https://github.com/tc39/proposal-temporal"><code>Temporal</code>
objects</a> let programs access an abstract view of
<code><abbr>tzdb</abbr></code> data, and are designed to replace <a
href="https://codeofmatt.com/javascript-date-type-is-horribly-broken/">JavaScript's
-problematic <samp>Date</samp> objects</a> when working with dates and times.
+problematic <code>Date</code> objects</a> when working with dates and times.
<li><a href="https://github.com/JuliaTime/">JuliaTime</a> contains a
compiler from <code><abbr>tz</abbr></code> source into
<a href="https://julialang.org/">Julia</a>. It is freely available
@@ -620,6 +617,16 @@ the Apache License.</li>
library that translates between <abbr>UT</abbr> and civil time and
can read <abbr>TZif</abbr> files. It is freely available under the Apache
License.</li>
+<li>The
+<a href="https://github.com/nayarsystems/posix_tz_db"><code>posix_tz_db</code>
+package</a> contains Python code
+to generate <abbr>CSV</abbr> and <abbr>JSON</abbr> tables that map
+<code><abbr>tz</abbr></code> settings to POSIX.1-2017-like approximations.
+For example, it maps <code>"Africa/Cairo"</code>
+to <code>"EET-2EEST,M4.5.5/0,M10.5.4/24"</code>,
+an approximation valid for Cairo timestamps from 2023 on.
+This can help porting to platforms that support only POSIX.1-2017.
+The package is freely available under the MIT license.</li>
<li><a href="https://github.com/derickr/timelib">Timelib</a> is a C
library that reads <abbr>TZif</abbr> files and converts
timestamps from one time zone or format to another.
@@ -666,9 +673,7 @@ available under a <abbr>BSD</abbr>-style license.</li>
<ul>
<li><a href="https://foxclocks.org">FoxClocks</a>
is an extension for <a href="https://www.google.com/chrome/">Google
-Chrome</a> and for <a
-href="https://wiki.mozilla.org/Modules/Toolkit">Mozilla
-Toolkit</a> applications like <a
+Chrome</a>, <a
href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> and <a
href="https://www.mozilla.org/en-US/thunderbird/">Thunderbird</a>.
It displays multiple clocks in the application window, and has a mapping
@@ -782,32 +787,9 @@ boundaries of <code><abbr>tzdb</abbr></code> timezones.
Its code is freely available under the <abbr>MIT</abbr> license, and
its data entries are freely available under the
<a href="https://opendatacommons.org/licenses/odbl/">Open Data Commons
-Open Database License</a>. The maps' borders appear to be quite accurate.</li>
-<li>Programmatic interfaces that map geographical coordinates via tz_world to
-<code><abbr>tzdb</abbr></code> timezones include:
-<ul>
-<li><a href="https://github.com/mj1856/GeoTimeZone">GeoTimeZone</a> is
-written in <a
-href="https://en.wikipedia.org/wiki/C_Sharp_(programming_language)">C#</a>
-and is freely available under the <abbr>MIT</abbr> license.</li>
-<li>The <a href="https://github.com/bradfitz/latlong">latlong package</a>
-is written in Go and is freely available under the Apache License.</li>
-<li><a href="https://github.com/drtimcooper/LatLongToTimezone">LatLongToTimezone</a>,
-in both Java and
-<a href="https://en.wikipedia.org/wiki/Swift_(programming_language)">Swift</a>
-form, is freely available under the MIT license.</li>
-<li>For Node.js,
-the <a href="https://www.npmjs.com/package/geo-tz">geo-tz module</a>
-is freely available under the MIT license, and
-the <a href="https://www.npmjs.com/package/tz-lookup">tz-lookup module</a>
-is in the public domain.</li>
-<li>The <a
-href="https://github.com/MrMinimal64/timezonefinder">timezonefinder</a>
-library for Python is freely available under the MIT license.
-<li>The <a
-href="https://github.com/gunyarakun/timezone_finder">timezone_finder</a>
-library for Ruby is freely available under the MIT license.</li>
-</ul></li>
+Open Database License</a>. The borders appear to be quite accurate.
+Its main web page lists more than twenty libraries
+for looking up a timezone name from a GPS coordinate.</li>
<li>Free access via a network API, if you register a key, is provided by
the <a
href="https://www.geonames.org/export/web-services.html#timezone">GeoNames
@@ -826,12 +808,9 @@ coordinates?</a>" discusses other geolocation possibilities.</li>
<li><a href="http://statoids.com/statoids.html">Administrative
Divisions of Countries ("Statoids")</a> lists
political subdivision data related to time zones.</li>
-<li><a href="https://home.kpn.nl/vanadovv/time/Multizones.html">Time
-zone boundaries for multizone countries</a> summarizes legal
-boundaries between time zones within countries.</li>
<li><a href="https://manifold.net/info/freestuff.shtml">Manifold Software
&ndash; GIS and Database Tools</a> includes a Manifold-format map of
-world time zone boundaries distributed under the
+world time zone boundaries circa 2007, distributed under the
<abbr>GPL</abbr>.</li>
<li>A ship within the <a
href="https://en.wikipedia.org/wiki/Territorial_waters">territorial
@@ -862,7 +841,7 @@ Lords</a> discusses how authoritarians manipulate civil time.</li>
<li><a href="https://www.w3.org/TR/timezone/">Working with Time Zones</a>
contains guidelines and best practices for software applications that
deal with civil time.</li>
-<li><a href="https://www.staff.science.uu.nl/~gent0113/idl/idl.htm">A History of
+<li><a href="https://webspace.science.uu.nl/~gent0113/idl/idl.htm">A History of
the International Date Line</a> tells the story of the most important
time zone boundary.</li>
<li><a href="http://statoids.com/tconcept.html">Basic Time
@@ -875,7 +854,7 @@ Zone Concepts</a> discusses terminological issues behind time zones.</li>
<dl>
<dt>Australia</dt>
<dd>The Parliamentary Library commissioned a <a
-href="https://www.aph.gov.au/binaries/library/pubs/rp/2009-10/10rp10.pdf">research
+href="https://parlinfo.aph.gov.au/parlInfo/download/library/prspub/359V6/upload_binary/359v60.pdf">research
paper on daylight saving time in Australia</a>.
The Bureau of Meteorology publishes a list of <a
href="http://www.bom.gov.au/climate/averages/tables/dst_times.shtml">Implementation
@@ -883,9 +862,9 @@ Dates of Daylight Savings Time within Australia</a>.</dd>
<dt>Belgium</dt>
<dd>The Royal Observatory of Belgium maintains a table of time in
Belgium (in
-<a href="https://www.astro.oma.be/GENERAL/INFO/nli001a.html"
+<a href="https://robinfo.oma.be/nl/astro-info/tijd/"
hreflang="nl">Dutch</a> and <a
-href="https://www.astro.oma.be/GENERAL/INFO/fri001a.html"
+href="https://robinfo.oma.be/fr/astro-info/heure/"
hreflang="fr">French</a>).</dd>
<dt>Brazil</dt>
<dd>The Time Service Department of the National Observatory
@@ -929,7 +908,7 @@ Congress has published a <a
href="https://www.diputados.gob.mx/bibliot/publica/inveyana/polisoc/horver/index.htm"
hreflang="es">history of Mexican local time (in Spanish)</a>.</dd>
<dt>Netherlands</dt>
-<dd><a href="https://www.staff.science.uu.nl/~gent0113/wettijd/wettijd.htm"
+<dd><a href="https://webspace.science.uu.nl/~gent0113/wettijd/wettijd.htm"
hreflang="nl">Legal time in the Netherlands (in Dutch)</a>
covers the history of local time in the Netherlands from ancient times.</dd>
<dt>New Zealand</dt>
@@ -938,7 +917,7 @@ href="https://www.dia.govt.nz/Daylight-Saving-History">History of
Daylight Saving</a>.</dd>
<dt>Palestine</dt>
<dd>The Ministry of Telecom and IT publishes a <a
-href="https://mtit.pna.ps/Site/TimeZoon"
+href="https://mtit.pna.ps/home/TimeZone"
hreflang="ar">history of clock changes (in Arabic)</a>.</dd>
<dt>Portugal</dt>
<dd>The Lisbon Astronomical Observatory publishes a
@@ -973,6 +952,9 @@ zone shifts, and many scientific studies have been conducted. This
section summarizes reviews and position statements based on
scientific literature in the area.</p>
<ul>
+<li>In 2022 the American Medical Association issued a
+<a href="https://www.ama-assn.org/press-center/press-releases/ama-calls-permanent-standard-time">statement
+supporting permanent standard time</a> on health grounds.</li>
<li>Carey RN, Sarma KM.
<a href="https://bmjopen.bmj.com/content/7/6/e014319.long">Impact of
daylight saving time on road traffic collision risk: a systematic
@@ -983,25 +965,36 @@ neither supports nor refutes road safety benefits from
shifts in time zones.</li>
<li>Havranek T, Herman D, Irsova D.
<a href="https://www.iaee.org/en/publications/ejarticle.aspx?id=3051">Does
-daylight saving save electricity? A meta-analysis.</a>
+daylight saving save electricity? A meta-analysis</a>.
<em>Energy J.</em> 2018;39(2):35&ndash;61.
doi:<a href="https://doi.org/10.5547/01956574.39.2.thav">10.5547/01956574.39.2.thav</a>.
This analyzes research literature and concludes, "Electricity savings
are larger for countries farther away from the equator, while
subtropical regions consume more electricity because of <abbr>DST</abbr>."</li>
-<li>Rishi MA, Ahmed O, Barrantes Perez JH <em>et al</em>.
-<a href="https://jcsm.aasm.org/doi/10.5664/jcsm.8780">Daylight saving time:
+<li>Malow BA. <a
+href="https://academic.oup.com/sleep/article/45/12/zsac236/6717940">It is time
+to abolish the clock change and adopt permanent
+standard time in the United States:
+a Sleep Research Society position statement</a>.
+<em>Sleep.</em> 2022;45(12):zsac236.
+doi:<a href="https://doi.org/10.1093/sleep/zsac236">10.1093/sleep/zsac236</a>.
+After reviewing the scientific literature, the Sleep Research Society
+advocates permanent standard time due to its health benefits.
+<li>Rishi MA, Cheng JY, Strang AR <em>et al</em>.
+<a href="https://jcsm.aasm.org/doi/10.5664/jcsm.10898">Permanent standard time
+is the optimal choice for health and safety:
an American Academy of Sleep Medicine position statement</a>.
-<em>J Clin Sleep Med.</em>
-2020;<a href="https://doi.org/10.5664/jcsm.8780">10.5664/jcsm.8780</a>.
-This argues for permanent standard time due to health risks of both
-<abbr>DST</abbr> transitions and permanent <abbr>DST</abbr>.</li>
+<em>J Clin Sleep Med.</em> 2024;20(1):121&ndash;125.
+doi:<a href="https://doi.org/10.5664/jcsm.10898">10.5664/jcsm.10898</a>.
+The AASM argues for permanent standard time due to health and safety risks
+and economic costs of both <abbr>DST</abbr> transitions and
+permanent <abbr>DST</abbr>.</li>
<li>Roenneberg T, Wirz-Justice A, Skene DJ <em>et al</em>.
-<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7205184/">Why
+<a href="https://journals.sagepub.com/doi/10.1177/0748730419854197">Why
should we abolish Daylight Saving Time?</a>
-<em>J Biol Rhythms</em>. 2019;34(3):227&ndash;230.
+<em>J Biol Rhythms.</em> 2019;34(3):227&ndash;230.
doi:<a href="https://doi.org/10.1177/0748730419854197">10.1177/0748730419854197</a>.
-This position paper of the Society for Research on Biological Rhythms
+The Society for Research on Biological Rhythms
opposes DST changes and permanent DST, and advocates that governments adopt
"permanent Standard Time for the health and safety of their citizens".</li>
</ul>
@@ -1117,9 +1110,12 @@ might be redefined
without Leap Seconds</a> gives pointers on this
contentious issue.
The General Conference on Weights and Measures
-<a href="https://www.bipm.org/en/cgpm-2022/resolution-4">voted in 2022</a>
+<a href="https://www.bipm.org/en/cgpm-2022/resolution-4">decided in 2022</a>
to discontinue the use of leap seconds by 2035, replacing them with an
as-yet-undetermined scheme some time after the year 2135.
+The World Radiocommunication Conference <a
+href="https://www.itu.int/dms_pub/itu-r/opb/act/R-ACT-WRC.15-2023-PDF-E.pdf">resolved
+in 2023</a> to cooperate with this process.
</li>
</ul>
</section>
diff --git a/tzfile.5 b/tzfile.5
index 59d9f6ba9103..867348d6726d 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
@@ -83,22 +85,22 @@ described in the file is associated with the time period
starting with the same-indexed transition time
and continuing up to but not including the next transition time.
(The last time type is present only for consistency checking with the
-POSIX-style TZ string described below.)
+POSIX.1-2017-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,22 +170,24 @@ 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
-for another time zone specified via a POSIX-style TZ string that lacks rules.
+for another time zone specified via
+a POSIX.1-2017-style TZ string that lacks rules.
For example, when TZ="EET\*-2EEST" and there is no TZif file "EET\*-2EEST",
the idea was to adapt the transition times from a TZif file with the
well-known name "posixrules" that is present only for this purpose and
@@ -211,13 +216,14 @@ the above header and data are followed by a second header and data,
identical in format except that
eight bytes are used for each transition time or leap second time.
(Leap second counts remain four bytes.)
-After the second header and data comes a newline-enclosed,
-POSIX-TZ-environment-variable-style string for use in handling instants
+After the second header and data comes a newline-enclosed string
+in the style of the contents of a POSIX.1-2017 TZ environment variable,
+for use in handling instants
after the last transition time stored in the file
or for all instants if the file has no transitions.
-The POSIX-style TZ string is empty (i.e., nothing between the newlines)
-if there is no POSIX-style representation for such instants.
-If nonempty, the POSIX-style TZ string must agree with the local time
+The TZ string is empty (i.e., nothing between the newlines)
+if there is no POSIX.1-2017-style representation for such instants.
+If nonempty, the TZ string must agree with the local time
type after the last transition time if present in the eight-byte data;
for example, given the string
.q "WET0WEST,M3.5.0/1,M10.5.0"
@@ -229,8 +235,8 @@ Also, if there is at least one transition, time type 0 is associated
with the time period from the indefinite past up to but not including
the earliest transition time.
.SS Version 3 format
-For version-3-format timezone files, the POSIX-TZ-style string may
-use two minor extensions to the POSIX TZ format, as described in
+For version-3-format timezone files, the TZ string may
+use two minor extensions to the POSIX.1-2017 TZ format, as described in
.BR newtzset (3).
First, the hours part of its transition times may be signed and range from
\-167 through 167 instead of the POSIX-required unsigned values
@@ -312,15 +318,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 +343,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.
+they cannot parse extensions to POSIX.1-2017 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 +376,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,12 +400,12 @@ 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 *
-Some readers mishandle POSIX-style TZ strings that
+.IP \(bu
+Some readers mishandle TZ strings that
contain
.q "<"
or
@@ -407,11 +416,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,23 +428,23 @@ 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.
These readers do not support locations like Ireland, which
-uses the equivalent of the POSIX TZ string
+uses the equivalent of the TZ string
.q "IST\*-1GMT0,M10.5.0,M3.5.0/1",
observing standard time
(IST, +01) in summer and daylight saving time (GMT, +00) in winter.
As a partial workaround, a writer can output data for the
-equivalent of the POSIX TZ string
+equivalent of the TZ string
.q "GMT0IST,M3.5.0/1,M10.5.0",
thus swapping standard and daylight saving time.
Although this workaround misidentifies which part of the year
uses daylight saving time, it records UT offsets and time zone
abbreviations correctly.
-.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 +455,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.5.txt b/tzfile.5.txt
index 6607de74abb5..ed1763ae956b 100644
--- a/tzfile.5.txt
+++ b/tzfile.5.txt
@@ -14,356 +14,367 @@ DESCRIPTION
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:
- * The magic four-byte ASCII sequence "TZif" identifies the file as a
- timezone information file.
-
- * A byte identifying the version of the file's format (as of 2021,
- either an ASCII NUL, "2", "3", or "4").
-
- * Fifteen bytes containing zeros reserved for future use.
-
- * Six four-byte integer values, in the following order:
-
- tzh_ttisutcnt
- The number of UT/local indicators stored in the file. (UT is
- Universal Time.)
-
- tzh_ttisstdcnt
- The number of standard/wall indicators stored in the file.
-
- tzh_leapcnt
- The number of leap seconds for which data entries are stored
- in the file.
-
- tzh_timecnt
- The number of transition times for which data entries are
- stored in the file.
-
- tzh_typecnt
- The number of local time types for which data entries are
- stored in the file (must not be zero).
-
- tzh_charcnt
- The number of bytes of time zone abbreviation strings stored
- in the file.
-
- The above header is followed by the following fields, whose lengths
- depend on the contents of the header:
-
- * 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 time(2)) at which the rules for
- computing local time change.
-
- * tzh_timecnt one-byte unsigned integer values; each one but the last
- tells which of the different types of local time types described in
- the file is associated with the time period starting with the same-
- indexed transition time and continuing up to but not including the
- next transition time. (The last time type is present only for
- consistency checking with the POSIX-style TZ string described below.)
- These values serve as indices into the next field.
-
- * tzh_typecnt ttinfo entries, each defined as follows:
-
- struct ttinfo {
- int32_t tt_utoff;
- unsigned char tt_isdst;
- unsigned char tt_desigidx;
- };
-
- Each structure is written as a four-byte signed integer value for
- tt_utoff, in network byte order, followed by a one-byte boolean for
- tt_isdst and a one-byte value for tt_desigidx. In each structure,
- tt_utoff gives the number of seconds to be added to UT, tt_isdst
- tells whether tm_isdst should be set by localtime(3) and tt_desigidx
- serves as an index into the array of time zone abbreviation bytes
- that follow the ttinfo entries in the file; if the designated string
- is "-00", the ttinfo entry is a placeholder indicating that local
- time is unspecified. The tt_utoff value is never equal to -2**31, to
- let 32-bit clients negate it without overflow. Also, in realistic
- applications tt_utoff is in the range [-89999, 93599] (i.e., more
- than -25 hours and less than 26 hours); this allows easy support by
- implementations that already support the POSIX-required range
- [-24:59:59, 25:59:59].
-
- * tzh_charcnt bytes that represent time zone designations, which are
- null-terminated byte strings, each indexed by the tt_desigidx values
- mentioned above. The byte strings can overlap if one is a suffix of
- the other. The encoding of these strings is not specified.
-
- * tzh_leapcnt pairs of four-byte values, written in network byte order;
- the first value of each pair gives the nonnegative time (as returned
- by time(2)) at which a leap second occurs or at which the leap second
- table expires; the second is a signed integer specifying the
- correction, which is the total number of leap seconds to be applied
- during the time period starting at the given time. The pairs of
- values are sorted in strictly ascending order by time. Each pair
- denotes one leap second, either positive or negative, except that if
- the last pair has the same correction as the previous one, the last
- pair denotes the leap second table's expiration time. Each leap
- second is at the end of a UTC calendar month. The first leap second
- has a nonnegative occurrence time, and is a positive leap second if
- and only if its correction is positive; the correction for each leap
- second after the first differs from the previous leap second by
- either 1 for a positive leap second, or -1 for a negative leap
- second. If the leap second table is empty, the leap-second
- correction is zero for all timestamps; otherwise, for timestamps
- before the first occurrence time, the leap-second correction is zero
- if the first pair's correction is 1 or -1, and is unspecified
- otherwise (which can happen only in files truncated at the start).
-
- * 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.
-
- * 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.
-
- The standard/wall and UT/local indicators were designed for
- transforming a TZif file's transition times into transitions
- appropriate for another time zone specified via a POSIX-style TZ string
- that lacks rules. For example, when TZ="EET-2EEST" and there is no
- TZif file "EET-2EEST", the idea was to adapt the transition times from
- a TZif file with the well-known name "posixrules" that is present only
- for this purpose and is a copy of the file "Europe/Brussels", a file
- with a different UT offset. POSIX does not specify this obsolete
- transformational behavior, the default rules are installation-
- dependent, and no implementation is known to support this feature for
- timestamps past 2037, so users desiring (say) Greek time should instead
- specify TZ="Europe/Athens" for better historical coverage, falling back
- on TZ="EET-2EEST,M3.5.0/3,M10.5.0/4" if POSIX conformance is required
- and older timestamps need not be handled accurately.
-
- The localtime(3) function normally uses the first ttinfo structure in
- the file if either tzh_timecnt is zero or the time argument is less
- than the first transition time recorded in the file.
+ o The magic four-byte ASCII sequence "TZif" identifies the file as a
+ timezone information file.
+
+ o A byte identifying the version of the file's format (as of 2021,
+ either an ASCII NUL, "2", "3", or "4").
+
+ o Fifteen bytes containing zeros reserved for future use.
+
+ o Six four-byte integer values, in the following order:
+
+ tzh_ttisutcnt
+ The number of UT/local indicators stored in the file. (UT
+ is Universal Time.)
+
+ tzh_ttisstdcnt
+ The number of standard/wall indicators stored in the file.
+
+ tzh_leapcnt
+ The number of leap seconds for which data entries are stored
+ in the file.
+
+ tzh_timecnt
+ The number of transition times for which data entries are
+ stored in the file.
+
+ tzh_typecnt
+ The number of local time types for which data entries are
+ stored in the file (must not be zero).
+
+ tzh_charcnt
+ The number of bytes of time zone abbreviation strings stored
+ in the file.
+
+ The above header is followed by the following fields, whose lengths
+ depend on the contents of the header:
+
+ o 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 time(2)) at which the
+ rules for computing local time change.
+
+ o tzh_timecnt one-byte unsigned integer values; each one but the
+ last tells which of the different types of local time types
+ described in the file is associated with the time period
+ starting with the same-indexed transition time and continuing up
+ to but not including the next transition time. (The last time
+ type is present only for consistency checking with the
+ POSIX.1-2017-style TZ string described below.) These values
+ serve as indices into the next field.
+
+ o tzh_typecnt ttinfo entries, each defined as follows:
+
+ struct ttinfo {
+ int32_t tt_utoff;
+ unsigned char tt_isdst;
+ unsigned char tt_desigidx;
+ };
+
+ Each structure is written as a four-byte signed integer value
+ for tt_utoff, in network byte order, followed by a one-byte
+ boolean for tt_isdst and a one-byte value for tt_desigidx. In
+ each structure, tt_utoff gives the number of seconds to be added
+ to UT, tt_isdst tells whether tm_isdst should be set by
+ localtime(3) and tt_desigidx serves as an index into the array
+ of time zone abbreviation bytes that follow the ttinfo entries
+ in the file; if the designated string is "-00", the ttinfo entry
+ is a placeholder indicating that local time is unspecified. The
+ tt_utoff value is never equal to -2**31, to let 32-bit clients
+ negate it without overflow. Also, in realistic applications
+ tt_utoff is in the range [-89999, 93599] (i.e., more than -25
+ hours and less than 26 hours); this allows easy support by
+ implementations that already support the POSIX-required range
+ [-24:59:59, 25:59:59].
+
+ o tzh_charcnt bytes that represent time zone designations, which
+ are null-terminated byte strings, each indexed by the
+ tt_desigidx values mentioned above. The byte strings can
+ overlap if one is a suffix of the other. The encoding of
+ these strings is not specified.
+
+ o tzh_leapcnt pairs of four-byte values, written in network byte
+ order; the first value of each pair gives the nonnegative time
+ (as returned by time(2)) at which a leap second occurs or at
+ which the leap second table expires; the second is a signed
+ integer specifying the correction, which is the total number
+ of leap seconds to be applied during the time period starting
+ at the given time. The pairs of values are sorted in strictly
+ ascending order by time. Each pair denotes one leap second,
+ either positive or negative, except that if the last pair has
+ the same correction as the previous one, the last pair denotes
+ the leap second table's expiration time. Each leap second is
+ at the end of a UTC calendar month. The first leap second has
+ a nonnegative occurrence time, and is a positive leap second
+ if and only if its correction is positive; the correction for
+ each leap second after the first differs from the previous
+ leap second by either 1 for a positive leap second, or -1 for
+ a negative leap second. If the leap second table is empty,
+ the leap-second correction is zero for all timestamps;
+ otherwise, for timestamps before the first occurrence time,
+ the leap-second correction is zero if the first pair's
+ correction is 1 or -1, and is unspecified otherwise (which can
+ happen only in files truncated at the start).
+
+ o 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.
+
+ o 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.
+
+ The standard/wall and UT/local indicators were designed for
+ transforming a TZif file's transition times into transitions
+ appropriate for another time zone specified via a
+ POSIX.1-2017-style TZ string that lacks rules. For example, when
+ TZ="EET-2EEST" and there is no TZif file "EET-2EEST", the idea was
+ to adapt the transition times from a TZif file with the well-known
+ name "posixrules" that is present only for this purpose and is a
+ copy of the file "Europe/Brussels", a file with a different UT
+ offset. POSIX does not specify this obsolete transformational
+ behavior, the default rules are installation-dependent, and no
+ implementation is known to support this feature for timestamps past
+ 2037, so users desiring (say) Greek time should instead specify
+ TZ="Europe/Athens" for better historical coverage, falling back on
+ TZ="EET-2EEST,M3.5.0/3,M10.5.0/4" if POSIX conformance is required
+ and older timestamps need not be handled accurately.
+
+ The localtime(3) function normally uses the first ttinfo structure
+ in the file if either tzh_timecnt is zero or the time argument is
+ less than the first transition time recorded in the file.
Version 2 format
- For version-2-format timezone files, the above header and data are
- followed by a second header and data, identical in format except that
- eight bytes are used for each transition time or leap second time.
- (Leap second counts remain four bytes.) After the second header and
- data comes a newline-enclosed, POSIX-TZ-environment-variable-style
- string for use in handling instants after the last transition time
- stored in the file or for all instants if the file has no transitions.
- The POSIX-style TZ string is empty (i.e., nothing between the newlines)
- if there is no POSIX-style representation for such instants. If
- nonempty, the POSIX-style TZ string must agree with the local time type
- after the last transition time if present in the eight-byte data; for
- example, given the string "WET0WEST,M3.5.0/1,M10.5.0" then if a last
- transition time is in July, the transition's local time type must
- specify a daylight-saving time abbreviated "WEST" that is one hour east
- of UT. Also, if there is at least one transition, time type 0 is
- associated with the time period from the indefinite past up to but not
- including the earliest transition time.
+ For version-2-format timezone files, the above header and data are
+ followed by a second header and data, identical in format except that
+ eight bytes are used for each transition time or leap second time.
+ (Leap second counts remain four bytes.) After the second header and
+ data comes a newline-enclosed string in the style of the contents of a
+ POSIX.1-2017 TZ environment variable, for use in handling instants
+ after the last transition time stored in the file or for all instants
+ if the file has no transitions. The TZ string is empty (i.e., nothing
+ between the newlines) if there is no POSIX.1-2017-style representation
+ for such instants. If nonempty, the TZ string must agree with the
+ local time type after the last transition time if present in the eight-
+ byte data; for example, given the string "WET0WEST,M3.5.0/1,M10.5.0"
+ then if a last transition time is in July, the transition's local time
+ type must specify a daylight-saving time abbreviated "WEST" that is one
+ hour east of UT. Also, if there is at least one transition, time type
+ 0 is associated with the time period from the indefinite past up to but
+ not including the earliest transition time.
Version 3 format
- For version-3-format timezone files, the POSIX-TZ-style string may use
- two minor extensions to the POSIX TZ format, as described in
- newtzset(3). First, the hours part of its transition times may be
- signed and range from -167 through 167 instead of the POSIX-required
- unsigned values from 0 through 24. Second, DST is in effect all year
- if it starts January 1 at 00:00 and ends December 31 at 24:00 plus the
- difference between daylight saving and standard time.
+ For version-3-format timezone files, the TZ string may use two minor
+ extensions to the POSIX.1-2017 TZ format, as described in newtzset(3).
+ First, the hours part of its transition times may be signed and range
+ from -167 through 167 instead of the POSIX-required unsigned values
+ from 0 through 24. Second, DST is in effect all year if it starts
+ January 1 at 00:00 and ends December 31 at 24:00 plus the difference
+ between daylight saving and standard time.
Version 4 format
- For version-4-format TZif files, the first leap second record can have
- a correction that is neither +1 nor -1, to represent truncation of the
- TZif file at the start. Also, if two or more leap second transitions
- are present and the last entry's correction equals the previous one,
- the last entry denotes the expiration of the leap second table instead
- of a leap second; timestamps after this expiration are unreliable in
- that future releases will likely add leap second entries after the
- expiration, and the added leap seconds will change how post-expiration
+ For version-4-format TZif files, the first leap second record can have
+ a correction that is neither +1 nor -1, to represent truncation of the
+ TZif file at the start. Also, if two or more leap second transitions
+ are present and the last entry's correction equals the previous one,
+ the last entry denotes the expiration of the leap second table instead
+ of a leap second; timestamps after this expiration are unreliable in
+ that future releases will likely add leap second entries after the
+ expiration, and the added leap seconds will change how post-expiration
timestamps are treated.
Interoperability considerations
Future changes to the format may append more data.
- Version 1 files are considered a legacy format and should not be
+ Version 1 files are considered a legacy format and should not be
generated, as they do not support transition times after the year 2038.
- Readers that understand only Version 1 must ignore any data that
+ Readers that understand only Version 1 must ignore any data that
extends beyond the calculated end of the version 1 data block.
Other than version 1, writers should generate the lowest version number
- needed by a file's data. For example, a writer should generate a
- version 4 file only if its leap second table either expires or is
- truncated at the start. Likewise, a writer not generating a version 4
- file should generate a version 3 file only if TZ string extensions are
+ needed by a file's data. For example, a writer should generate a
+ version 4 file only if its leap second table either expires or is
+ truncated at the start. Likewise, a writer not generating a version 4
+ file should generate a version 3 file only if TZ string extensions are
necessary to accurately model transition times.
- The sequence of time changes defined by the version 1 header and data
- block should be a contiguous sub-sequence of the time changes defined
- by the version 2+ header and data block, and by the footer. This
- guideline helps obsolescent version 1 readers agree with current
- readers about timestamps within the contiguous sub-sequence. It also
- lets writers not supporting obsolescent readers use a tzh_timecnt of
+ The sequence of time changes defined by the version 1 header and data
+ block should be a contiguous sub-sequence of the time changes defined
+ by the version 2+ header and data block, and by the footer. This
+ guideline helps obsolescent version 1 readers agree with current
+ readers about timestamps within the contiguous sub-sequence. It also
+ lets writers not supporting obsolescent readers use a tzh_timecnt of
zero in the version 1 data block to save space.
- When a TZif file contains a leap second table expiration time, TZif
- readers should either refuse to process post-expiration timestamps, or
- process them as if the expiration time did not exist (possibly with an
+ When a TZif file contains a leap second table expiration time, TZif
+ readers should either refuse to process post-expiration timestamps, or
+ process them as if the expiration time did not exist (possibly with an
error indication).
Time zone designations should consist of at least three (3) and no more
- than six (6) ASCII characters from the set of alphanumerics, "-", and
- "+". This is for compatibility with POSIX requirements for time zone
+ than six (6) ASCII characters from the set of alphanumerics, "-", and
+ "+". This is for compatibility with POSIX requirements for time zone
abbreviations.
- When reading a version 2 or higher file, readers should ignore the
+ When reading a version 2 or higher file, readers should ignore the
version 1 header and data block except for the purpose of skipping over
them.
- Readers should calculate the total lengths of the headers and data
+ Readers should calculate the total lengths of the headers and data
blocks and check that they all fit within the actual file size, as part
of a validity check for the file.
- When a positive leap second occurs, readers should append an extra
- second to the local minute containing the second just before the leap
- second. If this occurs when the UTC offset is not a multiple of 60
- seconds, the leap second occurs earlier than the last second of the
- local minute and the minute's remaining local seconds are numbered
+ When a positive leap second occurs, readers should append an extra
+ second to the local minute containing the second just before the leap
+ second. If this occurs when the UTC offset is not a multiple of 60
+ seconds, the leap second occurs earlier than the last second of the
+ local minute and the minute's remaining local seconds are numbered
through 60 instead of the usual 59; the UTC offset is unaffected.
Common interoperability issues
- This section documents common problems in reading or writing TZif
- files. Most of these are problems in generating TZif files for use by
+ 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:
- * to help TZif writers output files that avoid common pitfalls in older
- or buggy TZif readers,
+ o to help TZif writers output files that avoid common pitfalls in
+ older or buggy TZif readers,
- * to help TZif readers avoid common pitfalls when reading files
- generated by future TZif writers, and
+ o to help TZif readers avoid common pitfalls when reading files
+ generated by future TZif writers, and
- * to help any future specification authors see what sort of problems
- arise when the TZif format is changed.
+ o to help any future specification authors see what sort of problems
+ arise when the TZif format is changed.
- When new versions of the TZif format have been defined, a design goal
- has been that a reader can successfully use a TZif file even if the
- file is of a later TZif version than what the reader was designed for.
- When complete compatibility was not achieved, an attempt was made to
- limit glitches to rarely used timestamps and allow simple partial
- workarounds in writers designed to generate new-version data useful
- even for older-version readers. This section attempts to document
- these compatibility issues and workarounds, as well as to document
+ When new versions of the TZif format have been defined, a design goal
+ has been that a reader can successfully use a TZif file even if the
+ file is of a later TZif version than what the reader was designed for.
+ When complete compatibility was not achieved, an attempt was made to
+ limit glitches to rarely used timestamps and allow simple partial
+ workarounds in writers designed to generate new-version data useful
+ even for older-version readers. This section attempts to document
+ these compatibility issues and workarounds, as well as to document
other common bugs in readers.
Interoperability problems with TZif include the following:
- * 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.
-
- * 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.
-
- * Some readers designed for version 2 do not support permanent daylight
- saving time with transitions after 24:00 - e.g., a TZ string
- "EST5EDT,0/0,J365/25" denoting permanent Eastern Daylight Time (-04).
- As a workaround, a writer can substitute standard time for two time
- zones east, e.g., "XXX3EDT4,0/0,J365/23" for a time zone with a
- never-used standard time (XXX, -03) and negative daylight saving time
- (EDT, -04) all year. Alternatively, as a partial workaround a writer
- can substitute standard time for the next time zone east - e.g.,
- "AST4" for permanent Atlantic Standard Time (-04).
-
- * 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.
-
- * 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.
-
- * 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.
-
- * Some readers mishandle timestamps before the first transition that
- has a timestamp not less than -2**31. Readers that support only
- 32-bit timestamps are likely to be more prone to this problem, for
- example, when they process 64-bit transitions only some of which are
- representable in 32 bits. As a partial workaround, a writer can
- output a dummy transition at timestamp -2**31.
-
- * Some readers mishandle a transition if its timestamp has the minimum
- possible signed 64-bit value. Timestamps less than -2**59 are not
- recommended.
-
- * Some readers mishandle POSIX-style TZ strings that contain "<" or
- ">". As a partial workaround, a writer can avoid using "<" or ">"
- for time zone abbreviations containing only alphabetic characters.
-
- * Many readers mishandle time zone abbreviations that contain non-ASCII
- characters. These characters are not recommended.
-
- * 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, "-", and "+". These abbreviations are not
- recommended.
-
- * Some readers mishandle TZif files that specify daylight-saving time
- UT offsets that are less than the UT offsets for the corresponding
- standard time. These readers do not support locations like Ireland,
- which uses the equivalent of the POSIX TZ string
- "IST-1GMT0,M10.5.0,M3.5.0/1", observing standard time (IST, +01) in
- summer and daylight saving time (GMT, +00) in winter. As a partial
- workaround, a writer can output data for the equivalent of the POSIX
- TZ string "GMT0IST,M3.5.0/1,M10.5.0", thus swapping standard and
- daylight saving time. Although this workaround misidentifies which
- part of the year uses daylight saving time, it records UT offsets and
- time zone abbreviations correctly.
-
- * Some readers generate ambiguous timestamps for positive leap seconds
- that occur when the UTC offset is not a multiple of 60 seconds. For
- example, in a timezone with UTC offset +01:23:45 and with a positive
- leap second 78796801 (1972-06-30 23:59:60 UTC), some readers will map
- both 78796800 and 78796801 to 01:23:45 local time the next day
- instead of mapping the latter to 01:23:46, and they will map 78796815
- to 01:23:59 instead of to 01:23:60. This has not yet been a
- practical problem, since no civil authority has observed such UTC
- offsets since leap seconds were introduced in 1972.
-
- Some interoperability problems are reader bugs that are listed here
+ o 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.
+
+ o 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.1-2017 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.
+
+ o Some readers designed for version 2 do not support permanent
+ daylight saving time with transitions after 24:00 - e.g., a TZ
+ string "EST5EDT,0/0,J365/25" denoting permanent Eastern Daylight
+ Time (-04). As a workaround, a writer can substitute standard
+ time for two time zones east, e.g., "XXX3EDT4,0/0,J365/23" for a
+ time zone with a never-used standard time (XXX, -03) and negative
+ daylight saving time (EDT, -04) all year. Alternatively, as a
+ partial workaround a writer can substitute standard time for the
+ next time zone east - e.g., "AST4" for permanent Atlantic Standard
+ Time (-04).
+
+ o 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.
+
+ o 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.
+
+ o 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.
+
+ o Some readers mishandle timestamps before the first transition that
+ has a timestamp not less than -2**31. Readers that support only
+ 32-bit timestamps are likely to be more prone to this problem, for
+ example, when they process 64-bit transitions only some of which
+ are representable in 32 bits. As a partial workaround, a writer
+ can output a dummy transition at timestamp -2**31.
+
+ o Some readers mishandle a transition if its timestamp has the
+ minimum possible signed 64-bit value. Timestamps less than -2**59
+ are not recommended.
+
+ o Some readers mishandle TZ strings that contain "<" or ">". As a
+ partial workaround, a writer can avoid using "<" or ">" for time
+ zone abbreviations containing only alphabetic characters.
+
+ o Many readers mishandle time zone abbreviations that contain non-
+ ASCII characters. These characters are not recommended.
+
+ o 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, "-", and "+". These
+ abbreviations are not recommended.
+
+ o Some readers mishandle TZif files that specify daylight-saving
+ time UT offsets that are less than the UT offsets for the
+ corresponding standard time. These readers do not support
+ locations like Ireland, which uses the equivalent of the TZ string
+ "IST-1GMT0,M10.5.0,M3.5.0/1", observing standard time (IST, +01)
+ in summer and daylight saving time (GMT, +00) in winter. As a
+ partial workaround, a writer can output data for the equivalent of
+ the TZ string "GMT0IST,M3.5.0/1,M10.5.0", thus swapping standard
+ and daylight saving time. Although this workaround misidentifies
+ which part of the year uses daylight saving time, it records UT
+ offsets and time zone abbreviations correctly.
+
+ o Some readers generate ambiguous timestamps for positive leap
+ seconds that occur when the UTC offset is not a multiple of 60
+ seconds. For example, in a timezone with UTC offset +01:23:45 and
+ with a positive leap second 78796801 (1972-06-30 23:59:60 UTC),
+ some readers will map both 78796800 and 78796801 to 01:23:45 local
+ time the next day instead of mapping the latter to 01:23:46, and
+ they will map 78796815 to 01:23:59 instead of to 01:23:60. This
+ has not yet been a practical problem, since no civil authority has
+ observed such UTC offsets since leap seconds were introduced in
+ 1972.
+
+ Some interoperability problems are reader bugs that are listed here
mostly as warnings to developers of readers.
- * 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.
+ o 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.
- * 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.
+ o 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.
- * Some readers mishandle time zone abbreviations like "-08" that
- contain "+", "-", or digits.
+ o Some readers mishandle time zone abbreviations like "-08" that
+ contain "+", "-", or digits.
- * 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.
+ o 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.
- * 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 "+00".
+ o 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 "+00".
- * Some readers mishandle UT offsets that are not a multiple of one
- hour, or of 15 minutes, or of 1 minute.
+ o Some readers mishandle UT offsets that are not a multiple of one
+ hour, or of 15 minutes, or of 1 minute.
SEE ALSO
time(2), localtime(3), tzset(3), tzselect(8), zdump(8), zic(8).
- Olson A, Eggert P, Murchison K. The Time Zone Information Format
- (TZif). 2019 Feb. Internet RFC 8536 <https://datatracker.ietf.org/
- doc/html/rfc8536> doi:10.17487/RFC8536 <https://doi.org/10.17487/
- RFC8536>.
+ Olson A, Eggert P, Murchison K. The Time Zone Information Format
+ (TZif). 2019 Feb. Internet RFC 8536 doi:10.17487/RFC8536.
Time Zone Database tzfile(5)
diff --git a/tzfile.h b/tzfile.h
index 9cbdcff5c0c9..3155010ed17f 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 */
@@ -86,11 +78,11 @@ struct tzhead {
** time uses 8 rather than 4 chars,
** then a POSIX-TZ-environment-variable-style string for use in handling
** instants after the last transition time stored in the file
-** (with nothing between the newlines if there is no POSIX representation for
-** such instants).
+** (with nothing between the newlines if there is no POSIX.1-2017
+** representation for such instants).
**
** If tz_version is '3' or greater, the above is extended as follows.
-** First, the POSIX TZ string's hour offset may range from -167
+** First, the TZ string's hour offset may range from -167
** through 167 as compared to the POSIX-required 0 through 24.
** Second, its DST start time may be January 1 at 00:00 and its stop
** time December 31 at 24:00 plus the difference between DST and
diff --git a/tzselect.8 b/tzselect.8
index 4578090f9ea7..ee031614f3ed 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.8.txt b/tzselect.8.txt
index 1b626ab5fe86..033c77ea82e9 100644
--- a/tzselect.8.txt
+++ b/tzselect.8.txt
@@ -16,29 +16,29 @@ DESCRIPTION
OPTIONS
-c coord
- Instead of asking for continent and then country and then city,
- ask for selection from time zones whose largest cities are
- closest to the location with geographical coordinates coord.
+ Instead of asking for continent and then country and then city,
+ ask for selection from time zones whose largest cities are
+ closest to the location with geographical coordinates coord.
Use ISO 6709 notation for coord, that is, a latitude immediately
- followed by a longitude. The latitude and longitude should be
- signed integers followed by an optional decimal point and
- fraction: positive numbers represent north and east, negative
- south and west. Latitudes with two and longitudes with three
- integer digits are treated as degrees; latitudes with four or
+ followed by a longitude. The latitude and longitude should be
+ signed integers followed by an optional decimal point and
+ fraction: positive numbers represent north and east, negative
+ south and west. Latitudes with two and longitudes with three
+ integer digits are treated as degrees; latitudes with four or
six and longitudes with five or seven integer digits are treated
- as DDMM, DDDMM, DDMMSS, or DDDMMSS representing DD or DDD
- degrees, MM minutes, and zero or SS seconds, with any trailing
- fractions represent fractional minutes or (if SS is present)
- seconds. The decimal point is that of the current locale. For
+ as DDMM, DDDMM, DDMMSS, or DDDMMSS representing DD or DDD
+ degrees, MM minutes, and zero or SS seconds, with any trailing
+ fractions represent fractional minutes or (if SS is present)
+ seconds. The decimal point is that of the current locale. For
example, in the (default) C locale, -c +40.689-074.045 specifies
40.689 degrees N, 74.045 degrees W, -c +4041.4-07402.7 specifies
- 40 degrees 41.4 minutes N, 74 degrees 2.7 minutes W, and
+ 40 degrees 41.4 minutes N, 74 degrees 2.7 minutes W, and
-c +404121-0740240 specifies 40 degrees 41 minutes 21 seconds N,
- 74 degrees 2 minutes 40 seconds W. If coord is not one of the
+ 74 degrees 2 minutes 40 seconds W. If coord is not one of the
documented forms, the resulting behavior is unspecified.
-n limit
- When -c is used, display the closest limit locations (default
+ When -c is used, display the closest limit locations (default
10).
--help Output help information and exit.
@@ -47,9 +47,9 @@ OPTIONS
Output version information and exit.
ENVIRONMENT VARIABLES
- AWK Name of a Posix-compliant awk program (default: awk).
+ AWK Name of a POSIX-compliant awk program (default: awk).
- TZDIR Name of the directory containing timezone data files (default:
+ TZDIR Name of the directory containing timezone data files (default:
/usr/share/zoneinfo).
FILES
@@ -57,21 +57,21 @@ FILES
Table of ISO 3166 2-letter country codes and country names.
TZDIR/zone1970.tab
- Table of country codes, latitude and longitude, timezones, and
+ Table of country codes, latitude and longitude, timezones, and
descriptive comments.
TZDIR/TZ
Timezone data file for timezone TZ.
EXIT STATUS
- The exit status is zero if a timezone was successfully obtained from
+ The exit status is zero if a timezone was successfully obtained from
the user, nonzero otherwise.
SEE ALSO
newctime(3), tzfile(5), zdump(8), zic(8)
NOTES
- Applications should not assume that tzselect's output matches the
+ Applications should not assume that tzselect's output matches the
user's political preferences.
Time Zone Database tzselect(8)
diff --git a/tzselect.ksh b/tzselect.ksh
index 9a91acfc7412..38941bbc55e7 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,35 +21,44 @@ REPORT_BUGS_TO=tz@iana.org
# MirBSD Korn Shell <http://www.mirbsd.org/mksh.htm>
#
# For portability to Solaris 10 /bin/sh (supported by Oracle through
-# January 2024) this script avoids some POSIX features and common
-# extensions, such as $(...) (which works sometimes but not others),
-# $((...)), ! CMD, ${#ID}, ${ID##PAT}, ${ID%%PAT}, and $10.
-
+# 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) <https://www.gnu.org/software/gawk/>
# mawk <https://invisible-island.net/mawk/>
# nawk <https://github.com/onetrueawk/awk>
+#
+# 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.
+# This script does not want path expansion.
+set -f
# Specify default values for environment variables if they are unset.
: ${AWK=awk}
-: ${TZDIR=`pwd`}
+: ${PWD=`pwd`}
+: ${TZDIR=$PWD}
# 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 >/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 +95,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 2>/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 +108,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
@@ -108,7 +117,8 @@ then
else
doselect() {
# Field width of the prompt numbers.
- select_width=`expr $# : '.*'`
+ print_nargs_length="BEGIN {print length(\"$#\");}"
+ select_width=`$AWK "$print_nargs_length"`
select_i=
@@ -119,14 +129,14 @@ else
select_i=0
for select_word
do
- select_i=`expr $select_i + 1`
+ select_i=`$AWK "BEGIN { print $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`
+ shift `$AWK "BEGIN { print $select_i - 1 }"`
select_result=$1
break
fi
@@ -142,56 +152,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`
+shift `$AWK "BEGIN { print $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 +210,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 <TZ_COUNTRY_TABLE) {
- if ($0 !~ /^#/) cc_name[$1] = $2
- }
- for (i = 1; i <= ccs; i++) {
- country = cc_list[i]
- if (cc_elsewhere[country]) continue
- if (cc_name[country]) {
- country = cc_name[country]
- }
- print country
- }
+ nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
+ for (i = 1; i <= nlines; i++) {
+ $0 = line[i]
+ if ($0 !~ /^#/)
+ cc_name[$1] = $2
+ }
+ for (i = 1; i <= ccs; i++) {
+ country = cc_list[i]
+ if (cc_elsewhere[country])
+ continue
+ if (cc_name[country])
+ country = cc_name[country]
+ print country
+ }
}
'
-# Awk script to read a time zone table and output the same table,
+# Awk script to process a time zone table and output the same table,
# with each row preceded by its distance from 'here'.
# If output_times is set, each row is instead preceded by its local time
# and any apostrophes are escaped for the shell.
output_distances_or_times='
BEGIN {
+ coord = 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"
if (!output_times) {
- while (getline <TZ_COUNTRY_TABLE)
- if ($0 ~ /^[^#]/)
- country[$1] = $2
+ nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
+ for (i = 1; i <= nlines; i++) {
+ $0 = line[i]
+ if ($0 ~ /^#/)
+ continue
+ country[$1] = $2
+ }
country["US"] = "US" # Otherwise the strings get too long.
}
}
@@ -315,371 +342,466 @@ output_distances_or_times='
BEGIN {
coord_lat = convert_latitude(coord)
coord_long = convert_longitude(coord)
- }
- /^[^#]/ {
- inline[inlines++] = $0
- ncc = split($1, cc, /,/)
- for (i = 1; i <= ncc; i++)
- cc_used[cc[i]]++
- }
- END {
- for (h = 0; h < inlines; h++) {
- $0 = inline[h]
- line = $1 "\t" $2 "\t" $3
- sep = "\t"
- ncc = split($1, cc, /,/)
- split("", item_seen)
- item_seen[""] = 1
- for (i = 1; i <= ncc; i++) {
- item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
- if (item_seen[item]++) continue
- line = line sep item
- sep = "; "
+ nlines = split(TZ_ZONE_TABLE, line, /\n/)
+ for (h = 1; h <= nlines; h++) {
+ $0 = line[h]
+ if ($0 ~ /^#/)
+ continue
+ inline[inlines++] = $0
+ ncc = split($1, cc, /,/)
+ for (i = 1; i <= ncc; i++)
+ cc_used[cc[i]]++
}
- if (output_times) {
- fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n"
- gsub(/'\''/, "&\\\\&&", line)
- printf fmt, $3, h, line
- } else {
- here_lat = convert_latitude($2)
- here_long = convert_longitude($2)
- printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
+ for (h = 0; h < inlines; h++) {
+ $0 = inline[h]
+ outline = $1 "\t" $2 "\t" $3
+ sep = "\t"
+ ncc = split($1, cc, /,/)
+ split("", item_seen)
+ item_seen[""] = 1
+ for (i = 1; i <= ncc; i++) {
+ item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
+ if (item_seen[item]++)
+ continue
+ outline = outline sep item
+ sep = "; "
+ }
+ if (output_times) {
+ fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n"
+ gsub(/'\''/, "&\\\\&&", outline)
+ printf fmt, $3, h, outline
+ } else {
+ here_lat = convert_latitude($2)
+ here_long = convert_longitude($2)
+ printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \
+ outline
+ }
}
- }
}
'
# Begin the main loop. We come back here if the user wants to retry.
while
- echo >&2 'Please identify a location' \
- 'so that time zone rules can be set correctly.'
+ echo >&2 'Please identify a location' \
+ 'so that time zone rules can be set correctly.'
- continent=
- country=
- region=
+ continent=
+ country=
+ country_result=
+ region=
+ time=
+ TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE
- 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 a POSIX.1-2017 TZ string." \
+ "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;;
+ *)
+ # Get the first word of $continent. Path expansion is disabled
+ # so this works even with "*", which should not happen.
+ IFS=" "
+ for continent in $continent ""; do break; done
+ IFS=$newline;;
+ 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.1-2017 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.1-2017 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 |
+ $AWK "{print} NR == $location_limit { exit }"
+ `
+ 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 <TZ_COUNTRY_TABLE) {
- if ($0 !~ /^#/ && country == $2) {
- cc = $1
- break
- }
- }
- }
- /^#/ { next }
- $1 ~ cc { print $4 }
- '
- `
-
-
- # 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=`
- 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 <TZ_COUNTRY_TABLE) {
- if ($0 !~ /^#/ && country == $2) {
- cc = $1
- break
- }
- }
- }
- /^#/ { next }
- $1 ~ cc && ($4 == region || !region) { print $3 }
- '
- `;;
- 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
+ # 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
- # Use the proposed TZ to output the current date relative to UTC.
- # Loop until they agree in seconds.
- # Give up after 8 unsuccessful tries.
+ # 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
- 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
+ # 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
- # 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
+ # 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`
+ if $AWK '
+ function getsecs(d) {
+ return match(d, /.*:[0-5][0-9]/) ? substr(d, RLENGTH - 1, 2) : ""
+ }
+ BEGIN { exit getsecs(ARGV[1]) != getsecs(ARGV[2]) }
+ ' ="$TZdate" ="$UTdate"
+ then
+ extra_info="
+Selected time is now: $TZdate.
+Universal Time is now: $UTdate."
+ break
+ fi
+ 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 +812,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/version b/version
index 7daa77e00d99..04fe6744432f 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2023c
+2024a
diff --git a/zdump.8 b/zdump.8
index f77c0c798d74..c3f0bba60ba8 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.8.txt b/zdump.8.txt
index bcdd99b03c9e..35a341009c0a 100644
--- a/zdump.8.txt
+++ b/zdump.8.txt
@@ -16,77 +16,77 @@ OPTIONS
--help Output short usage message and exit.
- -i Output a description of time intervals. For each timezone on
- the command line, output an interval-format description of the
+ -i Output a description of time intervals. For each timezone on
+ the command line, output an interval-format description of the
timezone. See "INTERVAL FORMAT" below.
- -v Output a verbose description of time intervals. For each
+ -v Output a verbose description of time intervals. For each
timezone on the command line, print the times at the two extreme
- time values, the times (if present) at and just beyond the
- boundaries of years that localtime(3) and gmtime(3) can
- represent, and the times both one second before and exactly at
- each detected time discontinuity. Each line is followed by
- isdst=D where D is positive, zero, or negative depending on
- whether the given time is daylight saving time, standard time,
- or an unknown time type, respectively. Each line is also
- followed by gmtoff=N if the given local time is known to be N
+ time values, the times (if present) at and just beyond the
+ boundaries of years that localtime(3) and gmtime(3) can
+ represent, and the times both one second before and exactly at
+ each detected time discontinuity. Each line is followed by
+ isdst=D where D is positive, zero, or negative depending on
+ whether the given time is daylight saving time, standard time,
+ or an unknown time type, respectively. Each line is also
+ followed by gmtoff=N if the given local time is known to be N
seconds east of Greenwich.
- -V Like -v, except omit output concerning extreme time and year
+ -V Like -v, except omit output concerning extreme time and year
values. This generates output that is easier to compare to that
of implementations with different time representations.
-c [loyear,]hiyear
- Cut off interval output at the given year(s). Cutoff times are
- computed using the proleptic Gregorian calendar with year 0 and
- with Universal Time (UT) ignoring leap seconds. Cutoffs are at
- the start of each year, where the lower-bound timestamp is
- inclusive and the upper is exclusive; for example, -c 1970,2070
- selects transitions on or after 1970-01-01 00:00:00 UTC and
- before 2070-01-01 00:00:00 UTC. The default cutoff is
+ Cut off interval output at the given year(s). Cutoff times are
+ computed using the proleptic Gregorian calendar with year 0 and
+ with Universal Time (UT) ignoring leap seconds. Cutoffs are at
+ the start of each year, where the lower-bound timestamp is
+ inclusive and the upper is exclusive; for example, -c 1970,2070
+ selects transitions on or after 1970-01-01 00:00:00 UTC and
+ before 2070-01-01 00:00:00 UTC. The default cutoff is
-500,2500.
-t [lotime,]hitime
- Cut off interval output at the given time(s), given in decimal
- seconds since 1970-01-01 00:00:00 Coordinated Universal Time
- (UTC). The timezone determines whether the count includes leap
- seconds. As with -c, the cutoff's lower bound is inclusive and
+ Cut off interval output at the given time(s), given in decimal
+ seconds since 1970-01-01 00:00:00 Coordinated Universal Time
+ (UTC). The timezone determines whether the count includes leap
+ seconds. As with -c, the cutoff's lower bound is inclusive and
its upper bound is exclusive.
INTERVAL FORMAT
- The interval format is a compact text representation that is intended
- to be both human- and machine-readable. It consists of an empty line,
- then a line "TZ=string" where string is a double-quoted string giving
+ The interval format is a compact text representation that is intended
+ to be both human- and machine-readable. It consists of an empty line,
+ then a line "TZ=string" where string is a double-quoted string giving
the timezone, a second line "- - interval" describing the time interval
- before the first transition if any, and zero or more following lines
- "date time interval", one line for each transition time and following
+ before the first transition if any, and zero or more following lines
+ "date time interval", one line for each transition time and following
interval. Fields are separated by single tabs.
Dates are in yyyy-mm-dd format and times are in 24-hour hh:mm:ss format
where hh<24. Times are in local time immediately after the transition.
- A time interval description consists of a UT offset in signed +-hhmmss
- format, a time zone abbreviation, and an isdst flag. An abbreviation
- that equals the UT offset is omitted; other abbreviations are double-
- quoted strings unless they consist of one or more alphabetic
- characters. An isdst flag is omitted for standard time, and otherwise
- is a decimal integer that is unsigned and positive (typically 1) for
+ A time interval description consists of a UT offset in signed +-hhmmss
+ format, a time zone abbreviation, and an isdst flag. An abbreviation
+ that equals the UT offset is omitted; other abbreviations are double-
+ quoted strings unless they consist of one or more alphabetic
+ characters. An isdst flag is omitted for standard time, and otherwise
+ is a decimal integer that is unsigned and positive (typically 1) for
daylight saving time and negative for unknown.
In times and in UT offsets with absolute value less than 100 hours, the
- seconds are omitted if they are zero, and the minutes are also omitted
+ seconds are omitted if they are zero, and the minutes are also omitted
if they are also zero. Positive UT offsets are east of Greenwich. The
UT offset -00 denotes a UT placeholder in areas where the actual offset
- is unspecified; by convention, this occurs when the UT offset is zero
+ is unspecified; by convention, this occurs when the UT offset is zero
and the time zone abbreviation begins with "-" or is "zzz".
- In double-quoted strings, escape sequences represent unusual
+ In double-quoted strings, escape sequences represent unusual
characters. The escape sequences are \s for space, and \", \\, \f, \n,
- \r, \t, and \v with their usual meaning in the C programming language.
- E.g., the double-quoted string ""CET\s\"\\"" represents the character
+ \r, \t, and \v with their usual meaning in the C programming language.
+ E.g., the double-quoted string ""CET\s\"\\"" represents the character
sequence "CET "\".
- Here is an example of the output, with the leading empty line omitted.
- (This example is shown with tab stops set far enough apart so that the
+ Here is an example of the output, with the leading empty line omitted.
+ (This example is shown with tab stops set far enough apart so that the
tabbed columns line up.)
TZ="Pacific/Honolulu"
@@ -100,14 +100,14 @@ INTERVAL FORMAT
1947-06-08 02:30 -10 HST
Here, local time begins 10 hours, 31 minutes and 26 seconds west of UT,
- and is a standard time abbreviated LMT. Immediately after the first
- transition, the date is 1896-01-13 and the time is 12:01:26, and the
- following time interval is 10.5 hours west of UT, a standard time
- abbreviated HST. Immediately after the second transition, the date is
- 1933-04-30 and the time is 03:00:00 and the following time interval is
- 9.5 hours west of UT, is abbreviated HDT, and is daylight saving time.
- Immediately after the last transition the date is 1947-06-08 and the
- time is 02:30:00, and the following time interval is 10 hours west of
+ and is a standard time abbreviated LMT. Immediately after the first
+ transition, the date is 1896-01-13 and the time is 12:01:26, and the
+ following time interval is 10.5 hours west of UT, a standard time
+ abbreviated HST. Immediately after the second transition, the date is
+ 1933-04-30 and the time is 03:00:00 and the following time interval is
+ 9.5 hours west of UT, is abbreviated HDT, and is daylight saving time.
+ Immediately after the last transition the date is 1947-06-08 and the
+ time is 02:30:00, and the following time interval is 10 hours west of
UT, a standard time abbreviated HST.
Here are excerpts from another example:
@@ -122,20 +122,20 @@ INTERVAL FORMAT
2014-10-26 01 +03
2016-03-27 03 +04
- This time zone is east of UT, so its UT offsets are positive. Also,
- many of its time zone abbreviations are omitted since they duplicate
+ This time zone is east of UT, so its UT offsets are positive. Also,
+ many of its time zone abbreviations are omitted since they duplicate
the text of the UT offset.
LIMITATIONS
- Time discontinuities are found by sampling the results returned by
- localtime(3) at twelve-hour intervals. This works in all real-world
+ Time discontinuities are found by sampling the results returned by
+ localtime(3) at twelve-hour intervals. This works in all real-world
cases; one can construct artificial time zones for which this fails.
- In the -v and -V output, "UT" denotes the value returned by gmtime(3),
- which uses UTC for modern timestamps and some other UT flavor for
- timestamps that predate the introduction of UTC. No attempt is
- currently made to have the output use "UTC" for newer and "UT" for
- older timestamps, partly because the exact date of the introduction of
+ In the -v and -V output, "UT" denotes the value returned by gmtime(3),
+ which uses UTC for modern timestamps and some other UT flavor for
+ timestamps that predate the introduction of UTC. No attempt is
+ currently made to have the output use "UTC" for newer and "UT" for
+ older timestamps, partly because the exact date of the introduction of
UTC is problematic.
SEE ALSO
diff --git a/zdump.c b/zdump.c
index 6f9573ec7b17..7d99cc74bd30 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 c467efefe26a..0ad373a2dd17 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
@@ -171,7 +171,7 @@ boundaries, particularly if
causes a TZif file to contain explicit entries for
.RI pre- hi
transitions rather than concisely representing them
-with an extended POSIX TZ string.
+with an extended POSIX.1-2017 TZ string.
Also see the
.B "\*-b slim"
option for another way to shrink output size.
@@ -181,10 +181,10 @@ Generate redundant trailing explicit transitions for timestamps
that occur less than
.I hi
seconds since the Epoch, even though the transitions could be
-more concisely represented via the extended POSIX TZ string.
+more concisely represented via the extended POSIX.1-2017 TZ string.
This option does not affect the represented timestamps.
Although it accommodates nonstandard TZif readers
-that ignore the extended POSIX TZ string,
+that ignore the extended POSIX.1-2017 TZ string,
it increases the size of the altered output files.
.TP
.BI "\*-t " file
@@ -245,10 +245,10 @@ for
.PP
The output file does not contain all the information about the
long-term future of a timezone, because the future cannot be summarized as
-an extended POSIX TZ string. For example, as of 2023 this problem
+an extended POSIX.1-2017 TZ string. For example, as of 2023 this problem
occurs for Morocco's daylight-saving rules, as these rules are based
on predictions for when Ramadan will be observed, something that
-an extended POSIX TZ string cannot represent.
+an extended POSIX.1-2017 TZ string cannot represent.
.PP
The output contains data that may not be handled properly by client
code designed for older
@@ -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
@@ -360,24 +360,15 @@ an unquoted name should not contain characters from the set
Gives the first year in which the rule applies.
Any signed integer year can be supplied; the proleptic Gregorian calendar
is assumed, with year 0 preceding year 1.
-The word
-.B minimum
-(or an abbreviation) means the indefinite past.
-The word
-.B maximum
-(or an abbreviation) means the indefinite future.
Rules can describe times that are not representable as time values,
with the unrepresentable times ignored; this allows rules to be portable
among hosts with differing time value types.
.TP
.B TO
Gives the final year in which the rule applies.
-In addition to
-.B minimum
-and
+The word
.B maximum
-(as above),
-the word
+(or an abbreviation) means the indefinite future, and the word
.B only
(or an abbreviation)
may be used to repeat the value of the
@@ -404,7 +395,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 +404,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 +431,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 +445,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 +523,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 +654,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 +678,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 +708,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 +728,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 +782,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 +807,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.8.txt b/zic.8.txt
index cf86f584ca30..acef001bbafd 100644
--- a/zic.8.txt
+++ b/zic.8.txt
@@ -18,74 +18,75 @@ OPTIONS
--help Output short usage message and exit.
-b bloat
- Output backward-compatibility data as specified by bloat. If
- bloat is fat, generate additional data entries that work around
- potential bugs or incompatibilities in older software, such as
+ Output backward-compatibility data as specified by bloat. If
+ bloat is fat, generate additional data entries that work around
+ potential bugs or incompatibilities in older software, such as
software that mishandles the 64-bit generated data. If bloat is
- slim, keep the output files small; this can help check for the
- bugs and incompatibilities. The default is slim, as software
- that mishandles 64-bit data typically mishandles timestamps
- after the year 2038 anyway. Also see the -r option for another
+ slim, keep the output files small; this can help check for the
+ bugs and incompatibilities. The default is slim, as software
+ that mishandles 64-bit data typically mishandles timestamps
+ after the year 2038 anyway. Also see the -r option for another
way to alter output size.
-d directory
- Create time conversion information files in the named directory
+ Create time conversion information files in the named directory
rather than in the standard directory named below.
-l timezone
- Use timezone as local time. zic will act as if the input
+ Use timezone as local time. zic will act as if the input
contained a link line of the form
- Link timezone localtime
+ Link timezone localtime
If timezone is -, any already-existing link is removed.
-L leapsecondfilename
- Read leap second information from the file with the given name.
- If this option is not used, no leap second information appears
+ Read leap second information from the file with the given name.
+ If this option is not used, no leap second information appears
in output files.
-p timezone
- Use timezone's rules when handling nonstandard TZ strings like
- "EET-2EEST" that lack transition rules. zic will act as if the
+ Use timezone's rules when handling nonstandard TZ strings like
+ "EET-2EEST" that lack transition rules. zic will act as if the
input contained a link line of the form
- Link timezone posixrules
+ Link timezone posixrules
- Unless timezone is "-", this option is obsolete and poorly
- supported. Among other things it should not be used for
- timestamps after the year 2037, and it should not be combined
- with -b slim if timezone's transitions are at standard time or
- Universal Time (UT) instead of local time.
+ If timezone is "-" (the default), any already-existing link is
+ removed.
- If timezone is -, any already-existing link is removed.
+ Unless timezone is "-", this option is obsolete and poorly
+ supported. Among other things it should not be used for
+ timestamps after the year 2037, and it should not be combined
+ with -b slim if timezone's transitions are at standard time or
+ Universal Time (UT) instead of local time.
-r [@lo][/@hi]
- Limit the applicability of output files to timestamps in the
+ Limit the applicability of output files to timestamps in the
range from lo (inclusive) to hi (exclusive), where lo and hi are
- possibly signed decimal counts of seconds since the Epoch
- (1970-01-01 00:00:00 UTC). Omitted counts default to extreme
+ possibly signed decimal counts of seconds since the Epoch
+ (1970-01-01 00:00:00 UTC). Omitted counts default to extreme
values. The output files use UT offset 0 and abbreviation "-00"
- in place of the omitted timestamp data. For example, "zic -r
- @0" omits data intended for negative timestamps (i.e., before
- the Epoch), and "zic -r @0/@2147483648" outputs data intended
- only for nonnegative timestamps that fit into 31-bit signed
- integers. On platforms with GNU date, "zic -r @$(date +%s)"
- omits data intended for past timestamps. Although this option
- typically reduces the output file's size, the size can increase
- due to the need to represent the timestamp range boundaries,
- particularly if hi causes a TZif file to contain explicit
- entries for pre-hi transitions rather than concisely
- representing them with an extended POSIX TZ string. Also see
- the -b slim option for another way to shrink output size.
-
- -R @hi Generate redundant trailing explicit transitions for timestamps
+ in place of the omitted timestamp data. For example, "zic -r
+ @0" omits data intended for negative timestamps (i.e., before
+ the Epoch), and "zic -r @0/@2147483648" outputs data intended
+ only for nonnegative timestamps that fit into 31-bit signed
+ integers. On platforms with GNU date, "zic -r @$(date +%s)"
+ omits data intended for past timestamps. Although this option
+ typically reduces the output file's size, the size can increase
+ due to the need to represent the timestamp range boundaries,
+ particularly if hi causes a TZif file to contain explicit
+ entries for pre-hi transitions rather than concisely
+ representing them with an extended POSIX.1-2017 TZ string. Also
+ see the -b slim option for another way to shrink output size.
+
+ -R @hi Generate redundant trailing explicit transitions for timestamps
that occur less than hi seconds since the Epoch, even though the
transitions could be more concisely represented via the extended
- POSIX TZ string. This option does not affect the represented
- timestamps. Although it accommodates nonstandard TZif readers
- that ignore the extended POSIX TZ string, it increases the size
- of the altered output files.
+ POSIX.1-2017 TZ string. This option does not affect the
+ represented timestamps. Although it accommodates nonstandard
+ TZif readers that ignore the extended POSIX.1-2017 TZ string, it
+ increases the size of the altered output files.
-t file
When creating local time information, put the configuration link
@@ -96,356 +97,352 @@ OPTIONS
The input specifies a link to a link, something not supported by
some older parsers, including zic itself through release 2022e.
- A year that appears in a data file is outside the range of
+ A year that appears in a data file is outside the range of
representable years.
A time of 24:00 or more appears in the input. Pre-1998 versions
- of zic prohibit 24:00, and pre-2007 versions prohibit times
+ of zic prohibit 24:00, and pre-2007 versions prohibit times
greater than 24:00.
- A rule goes past the start or end of the month. Pre-2004
+ A rule goes past the start or end of the month. Pre-2004
versions of zic prohibit this.
A time zone abbreviation uses a %z format. Pre-2015 versions of
zic do not support this.
- A timestamp contains fractional seconds. Pre-2018 versions of
+ A timestamp contains fractional seconds. Pre-2018 versions of
zic do not support this.
The input contains abbreviations that are mishandled by pre-2018
- versions of zic due to a longstanding coding bug. These
- abbreviations include "L" for "Link", "mi" for "min", "Sa" for
+ versions of zic due to a longstanding coding bug. These
+ abbreviations include "L" for "Link", "mi" for "min", "Sa" for
"Sat", and "Su" for "Sun".
- The output file does not contain all the information about the
- long-term future of a timezone, because the future cannot be
- summarized as an extended POSIX TZ string. For example, as of
- 2023 this problem occurs for Morocco's daylight-saving rules, as
- these rules are based on predictions for when Ramadan will be
- observed, something that an extended POSIX TZ string cannot
- represent.
+ The output file does not contain all the information about the
+ long-term future of a timezone, because the future cannot be
+ summarized as an extended POSIX.1-2017 TZ string. For example,
+ as of 2023 this problem occurs for Morocco's daylight-saving
+ rules, as these rules are based on predictions for when Ramadan
+ will be observed, something that an extended POSIX.1-2017 TZ
+ string cannot represent.
- The output contains data that may not be handled properly by
- client code designed for older zic output formats. These
+ The output contains data that may not be handled properly by
+ client code designed for older zic output formats. These
compatibility issues affect only timestamps before 1970 or after
the start of 2038.
- The output contains a truncated leap second table, which can
- cause some older TZif readers to misbehave. This can occur if
- the -L option is used, and either an Expires line is present or
+ The output contains a truncated leap second table, which can
+ cause some older TZif readers to misbehave. This can occur if
+ the -L option is used, and either an Expires line is present or
the -r option is also used.
- The output file contains more than 1200 transitions, which may
- be mishandled by some clients. The current reference client
- supports at most 2000 transitions; pre-2014 versions of the
+ The output file contains more than 1200 transitions, which may
+ be mishandled by some clients. The current reference client
+ supports at most 2000 transitions; pre-2014 versions of the
reference client support at most 1200 transitions.
- A time zone abbreviation has fewer than 3 or more than 6
- characters. POSIX requires at least 3, and requires
+ A time zone abbreviation has fewer than 3 or more than 6
+ characters. POSIX requires at least 3, and requires
implementations to support at least 6.
An output file name contains a byte that is not an ASCII letter,
- "-", "/", or "_"; or it contains a file name component that
+ "-", "/", or "_"; or it contains a file name component that
contains more than 14 bytes or that starts with "-".
FILES
- Input files use the format described in this section; output files use
+ Input files use the format described in this section; output files use
tzfile(5) format.
- Input files should be text files, that is, they should be a series of
- zero or more lines, each ending in a newline byte and containing at
- most 2048 bytes counting the newline, and without any NUL bytes. The
- input text's encoding is typically UTF-8 or ASCII; it should have a
- unibyte representation for the POSIX Portable Character Set (PPCS)
+ Input files should be text files, that is, they should be a series of
+ zero or more lines, each ending in a newline byte and containing at
+ most 2048 bytes counting the newline, and without any NUL bytes. The
+ input text's encoding is typically UTF-8 or ASCII; it should have a
+ unibyte representation for the POSIX Portable Character Set (PPCS)
<https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap06
- .html> and the encoding's non-unibyte characters should consist
- entirely of non-PPCS bytes. Non-PPCS characters typically occur only
+ .html> and the encoding's non-unibyte characters should consist
+ entirely of non-PPCS bytes. Non-PPCS characters typically occur only
in comments: although output file names and time zone abbreviations can
- contain nearly any character, other software will work better if these
+ contain nearly any character, other software will work better if these
are limited to the restricted syntax described under the -v option.
- Input lines are made up of fields. Fields are separated from one
- another by one or more white space characters. The white space
- characters are space, form feed, carriage return, newline, tab, and
- vertical tab. Leading and trailing white space on input lines is
- ignored. An unquoted sharp character (#) in the input introduces a
- comment which extends to the end of the line the sharp character
- appears on. White space characters and sharp characters may be
+ Input lines are made up of fields. Fields are separated from one
+ another by one or more white space characters. The white space
+ characters are space, form feed, carriage return, newline, tab, and
+ vertical tab. Leading and trailing white space on input lines is
+ ignored. An unquoted sharp character (#) in the input introduces a
+ comment which extends to the end of the line the sharp character
+ appears on. White space characters and sharp characters may be
enclosed in double quotes (") if they're to be used as part of a field.
- Any line that is blank (after comment stripping) is ignored. Nonblank
+ Any line that is blank (after comment stripping) is ignored. Nonblank
lines are expected to be of one of three types: rule lines, zone lines,
and link lines.
- Names must be in English and are case insensitive. They appear in
+ Names must be in English and are case insensitive. They appear in
several contexts, and include month and weekday names and keywords such
- as maximum, only, Rolling, and Zone. A name can be abbreviated by
- omitting all but an initial prefix; any abbreviation must be
+ as maximum, only, Rolling, and Zone. A name can be abbreviated by
+ omitting all but an initial prefix; any abbreviation must be
unambiguous in context.
A rule line has the form
- Rule NAME FROM TO - IN ON AT SAVE LETTER/S
+ Rule NAME FROM TO - IN ON AT SAVE LETTER/S
For example:
- Rule US 1967 1973 - Apr lastSun 2:00w 1:00d D
+ Rule US 1967 1973 - Apr lastSun 2:00w 1:00d D
The fields that make up a rule line are:
- NAME Gives the name of the rule set that contains this line. The
- name must start with a character that is neither an ASCII digit
- nor "-" nor "+". To allow for future extensions, an unquoted
- name should not contain characters from the set
- "!$%&'()*,/:;<=>?@[\]^`{|}~".
-
- FROM Gives the first year in which the rule applies. Any signed
- integer year can be supplied; the proleptic Gregorian calendar
- is assumed, with year 0 preceding year 1. The word minimum (or
- an abbreviation) means the indefinite past. The word maximum
- (or an abbreviation) means the indefinite future. Rules can
- describe times that are not representable as time values, with
- the unrepresentable times ignored; this allows rules to be
- portable among hosts with differing time value types.
-
- TO Gives the final year in which the rule applies. In addition to
- minimum and maximum (as above), the word only (or an
- abbreviation) may be used to repeat the value of the FROM
- field.
-
- - Is a reserved field and should always contain "-" for
- compatibility with older versions of zic. It was previously
- known as the TYPE field, which could contain values to allow a
- separate script to further restrict in which "types" of years
- the rule would apply.
-
- IN Names the month in which the rule takes effect. Month names
- may be abbreviated.
-
- ON Gives the day on which the rule takes effect. Recognized forms
- include:
-
- 5 the fifth of the month
- lastSun the last Sunday in the month
- 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
-
- A weekday name (e.g., Sunday) or a weekday name preceded by
- "last" (e.g., lastSunday) may be abbreviated or spelled out in
- full. There must be no white space characters within the ON
- field. The "<=" and ">=" constructs can result in a day in the
- neighboring month; for example, the IN-ON combination "Oct
- Sun>=31" stands for the first Sunday on or after October 31,
- even if that Sunday occurs in November.
-
- AT Gives the time of day at which the rule takes effect, relative
- to 00:00, the start of a calendar day. Recognized forms
- include:
-
- 2 time in hours
- 2:00 time in hours and minutes
- 01:28:14 time in hours, minutes, and seconds
- 00:19:32.13 time with fractional seconds
- 12:00 midday, 12 hours after 00:00
- 15:00 3 PM, 15 hours after 00:00
- 24:00 end of day, 24 hours after 00:00
- 260:00 260 hours after 00:00
- -2:30 2.5 hours before 00:00
- - equivalent to 0
-
- Although zic rounds times to the nearest integer second
- (breaking ties to the even integer), the fractions may be
- useful to other applications requiring greater precision. The
- source format does not specify any maximum precision. Any of
- these forms may be followed by the letter w if the given time
- is local or "wall clock" time, s if the given time is standard
- time without any adjustment for daylight saving, or u (or g or
- z) if the given time is universal time; in the absence of an
- indicator, local (wall clock) time is assumed. These forms
- ignore leap seconds; for example, if a leap second occurs at
- 00:59:60 local time, "1:00" stands for 3601 seconds after local
- midnight instead of the usual 3600 seconds. The intent is that
- a rule line describes the instants when a clock/calendar set to
- the type of time specified in the AT field would show the
- specified date and time of day.
-
- SAVE Gives the amount of time to be added to local standard time
- when the rule is in effect, and whether the resulting time is
- standard or daylight saving. This field has the same format as
- the AT field except with a different set of suffix letters: s
- for standard time and d for daylight saving time. The suffix
- letter is typically omitted, and defaults to s if the offset is
- zero and to d otherwise. Negative offsets are allowed; in
- Ireland, for example, daylight saving time is observed in
- winter and has a negative offset relative to Irish Standard
- Time. The offset is merely added to standard time; for
- example, zic does not distinguish a 10:30 standard time plus an
- 0:30 SAVE from a 10:00 standard time plus a 1:00 SAVE.
+ NAME Gives the name of the rule set that contains this line. The
+ name must start with a character that is neither an ASCII digit
+ nor "-" nor "+". To allow for future extensions, an unquoted
+ name should not contain characters from the set
+ "!$%&'()*,/:;<=>?@[\]^`{|}~".
+
+ FROM Gives the first year in which the rule applies. Any signed
+ integer year can be supplied; the proleptic Gregorian calendar
+ is assumed, with year 0 preceding year 1. Rules can describe
+ times that are not representable as time values, with the
+ unrepresentable times ignored; this allows rules to be portable
+ among hosts with differing time value types.
+
+ TO Gives the final year in which the rule applies. The word
+ maximum (or an abbreviation) means the indefinite future, and
+ the word only (or an abbreviation) may be used to repeat the
+ value of the FROM field.
+
+ - Is a reserved field and should always contain "-" for
+ compatibility with older versions of zic. It was previously
+ known as the TYPE field, which could contain values to allow a
+ separate script to further restrict in which "types" of years
+ the rule would apply.
+
+ IN Names the month in which the rule takes effect. Month names may
+ be abbreviated.
+
+ ON Gives the day on which the rule takes effect. Recognized forms
+ include:
+
+ 5 the fifth of the month
+ lastSun the last Sunday in the month
+ 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
+
+ A weekday name (e.g., Sunday) or a weekday name preceded by
+ "last" (e.g., lastSunday) may be abbreviated or spelled out in
+ full. There must be no white space characters within the ON
+ field. The "<=" and ">=" constructs can result in a day in the
+ neighboring month; for example, the IN-ON combination "Oct
+ Sun>=31" stands for the first Sunday on or after October 31,
+ even if that Sunday occurs in November.
+
+ AT Gives the time of day at which the rule takes effect, relative
+ to 00:00, the start of a calendar day. Recognized forms
+ include:
+
+ 2 time in hours
+ 2:00 time in hours and minutes
+ 01:28:14 time in hours, minutes, and seconds
+ 00:19:32.13 time with fractional seconds
+ 12:00 midday, 12 hours after 00:00
+ 15:00 3 PM, 15 hours after 00:00
+ 24:00 end of day, 24 hours after 00:00
+ 260:00 260 hours after 00:00
+ -2:30 2.5 hours before 00:00
+ - equivalent to 0
+
+ Although zic rounds times to the nearest integer second
+ (breaking ties to the even integer), the fractions may be useful
+ to other applications requiring greater precision. The source
+ format does not specify any maximum precision. Any of these
+ forms may be followed by the letter w if the given time is local
+ or "wall clock" time, s if the given time is standard time
+ without any adjustment for daylight saving, or u (or g or z) if
+ the given time is universal time; in the absence of an
+ indicator, local (wall clock) time is assumed. These forms
+ ignore leap seconds; for example, if a leap second occurs at
+ 00:59:60 local time, "1:00" stands for 3601 seconds after local
+ midnight instead of the usual 3600 seconds. The intent is that
+ a rule line describes the instants when a clock/calendar set to
+ the type of time specified in the AT field would show the
+ specified date and time of day.
+
+ SAVE Gives the amount of time to be added to local standard time when
+ the rule is in effect, and whether the resulting time is
+ standard or daylight saving. This field has the same format as
+ the AT field except with a different set of suffix letters: s
+ for standard time and d for daylight saving time. The suffix
+ letter is typically omitted, and defaults to s if the offset is
+ zero and to d otherwise. Negative offsets are allowed; in
+ Ireland, for example, daylight saving time is observed in winter
+ and has a negative offset relative to Irish Standard Time. The
+ offset is merely added to standard time; for example, zic does
+ not distinguish a 10:30 standard time plus an 0:30 SAVE from a
+ 10:00 standard time plus a 1:00 SAVE.
LETTER/S
- Gives the "variable part" (for example, the "S" or "D" in "EST"
- or "EDT") of time zone abbreviations to be used when this rule
- is in effect. If this field is "-", the variable part is null.
+ Gives the "variable part" (for example, the "S" or "D" in "EST"
+ or "EDT") of time zone abbreviations to be used when this rule
+ is in effect. If this field is "-", the variable part is null.
A zone line has the form
- Zone NAME STDOFF RULES FORMAT [UNTIL]
+ Zone NAME STDOFF RULES FORMAT [UNTIL]
For example:
- Zone Asia/Amman 2:00 Jordan EE%sT 2017 Oct 27 01:00
+ Zone Asia/Amman 2:00 Jordan EE%sT 2017 Oct 27 01:00
The fields that make up a zone line are:
- NAME The name of the timezone. This is the name used in creating the
- time conversion information file for the timezone. It should not
- contain a file name component "." or ".."; a file name component
- is a maximal substring that does not contain "/".
-
- STDOFF
- The amount of time to add to UT to get standard time, without any
- adjustment for daylight saving. This field has the same format
- as the AT and SAVE fields of rule lines, except without suffix
- letters; begin the field with a minus sign if time must be
- subtracted from UT.
-
- RULES The name of the rules that apply in the timezone or,
- alternatively, a field in the same format as a rule-line SAVE
- column, giving the amount of time to be added to local standard
- time and whether the resulting time is standard or daylight
- saving. If this field is - then standard time always applies.
- When an amount of time is given, only the sum of standard time
- and this amount matters.
-
- FORMAT
- The format for time zone abbreviations. The pair of characters
- %s is used to show where the "variable part" of the time zone
- abbreviation goes. Alternatively, a format can use the pair of
- characters %z to stand for the UT offset in the form +-hh,
- +-hhmm, or +-hhmmss, using the shortest form that does not lose
- information, where hh, mm, and ss are the hours, minutes, and
- seconds east (+) or west (-) of UT. Alternatively, a slash (/)
- separates standard and daylight abbreviations. To conform to
- POSIX, a time zone abbreviation should contain only alphanumeric
- ASCII characters, "+" and "-". By convention, the time zone
- abbreviation "-00" is a placeholder that means local time is
- unspecified.
-
- UNTIL The time at which the UT offset or the rule(s) change for a
- location. It takes the form of one to four fields YEAR [MONTH
- [DAY [TIME]]]. If this is specified, the time zone information
- is generated from the given UT offset and rule change until the
- time specified, which is interpreted using the rules in effect
- just before the transition. The month, day, and time of day have
- the same format as the IN, ON, and AT fields of a rule; trailing
- fields can be omitted, and default to the earliest possible value
- for the missing fields.
-
- The next line must be a "continuation" line; this has the same
- form as a zone line except that the string "Zone" and the name
- are omitted, as the continuation line will place information
- starting at the time specified as the "until" information in the
- previous line in the file used by the previous line.
- Continuation lines may contain "until" information, just as zone
- lines do, indicating that the next line is a further
- continuation.
-
- If a zone changes at the same instant that a rule would otherwise take
- effect in the earlier zone or continuation line, the rule is ignored.
- A zone or continuation line L with a named rule set starts with
- standard time by default: that is, any of L's timestamps preceding L's
- earliest rule use the rule in effect after L's first transition into
- standard time. In a single zone it is an error if two rules take
- effect at the same instant, or if two zone changes take effect at the
+ NAME The name of the timezone. This is the name used in creating the
+ time conversion information file for the timezone. It should
+ not contain a file name component "." or ".."; a file name
+ component is a maximal substring that does not contain "/".
+
+ STDOFF The amount of time to add to UT to get standard time, without
+ any adjustment for daylight saving. This field has the same
+ format as the AT and SAVE fields of rule lines, except without
+ suffix letters; begin the field with a minus sign if time must
+ be subtracted from UT.
+
+ RULES The name of the rules that apply in the timezone or,
+ alternatively, a field in the same format as a rule-line SAVE
+ column, giving the amount of time to be added to local standard
+ time and whether the resulting time is standard or daylight
+ saving. If this field is - then standard time always applies.
+ When an amount of time is given, only the sum of standard time
+ and this amount matters.
+
+ FORMAT The format for time zone abbreviations. The pair of characters
+ %s is used to show where the "variable part" of the time zone
+ abbreviation goes. Alternatively, a format can use the pair of
+ characters %z to stand for the UT offset in the form +-hh,
+ +-hhmm, or +-hhmmss, using the shortest form that does not lose
+ information, where hh, mm, and ss are the hours, minutes, and
+ seconds east (+) or west (-) of UT. Alternatively, a slash (/)
+ separates standard and daylight abbreviations. To conform to
+ POSIX, a time zone abbreviation should contain only alphanumeric
+ ASCII characters, "+" and "-". By convention, the time zone
+ abbreviation "-00" is a placeholder that means local time is
+ unspecified.
+
+ UNTIL The time at which the UT offset or the rule(s) change for a
+ location. It takes the form of one to four fields YEAR [MONTH
+ [DAY [TIME]]]. If this is specified, the time zone information
+ is generated from the given UT offset and rule change until the
+ time specified, which is interpreted using the rules in effect
+ just before the transition. The month, day, and time of day
+ have the same format as the IN, ON, and AT fields of a rule;
+ trailing fields can be omitted, and default to the earliest
+ possible value for the missing fields.
+
+ The next line must be a "continuation" line; this has the same
+ form as a zone line except that the string "Zone" and the name
+ are omitted, as the continuation line will place information
+ starting at the time specified as the "until" information in the
+ previous line in the file used by the previous line.
+ Continuation lines may contain "until" information, just as zone
+ lines do, indicating that the next line is a further
+ continuation.
+
+ If a zone changes at the same instant that a rule would otherwise take
+ effect in the earlier zone or continuation line, the rule is ignored.
+ A zone or continuation line L with a named rule set starts with
+ standard time by default: that is, any of L's timestamps preceding L's
+ earliest rule use the rule in effect after L's first transition into
+ standard time. In a single zone it is an error if two rules take
+ effect at the same instant, or if two zone changes take effect at the
same instant.
- If a continuation line subtracts N seconds from the UT offset after a
- transition that would be interpreted to be later if using the
- continuation line's UT offset and rules, the "until" time of the
- previous zone or continuation line is interpreted according to the
- continuation line's UT offset and rules, and any rule that would
- otherwise take effect in the next N seconds is instead assumed to take
+ If a continuation line subtracts N seconds from the UT offset after a
+ transition that would be interpreted to be later if using the
+ continuation line's UT offset and rules, the "until" time of the
+ previous zone or continuation line is interpreted according to the
+ continuation line's UT offset and rules, and any rule that would
+ otherwise take effect in the next N seconds is instead assumed to take
effect simultaneously. For example:
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
Rule US 1967 2006 - Oct lastSun 2:00 0 S
Rule US 1967 1973 - Apr lastSun 2:00 1:00 D
- # Zone NAME STDOFF RULES FORMAT [UNTIL]
- Zone America/Menominee -5:00 - EST 1973 Apr 29 2:00
- -6:00 US C%sT
+ # Zone NAME STDOFF RULES FORMAT [UNTIL]
+ Zone America/Menominee -5:00 - EST 1973 Apr 29 2:00
+ -6:00 US C%sT
- Here, an incorrect reading would be there were two clock changes on
- 1973-04-29, the first from 02:00 EST (-05) to 01:00 CST (-06), and the
+ Here, an incorrect reading would be there were two clock changes on
+ 1973-04-29, the first from 02:00 EST (-05) to 01:00 CST (-06), and the
second an hour later from 02:00 CST (-06) to 03:00 CDT (-05). However,
zic interprets this more sensibly as a single transition from 02:00 CST
(-05) to 02:00 CDT (-05).
A link line has the form
- Link TARGET LINK-NAME
+ Link TARGET LINK-NAME
For example:
- Link Europe/Istanbul Asia/Istanbul
+ Link Europe/Istanbul Asia/Istanbul
- The TARGET field should appear as the NAME field in some zone line or
- as the LINK-NAME field in some link line. The LINK-NAME field is used
- as an alternative name for that zone; it has the same syntax as a zone
- line's NAME field. Links can chain together, although the behavior is
- unspecified if a chain of one or more links does not terminate in a
- Zone name. A link line can appear before the line that defines the
+ The TARGET field should appear as the NAME field in some zone line or
+ as the LINK-NAME field in some link line. The LINK-NAME field is used
+ as an alternative name for that zone; it has the same syntax as a zone
+ line's NAME field. Links can chain together, although the behavior is
+ unspecified if a chain of one or more links does not terminate in a
+ Zone name. A link line can appear before the line that defines the
link target. For example:
Link Greenwich G_M_T
Link Etc/GMT Greenwich
Zone Etc/GMT 0 - GMT
- The two links are chained together, and G_M_T, Greenwich, and Etc/GMT
+ The two links are chained together, and G_M_T, Greenwich, and Etc/GMT
all name the same zone.
- Except for continuation lines, lines may appear in any order in the
- input. However, the behavior is unspecified if multiple zone or link
+ Except for continuation lines, lines may appear in any order in the
+ input. However, the behavior is unspecified if multiple zone or link
lines define the same name.
- The file that describes leap seconds can have leap lines and an
+ The file that describes leap seconds can have leap lines and an
expiration line. Leap lines have the following form:
- Leap YEAR MONTH DAY HH:MM:SS CORR R/S
+ Leap YEAR MONTH DAY HH:MM:SS CORR R/S
For example:
- Leap 2016 Dec 31 23:59:60 + S
+ Leap 2016 Dec 31 23:59:60 + S
- The YEAR, MONTH, DAY, and HH:MM:SS fields tell when the leap second
+ The YEAR, MONTH, DAY, and HH:MM:SS fields tell when the leap second
happened. The CORR field should be "+" if a second was added or "-" if
- a second was skipped. The R/S field should be (an abbreviation of)
- "Stationary" if the leap second time given by the other fields should
- be interpreted as UTC or (an abbreviation of) "Rolling" if the leap
- second time given by the other fields should be interpreted as local
+ a second was skipped. The R/S field should be (an abbreviation of)
+ "Stationary" if the leap second time given by the other fields should
+ be interpreted as UTC or (an abbreviation of) "Rolling" if the leap
+ second time given by the other fields should be interpreted as local
(wall clock) time.
- Rolling leap seconds were implemented back when it was not clear
- whether common practice was rolling or stationary, with concerns that
- one would see Times Square ball drops where there'd be a "3... 2...
- 1... leap... Happy New Year" countdown, placing the leap second at
- midnight New York time rather than midnight UTC. However, this
- countdown style does not seem to have caught on, which means rolling
- leap seconds are not used in practice; also, they are not supported if
+ Rolling leap seconds were implemented back when it was not clear
+ whether common practice was rolling or stationary, with concerns that
+ one would see Times Square ball drops where there'd be a "3... 2...
+ 1... leap... Happy New Year" countdown, placing the leap second at
+ midnight New York time rather than midnight UTC. However, this
+ countdown style does not seem to have caught on, which means rolling
+ leap seconds are not used in practice; also, they are not supported if
the -r option is used.
The expiration line, if present, has the form:
- Expires YEAR MONTH DAY HH:MM:SS
+ Expires YEAR MONTH DAY HH:MM:SS
For example:
- Expires 2020 Dec 28 00:00:00
+ Expires 2020 Dec 28 00:00:00
The YEAR, MONTH, DAY, and HH:MM:SS fields give the expiration timestamp
in UTC for the leap second table.
EXTENDED EXAMPLE
- Here is an extended example of zic input, intended to illustrate many
+ Here is an extended example of zic input, intended to illustrate many
of its features.
# Rule NAME FROM TO - IN ON AT SAVE LETTER/S
@@ -466,29 +463,29 @@ EXTENDED EXAMPLE
Link Europe/Zurich Europe/Vaduz
- In this example, the EU rules are for the European Union and for its
- predecessor organization, the European Communities. The timezone is
- named Europe/Zurich and it has the alias Europe/Vaduz. This example
- says that Zurich was 34 minutes and 8 seconds east of UT until
- 1853-07-16 at 00:00, when the legal offset was changed to 7 degrees 26
- minutes 22.50 seconds, which works out to 0:29:45.50; zic treats this
- by rounding it to 0:29:46. After 1894-06-01 at 00:00 the UT offset
- became one hour and Swiss daylight saving rules (defined with lines
- beginning with "Rule Swiss") apply. From 1981 to the present, EU
- daylight saving rules have applied, and the UTC offset has remained at
+ In this example, the EU rules are for the European Union and for its
+ predecessor organization, the European Communities. The timezone is
+ named Europe/Zurich and it has the alias Europe/Vaduz. This example
+ says that Zurich was 34 minutes and 8 seconds east of UT until
+ 1853-07-16 at 00:00, when the legal offset was changed to 7 degrees 26
+ minutes 22.50 seconds, which works out to 0:29:45.50; zic treats this
+ by rounding it to 0:29:46. After 1894-06-01 at 00:00 the UT offset
+ became one hour and Swiss daylight saving rules (defined with lines
+ beginning with "Rule Swiss") apply. From 1981 to the present, EU
+ daylight saving rules have applied, and the UTC offset has remained at
one hour.
In 1941 and 1942, daylight saving time applied from the first Monday in
- May at 01:00 to the first Monday in October at 02:00. The pre-1981 EU
- daylight-saving rules have no effect here, but are included for
+ May at 01:00 to the first Monday in October at 02:00. The pre-1981 EU
+ daylight-saving rules have no effect here, but are included for
completeness. Since 1981, daylight saving has begun on the last Sunday
- in March at 01:00 UTC. Until 1995 it ended the last Sunday in
- September at 01:00 UTC, but this changed to the last Sunday in October
+ in March at 01:00 UTC. Until 1995 it ended the last Sunday in
+ September at 01:00 UTC, but this changed to the last Sunday in October
starting in 1996.
- For purposes of display, "LMT" and "BMT" were initially used,
- respectively. Since Swiss rules and later EU rules were applied, the
- time zone abbreviation has been CET for standard time and CEST for
+ For purposes of display, "LMT" and "BMT" were initially used,
+ respectively. Since Swiss rules and later EU rules were applied, the
+ time zone abbreviation has been CET for standard time and CEST for
daylight saving time.
FILES
@@ -499,15 +496,15 @@ FILES
Default timezone information directory.
NOTES
- For areas with more than two types of local time, you may need to use
- local standard time in the AT field of the earliest transition time's
- rule to ensure that the earliest transition time recorded in the
+ For areas with more than two types of local time, you may need to use
+ local standard time in the AT field of the earliest transition time's
+ rule to ensure that the earliest transition time recorded in the
compiled file is correct.
- If, for a particular timezone, a clock advance caused by the start of
- daylight saving coincides with and is equal to a clock retreat caused
- by a change in UT offset, zic produces a single transition to daylight
- saving at the new UT offset without any change in local (wall clock)
+ If, for a particular timezone, a clock advance caused by the start of
+ daylight saving coincides with and is equal to a clock retreat caused
+ by a change in UT offset, zic produces a single transition to daylight
+ saving at the new UT offset without any change in local (wall clock)
time. To get separate transitions use multiple zone continuation lines
specifying transition instants using universal time.
diff --git a/zic.c b/zic.c
index 55180513045a..00f00e307a30 100644
--- a/zic.c
+++ b/zic.c
@@ -14,6 +14,7 @@
#include "version.h"
#include "private.h"
+#include "tzdir.h"
#include "tzfile.h"
#include <fcntl.h>
@@ -34,6 +35,9 @@ static zic_t const
# define ZIC_MAX_ABBR_LEN_WO_WARN 6
#endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */
+/* Minimum and maximum years, assuming signed 32-bit time_t. */
+enum { YEAR_32BIT_MIN = 1901, YEAR_32BIT_MAX = 2038 };
+
/* An upper bound on how much a format might grow due to concatenation. */
enum { FORMAT_LEN_GROWTH_BOUND = 5 };
@@ -92,7 +96,6 @@ struct rule {
zic_t r_loyear; /* for example, 1986 */
zic_t r_hiyear; /* for example, 1986 */
- bool r_lowasnum;
bool r_hiwasnum;
int r_month; /* 0..11 */
@@ -164,13 +167,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 +188,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);
@@ -332,7 +330,7 @@ enum {
*/
enum {
- YR_MINIMUM,
+ YR_MINIMUM, /* "minimum" is for backward compatibility only */
YR_MAXIMUM,
YR_ONLY
};
@@ -416,12 +414,10 @@ static struct lookup const lasts[] = {
static struct lookup const begin_years[] = {
{ "minimum", YR_MINIMUM },
- { "maximum", YR_MAXIMUM },
{ NULL, 0 }
};
static struct lookup const end_years[] = {
- { "minimum", YR_MINIMUM },
{ "maximum", YR_MAXIMUM },
{ "only", YR_ONLY },
{ NULL, 0 }
@@ -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]);
@@ -2149,13 +2187,12 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
*/
cp = loyearp;
lp = byword(cp, begin_years);
- rp->r_lowasnum = lp == NULL;
- if (!rp->r_lowasnum) switch (lp->l_value) {
+ if (lp) switch (lp->l_value) {
case YR_MINIMUM:
- rp->r_loyear = ZIC_MIN;
- break;
- case YR_MAXIMUM:
- rp->r_loyear = ZIC_MAX;
+ warning(_("FROM year \"%s\" is obsolete;"
+ " treated as %d"),
+ cp, YEAR_32BIT_MIN - 1);
+ rp->r_loyear = YEAR_32BIT_MIN - 1;
break;
default: unreachable();
} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_loyear, &xs) != 1) {
@@ -2166,9 +2203,6 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
lp = byword(cp, end_years);
rp->r_hiwasnum = lp == NULL;
if (!rp->r_hiwasnum) switch (lp->l_value) {
- case YR_MINIMUM:
- rp->r_hiyear = ZIC_MIN;
- break;
case YR_MAXIMUM:
rp->r_hiyear = ZIC_MAX;
break;
@@ -2948,6 +2982,10 @@ rule_cmp(struct rule const *a, struct rule const *b)
return a->r_dayofmonth - b->r_dayofmonth;
}
+/* Store into RESULT a POSIX.1-2017 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)
{
@@ -3083,11 +3121,11 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
register char * envvar;
register int max_abbr_len;
register int max_envvar_len;
- register bool prodstic; /* all rules are min to max */
register int compat;
register bool do_extend;
register char version;
- ptrdiff_t lastatmax = -1;
+ zic_t nonTZlimtime = ZIC_MIN;
+ int nonTZlimtype = -1;
zic_t max_year0;
int defaulttype = -1;
@@ -3108,7 +3146,6 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
timecnt = 0;
typecnt = 0;
charcnt = 0;
- prodstic = zonecount == 1;
/*
** Thanks to Earl Chew
** for noting the need to unconditionally initialize startttisstd.
@@ -3126,12 +3163,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
updateminmax(zp->z_untilrule.r_loyear);
for (j = 0; j < zp->z_nrules; ++j) {
struct rule *rp = &zp->z_rules[j];
- if (rp->r_lowasnum)
- updateminmax(rp->r_loyear);
+ updateminmax(rp->r_loyear);
if (rp->r_hiwasnum)
updateminmax(rp->r_hiyear);
- if (rp->r_lowasnum || rp->r_hiwasnum)
- prodstic = false;
}
}
/*
@@ -3143,7 +3177,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
if (noise) {
if (!*envvar)
warning("%s %s",
- _("no POSIX environment variable for zone"),
+ _("no POSIX.1-2017 environment variable"
+ " for zone"),
zpfirst->z_name);
else if (compat != 0) {
/* Circa-COMPAT clients, and earlier clients, might
@@ -3155,37 +3190,12 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
}
}
if (do_extend) {
- /*
- ** Search through a couple of extra years past the obvious
- ** 400, to avoid edge cases. For example, suppose a non-POSIX
- ** rule applies from 2012 onwards and has transitions in March
- ** and September, plus some one-off transitions in November
- ** 2013. If zic looked only at the last 400 years, it would
- ** set max_year=2413, with the intent that the 400 years 2014
- ** through 2413 will be repeated. The last transition listed
- ** in the tzfile would be in 2413-09, less than 400 years
- ** after the last one-off transition in 2013-11. Two years
- ** might be overkill, but with the kind of edge cases
- ** available we're not sure that one year would suffice.
- */
- enum { years_of_observations = YEARSPERREPEAT + 2 };
-
if (min_year >= ZIC_MIN + years_of_observations)
min_year -= years_of_observations;
else min_year = ZIC_MIN;
if (max_year <= ZIC_MAX - years_of_observations)
max_year += years_of_observations;
else max_year = ZIC_MAX;
- /*
- ** Regardless of any of the above,
- ** for a "proDSTic" zone which specifies that its rules
- ** always have and always will be in effect,
- ** we only need one cycle to define the zone.
- */
- if (prodstic) {
- min_year = 1900;
- max_year = min_year + years_of_observations;
- }
}
max_year = max(max_year, (redundant_time / (SECSPERDAY * DAYSPERNYEAR)
+ EPOCH_YEAR + 1));
@@ -3193,17 +3203,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
if (want_bloat()) {
/* For the benefit of older systems,
generate data from 1900 through 2038. */
- if (min_year > 1900)
- min_year = 1900;
- if (max_year < 2038)
- max_year = 2038;
+ if (min_year > YEAR_32BIT_MIN - 1)
+ min_year = YEAR_32BIT_MIN - 1;
+ if (max_year < YEAR_32BIT_MAX)
+ max_year = YEAR_32BIT_MAX;
}
if (min_time < lo_time || hi_time < max_time)
unspecifiedtype = addtype(0, "-00", false, false, false);
for (i = 0; i < zonecount; ++i) {
- struct rule *prevrp = NULL;
/*
** A guess that may well be corrected later.
*/
@@ -3213,8 +3222,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 +3235,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 +3366,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 +3386,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,12 +3411,38 @@ 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
- ** for 400 years because we can't fill the POSIX-TZ field,
+ ** If we're extending the explicitly listed observations for
+ ** 400 years because we can't fill the POSIX.1-2017 TZ field,
** check whether we actually ended up explicitly listing
** observations through that period. If there aren't any
** near the end of the 400-year period, add a redundant