aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2026-07-09 06:38:09 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2026-07-09 06:38:09 +0000
commit2479b6921a20c7fe260ba1b30c5ca7f666e692a6 (patch)
tree68c2732b6a76ba5b46eb961b617265eb529f6daf
parentb17ccc1f4591d37eb3a671b37aecbd30546842f1 (diff)
-rw-r--r--Makefile45
-rw-r--r--NEWS77
-rw-r--r--localtime.c75
-rw-r--r--newctime.320
-rw-r--r--newstrftime.34
-rw-r--r--newtzset.316
-rw-r--r--newtzset.3.txt2
-rw-r--r--private.h24
-rw-r--r--theory.html18
-rw-r--r--time2posix.38
-rw-r--r--tz-art.html32
-rw-r--r--tz-how-to.html6
-rw-r--r--tz-link.html50
-rw-r--r--tzfile.5.txt3
-rw-r--r--version2
-rw-r--r--zdump.c66
-rw-r--r--zic.88
-rw-r--r--zic.8.txt25
-rw-r--r--zic.c351
19 files changed, 438 insertions, 394 deletions
diff --git a/Makefile b/Makefile
index 1e0a5903534d..f5a22f1896a4 100644
--- a/Makefile
+++ b/Makefile
@@ -114,7 +114,7 @@ ZDUMPDIR = $(BINDIR)
# The "zic" command goes in:
ZICDIR = $(TOPDIR)/$(USRDIR)/sbin
-# Manual pages go in subdirectories of. . .
+# Manual pages go in subdirectories of:
MANDIR = $(TOPDIR)/$(USRSHAREDIR)/man
# Library functions are put in an archive in LIBDIR.
@@ -145,7 +145,7 @@ TIME_T_ALTERNATIVES_TAIL = int_least32_t.ck uint_least32_t.ck \
# nonnegative TZ_CHANGE_INTERVAL also assumes this, so to be compatible with
# these, use "posix_only" or "posix_right". Use POSIX time on systems with
# leap smearing; this can work better than unsmeared "right" time with
-# applications that are not leap second aware, and is closer to unsmeared
+# applications that are not aware of leap seconds, and is closer to unsmeared
# "right" time than unsmeared POSIX time is (e.g., 0.5 vs 1.0 s max error).
REDO= posix_only
@@ -295,10 +295,10 @@ LDLIBS=
# -DTHREAD_SAFE to make localtime.c thread-safe, as POSIX requires;
# not needed by the main-program tz code, which is single-threaded.
# Append other compiler flags as needed, e.g., -pthread on GNU/Linux.
-# The following options can also be used:
+# With -DTHREAD_SAFE the following options can also be used:
# -DTHREAD_PREFER_SINGLE to prefer speed in single-threaded apps,
# at some cost in CPU time and energy in multi-threaded apps.
-# The following options can also be used:
+# With -DTHREAD_PREFER_SINGLE the following options can also be used:
# -DHAVE___ISTHREADED=1 if there is an extern int __isthreaded
# variable, 0 otherwise (default is guessed)
# -DHAVE_SYS_SINGLE_THREADED_H=0 if <sys/single_threaded.h> works,
@@ -377,12 +377,13 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \
-Wdeclaration-after-statement -Wdouble-promotion \
-Wduplicated-branches -Wduplicated-cond -Wflex-array-member-not-at-end \
-Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
- -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op \
+ -Wfree-labels -Wimplicit-fallthrough=5 -Winit-self \
+ -Wkeyword-macro -Wlogical-op \
-Wmissing-declarations -Wmissing-prototypes \
-Wmissing-variable-declarations -Wnested-externs \
-Wnull-dereference \
-Wold-style-definition -Woverlength-strings -Wpointer-arith \
- -Wshadow -Wshift-overflow=2 -Wstrict-overflow \
+ -Wshadow -Wshift-overflow=2 \
-Wstrict-prototypes -Wstringop-overflow=4 \
-Wsuggest-attribute=cold \
-Wsuggest-attribute=const -Wsuggest-attribute=format \
@@ -533,7 +534,7 @@ ZIC_INSTALL= $(ZIC) -d '$(DESTDIR)$(TZDIR)'
# 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.
+# Try AWK=gawk or AWK=nawk if your awk has the problems mentioned above.
AWK= awk
# The full path name of a POSIX-compliant shell, preferably one that supports
@@ -913,7 +914,7 @@ tzselect: tzselect.ksh version
check: check_mild back.ck now.ck
check_mild: check_web check_zishrink \
character-set.ck white-space.ck links.ck mainguard.ck \
- name-lengths.ck slashed-abbrs.ck sorted.ck \
+ name-lengths.ck news.ck slashed-abbrs.ck sorted.ck \
tables.ck ziguard.ck tzs.ck
# True if UTF8_LOCALE does not work;
@@ -1083,6 +1084,12 @@ zishrink-posix.ck zishrink-right.ck: \
rm -fr $@d t-$@d shrunk-$@d
touch $@
+# Check that NEWS has data release versions and dates in reverse order.
+news.ck: NEWS
+ grep '^Release [0-9][0-9][0-9][0-9]' NEWS | LC_ALL=C sort -cru
+ sed -n '/ -0000$$/!s/^Release [^ ]*//p' NEWS|LC_ALL=C sort -cru
+ touch $@
+
clean_misc:
rm -fr *.ckd *.dir
rm -f *.ck *.core *.o *.out *.t core core.* \
@@ -1139,7 +1146,8 @@ SET_TIMESTAMP_N = sh -c '\
# If DEST depends on A B C ... in this Makefile, callers should use
# $(SET_TIMESTAMP_DEP) DEST A B C ..., for the benefit of any
# downstream 'make' that considers equal timestamps to be out of date.
-# POSIX allows this 'make' behavior, and HP-UX 'make' does it.
+# POSIX allows this 'make' behavior, although only HP-UX 'make'
+# (which is no longer supported) did things that way.
# If all that matters is that the timestamp be reproducible
# and plausible, use $(SET_TIMESTAMP).
SET_TIMESTAMP = $(SET_TIMESTAMP_N) 0
@@ -1399,24 +1407,6 @@ $(ALL_ASC):
$(SET_TIMESTAMP) $(@:.t=) $(?:.t=)
touch $@
-TYPECHECK_CFLAGS = $(CFLAGS) -DTYPECHECK -D__time_t_defined -D_TIME_T
-typecheck: long-long.ck unsigned.ck
-long-long.ck unsigned.ck: $(VERSION_DEPS)
- rm -fr $@d
- mkdir $@d
- ln $(VERSION_DEPS) $@d
- cd $@d && \
- case $@ in \
- long-long.*) i="long long";; \
- unsigned.* ) i="unsigned" ;; \
- esac && \
- $(MAKE) \
- CFLAGS="$(TYPECHECK_CFLAGS) \"-Dtime_t=$$i\"" \
- TOPDIR="$$PWD" \
- install
- $@d/zdump -i -c 1970,1971 Europe/Rome
- touch $@
-
zonenames: tzdata.zi
@$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi
@@ -1442,6 +1432,5 @@ zic.o: private.h tzdir.h tzfile.h version.h
.PHONY: traditional_signatures traditional_signatures_version
.PHONY: traditional_tarballs traditional_tarballs_version
.PHONY: tailored_tarballs tailored_tarballs_version
-.PHONY: typecheck
.PHONY: zonenames zones
.PHONY: $(ZDS)
diff --git a/NEWS b/NEWS
index ea52e67e804a..dd962b54d529 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,68 @@
News for the tz database
+Release 2026c - 2026-07-08 10:23:58 -0700
+
+ Briefly:
+ Alberta moved to permanent -06 on 2026-06-18.
+ Morocco moves to permanent +00 on 2026-09-20.
+ More integer overflow bugs have been fixed in zic.
+
+ Changes to future timestamps
+
+ Alberta’s 2026-03-08 spring forward was its last foreseeable clock
+ change, as it moved to permanent -06 thereafter. (Thanks to Roozbeh
+ Pournader and others.) Model this with its traditional abbreviation
+ CST. Although the change to permanent -06 legally took place on
+ 2026-06-18, temporarily model the change to occur on 2026-11-01 at
+ 02:00 instead, for the same reason we introduced a similarly
+ temporary hack for British Columbia in 2026b.
+
+ Although another TZDB release will likely be needed soon because
+ Northwest Territories will likely follow Alberta, the legal
+ formalities have not yet taken place.
+
+ Morocco plans to move back to permanent UTC, without daylight
+ saving time transitions, on 2026-09-20 at 02:00. This also
+ affects Western Sahara.
+
+ Changes to code
+
+ zic no longer overflows integers when processing outlandish input
+ like ‘Zone Ouch 0 - LMT 9223372036854775807’, ‘Zone Ouch 0
+ 2562047788015215 LMT’, ‘Zone Ouch -2562047788015215:30:08 - LMT’,
+ and ‘Zone Ouch -2562047788015215:30:08 - %%z’. This avoids
+ undefined behavior in C. (Problems reported by Naveed Khan.)
+
+ On platforms that have EFTYPE, tzalloc now fails with errno set to
+ EFTYPE, not EINVAL, if it detects that the TZif file has an
+ invalid format or is not a regular file. Formerly it did this
+ only on NetBSD, and only when the file was not a regular file.
+
+ Unprivileged programs no longer require TZif files to be regular
+ files or reject relative names containing ".." components. This
+ reverts to the more-permissive 2025b behavior, as the stricter
+ behavior did not catch on in FreeBSD.
+
+ zic now reports any failure to remove a temporary file when
+ cleaning up after a previous failure. (Problem reported by Tom
+ Lane.)
+
+ Changes to commentary
+
+ Northwest Territories is expected to move to permanent -06 prior to
+ 2026-11-01 02:00, when clocks would otherwise fall back. (Thanks to
+ Tim Parenti and James Bellaire.) Model this with its traditional
+ abbreviation CST. Unfortunately the change is not yet official, so
+ it is currently present only as comments that can be uncommented as
+ needed.
+
+ Changes to build procedure
+
+ The undocumented ‘typecheck’ Makefile check rule has been removed.
+ It stopped working in 2025a and evidently nobody noticed.
+ The rule was superseded by ‘check_time_t_alternatives’ in 2013d.
+
+
Release 2026b - 2026-04-22 23:06:43 -0700
Briefly:
@@ -13,7 +76,7 @@ Release 2026b - 2026-04-22 23:06:43 -0700
(Thanks to Arthur David Olson.) Although the change to permanent
-07 legally took place on 2026-03-09, temporarily model the change
to occur on 2026-11-01 at 02:00 instead. This works around a
- limitation in CLDR v48.2 (2026-03-17). This temporary hack is
+ limitation in CLDR 48.1 (2026-01-08). This temporary hack is
planned to be removed after CLDR is fixed.
Changes to code
@@ -99,7 +162,7 @@ Release 2026a - 2026-03-01 22:59:49 -0800
than attempting to override this fallback with the contents of the
posixrules file. This removes library support that was declared
obsolete in release 2019b, and fixes some undefined behavior.
- (Undefined behavior reported by GitHub user Naveed8951.)
+ (Undefined behavior reported by Naveed Khan.)
The posix2time, posix2time_z, time2posix, and time2posix_z
functions now set errno=EOVERFLOW and return ((time_t) -1) if the
@@ -111,7 +174,7 @@ Release 2026a - 2026-03-01 22:59:49 -0800
Some other undefined behavior, triggered by TZif files containing
outlandish but conforming UT offsets or leap second corrections,
- has also been fixed. (Some of these bugs reported by Naveed8951.)
+ has also been fixed. (Some of these bugs reported by Naveed Khan.)
localtime.c no longer rejects TZif files that exactly fit in its
internal structures, fixing off-by-one typos introduced in 2014g.
@@ -221,7 +284,7 @@ Release 2025c - 2025-12-10 14:42:37 -0800
rarely changing and many threads call tzcode simultaneously.
It costs more CPU time and energy.
- The new CFLAGS option -TTHREAD_TM_MULTI causes localtime to return
+ The new CFLAGS option -DTHREAD_TM_MULTI causes localtime to return
a pointer to thread-specific memory, as FreeBSD does, instead of
to the same memory in all threads. This supports nonportable
programs that incorrectly use localtime instead of localtime_r.
@@ -551,7 +614,7 @@ Release 2024a - 2024-02-01 09:28:56 -0800
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.)
+ (Thanks to Martin Burnicki for liaising with the IERS.)
Changes to documentation
@@ -1709,7 +1772,7 @@ Release 2020a - 2020-04-23 16:03:47 -0700
zic -L now supports an Expires line in the leapseconds file, and
truncates the TZif output accordingly. This propagates leap
second expiration information into the TZif file, and avoids the
- abovementioned localtime.c bug as well as similar bugs present in
+ localtime.c bug mentioned above, as well as similar bugs present in
many client implementations. If no Expires line is present, zic
-L instead truncates the TZif output based on the #expires comment
present in leapseconds files distributed by tzdb 2018f and later;
@@ -6666,7 +6729,7 @@ few (e.g., code2012c-data2012d) have tarballs with mixed version
numbers. Recent releases also come in an experimental format
consisting of a single tarball tzdb-R.tar.lz with extra data.
-Release timestamps are taken from the release's commit (for newer,
+A release’s timestamp is taken from the release’s commit (for newer,
Git-based releases), from the newest file in the tarball (for older
releases, where this info is available) or from the email announcing
the release (if all else fails; these are marked with a time zone
diff --git a/localtime.c b/localtime.c
index 0091b81eba3d..be3fe1d5d8f4 100644
--- a/localtime.c
+++ b/localtime.c
@@ -227,7 +227,7 @@ struct timespec { time_t tv_sec; long tv_nsec; };
/* How many seconds to wait before checking the default TZif file again.
Negative means no checking. Default to 61 if DETECT_TZ_CHANGES
- (as circa 2025 FreeBSD builds its localtime.c with -DDETECT_TZ_CHANGES),
+ (as FreeBSD optionally builds its localtime.c with -DDETECT_TZ_CHANGES),
and to -1 otherwise. */
#ifndef TZ_CHANGE_INTERVAL
# ifdef DETECT_TZ_CHANGES
@@ -288,10 +288,12 @@ typedef intmax_t timex_t;
# endif
#endif
-/* Placeholders for platforms lacking openat. */
+/* Placeholders for platforms lacking AT_FCWD, openat, and fstatat. */
#ifndef AT_FDCWD
# define AT_FDCWD (-1) /* any negative value will do */
static int openat(int dd, char const *path, int oflag) { unreachable (); }
+static int fstatat(int dd, char const *path, struct stat *st, int flags)
+{ unreachable(); }
#endif
/* Port to platforms that lack some O_* flags. Unless otherwise
@@ -425,8 +427,7 @@ static char const *utc = etc_utc + sizeof "Etc/" - 1;
approach of opening "/usr/share/zoneinfo/America/Los_Angeles".
Although the OPENAT_TZDIR approach is less efficient, suffers from
spurious EMFILE and ENFILE failures, and is no more secure in practice,
- it is how bleeding edge FreeBSD did things from August 2025
- through at least September 2025. */
+ bleeding edge FreeBSD started doing it this way in August 2025. */
#ifndef OPENAT_TZDIR
# define OPENAT_TZDIR 0
#endif
@@ -917,6 +918,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
int dd = AT_FDCWD;
int oflags = (O_RDONLY | O_BINARY | O_CLOEXEC | O_CLOFORK
| O_IGNORE_CTTY | O_NOCTTY | O_REGULAR);
+ bool might_escape = false;
int err;
struct stat st;
st.st_ctime = 0;
@@ -943,16 +945,8 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
continue;
else if (issetugid())
return ENOTCAPABLE;
- else if (!O_REGULAR) {
- /* Check for devices, as their mere opening could have
- unwanted side effects. Though racy, there is no
- portable way to fix the races. This check is needed
- only for files not otherwise known to be non-devices. */
- if (stat(name, &st) < 0)
- return errno;
- if (!S_ISREG(st.st_mode))
- return EINVAL;
- }
+ else
+ might_escape = true;
}
if (relname[0] != '/') {
@@ -964,8 +958,12 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
for (component = relname; component[0]; component++)
if (component[0] == '.' && component[1] == '.'
&& component[2] == '/'
- && (component == relname || component[-1] == '/'))
- return ENOTCAPABLE;
+ && (component == relname || component[-1] == '/')) {
+ if (issetugid())
+ return ENOTCAPABLE;
+ might_escape = true;
+ break;
+ }
}
if (OPENAT_TZDIR && !SUPPRESS_TZDIR) {
@@ -978,7 +976,10 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
| O_BINARY | O_CLOEXEC | O_CLOFORK | O_DIRECTORY));
if (dd < 0)
return errno;
- oflags |= O_RESOLVE_BENEATH;
+ if (O_RESOLVE_BENEATH && issetugid()) {
+ oflags |= O_RESOLVE_BENEATH;
+ might_escape = false;
+ }
}
}
@@ -1014,6 +1015,18 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
#endif
}
+ /* For a platform that lacks O_REGULAR and a file that might
+ be outside TZDIR, check that it is a regular file,
+ as merely opening a device could have unwanted side effects.
+ Though racy, there is no portable way to fix the race. */
+ if (!O_REGULAR && might_escape) {
+ /* (oflags & O_RESOLVE_BENEATH) must be zero here. */
+ if ((OPENAT_TZDIR ? fstatat(dd, relname, &st, 0) : stat(name, &st))
+ < 0)
+ return errno;
+ if (!S_ISREG(st.st_mode))
+ return EFTYPE;
+ }
fid = OPENAT_TZDIR ? openat(dd, relname, oflags) : open(name, oflags);
err = errno;
if (0 <= dd)
@@ -1036,7 +1049,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
}
up = &lsp->u.u;
nread = read(fid, up->buf, sizeof up->buf);
- err = tzheadsize <= nread ? 0 : nread < 0 ? errno : EINVAL;
+ err = tzheadsize <= nread ? 0 : nread < 0 ? errno : EFTYPE;
}
close(fid);
if (err)
@@ -1064,7 +1077,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
&& 0 <= charcnt && charcnt <= TZ_MAX_CHARS
&& 0 <= ttisstdcnt && ttisstdcnt <= TZ_MAX_TYPES
&& 0 <= ttisutcnt && ttisutcnt <= TZ_MAX_TYPES))
- return EINVAL;
+ return EFTYPE;
datablock_size
= (timecnt * stored /* ats */
+ timecnt /* types */
@@ -1074,12 +1087,12 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
+ ttisstdcnt /* ttisstds */
+ ttisutcnt); /* ttisuts */
if (nread < tzheadsize + datablock_size)
- return EINVAL;
+ return EFTYPE;
if (skip_datablock)
p += datablock_size;
else if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0)
&& (ttisutcnt == typecnt || ttisutcnt == 0)))
- return EINVAL;
+ return EFTYPE;
else {
int_fast64_t prevtr = -1;
int_fast32_2s prevcorr;
@@ -1102,7 +1115,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
? TIME_T_MIN : at);
if (timecnt && attime <= sp->ats[timecnt - 1]) {
if (attime < sp->ats[timecnt - 1])
- return EINVAL;
+ return EFTYPE;
sp->types[i - 1] = 0;
timecnt--;
}
@@ -1115,7 +1128,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
for (i = 0; i < sp->timecnt; ++i) {
unsigned char typ = *p++;
if (sp->typecnt <= typ)
- return EINVAL;
+ return EFTYPE;
if (sp->types[i])
sp->types[timecnt++] = typ;
}
@@ -1129,18 +1142,18 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
cause trouble both in this file and in callers.
Also, it violates RFC 9636 section 3.2. */
if (utoff < -TWO_31_MINUS_1)
- return EINVAL;
+ return EFTYPE;
ttisp = &sp->ttis[i];
ttisp->tt_utoff = utoff;
p += 4;
isdst = *p++;
if (! (isdst < 2))
- return EINVAL;
+ return EFTYPE;
ttisp->tt_isdst = isdst;
desigidx = *p++;
if (! (desigidx < sp->charcnt))
- return EINVAL;
+ return EFTYPE;
ttisp->tt_desigidx = desigidx;
}
for (i = 0; i < sp->charcnt; ++i)
@@ -1159,7 +1172,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
/* Leap seconds cannot occur before the Epoch,
or out of order. */
if (tr <= prevtr)
- return EINVAL;
+ return EFTYPE;
/* To avoid other botches in this code, each leap second's
correction must differ from the previous one's by 1
@@ -1171,7 +1184,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
? corr == prevcorr + 1
: (corr == prevcorr
|| corr == prevcorr - 1))))
- return EINVAL;
+ return EFTYPE;
prevtr = tr;
prevcorr = corr;
@@ -1193,7 +1206,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
ttisp->tt_ttisstd = false;
else {
if (*p != true && *p != false)
- return EINVAL;
+ return EFTYPE;
ttisp->tt_ttisstd = *p++;
}
}
@@ -1205,7 +1218,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
ttisp->tt_ttisut = false;
else {
if (*p != true && *p != false)
- return EINVAL;
+ return EFTYPE;
ttisp->tt_ttisut = *p++;
}
}
@@ -1290,7 +1303,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags,
}
}
if (sp->typecnt == 0)
- return EINVAL;
+ return EFTYPE;
return 0;
}
diff --git a/newctime.3 b/newctime.3
index a8779e6a7c25..f22e69f44535 100644
--- a/newctime.3
+++ b/newctime.3
@@ -5,34 +5,34 @@
asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
.SH SYNOPSIS
.nf
-.B #include <time.h>
+.B "#include <time.h>"
.PP
-.B struct tm *localtime(time_t const *clock);
+.B "struct tm *localtime(time_t const *clock);"
.B "struct tm *localtime_r(time_t const *restrict clock,"
.B " struct tm *restrict result);"
.B "struct tm *localtime_rz(timezone_t restrict zone,"
.B " time_t const *restrict clock,"
.B " struct tm *restrict result);"
.PP
-.B struct tm *gmtime(time_t const *clock);
+.B "struct tm *gmtime(time_t const *clock);"
.B "struct tm *gmtime_r(time_t const *restrict clock,"
.B " struct tm *restrict result);"
.PP
-.B time_t mktime(struct tm *tm);
+.B "time_t mktime(struct tm *tm);"
.B "time_t mktime_z(timezone_t restrict zone,"
.B " struct tm *restrict tm);"
.PP
-.B double difftime(time_t time1, time_t time0);
+.B "double difftime(time_t time1, time_t time0);"
.PP
-.B [[deprecated]] char *asctime(struct tm const *tm);
-.B [[deprecated]] char *ctime(time_t const *clock);
+.B "[[deprecated]] char *asctime(struct tm const *tm);"
+.B "[[deprecated]] char *ctime(time_t const *clock);"
.PP
/* Only in POSIX.1-2017 and earlier. */
-.B char *ctime_r(time_t const *clock, char *buf);
+.B "char *ctime_r(time_t const *clock, char *buf);"
.B "char *asctime_r(struct tm const *restrict tm,"
.B " char *restrict result);"
.PP
-.B cc ... \-ltz
+.BR cc " ... " \-ltz
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
@@ -240,7 +240,7 @@ are in the
.B <time.h>
header file.
The structure (of type)
-.B struct tm
+.B "struct tm"
includes the following fields:
.RS
.PP
diff --git a/newstrftime.3 b/newstrftime.3
index 182f44f738a3..7d23b14ace72 100644
--- a/newstrftime.3
+++ b/newstrftime.3
@@ -40,12 +40,12 @@
strftime \- format date and time
.SH SYNOPSIS
.nf
-.B #include <time.h>
+.B "#include <time.h>"
.PP
.B "size_t strftime(char *restrict buf, size_t maxsize,"
.B " char const *restrict format, struct tm const *restrict timeptr);"
.PP
-.B cc ... \-ltz
+.BR cc " ... " \-ltz
.fi
.SH DESCRIPTION
.ie '\(lq'' .ds lq \&"\"
diff --git a/newtzset.3 b/newtzset.3
index 77e76c9d6974..366bf3024808 100644
--- a/newtzset.3
+++ b/newtzset.3
@@ -5,23 +5,23 @@
tzset \- initialize time conversion information
.SH SYNOPSIS
.nf
-.B #include <time.h>
+.B "#include <time.h>"
.PP
.BI "timezone_t tzalloc(char const *" TZ );
.PP
.BI "void tzfree(timezone_t " tz );
.PP
-.B void tzset(void);
+.B "void tzset(void);"
.PP
/* Optional and obsolescent: */
.br
-.B extern char *tzname[];
+.B "extern char *tzname[];"
.br
-.B extern long timezone;
+.B "extern long timezone;"
.br
-.B extern int daylight;
+.B "extern int daylight;"
.PP
-.B cc ... \-ltz
+.BR cc " ... " \-ltz
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
@@ -161,7 +161,7 @@ seconds) \*(en if present \*(en between zero and 59. If preceded by a
.q "\-" ,
the time zone shall be east of the Prime Meridian; otherwise it shall be
west (which may be indicated by an optional preceding
-.q "+" .
+.q "+" ).
.TP
.I rule
Indicates when to change to and back from daylight saving time. The
@@ -420,7 +420,7 @@ callers can instead use the
and
.I tm_zone
members of
-.B struct tm,
+.BR "struct tm",
or use
.B strftime
with "%z" or "%Z".
diff --git a/newtzset.3.txt b/newtzset.3.txt
index 21c4ae3958d7..de4fc6fff2ba 100644
--- a/newtzset.3.txt
+++ b/newtzset.3.txt
@@ -87,7 +87,7 @@ DESCRIPTION
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 “+”.
+ optional preceding “+”).
rule Indicates when to change to and back from daylight saving
time. The rule has the form:
diff --git a/private.h b/private.h
index ee191b4ec33c..7918082dca1b 100644
--- a/private.h
+++ b/private.h
@@ -243,6 +243,9 @@ strnlen (char const *s, size_t maxlen)
# define EINVAL ERANGE
#endif
+#ifndef EFTYPE
+# define EFTYPE EINVAL
+#endif
#ifndef ELOOP
# define ELOOP EINVAL
#endif
@@ -951,6 +954,11 @@ ATTRIBUTE_POSIX2TIME time_t time2posix_z(timezone_t, time_t);
#define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(type) (TYPE_SIGNED (type) && (! ~ (type) -1))
+/* Yield the value of the arithmetic expression X after integer promotion.
+ This is safer than a cast, which in general would accept even
+ pointers and which might trap or yield a value not equal to X. */
+#define INT_PROMOTE(x) (+(x))
+
/* Minimum and maximum of two values. Use lower case to avoid
naming clashes with standard include files. */
#define max(a, b) ((a) > (b) ? (a) : (b))
@@ -1071,12 +1079,16 @@ time_t timeoff(struct tm *, long);
** The default is to use gettext if available, and use MSGID otherwise.
*/
-#if HAVE_GETTEXT
-# define _(msgid) gettext(msgid)
-#else /* !HAVE_GETTEXT */
-# define _(msgid) (msgid)
-#endif /* !HAVE_GETTEXT */
-#define N_(msgid) (msgid)
+#ifndef _
+# if HAVE_GETTEXT
+# define _(msgid) gettext(msgid)
+# else
+# define _(msgid) (msgid)
+# endif
+#endif
+#ifndef N_
+# define N_(msgid) (msgid)
+#endif
#if !defined TZ_DOMAIN && defined HAVE_GETTEXT
# define TZ_DOMAIN "tz"
diff --git a/theory.html b/theory.html
index 12f4f7f4b439..8f76aaf0b3c8 100644
--- a/theory.html
+++ b/theory.html
@@ -592,10 +592,10 @@ in decreasing order of importance:
locations while uninhabited.
The leading "<code>-</code>" is a flag that the <abbr>UT</abbr> offset is in
some sense undefined; this notation is derived
- from <a href="https://www.rfc-editor.org/rfc/rfc3339">Internet
+ from <a href="https://www.rfc-editor.org/info/rfc3339/">Internet
<abbr title="Request For Comments">RFC</abbr> 3339</a>.
(The abbreviation Z that
- <a href="https://www.rfc-editor.org/rfc/rfc9557">Internet
+ <a href="https://www.rfc-editor.org/info/rfc9557/">Internet
<abbr>RFC</abbr> 9557</a> uses for this concept
would violate the POSIX requirement
of at least three characters in an abbreviation.)
@@ -632,7 +632,7 @@ Errors in the <code><abbr>tz</abbr></code> database arise from many sources:
timestamps, and current predictions
will be incorrect after future governments change the rules.
For example, if today someone schedules a meeting for 13:00 next
- October 1, Casablanca time, and tomorrow Morocco changes its
+ October 1, Dublin time, and tomorrow Ireland changes its
daylight saving rules, software can mess up after the rule change
if it blithely relies on conversions made before the change.
</li>
@@ -657,7 +657,7 @@ Errors in the <code><abbr>tz</abbr></code> database arise from many sources:
zones at all, often not even a stable landscape of mean times,
prior to the middle decades of the twentieth century”.
See: Timothy Shenk, <a
-href="https://dissentmagazine.org/blog/booked-a-global-history-of-time-vanessa-ogle/">Booked:
+href="https://dissentmagazine.org/online_articles/booked-a-global-history-of-time-vanessa-ogle/">Booked:
A Global History of Time</a>. <cite>Dissent</cite> 2015-12-17.
</li>
<li>
@@ -938,7 +938,7 @@ with problems that were fixed in later POSIX editions.
has a format that is hard to describe and is error-prone in practice.
Also, proleptic <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
+ Palestine), or with situations where more than two time zone
abbreviations or <abbr>UT</abbr> offsets are used in an area.
</p>
@@ -1128,7 +1128,7 @@ However POSIX.1-2024, like earlier POSIX editions, has some limitations:
the name of a file from which time-related information is read.
The file’s format is <dfn><abbr>TZif</abbr></dfn>,
a timezone information format that contains binary data; see
- <a href="https://www.rfc-editor.org/rfc/rfc9636">Internet
+ <a href="https://www.rfc-editor.org/info/rfc9636/">Internet
<abbr>RFC</abbr> 9636</a>.
The daylight saving time rules to be used for a
particular timezone are encoded in the
@@ -1212,8 +1212,8 @@ The vestigial <abbr>API</abbr>s are:
</li>
<li>
The <code>tm_isdst</code> member is almost never needed and most of
- its uses should be discouraged in favor of the abovementioned
- <abbr>API</abbr>s.
+ its uses should be discouraged in favor of the
+ <abbr>API</abbr>s mentioned above.
It was intended as an index into the <code>tzname</code> variable,
but as mentioned previously that usage is obsolete.
Although it can still be used in arguments to
@@ -1558,7 +1558,7 @@ Sources for time on other planets:
Michael Allison and Robert Schmunk,
“<a href="https://www.giss.nasa.gov/tools/mars24/help/notes.html">Technical
Notes on Mars Solar Time as Adopted by the Mars24 Sunclock</a>”
- (2020-03-08).
+ (2023-05-15).
</li>
<li>
Zara Mirmalek,
diff --git a/time2posix.3 b/time2posix.3
index adbff83aa808..f76915b14456 100644
--- a/time2posix.3
+++ b/time2posix.3
@@ -5,13 +5,13 @@
time2posix, posix2time \- convert seconds since the Epoch
.SH SYNOPSIS
.nf
-.B #include <time.h>
+.B "#include <time.h>"
.PP
-.B time_t time2posix(time_t t);
+.B "time_t time2posix(time_t t);"
.PP
-.B time_t posix2time(time_t t);
+.B "time_t posix2time(time_t t);"
.PP
-.B cc ... \-ltz
+.BR cc " ... " \-ltz
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
diff --git a/tz-art.html b/tz-art.html
index 7870a6187390..77c7983c49fd 100644
--- a/tz-art.html
+++ b/tz-art.html
@@ -6,10 +6,10 @@
<style>
ul {padding-left: 1.3rem;}
</style>
-<title>Time and the Arts</title>
+<title>Time and the arts</title>
</head>
<body>
-<h1>Time and the Arts</h1>
+<h1>Time and the arts</h1>
<h2>Documentaries</h2>
<ul>
<li>
@@ -201,6 +201,13 @@ time zone associated with a video timestamp.
Jonah’s inability to understand <abbr>DST</abbr> ends up impressing a wealthy
backer who sets him up for a 2020 presidential run.
</li>
+<li>
+“In the Zone”, <em>The Magic School Bus Rides Again</em>, season 3,
+episode 3, Netflix, 2020-12-26.
+During a New Year’s Eve journey, a software update glitch causes the
+Magic School Bus and its passengers to transform every time they cross
+time zone boundaries. The visit to the South Pole is especially tricky.
+</li>
</ul>
<h2>Books, plays, and magazines</h2>
<ul>
@@ -481,20 +488,16 @@ The related book <em>What If?</em> has an entry
“<a href="https://what-if.xkcd.com/26/">Leap Seconds</a>” (2012-12-31).
</li>
<li>
-Pig kills time in <a
-href="https://www.gocomics.com/pearlsbeforeswine/2016/11/06"><em>Pearls
-Before Swine</em> (2016-11-06)</a>.
+Pig kills time in <em>Pearls Before Swine</em> (2016-11-06).
</li>
<li>
-Stonehenge is abandoned in <a
-href="https://www.gocomics.com/nonsequitur/2017/03/12"><em>Non Sequitur</em>
-(2017-03-12)</a>.
+Stonehenge is abandoned in <em>Non Sequitur</em> (2017-03-12).
</li>
<li>
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>
+<em>Frazz</em> (2023-12-31),
+while Peter and Jason go multi-lingual and -zonal in
+<a href="https://foxtrot.com/2023/12/31/toasty/"><em>FoxTrot</em>
(the same day)</a>.
</li>
<li>
@@ -503,9 +506,7 @@ Peppermint Patty: “What if the world comes to an end tonight, Marcie?”
Marcie: “I promise there’ll be a tomorrow, sir ... in fact,
it’s already tomorrow in Australia!”
<br>
-(Charles M. Schulz,
-<a href="https://www.gocomics.com/peanuts/1980/06/13"><em>Peanuts</em>,
-1980-06-13</a>)
+(Charles M. Schulz, <em>Peanuts</em>, 1980-06-13)
</li>
</ul>
<h2>Jokes</h2>
@@ -612,8 +613,7 @@ but maybe in your time zone I haven’t finished yet. So stay tuned!”
</ul>
<h2>See also</h2>
<ul>
-<li><a href="tz-link.html">Time Zone and Daylight Saving
-Time Data</a></li>
+<li><a href="tz-link.html">Time zone and daylight saving time data</a></li>
</ul>
<footer>
diff --git a/tz-how-to.html b/tz-how-to.html
index 9f4f2284c7b4..82a6176bef25 100644
--- a/tz-how-to.html
+++ b/tz-how-to.html
@@ -15,9 +15,9 @@ ul {padding-left: 1.3rem;}
</style>
</head>
<body>
-<h2>How to Read the <a href="https://en.wikipedia.org/wiki/Tz_database">tz
-Database</a> Source Files</h2>
-<h3>by Bill Seymour</h3>
+<h1>How to Read the <a href="https://en.wikipedia.org/wiki/Tz_database">tz
+Database</a> Source Files</h1>
+<h2>by Bill Seymour</h2>
<p>This guide uses the <code>America/Chicago</code> and
<code>Pacific/Honolulu</code> zones as examples of how to infer
times of day from the <a href="tz-link.html">tz database</a>
diff --git a/tz-link.html b/tz-link.html
index 4d2c12b09452..2a7da95e9d27 100644
--- a/tz-link.html
+++ b/tz-link.html
@@ -197,9 +197,9 @@ more errors.</p>
After obtaining the code and data files, see the
<code>README</code> file for what to do next.
The code lets you compile the <code><abbr>tz</abbr></code> source files into
-machine-readable binary files, one for each location. The binary files
-are in a special format specified by
-<a href="https://www.rfc-editor.org/rfc/rfc9636">The
+machine-readable binary files, one for each location.
+The binary file format is specified by
+<a href="https://www.rfc-editor.org/info/rfc9636/">The
Time Zone Information Format (<abbr>TZif</abbr>)</a>
(Internet <abbr title="Request For Comments">RFC</abbr> 9636).
The code also lets
@@ -268,7 +268,7 @@ Studio Code</a>.
</p>
<p>
For further information about updates, please see
-<a href="https://www.rfc-editor.org/rfc/rfc6557">Procedures for
+<a href="https://www.rfc-editor.org/info/rfc6557/">Procedures for
Maintaining the Time Zone Database</a> (Internet <abbr>RFC</abbr> 6557).
More detail can be
found in <a href="theory.html">Theory and pragmatics of the
@@ -336,7 +336,7 @@ the Timing of Time Zone Changes</a>” for examples.
an encyclopedic summary.</li>
<li><a href="tz-how-to.html">How to Read the
tz Database Source Files</a> explains the <code><abbr>tz</abbr></code>
-database format.</li>
+database source format.</li>
<li><a
href="https://blog.jonudell.net/2009/10/23/a-literary-appreciation-of-the-olsonzoneinfotz-database/">A
literary appreciation of the Olson/Zoneinfo/tz database</a> comments on the
@@ -385,24 +385,22 @@ calculates the current time difference between locations.</li>
<section>
<h2 id="protocols">Network protocols for <code><abbr>tz</abbr></code> data</h2>
<ul>
-<li><a href="https://www.rfc-editor.org/rfc/rfc7808">Time Zone
-Data Distribution Service</a> (TZDIST, Internet <abbr>RFC</abbr> 7808)
+<li><a href="https://www.rfc-editor.org/info/rfc7808/">Time Zone
+Data Distribution Service</a> (Internet <abbr>RFC</abbr> 7808)
is associated with
-<a href="https://www.rfc-editor.org/rfc/rfc7809">CalDAV</a>
+<a href="https://www.rfc-editor.org/info/rfc7809/">CalDAV</a>
(Internet <abbr>RFC</abbr> 7809), a calendar access protocol for
-transferring time zone data by reference.
-<a href="https://devguide.calconnect.org/Time-Zones/TZDS/">TZDIST
-implementations</a> are available.</li>
-<li>The <a href="https://www.rfc-editor.org/rfc/rfc5545">iCalendar format</a>
-(Internet <abbr>RFC</abbr> 5445)
+transferring time zone data by reference.</li>
+<li>The <a href="https://www.rfc-editor.org/info/rfc5545/">iCalendar format</a>
+(Internet <abbr>RFC</abbr> 5545)
covers time zone
data; see its VTIMEZONE calendar component.
The iCalendar format requires specialized parsers and generators; a
-variant <a href="https://www.rfc-editor.org/rfc/rfc6321">xCal</a>
+variant <a href="https://www.rfc-editor.org/info/rfc6321/">xCal</a>
(Internet <abbr>RFC</abbr> 6321) uses
<a href="https://www.w3.org/XML/"><abbr
title="Extensible Markup Language">XML</abbr></a> format, and a variant
-<a href="https://www.rfc-editor.org/rfc/rfc7265">jCal</a>
+<a href="https://www.rfc-editor.org/info/rfc7265/">jCal</a>
(Internet <abbr>RFC</abbr> 7265)
uses <a href="https://www.json.org/json-en.html"><abbr
title="JavaScript Object Notation">JSON</abbr></a> format.</li>
@@ -417,7 +415,8 @@ distributions you can generally work around compatibility problems by
running the command <code>make rearguard_tarballs</code> and compiling
from the resulting tarballs instead.</p>
<ul>
-<li><a href="https://github.com/libical/vzic">Vzic</a> is a <a
+<li><a href="https://github.com/libical">Libical</a>
+contains <code>vzic</code>, <a
href="https://en.wikipedia.org/wiki/C_(programming_language)">C</a>
program that compiles
<code><abbr>tz</abbr></code> source into iCalendar-compatible VTIMEZONE files.
@@ -596,8 +595,7 @@ license.</li>
<li>The <a
href="https://www.gnu.org/software/libc/"><abbr>GNU</abbr> C
Library</a>
-has an independent, thread-safe implementation of
-a <abbr>TZif</abbr> file reader.
+has a thread-safe <abbr>TZif</abbr> file reader.
This library is freely available under the LGPL
and is widely used in <abbr>GNU</abbr>/Linux systems.</li>
<li><a href="https://www.gnome.org">GNOME</a>’s
@@ -725,8 +723,6 @@ sources for much of the older <code><abbr>tz</abbr></code> data</a>,
they are unreliable as Shanks appears to have
guessed many <abbr>UT</abbr> offsets and transitions. The atlases cite no
sources and do not indicate which entries are guesswork.</li>
-<li><a href="https://en.wikipedia.org/wiki/HP-UX">HP-UX</a> has a database in
-its own <code>tztab</code>(4) format.</li>
<li>Microsoft Windows has proprietary data mentioned
<a href="#System.TimeZoneInfo">above</a>.</li>
<li><a href="https://www.worldtimeserver.com">World Time Server</a>
@@ -1046,7 +1042,7 @@ title="Institute of Electrical and Electronics Engineers">IEEE</abbr> 1588)
can achieve submicrosecond clock accuracy on a local area network
with special-purpose hardware.</li>
<li><a
-href="https://www.rfc-editor.org/rfc/rfc4833">Timezone
+href="https://www.rfc-editor.org/info/rfc4833/">Timezone
Options for <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr></a>
(Internet <abbr>RFC</abbr> 4833)
specifies a <a
@@ -1125,11 +1121,11 @@ half second, even though every <abbr>POSIX</abbr> minute has exactly
sixty seconds. This approach works with the default <code><abbr>tz</abbr></code>
<code>"posix"</code> configuration, is <a
href="https://gitlab.com/NTPsec/ntpsec/-/blob/master/docs/leapsmear.adoc">supported</a>
-by the abovementioned <abbr>NTP</abbr> implementations, <a
+by the <abbr>NTP</abbr> implementations mentioned above, <a
href="https://github.com/google/unsmear">supports</a> conversion between
<abbr>UTC</abbr> and smeared <abbr>POSIX</abbr> timestamps, and is used by major
cloud service providers. However, according to
-<a href="https://www.rfc-editor.org/rfc/rfc8633#section-3.7.1">§3.7.1 of
+<a href="https://www.rfc-editor.org/info/rfc8633#section-3.7.1">§3.7.1 of
Network Time Protocol Best Current Practices</a>
(Internet <abbr>RFC</abbr> 8633), leap smearing is not suitable for
applications requiring accurate <abbr>UTC</abbr> or civil time,
@@ -1200,16 +1196,16 @@ interchange – Part 1: Basic rules</em></a>.</li>
<a href="https://www.w3.org/TR/xmlschema/#dateTime"><abbr>XML</abbr>
Schema: Datatypes – dateTime</a> specifies a format inspired by
<abbr>ISO</abbr> 8601 that is in common use in <abbr>XML</abbr> data.</li>
-<li><a href="https://www.rfc-editor.org/rfc/rfc5322#section-3.3">§3.3 of
+<li><a href="https://www.rfc-editor.org/info/rfc5322#section-3.3">§3.3 of
Internet Message Format</a> (Internet <abbr>RFC</abbr> 5322)
specifies the time notation used in email and <a
href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"><abbr>HTTP</abbr></a>
headers.</li>
<li>
-<a href="https://www.rfc-editor.org/rfc/rfc3339">Date and Time
+<a href="https://www.rfc-editor.org/info/rfc3339/">Date and Time
on the Internet: Timestamps</a> (Internet <abbr>RFC</abbr> 3339)
specifies an <abbr>ISO</abbr> 8601 profile for use in new Internet protocols.
-An extension, <a href="https://www.rfc-editor.org/rfc/rfc9557">Date
+An extension, <a href="https://www.rfc-editor.org/info/rfc9557/">Date
and Time on the Internet: Timestamps with Additional Information</a>
(Internet <abbr>RFC</abbr> 9557) extends this profile
to let you specify the <code><abbr>tzdb</abbr></code> timezone of a timestamp
@@ -1254,7 +1250,7 @@ is called “<abbr>GMT</abbr>”.</li>
<ul>
<li><a href="theory.html">Theory and pragmatics of the
<code><abbr>tz</abbr></code> code and data</a></li>
-<li><a href="tz-art.html">Time and the Arts</a></li>
+<li><a href="tz-art.html">Time and the arts</a></li>
</ul>
</section>
diff --git a/tzfile.5.txt b/tzfile.5.txt
index 00c2e62d398d..d812b0ea6003 100644
--- a/tzfile.5.txt
+++ b/tzfile.5.txt
@@ -402,6 +402,7 @@ 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). October 2024. Internet RFC 9636 doi:10.17487/RFC9636.
+ (TZif). October 2024. Internet RFC 9636 ⟨https://www.rfc-editor.org/
+ rfc/rfc9636⟩ doi:10.17487/RFC9636 ⟨https://doi.org/10.17487/RFC9636⟩.
Time Zone Database tzfile(5)
diff --git a/version b/version
index 75d34ee38931..9217a2da674e 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-2026b
+2026c
diff --git a/zdump.c b/zdump.c
index c7812eae9d2b..54ef63c0dd29 100644
--- a/zdump.c
+++ b/zdump.c
@@ -212,16 +212,6 @@ localtime_rz(ATTRIBUTE_MAYBE_UNUSED timezone_t rz, time_t *tp, struct tm *tmp)
return localtime_r(tp, tmp);
}
-# ifdef TYPECHECK
-# undef mktime_z
-# define mktime_z zdump_mktime_z
-static time_t
-mktime_z(timezone_t tz, struct tm *tmp)
-{
- return mktime(tmp);
-}
-# endif
-
# undef tzalloc
# undef tzfree
# define tzalloc zdump_tzalloc
@@ -330,42 +320,6 @@ my_gmtime_r(time_t *tp, struct tm *tmp)
return USE_LOCALTIME_RZ ? localtime_rz(gmtz, tp, tmp) : gmtime_r(tp, tmp);
}
-#ifndef TYPECHECK
-# define my_localtime_rz localtime_rz
-#else /* !defined TYPECHECK */
-
-static struct tm *
-my_localtime_rz(timezone_t tz, time_t *tp, struct tm *tmp)
-{
- tmp = localtime_rz(tz, tp, tmp);
- if (tmp) {
- struct tm tm;
- register time_t t;
-
- tm = *tmp;
- t = mktime_z(tz, &tm);
- if (t != *tp) {
- fflush(stdout);
- fprintf(stderr, "\n%s: ", progname);
- fprintf(stderr, tformat(), *tp);
- fprintf(stderr, " ->");
- fprintf(stderr, " year=%d", tmp->tm_year);
- fprintf(stderr, " mon=%d", tmp->tm_mon);
- fprintf(stderr, " mday=%d", tmp->tm_mday);
- fprintf(stderr, " hour=%d", tmp->tm_hour);
- fprintf(stderr, " min=%d", tmp->tm_min);
- fprintf(stderr, " sec=%d", tmp->tm_sec);
- fprintf(stderr, " isdst=%d", tmp->tm_isdst);
- fprintf(stderr, " -> ");
- fprintf(stderr, tformat(), t);
- fprintf(stderr, "\n");
- errout = true;
- }
- }
- return tmp;
-}
-#endif /* !defined TYPECHECK */
-
static void
abbrok(const char *const abbrp, const char *const zone)
{
@@ -600,16 +554,16 @@ main(int argc, char *argv[])
t = absolute_min_time;
if (! (iflag | Vflag)) {
show(tz, argv[i], t, true);
- if (my_localtime_rz(tz, &t, &tm) == NULL
+ if (localtime_rz(tz, &t, &tm) == NULL
&& t < cutlotime) {
time_t newt = cutlotime;
- if (my_localtime_rz(tz, &newt, &newtm) != NULL)
+ if (localtime_rz(tz, &newt, &newtm) != NULL)
showextrema(tz, argv[i], t, NULL, newt);
}
}
if (t + 1 < cutlotime)
t = cutlotime - 1;
- tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
+ tm_ok = localtime_rz(tz, &t, &tm) != NULL;
if (tm_ok) {
ab = saveabbr(&abbrev, &abbrevsize, &tm);
if (iflag) {
@@ -651,9 +605,9 @@ main(int argc, char *argv[])
time_t newt = absolute_max_time;
t = cuthitime;
if (t < newt) {
- struct tm *tmp = my_localtime_rz(tz, &t, &tm);
+ struct tm *tmp = localtime_rz(tz, &t, &tm);
if (tmp != NULL
- && my_localtime_rz(tz, &newt, &newtm) == NULL)
+ && localtime_rz(tz, &newt, &newtm) == NULL)
showextrema(tz, argv[i], t, tmp, newt);
}
show(tz, argv[i], absolute_max_time, true);
@@ -728,7 +682,7 @@ hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok)
caller already did that. On platforms without TM_ZONE,
tzname may have been altered since our caller broke down
LOT, and tzname needs to be changed back. */
- bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
+ bool lotm_ok = localtime_rz(tz, &lot, &lotm) != NULL;
bool tm_ok;
char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL;
@@ -739,7 +693,7 @@ hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok)
time_t t = (rem_sum == 2) - (rem_sum < 0) + lot / 2 + hit / 2;
if (t == lot)
break;
- tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
+ tm_ok = localtime_rz(tz, &t, &tm) != NULL;
if (lotm_ok == tm_ok
&& (only_ok
|| (ab && tm.tm_isdst == lotm.tm_isdst
@@ -847,7 +801,7 @@ show(timezone_t tz, char *zone, time_t t, bool v)
}
printf(" = ");
}
- tmp = my_localtime_rz(tz, &t, &tm);
+ tmp = localtime_rz(tz, &t, &tm);
if (tmp == NULL) {
printf(tformat(), t);
printf(_(" (localtime failed)"));
@@ -884,7 +838,7 @@ showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
: hi + (hi < TIME_T_MAX));
if (SECSPERDAY < boundary - lo) {
lo = boundary - SECSPERDAY;
- lotmp = my_localtime_rz(tz, &lo, &localtm[old]);
+ lotmp = localtime_rz(tz, &lo, &localtm[old]);
}
if (lotmp)
localtm[old] = *lotmp;
@@ -899,7 +853,7 @@ showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
worth the trouble. */
for (t = lo + 1; t < hi; t++) {
bool new = !old;
- if (! my_localtime_rz(tz, &t, &localtm[new]))
+ if (! localtime_rz(tz, &t, &localtm[new]))
localtm[new].tm_sec = -1;
if (! my_gmtime_r(&t, &gmtm[new]))
gmtm[new].tm_sec = -1;
diff --git a/zic.8 b/zic.8
index 05eea105e436..3e32e85c4796 100644
--- a/zic.8
+++ b/zic.8
@@ -83,6 +83,7 @@ For example, for a zone named America/Los_Angeles
the directory America should already exist.
By default, the directory and its ancestors are created
if they do not already exist.
+.TP
.BI "\-d " directory
Create time conversion information files in the named directory rather than
in the standard directory named below.
@@ -123,7 +124,8 @@ is
(the default), any already-existing link is removed.
.sp
Unless
-.I timezone is
+.I timezone
+is
.q "\-" ,
this option is obsolete and is no longer supported by most runtimes.
Among other things it should not be used for timestamps after the year 2037,
@@ -274,8 +276,8 @@ 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
-a proleptic TZ string. For example, as of 2023 this problem
-occurs for Morocco's daylight-saving rules, as these rules are based
+a proleptic TZ string. For example, as of 2026 this problem
+occurs for Palestine's daylight-saving rules, as these rules are based
on predictions for when Ramadan will be observed, something that
a proleptic TZ string cannot represent.
.PP
diff --git a/zic.8.txt b/zic.8.txt
index 426978307e88..77b5c62f6878 100644
--- a/zic.8.txt
+++ b/zic.8.txt
@@ -31,9 +31,11 @@ OPTIONS
-D Do not create ancestor directories of output files, For example,
for a zone named America/Los_Angeles the directory America
should already exist. By default, the directory and its
- ancestors are created if they do not already exist. -d
- directory Create time conversion information files in the named
- directory rather than in the standard directory named below.
+ ancestors are created if they do not already exist.
+
+ -d 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
@@ -148,8 +150,8 @@ OPTIONS
The output file does not contain all the information about the
long-term future of a timezone, because the future cannot be
- summarized as a proleptic TZ string. For example, as of 2023
- this problem occurs for Morocco's daylight-saving rules, as
+ summarized as a proleptic TZ string. For example, as of 2026
+ this problem occurs for Palestine's daylight-saving rules, as
these rules are based on predictions for when Ramadan will be
observed, something that a proleptic TZ string cannot represent.
@@ -184,12 +186,13 @@ FILES
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) and
- the encoding's non-unibyte characters should consist entirely of non-
- PPCS bytes. Non-PPCS characters typically occur only in comments:
- although output file names and time zone abbreviations can contain
- nearly any character, other software will work better if these are
- limited to the restricted syntax described under the -v option.
+ unibyte representation for the POSIX Portable Character Set (PPCS)
+ ⟨https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap06
+ .html⟩ and the encoding's non-unibyte characters should consist
+ entirely of non-PPCS bytes. Non-PPCS characters typically occur only
+ in comments: although output file names and time zone abbreviations can
+ contain nearly any character, other software will work better if these
+ are limited to the restricted syntax described under the -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
diff --git a/zic.c b/zic.c
index 7ecf3b9115f3..d5e521d6b664 100644
--- a/zic.c
+++ b/zic.c
@@ -262,7 +262,6 @@ static void checkabbr(char const *);
static void check_for_signal(void);
static void dolink(const char *, const char *, bool);
static int getfields(char *, char **, int);
-static zic_t gethms(const char * string, const char * errstring);
static zic_t getsave(char *, bool *);
static void inexpires(char **, int);
static void infile(int, char const *);
@@ -280,6 +279,7 @@ static void mkdirs(char const *, bool);
static zic_t oadd(zic_t t1, zic_t t2);
static zic_t omul(zic_t, zic_t);
static void outzone(const struct zone * zp, ptrdiff_t ntzones);
+static void remove_temp(char const *);
static zic_t rpytime(const struct rule * rp, zic_t wantedy);
static bool rulesub(struct rule * rp,
const char * loyearp, const char * hiyearp,
@@ -413,10 +413,10 @@ enum {
};
/* The maximum number of fields on any of the above lines.
- (The "+"s pacify gcc -Wenum-compare.) */
+ (This uses INT_PROMOTE to pacify gcc -Wenum-compare.) */
enum {
- MAX_FIELDS = max(max(+RULE_FIELDS, +LINK_FIELDS),
- max(+LEAP_FIELDS, +EXPIRES_FIELDS))
+ MAX_FIELDS = max(max(INT_PROMOTE(RULE_FIELDS), INT_PROMOTE(LINK_FIELDS)),
+ max(INT_PROMOTE(LEAP_FIELDS), INT_PROMOTE(EXPIRES_FIELDS)))
};
/*
@@ -704,7 +704,7 @@ eat(int fnum, lineno num)
}
ATTRIBUTE_FORMAT((printf, 1, 0)) static void
-verror(const char *const string, va_list args)
+verror(char const *msgid, va_list args)
{
check_for_signal();
/*
@@ -715,7 +715,7 @@ verror(const char *const string, va_list args)
if (filenum)
fprintf(stderr, _("\"%s\", line %"PRIdMAX": "),
filename(filenum), linenum);
- vfprintf(stderr, string, args);
+ vfprintf(stderr, _(msgid), args);
if (rfilenum)
fprintf(stderr, _(" (rule from \"%s\", line %"PRIdMAX")"),
filename(rfilenum), rlinenum);
@@ -723,22 +723,22 @@ verror(const char *const string, va_list args)
}
ATTRIBUTE_FORMAT((printf, 1, 2)) static void
-error(const char *const string, ...)
+error(char const *msgid, ...)
{
va_list args;
- va_start(args, string);
- verror(string, args);
+ va_start(args, msgid);
+ verror(msgid, args);
va_end(args);
errors = true;
}
ATTRIBUTE_FORMAT((printf, 1, 2)) static void
-warning(const char *const string, ...)
+warning(char const *msgid, ...)
{
va_list args;
fprintf(stderr, _("warning: "));
- va_start(args, string);
- verror(string, args);
+ va_start(args, msgid);
+ verror(msgid, args);
va_end(args);
warnings = true;
}
@@ -841,7 +841,7 @@ close_file(FILE *stream, char const *dir, char const *name,
name ? name : "", name ? ": " : "",
e);
if (tempname)
- remove(tempname);
+ remove_temp(tempname);
exit(EXIT_FAILURE);
}
}
@@ -1039,7 +1039,7 @@ make_links(void)
/* If this pass examined all its links, start the next pass. */
if (i == j) {
if (nalinks - i == pass_size) {
- error(_("\"Link %s %s\" is part of a link cycle"),
+ error(N_("\"Link %s %s\" is part of a link cycle"),
links[i].l_target, links[i].l_linkname);
break;
}
@@ -1050,7 +1050,7 @@ make_links(void)
/* Diagnose self links, which the cycle detection algorithm would not
otherwise catch. */
if (strcmp(links[i].l_target, links[i].l_linkname) == 0) {
- error(_("link %s targets itself"), links[i].l_target);
+ error(N_("link %s targets itself"), links[i].l_target);
continue;
}
@@ -1070,11 +1070,11 @@ make_links(void)
if (noise && i < nlinks) {
if (l)
- warning(_("link %s targeting link %s mishandled by pre-2023 zic"),
+ warning(N_("link %s targeting link %s mishandled by pre-2023 zic"),
links[i].l_linkname, links[i].l_target);
else if (bsearch(links[i].l_target, links, nlinks, sizeof *links,
bsearch_linkcmp))
- warning(_("link %s targeting link %s"),
+ warning(N_("link %s targeting link %s"),
links[i].l_linkname, links[i].l_target);
}
check_for_signal();
@@ -1264,14 +1264,14 @@ main(int argc, char **argv)
case 'b':
if (strcmp(optarg, "slim") == 0) {
if (0 < bloat)
- error(_("incompatible -b options"));
+ error(N_("incompatible -b options"));
bloat = -1;
} else if (strcmp(optarg, "fat") == 0) {
if (bloat < 0)
- error(_("incompatible -b options"));
+ error(N_("incompatible -b options"));
bloat = 1;
} else
- error(_("invalid option: -b '%s'"), optarg);
+ error(N_("invalid option: -b '%s'"), optarg);
break;
case 'd':
if (directory)
@@ -1300,8 +1300,8 @@ main(int argc, char **argv)
if (psxrules)
duplicate_options("-p");
if (strcmp(optarg, "-") != 0)
- warning(_("-p is obsolete"
- " and likely ineffective"));
+ warning(N_("-p is obsolete"
+ " and likely ineffective"));
psxrules = optarg;
break;
case 't':
@@ -1320,7 +1320,7 @@ main(int argc, char **argv)
}
break;
case 'y':
- warning(_("-y ignored"));
+ warning(N_("-y ignored"));
break;
case 'L':
if (leapsec)
@@ -1349,7 +1349,7 @@ main(int argc, char **argv)
}
break;
case 's':
- warning(_("-s ignored"));
+ warning(N_("-s ignored"));
break;
}
if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
@@ -1419,30 +1419,30 @@ componentcheck(char const *name, char const *component,
ptrdiff_t component_len = component_end - component;
if (component_len == 0) {
if (!*name)
- error(_("empty file name"));
+ error(N_("empty file name"));
else
- error(_(component == name
- ? "file name '%s' begins with '/'"
- : *component_end
- ? "file name '%s' contains '//'"
- : "file name '%s' ends with '/'"),
- name);
+ error((component == name
+ ? N_("file name '%s' begins with '/'")
+ : *component_end
+ ? N_("file name '%s' contains '//'")
+ : N_("file name '%s' ends with '/'")),
+ name);
return false;
}
if (0 < component_len && component_len <= 2
&& component[0] == '.' && component_end[-1] == '.') {
int len = component_len;
- error(_("file name '%s' contains '%.*s' component"),
+ error(N_("file name '%s' contains '%.*s' component"),
name, len, component);
return false;
}
if (noise) {
if (0 < component_len && component[0] == '-')
- warning(_("file name '%s' component contains leading '-'"),
+ warning(N_("file name '%s' component contains leading '-'"),
name);
if (component_len_max < component_len)
- warning(_("file name '%s' contains overlength component"
- " '%.*s...'"),
+ warning(N_("file name '%s' contains overlength component"
+ " '%.*s...'"),
name, component_len_max, component);
}
return true;
@@ -1469,8 +1469,8 @@ namecheck(const char *name)
unsigned char c = *cp;
if (noise && !strchr(benign, c)) {
warning((strchr(printable_and_not_benign, c)
- ? _("file name '%s' contains byte '%c'")
- : _("file name '%s' contains byte '\\%o'")),
+ ? N_("file name '%s' contains byte '%c'")
+ : N_("file name '%s' contains byte '\\%o'")),
name, c);
}
if (c == '/') {
@@ -1658,16 +1658,25 @@ rename_dest(char *tempname, char const *name)
if (tempname) {
if (rename(tempname, name) != 0) {
int rename_errno = errno;
- remove(tempname);
fprintf(stderr, _("%s: rename to %s%s%s: %s\n"),
progname, diagdir(name), diagslash(name), name,
strerror(rename_errno));
+ remove_temp(tempname);
exit(EXIT_FAILURE);
}
free(tempname);
}
}
+/* Remove the temporary file TEMP, diagnosing any failure. */
+static void
+remove_temp(char const *temp)
+{
+ if (remove(temp) < 0)
+ fprintf(stderr, _("%s: Can't remove temporary file %s%s%s: %s\n"),
+ progname, diagdir(temp), diagslash(temp), temp, strerror(errno));
+}
+
/* 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
@@ -1818,7 +1827,7 @@ dolink(char const *target, char const *linkname, bool staysymlink)
free(linkalloc);
if (symlink_errno == 0) {
if (link_errno != ENOTSUP && link_errno != EEXIST)
- warning(_("symbolic link used because hard link failed: %s"),
+ warning(N_("symbolic link used because hard link failed: %s"),
strerror(link_errno));
} else {
FILE *fp, *tp;
@@ -1836,12 +1845,12 @@ dolink(char const *target, char const *linkname, bool staysymlink)
close_file(tp, directory, linkname, tempname);
close_file(fp, directory, target, NULL);
if (link_errno != ENOTSUP)
- warning(_("copy used because hard link failed: %s"),
+ warning(N_("copy used because hard link failed: %s"),
strerror(link_errno));
else if (symlink_errno < 0)
- warning(_("copy used because symbolic link not obvious"));
+ warning(N_("copy used because symbolic link not obvious"));
else if (symlink_errno != ENOTSUP)
- warning(_("copy used because symbolic link failed: %s"),
+ warning(N_("copy used because symbolic link failed: %s"),
strerror(symlink_errno));
}
}
@@ -1892,9 +1901,9 @@ associate(void)
if (rules[i].r_filenum == rules[i + 1].r_filenum)
continue;
eat(rules[i].r_filenum, rules[i].r_linenum);
- warning(_("same rule name in multiple files"));
+ warning(N_("same rule name in multiple files"));
eat(rules[i + 1].r_filenum, rules[i + 1].r_linenum);
- warning(_("same rule name in multiple files"));
+ warning(N_("same rule name in multiple files"));
for (j = i + 2; j < nrules; ++j) {
if (strcmp(rules[i].r_name,
rules[j].r_name) != 0)
@@ -1940,7 +1949,7 @@ associate(void)
** a '%s' in the format is a bad thing.
*/
if (zp->z_format_specifier == 's')
- error("%s", _("%s in ruleless zone"));
+ error(N_("%%s in ruleless zone"));
}
}
if (errors)
@@ -1958,21 +1967,21 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
for (; (ch = getc(fp)) != '\n'; check_for_signal()) {
if (ch < 0) {
if (ferror(fp)) {
- error(_("input error"));
+ error(N_("input error"));
exit(EXIT_FAILURE);
}
if (linelen == 0)
return false;
- error(_("unterminated line"));
+ error(N_("unterminated line"));
exit(EXIT_FAILURE);
}
if (!ch) {
- error(_("NUL input byte"));
+ error(N_("NUL input byte"));
exit(EXIT_FAILURE);
}
buf[linelen++] = ch;
if (linelen == bufsize) {
- error(_("line too long"));
+ error(N_("line too long"));
exit(EXIT_FAILURE);
}
}
@@ -2018,7 +2027,7 @@ infile(int fnum, char const *name)
= fnum < 0 ? leap_line_codes : zi_line_codes;
lp = byword(fields[0], line_codes);
if (lp == NULL)
- error(_("input line of unknown type"));
+ error(N_("input line of unknown type"));
else switch (lp->l_value) {
case LC_RULE:
inrule(fields, nfields);
@@ -2046,7 +2055,7 @@ infile(int fnum, char const *name)
}
close_file(fp, NULL, filename(fnum), NULL);
if (wantcont)
- error(_("expected continuation line not found"));
+ error(N_("expected continuation line not found"));
}
/*
@@ -2054,13 +2063,13 @@ infile(int fnum, char const *name)
** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
** into a number of seconds.
** A null string maps to zero.
-** Call error with errstring and return zero on errors.
+** Call error with msgid and return zero on errors.
*/
static zic_t
-gethms(char const *string, char const *errstring)
+gethms(char const *string, char const *msgid)
{
- zic_t hh;
+ zic_t hh, r;
int sign, mm = 0, ss = 0;
char hhx, mmx, ssx, xr = '0', xs;
int tenths = 0;
@@ -2082,29 +2091,37 @@ gethms(char const *string, char const *errstring)
case 7:
ok &= ssx == '.';
if (ok && noise)
- warning(_("fractional seconds rejected by"
- " pre-2018 versions of zic"));
+ warning(N_("fractional seconds rejected by"
+ " pre-2018 versions of zic"));
ATTRIBUTE_FALLTHROUGH;
case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH;
case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH;
case 1: break;
}
- if (!ok) {
- error("%s", errstring);
- return 0;
- }
- if (hh < 0 ||
+ if (!ok || hh < 0 ||
mm < 0 || mm >= MINSPERHOUR ||
ss < 0 || ss > SECSPERMIN) {
- error("%s", errstring);
+ error(N_("%s: %s"), string, _(msgid));
return 0;
}
ss += 5 + ((ss ^ 1) & (xr == '0')) <= tenths; /* Round to even. */
if (noise && (hh > HOURSPERDAY ||
(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
-warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
- return oadd(omul(hh, sign * SECSPERHOUR),
- sign * (mm * SECSPERMIN + ss));
+ warning(N_("values over 24 hours"
+ " not handled by pre-2007 versions of zic"));
+ r = oadd(omul(hh, sign * SECSPERHOUR), sign * (mm * SECSPERMIN + ss));
+
+ /* Check that 4 * R fits in zic_t. This is more than enough
+ for times of day and for UT offsets in TZif files, and it
+ avoids later problems during arithmetic on these numbers,
+ as stringzone can compute up to 3 times one of these values
+ when computing fake timezones, and stringrule can do a bit more.
+ Do not limit R strictly to 32 bits here, as it is OK to go
+ over the TZif limit temporarily so long as the result fits. */
+ omul(r, 4);
+ /* Discard omul's return value, as it is not needed here. */
+
+ return r;
}
static zic_t
@@ -2120,7 +2137,7 @@ getsave(char *field, bool *isdst)
case 's': dst = 0; *ep = '\0'; break;
}
}
- save = gethms(field, _("invalid saved time"));
+ save = gethms(field, N_("invalid saved time"));
*isdst = dst < 0 ? save != 0 : dst;
return save;
}
@@ -2131,7 +2148,7 @@ inrule(char **fields, int nfields)
struct rule r;
if (nfields != RULE_FIELDS) {
- error(_("wrong number of fields on Rule line"));
+ error(N_("wrong number of fields on Rule line"));
return;
}
switch (*fields[RF_NAME]) {
@@ -2140,7 +2157,7 @@ inrule(char **fields, int nfields)
case '+': case '-':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- error(_("Invalid rule name \"%s\""), fields[RF_NAME]);
+ error(N_("Invalid rule name \"%s\""), fields[RF_NAME]);
return;
}
r.r_filenum = filenum;
@@ -2164,23 +2181,23 @@ inzone(char **fields, int nfields)
register ptrdiff_t i;
if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
- error(_("wrong number of fields on Zone line"));
+ error(N_("wrong number of fields on Zone line"));
return false;
}
if (lcltime != NULL && strcmp(fields[ZF_NAME], tzdefault) == 0) {
- error(_("\"Zone %s\" line and -l option are mutually exclusive"),
+ error(N_("\"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"),
+ error(N_("\"Zone %s\" line and -p option are mutually exclusive"),
TZDEFRULES);
return false;
}
for (i = 0; i < nzones; ++i)
if (zones[i].z_name != NULL &&
strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
- error(_("duplicate zone name %s"
+ error(N_("duplicate zone name %s"
" (file \"%s\", line %"PRIdMAX")"),
fields[ZF_NAME],
filename(zones[i].z_filenum),
@@ -2194,7 +2211,7 @@ static bool
inzcont(char **fields, int nfields)
{
if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
- error(_("wrong number of fields on Zone continuation line"));
+ error(N_("wrong number of fields on Zone continuation line"));
return false;
}
return inzsub(fields, nfields, true);
@@ -2233,12 +2250,12 @@ inzsub(char **fields, int nfields, bool iscont)
}
z.z_filenum = filenum;
z.z_linenum = linenum;
- z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
+ z.z_stdoff = gethms(fields[i_stdoff], N_("invalid UT offset"));
cp = strchr(fields[i_format], '%');
if (cp) {
if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/')) {
- error(_("invalid abbreviation format"));
+ error(N_("invalid abbreviation format"));
return false;
}
}
@@ -2264,8 +2281,8 @@ inzsub(char **fields, int nfields, bool iscont)
z.z_untilrule.r_loyear);
if (iscont && nzones > 0 &&
zones[nzones - 1].z_untiltime >= z.z_untiltime) {
- error(_("Zone continuation line end time is"
- " not after end time of previous line"));
+ error(N_("Zone continuation line end time is"
+ " not after end time of previous line"));
return false;
}
}
@@ -2275,7 +2292,7 @@ inzsub(char **fields, int nfields, bool iscont)
if (z.z_format_specifier == 'z') {
cp1[cp - fields[i_format]] = 's';
if (noise)
- warning(_("format '%s' not handled by pre-2015 versions of zic"),
+ warning(N_("format '%s' not handled by pre-2015 versions of zic"),
fields[i_format]);
}
zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
@@ -2305,7 +2322,7 @@ getleapdatetime(char **fields, bool expire_line)
/*
** Leapin' Lizards!
*/
- error(_("invalid leaping year"));
+ error(N_("invalid leaping year"));
return -1;
}
if (!expire_line) {
@@ -2327,7 +2344,7 @@ getleapdatetime(char **fields, bool expire_line)
dayoff = oadd(dayoff, i);
}
if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
- error(_("invalid month name"));
+ error(N_("invalid month name"));
return -1;
}
month = lp->l_value;
@@ -2340,15 +2357,15 @@ getleapdatetime(char **fields, bool expire_line)
cp = fields[LP_DAY];
if (sscanf(cp, "%d%c", &day, &xs) != 1 ||
day <= 0 || day > len_months[isleap(year)][month]) {
- error(_("invalid day of month"));
+ error(N_("invalid day of month"));
return -1;
}
dayoff = oadd(dayoff, day - 1);
t = omul(dayoff, SECSPERDAY);
- tod = gethms(fields[LP_TIME], _("invalid time of day"));
+ tod = gethms(fields[LP_TIME], N_("invalid time of day"));
t = tadd(t, tod);
if (t < 0)
- error(_("leap second precedes Epoch"));
+ error(N_("leap second precedes Epoch"));
return t;
}
@@ -2356,13 +2373,13 @@ static void
inleap(char **fields, int nfields)
{
if (nfields != LEAP_FIELDS)
- error(_("wrong number of fields on Leap line"));
+ error(N_("wrong number of fields on Leap line"));
else {
zic_t t = getleapdatetime(fields, false);
if (0 <= t) {
struct lookup const *lp = byword(fields[LP_ROLL], leap_types);
if (!lp)
- error(_("invalid Rolling/Stationary field on Leap line"));
+ error(N_("invalid Rolling/Stationary field on Leap line"));
else {
int correction = 0;
if (!fields[LP_CORR][0]) /* infile() turns "-" into "". */
@@ -2370,7 +2387,7 @@ inleap(char **fields, int nfields)
else if (strcmp(fields[LP_CORR], "+") == 0)
correction = 1;
else
- error(_("invalid CORRECTION field on Leap line"));
+ error(N_("invalid CORRECTION field on Leap line"));
if (correction)
leapadd(t, correction, lp->l_value);
}
@@ -2382,9 +2399,9 @@ static void
inexpires(char **fields, int nfields)
{
if (nfields != EXPIRES_FIELDS)
- error(_("wrong number of fields on Expires line"));
+ error(N_("wrong number of fields on Expires line"));
else if (0 <= leapexpires)
- error(_("multiple Expires lines"));
+ error(N_("multiple Expires lines"));
else
leapexpires = getleapdatetime(fields, true);
}
@@ -2395,11 +2412,11 @@ inlink(char **fields, int nfields)
struct link l;
if (nfields != LINK_FIELDS) {
- error(_("wrong number of fields on Link line"));
+ error(N_("wrong number of fields on Link line"));
return;
}
if (*fields[LF_TARGET] == '\0') {
- error(_("blank TARGET field on Link line"));
+ error(N_("blank TARGET field on Link line"));
return;
}
if (! namecheck(fields[LF_LINKNAME]))
@@ -2424,7 +2441,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
char xs;
if ((lp = byword(monthp, mon_names)) == NULL) {
- error(_("invalid month name"));
+ error(N_("invalid month name"));
return false;
}
rp->r_month = lp->l_value;
@@ -2453,7 +2470,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
break;
}
}
- rp->r_tod = gethms(dp, _("invalid time of day"));
+ rp->r_tod = gethms(dp, N_("invalid time of day"));
free(dp);
/*
** Year work.
@@ -2462,14 +2479,14 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
lp = byword(cp, begin_years);
if (lp) switch (lp->l_value) {
case YR_MINIMUM:
- warning(_("FROM year \"%s\" is obsolete;"
- " treated as %d"),
+ warning(N_("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) {
- error(_("invalid starting year"));
+ error(N_("invalid starting year"));
return false;
}
cp = hiyearp;
@@ -2484,16 +2501,16 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
break;
default: unreachable();
} else if (sscanf(cp, "%"SCNdZIC"%c", &rp->r_hiyear, &xs) != 1) {
- error(_("invalid ending year"));
+ error(N_("invalid ending year"));
return false;
}
if (rp->r_loyear > rp->r_hiyear) {
- error(_("starting year greater than ending year"));
+ error(N_("starting year greater than ending year"));
return false;
}
if (*typep != '\0') {
- error(_("year type \"%s\" is unsupported; use \"-\" instead"),
- typep);
+ error(N_("year type \"%s\" is unsupported; use \"-\" instead"),
+ typep);
return false;
}
/*
@@ -2526,12 +2543,12 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
if (rp->r_dycode != DC_DOM) {
*ep++ = 0;
if (*ep++ != '=') {
- error(_("invalid day of month"));
+ error(N_("invalid day of month"));
free(dp);
return false;
}
if ((lp = byword(dp, wday_names)) == NULL) {
- error(_("invalid weekday name"));
+ error(N_("invalid weekday name"));
free(dp);
return false;
}
@@ -2540,7 +2557,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
if (sscanf(ep, "%d%c", &rp->r_dayofmonth, &xs) != 1 ||
rp->r_dayofmonth <= 0 ||
(rp->r_dayofmonth > len_months[1][rp->r_month])) {
- error(_("invalid day of month"));
+ error(N_("invalid day of month"));
free(dp);
return false;
}
@@ -2722,15 +2739,15 @@ writezone(const char *const name, const char *const string, char version,
if (noise) {
if (1200 < timecnt) {
if (TZ_MAX_TIMES < timecnt)
- warning(_("reference clients mishandle"
- " more than %d transition times"),
+ warning(N_("reference clients mishandle"
+ " more than %d transition times"),
TZ_MAX_TIMES);
else
- warning(_("pre-2014 clients may mishandle"
- " more than 1200 transition times"));
+ warning(N_("pre-2014 clients may mishandle"
+ " more than 1200 transition times"));
}
if (TZ_MAX_LEAPS < leapcnt)
- warning(_("reference clients mishandle more than %d leap seconds"),
+ warning(N_("reference clients mishandle more than %d leap seconds"),
TZ_MAX_LEAPS);
}
/*
@@ -2771,16 +2788,16 @@ writezone(const char *const name, const char *const string, char version,
continue;
if (r->leapexpiry) {
if (noise)
- warning(_("%s: pre-2021b clients may mishandle"
- " leap second expiry"),
+ warning(N_("%s: pre-2021b clients may mishandle"
+ " leap second expiry"),
name);
version = '4';
}
if (0 < r->leapcount
&& leap[r->leapbase].corr != 1 && leap[r->leapbase].corr != -1) {
if (noise)
- warning(_("%s: pre-2021b clients may mishandle"
- " leap second table truncation"),
+ warning(N_("%s: pre-2021b clients may mishandle"
+ " leap second table truncation"),
name);
version = '4';
}
@@ -2828,7 +2845,7 @@ writezone(const char *const name, const char *const string, char version,
thismax = max_time;
}
if (toomanytimes)
- error(_("too many transition times"));
+ error(N_("too many transition times"));
locut = thismin < lo_time && lo_time <= thismax;
hicut = thismin <= hi_time && hi_time < thismax;
@@ -2995,8 +3012,8 @@ writezone(const char *const name, const char *const string, char version,
}
if (pass == 2 && noise && 50 < thischarcnt)
- warning(_("%s: pre-2026 reference clients mishandle"
- " more than 50 bytes of abbreviations"),
+ warning(N_("%s: pre-2026 reference clients mishandle"
+ " more than 50 bytes of abbreviations"),
name);
/* Output a LO_TIME transition if needed; see limitrange.
@@ -3081,26 +3098,21 @@ writezone(const char *const name, const char *const string, char version,
static char const *
abbroffset(char *buf, zic_t offset)
{
- char sign = '+';
- int seconds, minutes;
-
- if (offset < 0) {
- offset = -offset;
- sign = '-';
- }
-
- seconds = offset % SECSPERMIN;
- offset /= SECSPERMIN;
- minutes = offset % MINSPERHOUR;
- offset /= MINSPERHOUR;
- if (100 <= offset) {
- error(_("%%z UT offset magnitude exceeds 99:59:59"));
+ zic_t offset_lim = 100L * SECSPERHOUR;
+ if (! (-offset_lim < offset && offset < offset_lim)) {
+ error(N_("%%z UT offset magnitude exceeds 99:59:59"));
return "%z";
} else {
+ char sign = offset < 0 ? '-' : '+';
+ int_fast32_t abs_offset = offset < 0 ? -offset : offset;
+ int seconds = abs_offset % SECSPERMIN;
+ int abs_minutes_offset = abs_offset / SECSPERMIN;
+ int minutes = abs_minutes_offset % MINSPERHOUR;
+ int hours = abs_minutes_offset / MINSPERHOUR;
char *p = buf;
*p++ = sign;
- *p++ = '0' + offset / 10;
- *p++ = '0' + offset % 10;
+ *p++ = '0' + hours / 10;
+ *p++ = '0' + hours % 10;
if (minutes | seconds) {
*p++ = '0' + minutes / 10;
*p++ = '0' + minutes % 10;
@@ -3475,15 +3487,14 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
do_extend = compat < 0;
if (noise) {
if (!*envvar)
- warning("%s %s",
- _("no proleptic TZ string for zone"),
+ warning(N_("no proleptic TZ string for zone %s"),
zpfirst->z_name);
else if (compat != 0) {
/* Circa-COMPAT clients, and earlier clients, might
not work for this zone when given dates before
1970 or after 2038. */
- warning(_("%s: pre-%d clients may mishandle"
- " distant timestamps"),
+ warning(N_("%s: pre-%d clients may mishandle"
+ " distant timestamps"),
zpfirst->z_name, compat);
}
}
@@ -3528,7 +3539,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
int type;
save = zp->z_save;
doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
- type = addtype(oadd(zp->z_stdoff, save),
+ type = addtype(zp->z_stdoff + save,
startbuf, zp->z_isdst, startttisstd,
startttisut);
if (usestart) {
@@ -3600,22 +3611,22 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
r->r_filenum, r->r_linenum);
offset = r->r_todisut ? 0 : stdoff;
if (!r->r_todisstd)
- offset = oadd(offset, save);
+ offset += save;
jtime = r->r_temp;
jtime = tadd(jtime, -offset);
if (k < 0 || jtime < ktime) {
k = j;
ktime = jtime;
} else if (jtime == ktime) {
- char const *dup_rules_msg =
- _("two rules for same instant");
+ static char const dup_rules_msgid[] =
+ N_("two rules for same instant");
eats(zp->z_filenum, zp->z_linenum,
r->r_filenum, r->r_linenum);
- warning("%s", dup_rules_msg);
+ warning(dup_rules_msgid);
r = &zp->z_rules[k];
eats(zp->z_filenum, zp->z_linenum,
r->r_filenum, r->r_linenum);
- error("%s", dup_rules_msg);
+ error(dup_rules_msgid);
}
}
if (k < 0)
@@ -3624,7 +3635,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
rp->r_todo = false;
if (useuntil && ktime >= untiltime) {
if (!*startbuf
- && (oadd(zp->z_stdoff, rp->r_save)
+ && (zp->z_stdoff + rp->r_save
== startoff))
doabbr(startbuf, zp, rp->r_abbrvar,
rp->r_isdst, rp->r_save,
@@ -3636,8 +3647,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
usestart = false;
if (usestart) {
if (ktime < starttime) {
- startoff = oadd(zp->z_stdoff,
- save);
+ startoff = zp->z_stdoff + save;
doabbr(startbuf, zp,
rp->r_abbrvar,
rp->r_isdst,
@@ -3646,8 +3656,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
continue;
}
if (*startbuf == '\0'
- && startoff == oadd(zp->z_stdoff,
- save)) {
+ && startoff == (zp->z_stdoff
+ + save)) {
doabbr(startbuf,
zp,
rp->r_abbrvar,
@@ -3660,7 +3670,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
rp->r_filenum, rp->r_linenum);
doabbr(ab, zp, rp->r_abbrvar,
rp->r_isdst, rp->r_save, false);
- offset = oadd(zp->z_stdoff, rp->r_save);
+ offset = zp->z_stdoff + rp->r_save;
type = addtype(offset, ab, rp->r_isdst,
rp->r_todisstd, rp->r_todisut);
if (defaulttype < 0 && !rp->r_isdst)
@@ -3681,7 +3691,7 @@ 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"
+ error(N_("can't determine time zone abbreviation"
" to use just after until time"));
else {
int type = addtype(startoff, startbuf, isdst,
@@ -3784,7 +3794,7 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
/* RFC 9636 section 3.2 specifies this range for utoff. */
if (! (-TWO_31_MINUS_1 <= utoff && utoff <= TWO_31_MINUS_1)) {
- error(_("UT offset out of range"));
+ error(N_("UT offset out of range"));
exit(EXIT_FAILURE);
}
if (!want_bloat())
@@ -3813,7 +3823,7 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
** many.
*/
if (typecnt >= TZ_MAX_TYPES) {
- error(_("too many local time types"));
+ error(N_("too many local time types"));
exit(EXIT_FAILURE);
}
i = typecnt++;
@@ -3831,7 +3841,7 @@ leapadd(zic_t t, int correction, int rolling)
register ptrdiff_t i;
if (rolling && (lo_time != min_time || hi_time != max_time)) {
- error(_("Rolling leap seconds not supported with -r"));
+ error(N_("Rolling leap seconds not supported with -r"));
exit(EXIT_FAILURE);
}
leap = growalloc(leap, sizeof *leap, leapcnt, &leap_alloc);
@@ -3857,7 +3867,7 @@ adjleap(void)
*/
for (i = 0; i < leapcnt; ++i) {
if (leap[i].trans - prevtrans < 28 * SECSPERDAY) {
- error(_("Leap seconds too close together"));
+ error(N_("Leap seconds too close together"));
exit(EXIT_FAILURE);
}
prevtrans = leap[i].trans;
@@ -3868,7 +3878,7 @@ adjleap(void)
if (0 <= leapexpires) {
leapexpires = oadd(leapexpires, last);
if (! (leapcnt == 0 || (leap[leapcnt - 1].trans < leapexpires))) {
- error(_("last Leap time does not precede Expires time"));
+ error(N_("last Leap time does not precede Expires time"));
exit(EXIT_FAILURE);
}
}
@@ -3978,7 +3988,7 @@ byword(const char *word, const struct lookup *table)
Warn about any usage of the undocumented prefix "last-". */
if (table == lasts && ciprefix("last", word) && word[4]) {
if (word[4] == '-')
- warning(_("\"%s\" is undocumented; use \"last%s\" instead"),
+ warning(N_("\"%s\" is undocumented; use \"last%s\" instead"),
word, word + 5);
else {
word += 4;
@@ -4009,7 +4019,7 @@ byword(const char *word, const struct lookup *table)
for (lp = table; lp->l_word; lp++)
if (itsabbr(word, lp->l_word)) {
if (pre_2017c_match) {
- warning(_("\"%s\" is ambiguous in pre-2017c zic"), word);
+ warning(N_("\"%s\" is ambiguous in pre-2017c zic"), word);
break;
}
pre_2017c_match = true;
@@ -4040,7 +4050,7 @@ getfields(char *cp, char **array, int arrayelts)
if (*dp != '\0')
++dp;
else {
- error(_("Odd number of quotation marks"));
+ error(N_("Odd number of quotation marks"));
exit(EXIT_FAILURE);
}
} while (*cp && *cp != '#' && !is_space(*cp));
@@ -4048,7 +4058,7 @@ getfields(char *cp, char **array, int arrayelts)
++cp;
*dp = '\0';
if (nsubs == arrayelts) {
- error(_("Too many input fields"));
+ error(N_("Too many input fields"));
exit(EXIT_FAILURE);
}
array[nsubs++] = dstart + (*dstart == '-' && dp == dstart + 1);
@@ -4059,7 +4069,7 @@ getfields(char *cp, char **array, int arrayelts)
ATTRIBUTE_NORETURN static void
time_overflow(void)
{
- error(_("time overflow"));
+ error(N_("time overflow"));
exit(EXIT_FAILURE);
}
@@ -4128,11 +4138,11 @@ rpytime(const struct rule *rp, zic_t wantedy)
y = EPOCH_YEAR;
/* dayoff = floor((wantedy - y) / YEARSPERREPEAT) * DAYSPERREPEAT,
- sans overflow. */
+ checking for overflow. */
yrem = wantedy % YEARSPERREPEAT - y % YEARSPERREPEAT;
- dayoff = ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
- + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0))
- * DAYSPERREPEAT);
+ dayoff = omul ((wantedy / YEARSPERREPEAT - y / YEARSPERREPEAT
+ + yrem / YEARSPERREPEAT - (yrem % YEARSPERREPEAT < 0)),
+ DAYSPERREPEAT);
/* wantedy = y + ((wantedy - y) mod YEARSPERREPEAT), sans overflow. */
wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT;
@@ -4151,7 +4161,7 @@ rpytime(const struct rule *rp, zic_t wantedy)
if (rp->r_dycode == DC_DOWLEQ)
--i;
else {
- error(_("use of 2/29 in non leap-year"));
+ error(N_("use of 2/29 in non leap-year"));
exit(EXIT_FAILURE);
}
}
@@ -4177,8 +4187,9 @@ rpytime(const struct rule *rp, zic_t wantedy)
}
if (i < 0 || i >= len_months[isleap(y)][m]) {
if (noise)
- warning(_("rule goes past start/end of month; \
-will not work with pre-2004 versions of zic"));
+ warning(N_("rule goes past start/end of month;"
+ " will not work with pre-2004"
+ " versions of zic"));
}
}
t = omul(dayoff, SECSPERDAY);
@@ -4198,13 +4209,13 @@ checkabbr(char const *string)
|| *cp == '-' || *cp == '+')
++cp;
if (noise && cp - string < 3)
- mp = _("time zone abbreviation has fewer than 3 characters");
+ mp = N_("time zone abbreviation has fewer than 3 characters");
if (cp - string > ZIC_MAX_ABBR_LEN_WO_WARN)
- mp = _("time zone abbreviation has too many characters");
+ mp = N_("time zone abbreviation has too many characters");
if (*cp != '\0')
-mp = _("time zone abbreviation differs from POSIX standard");
+ mp = N_("time zone abbreviation differs from POSIX standard");
if (mp != NULL)
- warning("%s (%s)", mp, string);
+ warning(N_("%s (%s)"), _(mp), string);
}
}
@@ -4243,7 +4254,7 @@ addabbr(char chs[TZ_MAX_CHARS], int *pnchs, char const *abbr)
i += clen + 1;
}
if (TZ_MAX_CHARS < nchs + nchs_incr) {
- error(_("too many, or too long, time zone abbreviations"));
+ error(N_("too many, or too long, time zone abbreviations"));
exit(EXIT_FAILURE);
}
memmove(&chs[i + nchs_incr], &chs[i], nchs - i);
@@ -4301,7 +4312,7 @@ mkdirs(char const *argname, bool ancestors)
int err = errno;
if (err == ELOOP || err == ENAMETOOLONG
|| err == ENOENT || err == ENOTDIR) {
- error(_("%s: Can't create directory %s: %s"),
+ error(N_("%s: Can't create directory %s: %s"),
progname, name, strerror(err));
exit(EXIT_FAILURE);
}