Update tzcode to 2025b

MFC after:	3 weeks

Differential Revision:	https://reviews.freebsd.org/D52103
This commit is contained in:
Dag-Erling Smørgrav 2025-08-25 12:06:59 +00:00
parent 2326db40a1
commit 967a49a21a
20 changed files with 1121 additions and 751 deletions

View file

@ -137,7 +137,7 @@ TIME_T_ALTERNATIVES_TAIL = int_least32_t.ck uint_least32_t.ck \
uint_least64_t.ck
# What kind of TZif data files to generate. (TZif is the binary time
# zone data format that zic generates; see Internet RFC 8536.)
# zone data format that zic generates; see Internet RFC 9636.)
# If you want only POSIX time, with time values interpreted as
# seconds since the epoch (not counting leap seconds), use
# REDO= posix_only
@ -255,6 +255,7 @@ 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
# -DMKTIME_MIGHT_OVERFLOW if mktime might fail due to time_t overflow
# -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'.
@ -262,7 +263,7 @@ LDLIBS=
# 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
# -Dssize_t=int on hosts like MS-Windows that lack ssize_t
# -DSUPPORT_C89=0 if the tzcode library should not support C89 callers
# Although -DSUPPORT_C89=0 might work around latent bugs in callers,
# it does not conform to POSIX.
@ -285,7 +286,7 @@ LDLIBS=
# This mishandles some past timestamps, as US DST rules have changed.
# It also mishandles settings like TZ='EET-2EEST' for eastern Europe,
# as Europe and US DST rules differ.
# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 255)
# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 254)
# -DUNINIT_TRAP if reading uninitialized storage can cause problems
# other than simply getting garbage data
# -DUSE_LTZ=0 to build zdump with the system time zone library
@ -319,7 +320,8 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \
$(GCC_INSTRUMENT) \
-Wall -Wextra \
-Walloc-size-larger-than=100000 -Warray-bounds=2 \
-Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wdate-time \
-Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wcast-qual \
-Wdate-time \
-Wdeclaration-after-statement -Wdouble-promotion \
-Wduplicated-branches -Wduplicated-cond -Wflex-array-member-not-at-end \
-Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
@ -336,7 +338,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \
-Wsuggest-attribute=noreturn -Wsuggest-attribute=pure \
-Wtrampolines -Wundef -Wunused-macros -Wuse-after-free=3 \
-Wvariadic-macros -Wvla -Wwrite-strings \
-Wno-format-nonliteral -Wno-sign-compare
-Wno-format-nonliteral -Wno-sign-compare -Wno-type-limits
#
# If your system has a "GMT offset" field in its "struct tm"s
# (or if you decide to add such a field in your system's "time.h" file),
@ -614,8 +616,8 @@ TZS_YEAR= 2050
TZS_CUTOFF_FLAG= -c $(TZS_YEAR)
TZS= to$(TZS_YEAR).tzs
TZS_NEW= to$(TZS_YEAR)new.tzs
TZS_DEPS= $(YDATA) asctime.c localtime.c \
private.h tzfile.h zdump.c zic.c
TZS_DEPS= $(YDATA) localtime.c private.h \
strftime.c tzfile.h zdump.c zic.c
TZDATA_DIST = $(COMMON) $(DATA) $(MISC)
# EIGHT_YARDS is just a yard short of the whole ENCHILADA.
EIGHT_YARDS = $(TZDATA_DIST) $(DOCS) $(SOURCES) tzdata.zi
@ -855,10 +857,10 @@ tzselect: tzselect.ksh version
chmod +x $@.out
mv $@.out $@
check: check_mild back.ck
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 now.ck slashed-abbrs.ck sorted.ck \
name-lengths.ck slashed-abbrs.ck sorted.ck \
tables.ck ziguard.ck tzs.ck
# True if UTF8_LOCALE does not work;
@ -1103,7 +1105,7 @@ set-timestamps.out: $(EIGHT_YARDS)
touch -md @1 test.out; then \
rm -f test.out && \
for file in $$files; do \
if git diff --quiet $$file; then \
if git diff --quiet HEAD $$file; then \
time=$$(TZ=UTC0 git log -1 \
--format='tformat:%cd' \
--date='format:%Y-%m-%dT%H:%M:%SZ' \
@ -1354,13 +1356,13 @@ long-long.ck unsigned.ck: $(VERSION_DEPS)
zonenames: tzdata.zi
@$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi
asctime.o: private.h tzfile.h
asctime.o: private.h
date.o: private.h
difftime.o: private.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 tzdir.h version.h
localtime.o: private.h tzdir.h tzfile.h
strftime.o: localtime.c private.h tzdir.h tzfile.h
zdump.o: private.h version.h
zic.o: private.h tzdir.h tzfile.h version.h
.PHONY: ALL INSTALL all
.PHONY: check check_mild check_time_t_alternatives

View file

@ -1,5 +1,108 @@
News for the tz database
Release 2025b - 2025-03-22 13:40:46 -0700
Briefly:
New zone for Aysén Region in Chile which moves from -04/-03 to -03.
Changes to future timestamps
Chile's Aysén Region moves from -04/-03 to -03 year-round, joining
Magallanes Region. The region will not change its clocks on
2025-04-05 at 24:00, diverging from America/Santiago and creating a
new zone America/Coyhaique. (Thanks to Yonathan Dossow.) Model
this as a change to standard offset effective 2025-03-20.
Changes to past timestamps
Iran switched from +04 to +0330 on 1978-11-10 at 24:00, not at
year end. (Thanks to Roozbeh Pournader.)
Changes to code
'zic -l TIMEZONE -d . -l /some/other/file/system' no longer
attempts to create an incorrect symlink, and no longer has a
read buffer underflow. (Problem reported by Evgeniy Gorbanev.)
Release 2025a - 2025-01-15 10:47:24 -0800
Briefly:
Paraguay adopted permanent -03 starting spring 2024.
Improve pre-1991 data for the Philippines.
Etc/Unknown is now reserved.
Changes to future timestamps
Paraguay stopped changing its clocks after the spring-forward
transition on 2024-10-06, so it is now permanently at -03.
(Thanks to Heitor David Pinto and Even Scharning.)
This affects timestamps starting 2025-03-22, as well as the
obsolescent tm_isdst flags starting 2024-10-15.
Changes to past timestamps
Correct timestamps for the Philippines before 1900, and from 1937
through 1990. (Thanks to P Chan for the heads-up and citations.)
This includes adjusting local mean time before 1899; fixing
transitions in September 1899, January 1937, and June 1954; adding
transitions in December 1941, November 1945, March and September
1977, and May and July 1990; and removing incorrect transitions in
March and September 1978.
Changes to data
Add zone1970.tab lines for the Concordia and Eyre Bird Observatory
research stations. (Thanks to Derick Rethans and Jule Dabars.)
Changes to code
strftime %s now generates the correct numeric string even when the
represented number does not fit into time_t. This is better than
generating the numeric equivalent of (time_t) -1, as strftime did
in TZDB releases 96a (when %s was introduced) through 2020a and in
releases 2022b through 2024b. It is also better than failing and
returning 0, as strftime did in releases 2020b through 2022a.
strftime now outputs an invalid conversion specifier as-is,
instead of eliding the leading '%', which confused debugging.
An invalid TZ now generates the time zone abbreviation "-00", not
"UTC", to help the user see that an error has occurred. (Thanks
to Arthur David Olson for suggesting a "wrong result".)
mktime and timeoff no longer incorrectly fail merely because a
struct tm component near INT_MIN or INT_MAX overflows when a
lower-order component carries into it.
TZNAME_MAXIMUM, the maximum number of bytes in a proleptic TZ
string's time zone abbreviation, now defaults to 254 not 255.
This helps reduce the size of internal state from 25480 to 21384
on common platforms. This change should not be a problem, as
nobody uses such long "abbreviations" and the longstanding tzcode
maximum was 16 until release 2023a. For those who prefer no
arbitrary limits, you can now specify TZNAME_MAXIMUM values up to
PTRDIFF_MAX, a limit forced by C anyway; formerly tzcode silently
misbehaved unless TZNAME_MAXIMUM was less than INT_MAX.
tzset and related functions no longer leak a file descriptor if
another thread forks or execs at about the same time and if the
platform has O_CLOFORK and O_CLOEXEC respectively. Also, the
functions no longer let a TZif file become a controlling terminal.
'zdump -' now reads TZif data from /dev/stdin.
(From a question by Arthur David Olson.)
Changes to documentation
The name Etc/Unknown is now reserved: it will not be used by TZDB.
This is for compatibility with CLDR, which uses the string
"Etc/Unknown" for an unknown or invalid timezone. (Thanks to
Justin Grant, Mark Davis, and Guy Harris.)
Cite Internet RFC 9636, which obsoletes RFC 8536 for TZif format.
Release 2024b - 2024-09-04 12:27:47 -0700
Briefly:
@ -116,7 +219,7 @@ Release 2024b - 2024-09-04 12:27:47 -0700
Changes to commentary
Commentary about historical transitions in Portugal and her former
colonies has been expanded with links to many relevant legislation.
colonies has been expanded with links to relevant legislation.
(Thanks to Tim Parenti.)
@ -204,10 +307,10 @@ Release 2023d - 2023-12-21 20:02:24 -0800
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.
reverts to its common practice before 1981. (Thanks to Jule Dabars.)
Fix predictions for DST transitions in Palestine in 2072-2075,
correcting a typo introduced in 2023a.
correcting a typo introduced in 2023a. (Thanks to Jule Dabars.)
Changes to past and future timestamps

View file

@ -7,6 +7,7 @@
/*
** Avoid the temptation to punt entirely to strftime;
** strftime can behave badly when tm components are out of range, and
** the output of strftime is supposed to be locale specific
** whereas the output of asctime is supposed to be constant.
*/
@ -18,27 +19,6 @@
#include "un-namespace.h"
#include <stdio.h>
/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
** and may assume that the newline always lands in the same place.
** For years that are less than four digits, we pad the output with
** leading zeroes to get the newline in the traditional place.
** The -4 ensures that we get four characters of output even if
** we call a strftime variant that produces fewer characters for some years.
** This conforms to recent ISO C and POSIX standards, which say behavior
** is undefined when the year is less than 1000 or greater than 9999.
*/
static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n";
enum { STD_ASCTIME_BUF_SIZE = 26 };
/*
** Big enough for something such as
@ -52,14 +32,24 @@ enum { STD_ASCTIME_BUF_SIZE = 26 };
*/
static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
/* A similar buffer for ctime.
C89 requires that they be the same buffer.
This requirement was removed in C99, so support it only if requested,
as support is more likely to lead to bugs in badly written programs. */
#if SUPPORT_C89
# define buf_ctime buf_asctime
#else
static char buf_ctime[sizeof buf_asctime];
/* On pre-C99 platforms, a snprintf substitute good enough for us. */
#if !HAVE_SNPRINTF
# include <stdarg.h>
ATTRIBUTE_FORMAT((printf, 3, 4)) static int
my_snprintf(char *s, size_t size, char const *format, ...)
{
int n;
va_list args;
char stackbuf[sizeof buf_asctime];
va_start(args, format);
n = vsprintf(stackbuf, format, args);
va_end (args);
if (0 <= n && n < size)
memcpy (s, stackbuf, n + 1);
return n;
}
# undef snprintf
# define snprintf my_snprintf
#endif
/* Publish asctime_r and ctime_r only when supporting older POSIX. */
@ -84,14 +74,19 @@ asctime_r(struct tm const *restrict timeptr, char *restrict buf)
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
const char * wn;
const char * mn;
char year[INT_STRLEN_MAXIMUM(int) + 2];
char result[sizeof buf_asctime];
register const char * wn;
register const char * mn;
int year, mday, hour, min, sec;
long long_TM_YEAR_BASE = TM_YEAR_BASE;
size_t bufsize = (buf == buf_asctime
? sizeof buf_asctime : STD_ASCTIME_BUF_SIZE);
if (timeptr == NULL) {
strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
/* Set errno now, since strcpy might change it in
POSIX.1-2017 and earlier. */
errno = EINVAL;
return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
return buf;
}
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
wn = "???";
@ -99,25 +94,41 @@ asctime_r(struct tm const *restrict timeptr, char *restrict buf)
if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
mn = "???";
else mn = mon_name[timeptr->tm_mon];
/*
** Use strftime's %Y to generate the year, to avoid overflow problems
** when computing timeptr->tm_year + TM_YEAR_BASE.
** Assume that strftime is unaffected by other out-of-range members
** (e.g., timeptr->tm_mday) when processing "%Y".
*/
strftime(year, sizeof year, "%Y", timeptr);
/*
** We avoid using snprintf since it's not available on all systems.
*/
sprintf(result,
((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
year);
if (strlen(result) < STD_ASCTIME_BUF_SIZE
|| buf == buf_ctime || buf == buf_asctime)
return strcpy(buf, result);
year = timeptr->tm_year;
mday = timeptr->tm_mday;
hour = timeptr->tm_hour;
min = timeptr->tm_min;
sec = timeptr->tm_sec;
/* Vintage programs are coded for years that are always four bytes long
and may assume that the newline always lands in the same place.
For years that are less than four bytes, pad the output with
leading zeroes to get the newline in the traditional place.
For years longer than four bytes, put extra spaces before the year
so that vintage code trying to overwrite the newline
won't overwrite a digit within a year and truncate the year,
using the principle that no output is better than wrong output.
This conforms to ISO C and POSIX standards, which say behavior
is undefined when the year is less than 1000 or greater than 9999.
Also, avoid overflow when formatting tm_year + TM_YEAR_BASE. */
if ((year <= LONG_MAX - TM_YEAR_BASE
? snprintf (buf, bufsize,
((-999 - TM_YEAR_BASE <= year
&& year <= 9999 - TM_YEAR_BASE)
? "%s %s%3d %.2d:%.2d:%.2d %04ld\n"
: "%s %s%3d %.2d:%.2d:%.2d %ld\n"),
wn, mn, mday, hour, min, sec,
year + long_TM_YEAR_BASE)
: snprintf (buf, bufsize,
"%s %s%3d %.2d:%.2d:%.2d %d%d\n",
wn, mn, mday, hour, min, sec,
year / 10 + TM_YEAR_BASE / 10,
year % 10))
< bufsize)
return buf;
else {
errno = EOVERFLOW;
return NULL;
@ -142,5 +153,8 @@ ctime_r(const time_t *timep, char *buf)
char *
ctime(const time_t *timep)
{
return ctime_r(timep, buf_ctime);
/* Do not call localtime_r, as C23 requires ctime to initialize the
static storage that localtime updates. */
struct tm *tmp = localtime(timep);
return tmp ? asctime(tmp) : NULL;
}

View file

@ -6,15 +6,13 @@ date \- show and set date and time
.SH SYNOPSIS
.if n .nh
.if n .na
.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B date
[
.B \*-u
.B \-u
] [
.B \*-c
.B \-c
] [
.B \*-r
.B \-r
.I seconds
] [
.BI + format
@ -35,7 +33,7 @@ command
without arguments writes the date and time to the standard output in
the form
.ce 1
Wed Mar 8 14:54:40 EST 1989
Sat Mar 8 14:54:40 EST 2025
.br
with
.B EST
@ -49,99 +47,24 @@ If a command-line argument starts with a plus sign (\c
.q "\fB+\fP" ),
the rest of the argument is used as a
.I format
that controls what appears in the output.
In the format, when a percent sign (\c
.q "\fB%\fP"
appears,
it and the character after it are not output,
but rather identify part of the date or time
to be output in a particular way
(or identify a special character to output):
.nf
.sp
.if t .in +.5i
.if n .in +2
.ta \w'%M\0\0'u +\w'Wed Mar 8 14:54:40 EST 1989\0\0'u
Sample output Explanation
%a Wed Abbreviated weekday name*
%A Wednesday Full weekday name*
%b Mar Abbreviated month name*
%B March Full month name*
%c Wed Mar 08 14:54:40 1989 Date and time*
%C 19 Century
%d 08 Day of month (always two digits)
%D 03/08/89 Month/day/year (eight characters)
%e 8 Day of month (leading zero blanked)
%h Mar Abbreviated month name*
%H 14 24-hour-clock hour (two digits)
%I 02 12-hour-clock hour (two digits)
%j 067 Julian day number (three digits)
%k 2 12-hour-clock hour (leading zero blanked)
%l 14 24-hour-clock hour (leading zero blanked)
%m 03 Month number (two digits)
%M 54 Minute (two digits)
%n \\n newline character
%p PM AM/PM designation
%r 02:54:40 PM Hour:minute:second AM/PM designation
%R 14:54 Hour:minute
%S 40 Second (two digits)
%t \\t tab character
%T 14:54:40 Hour:minute:second
%U 10 Sunday-based week number (two digits)
%w 3 Day number (one digit, Sunday is 0)
%W 10 Monday-based week number (two digits)
%x 03/08/89 Date*
%X 14:54:40 Time*
%y 89 Last two digits of year
%Y 1989 Year in full
%z -0500 Numeric time zone
%Z EST Time zone abbreviation
%+ Wed Mar 8 14:54:40 EST 1989 Default output format*
.if t .in -.5i
.if n .in -2
* The exact output depends on the locale.
.sp
.fi
If a character other than one of those shown above appears after
a percent sign in the format,
that following character is output.
All other characters in the format are copied unchanged to the output;
a newline character is always added at the end of the output.
.PP
In Sunday-based week numbering,
the first Sunday of the year begins week 1;
days preceding it are part of
.q "week 0" .
In Monday-based week numbering,
the first Monday of the year begins week 1.
.PP
To set the date, use a command line argument with one of the following forms:
.nf
.if t .in +.5i
.if n .in +2
.ta \w'198903081454\0'u
1454 24-hour-clock hours (first two digits) and minutes
081454 Month day (first two digits), hours, and minutes
03081454 Month (two digits, January is 01), month day, hours, minutes
8903081454 Year, month, month day, hours, minutes
0308145489 Month, month day, hours, minutes, year
(on System V-compatible systems)
030814541989 Month, month day, hours, minutes, four-digit year
198903081454 Four-digit year, month, month day, hours, minutes
.if t .in -.5i
.if n .in -2
.fi
If the century, year, month, or month day is not given,
the current value is used.
Any of the above forms may be followed by a period and two digits that give
the seconds part of the new time; if no seconds are given, zero is assumed.
that is processed by
.BR strftime (3)
to determine what to output;
a newline character is appended.
For example, the shell command:
.ce 1
date +"%Y\-%m\-%d %H:%M:%S %z"
.br
outputs a line like
.q "2025\-03\-08 14:54:40 \-0500"
instead.
.PP
These options are available:
.TP
.BR \*-u " or " \*-c
.BR \-u " or " \-c
Use Universal Time when setting and showing the date and time.
.TP
.BI "\*-r " seconds
.BI "\-r " seconds
Output the date that corresponds to
.I seconds
past the epoch of 1970-01-01 00:00:00 UTC, where
@ -149,16 +72,13 @@ past the epoch of 1970-01-01 00:00:00 UTC, where
should be an integer, either decimal, octal (leading 0), or
hexadecimal (leading 0x), preceded by an optional sign.
.SH FILES
.ta \w'/usr/share/zoneinfo/posixrules\0\0'u
.ta \w'/usr/share/zoneinfo/Etc/UTC\0\0'u
/etc/localtime local timezone file
.br
/usr/lib/locale/\f2L\fP/LC_TIME description of time locale \f2L\fP
.br
/usr/share/zoneinfo timezone directory
.br
/usr/share/zoneinfo/posixrules default DST rules (obsolete)
.br
/usr/share/zoneinfo/GMT for UTC leap seconds
.PP
If /usr/share/zoneinfo/GMT is absent,
UTC leap seconds are loaded from /usr/share/zoneinfo/GMT0 if present.
/usr/share/zoneinfo/Etc/UTC for UTC leap seconds
.SH SEE ALSO
.BR strftime (3).

File diff suppressed because it is too large Load diff

View file

@ -5,43 +5,34 @@
asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
.SH SYNOPSIS
.nf
.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
.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);
.PP
.B double difftime(time_t time1, time_t time0);
.PP
.B [[deprecated]] char *asctime(struct tm const *tm);
.PP
/* Only in POSIX.1-2017 and earlier. */
.B "char *asctime_r(struct tm const *restrict tm,"
.B " char *restrict result);"
.PP
.B struct tm *localtime(time_t const *clock);
.PP
.B "struct tm *localtime_r(time_t const *restrict clock,"
.B " struct tm *restrict result);"
.PP
.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);
.PP
.B "struct tm *gmtime_r(time_t const *restrict clock,"
.B " struct tm *restrict result);"
.PP
.B time_t mktime(struct tm *tm);
.PP
.B "time_t mktime_z(timezone_t restrict zone,"
.B " struct tm *restrict tm);"
.PP
.B cc ... \*-ltz
.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);
.PP
/* Only in POSIX.1-2017 and earlier. */
.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
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
@ -54,30 +45,26 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
\\$3\*(lq\\$1\*(rq\\$2
..
The
.B ctime
function
converts a long integer, pointed to by
.B localtime
and
.B gmtime
functions
convert an integer, pointed to by
.IR clock ,
and returns a pointer to a
string of the form
.br
.ce
.eo
Thu Nov 24 18:22:48 1986\n\0
.br
.ec
Years requiring fewer than four characters are padded with leading zeroes.
For years longer than four characters, the string is of the form
.br
.ce
.eo
Thu Nov 24 18:22:48 81986\n\0
.ec
.br
with five spaces before the year.
These unusual formats are designed to make it less likely that older
software that expects exactly 26 bytes of output will mistakenly output
misleading values for out-of-range years.
and
return pointers to
.q "tm"
structures, described below.
If the integer is out of range for conversion,
these functions return a null pointer.
The
.B localtime
function
corrects for the time zone and any time zone adjustments
(such as Daylight Saving Time in the United States).
The
.B gmtime
function converts to Coordinated Universal Time.
.PP
The
.BI * clock
@ -91,47 +78,6 @@ introduction of UTC and are some other flavor of Universal Time (UT).
Some implementations support leap seconds, in contradiction to POSIX.
.PP
The
.B ctime
function is deprecated starting in C23.
Callers can use
.B localtime_r
and
.B strftime
instead.
.PP
The
.B localtime
and
.B gmtime
functions
return pointers to
.q "tm"
structures, described below.
The
.B localtime
function
corrects for the time zone and any time zone adjustments
(such as Daylight Saving Time in the United States).
.PP
The
.B gmtime
function
converts to Coordinated Universal Time.
.PP
The
.B asctime
function
converts a time value contained in a
.q "tm"
structure to a string,
as shown in the above example,
and returns a pointer to the string.
This function is deprecated starting in C23.
Callers can use
.B strftime
instead.
.PP
The
.B mktime
function
converts the broken-down time,
@ -204,6 +150,52 @@ returns the difference between two calendar times,
expressed in seconds.
.PP
The
.B asctime
function
converts a time value contained in a
.q "tm"
structure to a pointer to a
string of the form
.br
.ce
.eo
Thu Nov 24 18:22:48 1986\n\0
.br
.ec
Years requiring fewer than four characters are padded with leading zeroes.
For years longer than four characters, the string is of the form
.br
.ce
.eo
Thu Nov 24 18:22:48 81986\n\0
.ec
.br
with five spaces before the year.
This unusual format is designed to make it less likely that older
software that expects exactly 26 bytes of output will mistakenly output
misleading values for out-of-range years.
This function is deprecated starting in C23.
Callers can use
.B strftime
instead.
.PP
The
.B ctime
function is equivalent to calliing
.B localtime
and then calling
.B asctime
on the result.
Like
.BR asctime ,
this function is deprecated starting in C23.
Callers can use
.B localtime
and
.B strftime
instead.
.PP
The
.BR ctime_r ,
.BR localtime_r ,
.BR gmtime_r ,

View file

@ -40,8 +40,6 @@
strftime \- format date and time
.SH SYNOPSIS
.nf
.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
.B "size_t strftime(char *restrict buf, size_t maxsize,"
@ -93,7 +91,7 @@ If a bracketed member name is followed by
.B strftime
can use the named member even though POSIX.1-2024 does not list it;
if the name is followed by
.q \*- ,
.q \- ,
.B strftime
ignores the member even though POSIX.1-2024 lists it
which means portable code should set it.
@ -139,11 +137,14 @@ is replaced by the locale's appropriate date and time representation.
.IR tm_sec ,
.IR tm_gmtoff ,
.IR tm_zone ,
.IR tm_isdst \*-].
.IR tm_isdst \-].
.TP
%D
is equivalent to
.c %m/%d/%y .
Although used in the United States for current dates,
this format is ambiguous elsewhere
and for dates that might involve other centuries.
.RI [ tm_year ,
.IR tm_mon ,
.IR tm_mday ]
@ -167,6 +168,8 @@ is equivalent to
.TP
%G
is replaced by the ISO 8601 year with century as a decimal number.
This is the year that includes the greater part of the week.
(Monday as the first day of a week).
See also the
.c %V
conversion specification.
@ -176,11 +179,7 @@ conversion specification.
.TP
%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
.c %V
conversion specification.
Since it omits the century, it is ambiguous for dates.
.RI [ tm_year ,
.IR tm_yday ,
.IR tm_wday ]
@ -249,9 +248,22 @@ of leap seconds.
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
it can have glitches on other platforms
(notably obsolescent platforms lacking
.I tm_gmtoff
or where
.B time_t
is no wider than int), and POSIX allows
.B strftime
to set
.B errno
to
.B EINVAL
or
.B EOVERFLOW
and return 0 if the number of seconds would be negative or out of range for
.BR time_t .
Portable code should therefore format a
.B time_t
value directly via something like
.B sprintf
@ -267,7 +279,7 @@ with "%s".
.IR tm_min ,
.IR tm_sec ,
.IR tm_gmtoff +,
.IR tm_isdst \*-].
.IR tm_isdst \-].
.TP
%T
is replaced by the time in the format
@ -284,7 +296,7 @@ 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 \*-]
.IR tm_year \-]
.TP
%u
is replaced by the weekday (Monday as the first day of the week)
@ -318,31 +330,33 @@ as a decimal number [0,6].
.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 \*-,
.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 \*-].
.IR tm_isdst \-].
.TP
%x
is replaced by the locale's appropriate date representation.
This format can be ambiguous for dates, e.g.,
it can generate "01/02/03" in the C locale.
.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 \*-].
.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.
@ -350,28 +364,29 @@ is replaced by the year with century as a decimal number.
.TP
%y
is replaced by the year without century as a decimal number [00,99].
Since it omits the century, it is ambiguous for dates.
.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 \*-]
.IR tm_isdst \-]
.TP
%z
is replaced by the offset from the Prime Meridian
in the format +HHMM or \*-HHMM (ISO 8601) as appropriate,
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
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 uninhabited, and corresponds to a zero offset when the
time zone abbreviation begins with
.q "\*-" .
.q "\-" .
.RI [ tm_gmtoff ,
.IR tm_zone +,
.IR tm_isdst \*-]
.IR tm_isdst \-]
.TP
%%
is replaced by a single %.
@ -418,15 +433,6 @@ This function fails if:
The total number of resulting bytes, including the terminating
NUL character, is more than
.IR maxsize .
.PP
This function may fail if:
.TP
[EOVERFLOW]
The format includes an
.c %s
conversion and the number of seconds since the Epoch cannot be represented
in a
.c time_t .
.SH SEE ALSO
.BR date (1),
.BR getenv (3),

View file

@ -5,8 +5,6 @@
tzset \- initialize time conversion information
.SH SYNOPSIS
.nf
.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.B #include <time.h>
.PP
.BI "timezone_t tzalloc(char const *" TZ );
@ -23,7 +21,7 @@ tzset \- initialize time conversion information
.br
.B extern int daylight;
.PP
.B cc ... \*-ltz
.B cc ... \-ltz
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
@ -110,7 +108,7 @@ except a leading colon
digits, comma
.RB ( , ),
ASCII minus
.RB ( \*- ),
.RB ( \- ),
ASCII plus
.RB ( + ),
and NUL bytes are allowed.
@ -150,7 +148,7 @@ 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) \*(en if present \*(en between zero and 59. If preceded by a
.q "\*-" ,
.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 "+" .
@ -239,7 +237,7 @@ values that directly specify the timezone.
stands for US Eastern Standard
Time (EST), 5 hours behind UT, without daylight saving.
.TP
.B <+12>\*-12<+13>,M11.1.0,M1.2.1/147
.B <+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
@ -249,34 +247,34 @@ and daylight saving time are
and
.q "+13".
.TP
.B IST\*-2IDT,M3.4.4/26,M10.5.0
.B 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 March
23), and falling back on October's last Sunday at 02:00.
.TP
.B <\*-04>4<\*-03>,J1/0,J365/25
.B <\-04>4<\-03>,J1/0,J365/25
stands for permanent daylight saving time, 3 hours behind UT with
abbreviation
.q "\*-03".
.q "\-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
.B <\*-04>
.B <\-04>
is a placeholder.
.TP
.B <\*-03>3<\*-02>,M3.5.0/\*-2,M10.5.0/\*-1
.B <\-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
.q "\*-03"
.q "\-03"
and
.q "\*-02".
.q "\-02".
.PP
If
.I TZ

View file

@ -37,6 +37,38 @@
# define SUPPORT_C89 1
#endif
/* The following feature-test macros should be defined before
any #include of a system header. */
/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
#define _GNU_SOURCE 1
/* Fix asctime_r on Solaris 11. */
#define _POSIX_PTHREAD_SEMANTICS 1
/* Enable strtoimax on pre-C99 Solaris 11. */
#define __EXTENSIONS__ 1
/* Cause MS-Windows headers to define POSIX names. */
#define _CRT_DECLARE_NONSTDC_NAMES 1
/* Prevent MS-Windows headers from defining min and max. */
#define NOMINMAX 1
/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
setting _TIME_BITS to 64 does not work. The code does not
otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
use off_t or functions like 'stat' that depend on off_t. */
#ifndef _TIME_BITS
# ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
# endif
# if _FILE_OFFSET_BITS == 64
# define _TIME_BITS 64
# endif
#endif
/* End of feature-test macro definitions. */
#ifndef __STDC_VERSION__
# define __STDC_VERSION__ 0
#endif
@ -51,6 +83,7 @@
#endif
#if __STDC_VERSION__ < 202311
# undef static_assert
# define static_assert(cond) extern int static_assert_check[(cond) ? 1 : -1]
#endif
@ -87,11 +120,11 @@
#if !defined HAVE_GETTEXT && defined __has_include
# if __has_include(<libintl.h>)
# define HAVE_GETTEXT true
# define HAVE_GETTEXT 1
# endif
#endif
#ifndef HAVE_GETTEXT
# define HAVE_GETTEXT false
# define HAVE_GETTEXT 0
#endif
#ifndef HAVE_INCOMPATIBLE_CTIME_R
@ -124,20 +157,20 @@
#if !defined HAVE_SYS_STAT_H && defined __has_include
# if !__has_include(<sys/stat.h>)
# define HAVE_SYS_STAT_H false
# define HAVE_SYS_STAT_H 0
# endif
#endif
#ifndef HAVE_SYS_STAT_H
# define HAVE_SYS_STAT_H true
# define HAVE_SYS_STAT_H 1
#endif
#if !defined HAVE_UNISTD_H && defined __has_include
# if !__has_include(<unistd.h>)
# define HAVE_UNISTD_H false
# define HAVE_UNISTD_H 0
# endif
#endif
#ifndef HAVE_UNISTD_H
# define HAVE_UNISTD_H true
# define HAVE_UNISTD_H 1
#endif
#ifndef NETBSD_INSPIRED
@ -149,25 +182,6 @@
# define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
#define _GNU_SOURCE 1
/* Fix asctime_r on Solaris 11. */
#define _POSIX_PTHREAD_SEMANTICS 1
/* Enable strtoimax on pre-C99 Solaris 11. */
#define __EXTENSIONS__ 1
/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
setting _TIME_BITS to 64 does not work. The code does not
otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
use off_t or functions like 'stat' that depend on off_t. */
#ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
#endif
#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
# define _TIME_BITS 64
#endif
/*
** Nested includes
*/
@ -260,6 +274,10 @@
# endif
#endif
#ifndef HAVE_SNPRINTF
# define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
#endif
#ifndef HAVE_STRFTIME_L
# if _POSIX_VERSION < 200809
# define HAVE_STRFTIME_L 0
@ -305,7 +323,7 @@
** stdint.h, even with pre-C99 compilers.
*/
#if !defined HAVE_STDINT_H && defined __has_include
# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
# define HAVE_STDINT_H 1 /* C23 __has_include implies C99 stdint.h. */
#endif
#ifndef HAVE_STDINT_H
# define HAVE_STDINT_H \
@ -375,11 +393,15 @@ typedef int int_fast32_t;
# endif
#endif
#ifndef INT_LEAST32_MAX
typedef int_fast32_t int_least32_t;
#endif
#ifndef INTMAX_MAX
# ifdef LLONG_MAX
typedef long long intmax_t;
# ifndef HAVE_STRTOLL
# define HAVE_STRTOLL true
# define HAVE_STRTOLL 1
# endif
# if HAVE_STRTOLL
# define strtoimax strtoll
@ -459,7 +481,7 @@ typedef unsigned long uintmax_t;
hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
#if !defined HAVE_STDCKDINT_H && defined __has_include
# if __has_include(<stdckdint.h>)
# define HAVE_STDCKDINT_H true
# define HAVE_STDCKDINT_H 1
# endif
#endif
#ifdef HAVE_STDCKDINT_H
@ -554,13 +576,26 @@ typedef unsigned long uintmax_t;
# define ATTRIBUTE_REPRODUCIBLE /* empty */
#endif
#if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(unsequenced)
# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
# endif
#endif
#ifndef ATTRIBUTE_UNSEQUENCED
# define ATTRIBUTE_UNSEQUENCED /* empty */
#endif
/* GCC attributes that are useful in tzcode.
__attribute__((const)) is stricter than [[unsequenced]],
so the latter is an adequate substitute in non-GCC C23 platforms.
__attribute__((pure)) is stricter than [[reproducible]],
so the latter is an adequate substitute in non-GCC C23 platforms. */
#if __GNUC__ < 3
# define ATTRIBUTE_CONST ATTRIBUTE_UNSEQUENCED
# define ATTRIBUTE_FORMAT(spec) /* empty */
# define ATTRIBUTE_PURE ATTRIBUTE_REPRODUCIBLE
#else
# define ATTRIBUTE_CONST __attribute__((const))
# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
# define ATTRIBUTE_PURE __attribute__((pure))
#endif
@ -593,6 +628,12 @@ typedef unsigned long uintmax_t;
# define RESERVE_STD_EXT_IDS 0
#endif
#ifdef time_tz
# define defined_time_tz true
#else
# define defined_time_tz false
#endif
/* If standard C identifiers with external linkage (e.g., localtime)
are reserved and are not already being renamed anyway, rename them
as if compiling with '-Dtime_tz=time_t'. */
@ -608,9 +649,9 @@ typedef unsigned long uintmax_t;
** typical platforms.
*/
#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
# define TZ_TIME_T 1
# define TZ_TIME_T true
#else
# define TZ_TIME_T 0
# define TZ_TIME_T false
#endif
#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
@ -707,7 +748,7 @@ DEPRECATED_IN_C23 char *ctime(time_t const *);
char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime_r(time_t const *, char *);
#endif
double difftime(time_t, time_t);
ATTRIBUTE_CONST double difftime(time_t, time_t);
size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict);
# if HAVE_STRFTIME_L
@ -729,9 +770,9 @@ void tzset(void);
|| defined __GLIBC__ || defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
# define HAVE_DECL_TIMEGM true
# define HAVE_DECL_TIMEGM 1
# else
# define HAVE_DECL_TIMEGM false
# define HAVE_DECL_TIMEGM 0
# endif
#endif
#if !HAVE_DECL_TIMEGM && !defined timegm
@ -771,7 +812,11 @@ extern long altzone;
*/
#ifndef STD_INSPIRED
# define STD_INSPIRED 0
# ifdef __NetBSD__
# define STD_INSPIRED 1
# else
# define STD_INSPIRED 0
# endif
#endif
#if STD_INSPIRED
# if TZ_TIME_T || !defined offtime
@ -880,7 +925,7 @@ ATTRIBUTE_PURE time_t time2posix_z(timezone_t, time_t);
default: TIME_T_MAX_NO_PADDING) \
: (time_t) -1)
enum { SIGNED_PADDING_CHECK_NEEDED
= _Generic((time_t) 0,
= _Generic((time_t) 0,
signed char: false, short: false,
int: false, long: false, long long: false,
default: true) };
@ -927,8 +972,8 @@ 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. */
/* strftime.c sometimes needs access to timeoff if it is not already public.
tz_private_timeoff should be used only by localtime.c and strftime.c. */
#if (!defined EXTERN_TIMEOFF \
&& defined TM_GMTOFF && (200809 < _POSIX_VERSION || ! UNINIT_TRAP))
# ifndef timeoff

View file

@ -39,8 +39,63 @@
#include <locale.h>
#include <stdio.h>
/* If true, the value returned by an idealized unlimited-range mktime
always fits into an integer type with bounds MIN and MAX.
If false, the value might not fit.
This macro is usable in #if if its arguments are.
Add or subtract 2**31 - 1 for the maximum UT offset allowed in a TZif file,
divide by the maximum number of non-leap seconds in a year,
divide again by two just to be safe,
and account for the tm_year origin (1900) and time_t origin (1970). */
#define MKTIME_FITS_IN(min, max) \
((min) < 0 \
&& ((min) + 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900 < INT_MIN \
&& INT_MAX < ((max) - 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900)
/* MKTIME_MIGHT_OVERFLOW is true if mktime can fail due to time_t overflow
or if it is not known whether mktime can fail,
and is false if mktime definitely cannot fail.
This macro is usable in #if, and so does not use TIME_T_MAX or sizeof.
If the builder has not configured this macro, guess based on what
known platforms do. When in doubt, guess true. */
#ifndef MKTIME_MIGHT_OVERFLOW
# if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
# include <sys/param.h>
# endif
# if ((/* The following heuristics assume native time_t. */ \
defined_time_tz) \
|| ((/* Traditional time_t is 'long', so if 'long' is not wide enough \
assume overflow unless we're on a known-safe host. */ \
!MKTIME_FITS_IN(LONG_MIN, LONG_MAX)) \
&& (/* GNU C Library 2.29 (2019-02-01) and later has 64-bit time_t \
if __TIMESIZE is 64. */ \
!defined __TIMESIZE || __TIMESIZE < 64) \
&& (/* FreeBSD 12 r320347 (__FreeBSD_version 1200036; 2017-06-26), \
and later has 64-bit time_t on all platforms but i386 which \
is currently scheduled for end-of-life on 2028-11-30. */ \
!defined __FreeBSD_version || __FreeBSD_version < 1200036 \
|| defined __i386) \
&& (/* NetBSD 6.0 (2012-10-17) and later has 64-bit time_t. */ \
!defined __NetBSD_Version__ || __NetBSD_Version__ < 600000000) \
&& (/* OpenBSD 5.5 (2014-05-01) and later has 64-bit time_t. */ \
!defined OpenBSD || OpenBSD < 201405)))
# define MKTIME_MIGHT_OVERFLOW 1
# else
# define MKTIME_MIGHT_OVERFLOW 0
# endif
#endif
/* Check that MKTIME_MIGHT_OVERFLOW is consistent with time_t's range. */
static_assert(MKTIME_MIGHT_OVERFLOW
|| MKTIME_FITS_IN(TIME_T_MIN, TIME_T_MAX));
#if MKTIME_MIGHT_OVERFLOW
/* Do this after system includes as it redefines time_t, mktime, timeoff. */
# define USE_TIMEX_T true
# include "localtime.c"
#endif
#ifndef DEPRECATE_TWO_DIGIT_YEARS
# define DEPRECATE_TWO_DIGIT_YEARS false
# define DEPRECATE_TWO_DIGIT_YEARS 0
#endif
struct lc_time_T {
@ -135,10 +190,6 @@ strftime(char *restrict s, size_t maxsize, char const *restrict format,
tzset();
p = _fmt(format, t, s, s + maxsize, &warn);
if (!p) {
errno = EOVERFLOW;
return 0;
}
if (DEPRECATE_TWO_DIGIT_YEARS
&& warn != IN_NONE && getenv(YEAR_2000_NAME)) {
fprintf(stderr, "\n");
@ -170,7 +221,12 @@ _fmt(const char *format, const struct tm *t, char *pt,
if (*format == '%') {
label:
switch (*++format) {
case '\0':
default:
/* Output unknown conversion specifiers as-is,
to aid debugging. This includes '%' at
format end. This conforms to C23 section
7.29.3.5 paragraph 6, which says behavior
is undefined here. */
--format;
break;
case 'A':
@ -327,16 +383,17 @@ label:
tm.tm_mday = t->tm_mday;
tm.tm_mon = t->tm_mon;
tm.tm_year = t->tm_year;
/* Get the time_t value for TM.
Native time_t, or its redefinition
by localtime.c above, is wide enough
so that this cannot overflow. */
#ifdef TM_GMTOFF
mkt = timeoff(&tm, t->TM_GMTOFF);
#else
tm.tm_isdst = t->tm_isdst;
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
than strftime failing. */
if (TYPE_SIGNED(time_t)) {
intmax_t n = mkt;
sprintf(buf, "%"PRIdMAX, n);
@ -590,12 +647,6 @@ label:
warnp);
continue;
case '%':
/*
** X311J/88-090 (4.12.3.5): if conversion char is
** undefined, behavior is undefined. Print out the
** character itself as printf(3) also does.
*/
default:
break;
}
}

View file

@ -123,8 +123,9 @@ If geolocation information is available, a selection interface can
locate the user on a timezone map or prioritize names that are
geographically close. For an example selection interface, see the
<code>tzselect</code> program in the <code><abbr>tz</abbr></code> code.
The <a href="https://cldr.unicode.org">Unicode Common Locale Data
Repository</a> contains data that may be useful for other selection
Unicode's <a href="https://cldr.unicode.org">Common Locale Data
Repository (<abbr>CLDR</abbr>)</a>
contains data that may be useful for other selection
interfaces; it maps timezone names like <code>Europe/Prague</code> to
locale-dependent strings like "Prague", "Praha", "Прага", and "布拉格".
</p>
@ -200,6 +201,8 @@ in decreasing order of importance:
<li>
A name must not be empty, or contain '<code>//</code>', or
start or end with '<code>/</code>'.
Also, a name must not be '<code>Etc/Unknown</code>', as
<abbr>CLDR</abbr> uses that string for an unknown or invalid timezone.
</li>
<li>
Do not use names that differ only in case.
@ -220,10 +223,18 @@ in decreasing order of importance:
do not need locations, since local time is not defined there.
</li>
<li>
If all the clocks in a timezone have agreed since 1970,
do not bother to include more than one timezone
even if some of the clocks disagreed before 1970.
If all clocks in a region have agreed since 1970,
give them just one name even if some of the clocks disagreed before 1970,
or reside in different countries or in notable or faraway locations.
Otherwise these tables would become annoyingly large.
For example, do not create a name <code>Indian/Crozet</code>
as a near-duplicate or alias of <code>Asia/Dubai</code>
merely because they are different countries or territories,
or their clocks disagreed before 1970, or the
<a href="https://en.wikipedia.org/wiki/Crozet_Islands">Crozet Islands</a>
are notable in their own right,
or the Crozet Islands are not adjacent to other locations
that use <code>Asia/Dubai</code>.
</li>
<li>
If boundaries between regions are fluid, such as during a war or
@ -579,10 +590,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://datatracker.ietf.org/doc/html/rfc3339">Internet
from <a href="https://www.rfc-editor.org/rfc/rfc3339">Internet
<abbr title="Request For Comments">RFC</abbr> 3339</a>.
(The abbreviation 'Z' that
<a href="https://datatracker.ietf.org/doc/html/rfc9557">Internet
<a href="https://www.rfc-editor.org/rfc/rfc9557">Internet
<abbr>RFC</abbr> 9557</a> uses for this concept
would violate the POSIX requirement
of at least three characters in an abbreviation.)
@ -1115,8 +1126,8 @@ 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://datatracker.ietf.org/doc/html/8536">Internet
<abbr>RFC</abbr> 8536</a>.
<a href="https://www.rfc-editor.org/rfc/9636">Internet
<abbr>RFC</abbr> 9636</a>.
The daylight saving time rules to be used for a
particular timezone are encoded in the
<abbr>TZif</abbr> file; the format of the file allows <abbr>US</abbr>,
@ -1201,12 +1212,15 @@ The vestigial <abbr>API</abbr>s are:
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.
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
<code>mktime</code> to disambiguate timestamps near
a <abbr>DST</abbr> transition when the clock jumps back on
platforms lacking <code>tm_gmtoff</code>, this
disambiguation does not work when standard time itself jumps back,
which can occur when a location changes to a time zone with a
disambiguation works only for proleptic <code>TZ</code> strings;
it does not work in general for geographical timezones,
such as when a location changes to a time zone with a
lesser <abbr>UT</abbr> offset.
</li>
</ul>
@ -1223,8 +1237,8 @@ The vestigial <abbr>API</abbr>s are:
Programs that in the past used the <code>timezone</code> function
may now examine <code>localtime(&amp;clock)-&gt;tm_zone</code>
(if <code>TM_ZONE</code> is defined) or
<code>tzname[localtime(&amp;clock)-&gt;tm_isdst]</code>
(if <code>HAVE_TZNAME</code> is nonzero) to learn the correct time
use <code>strftime</code> with a <code>%Z</code> conversion specification
to learn the correct time
zone abbreviation to use.
</li>
<li>

View file

@ -194,9 +194,9 @@ After obtaining the code and data files, see the
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://datatracker.ietf.org/doc/html/8536">The
<a href="https://www.rfc-editor.org/rfc/9636">The
Time Zone Information Format (<abbr>TZif</abbr>)</a>
(Internet <abbr title="Request For Comments">RFC</abbr> 8536).
(Internet <abbr title="Request For Comments">RFC</abbr> 9636).
The code also lets
you read a <abbr>TZif</abbr> file and interpret timestamps for that
location.</p>
@ -260,7 +260,7 @@ Studio Code</a>.
</p>
<p>
For further information about updates, please see
<a href="https://datatracker.ietf.org/doc/html/rfc6557">Procedures for
<a href="https://www.rfc-editor.org/rfc/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
@ -379,26 +379,26 @@ calculates the current time difference between locations.</li>
<li>The <a href="https://www.ietf.org">Internet Engineering Task Force</a>'s
<a href="https://datatracker.ietf.org/wg/tzdist/charter/">Time Zone Data
Distribution Service (tzdist) working group</a> defined <a
href="https://datatracker.ietf.org/doc/html/rfc7808">TZDIST</a>
href="https://www.rfc-editor.org/rfc/rfc7808">TZDIST</a>
(Internet <abbr>RFC</abbr> 7808), a time zone data distribution service,
along with <a href="https://datatracker.ietf.org/doc/html/rfc7809">CalDAV</a>
along with <a href="https://www.rfc-editor.org/rfc/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.
The <a href="https://www.ietf.org/mailman/listinfo/tzdist-bis">tzdist-bis
mailing list</a> discusses possible extensions.</li>
<li>The <a href="https://datatracker.ietf.org/doc/html/rfc5545">
<li>The <a href="https://www.rfc-editor.org/rfc/rfc5545">
Internet Calendaring and Scheduling Core Object Specification
(iCalendar)</a> (Internet <abbr>RFC</abbr> 5445)
covers time zone
data; see its VTIMEZONE calendar component.
The iCalendar format requires specialized parsers and generators; a
variant <a href="https://datatracker.ietf.org/doc/html/rfc6321">xCal</a>
variant <a href="https://www.rfc-editor.org/rfc/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://datatracker.ietf.org/doc/html/rfc7265">jCal</a>
<a href="https://www.rfc-editor.org/rfc/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>
@ -935,7 +935,13 @@ with perhaps the best-documented history of clock adjustments.</dd>
<dt>United States</dt>
<dd>The Department of Transportation's <a
href="https://www.transportation.gov/regulations/recent-time-zone-proceedings">Recent
Time Zone Proceedings</a> lists changes to time zone boundaries.</dd>
Time Zone Proceedings</a> lists changes to
official written time zone boundaries, and its <a
href="https://geodata.bts.gov/datasets/usdot::time-zones/about">Time
Zones dataset</a> maps current boundaries.
These boundaries are only for standard time, so the current map puts
all of Arizona in one time zone even though part of Arizona
observes <abbr>DST</abbr> and part does not.</dd>
<dt>Uruguay</dt>
<dd>The Oceanography, Hydrography, and Meteorology Service of the Uruguayan
Navy (SOHMA) publishes an annual <a
@ -979,6 +985,14 @@ a Sleep Research Society position statement</a>.
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>Neumann P, von Blanckenburg K. <a
href="https://journals.sagepub.com/doi/full/10.1177/0961463X241310562">What
time will it be? A comprehensive literature review on daylight saving time</a>.
<em>Time Soc</em>. 2025-01-21.
doi:<a href="https://doi.org/10.1177/0961463X241310562">10.1177/0961463X241310562</a>.
This reviews DST's effects on electricity, health, crime, road safety,
and the economy, focusing on research since 2010, and concludes that
year-round standard time is preferable overall.
<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:
@ -994,7 +1008,8 @@ should we abolish Daylight Saving Time?</a>
<em>J Biol Rhythms.</em> 2019;34(3):227&ndash;230.
doi:<a href="https://doi.org/10.1177/0748730419854197">10.1177/0748730419854197</a>.
The Society for Research on Biological Rhythms
opposes DST changes and permanent DST, and advocates that governments adopt
opposes <abbr>DST</abbr> changes and permanent <abbr>DST</abbr>,
and advocates that governments adopt
"permanent Standard Time for the health and safety of their citizens".</li>
</ul>
</section>
@ -1023,7 +1038,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://datatracker.ietf.org/doc/html/rfc4833">Timezone
href="https://www.rfc-editor.org/rfc/rfc4833">Timezone
Options for <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr></a>
(Internet <abbr>RFC</abbr> 4833)
specifies a <a
@ -1105,7 +1120,7 @@ the abovementioned <abbr>NTP</abbr> implementations, <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://datatracker.ietf.org/doc/html/rfc8633#section-3.7.1">&sect;3.7.1 of
<a href="https://www.rfc-editor.org/rfc/rfc8633#section-3.7.1">&sect;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,
@ -1165,16 +1180,16 @@ interchange &ndash; Part 1: Basic rules</em></a>.</li>
<a href="https://www.w3.org/TR/xmlschema/#dateTime"><abbr>XML</abbr>
Schema: Datatypes &ndash; 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://datatracker.ietf.org/doc/html/rfc5322#section-3.3">&sect;3.3 of
<li><a href="https://www.rfc-editor.org/rfc/rfc5322#section-3.3">&sect;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://datatracker.ietf.org/doc/html/rfc3339">Date and Time
<a href="https://www.rfc-editor.org/rfc/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://datatracker.ietf.org/doc/html/rfc9557">Date
An extension, <a href="https://www.rfc-editor.org/rfc/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

View file

@ -11,7 +11,7 @@ The timezone information files used by
.Xr tzset 3
are found under
.Pa /usr/share/zoneinfo .
These files use the format described in Internet RFC 8536.
These files use the format described in Internet RFC 9636.
Each file is a sequence of 8-bit bytes.
In a file, a binary integer is represented by a sequence of one or
more bytes in network order (bigendian, or high-order byte first),
@ -107,7 +107,7 @@ and
serves as an index into the array of time zone abbreviation bytes
that follow the
.Vt ttinfo
entries in the file; if the designated string is "\*-00", the
entries in the file; if the designated string is "\-00", the
.Vt ttinfo
entry is a placeholder indicating that local time is unspecified.
The
@ -128,7 +128,7 @@ The byte strings can overlap if one is a suffix of the other.
The encoding of these strings is not specified.
.It Va tzh_leapcnt
pairs of four-byte values, written in network byte order;
the first value of each pair gives the nonnegative time
the first value of each pair gives the non-negative time
(as returned by
.Xr time 3 )
at which a leap second occurs or at which the leap second table expires;
@ -141,7 +141,7 @@ 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,
The first leap second has a non-negative 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,
@ -168,7 +168,7 @@ 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 proleptic TZ string that lacks rules.
For example, when TZ="EET\*-2EEST" and there is no TZif file "EET\*-2EEST",
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.
@ -177,7 +177,7 @@ 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
TZ="EET\-2EEST,M3.5.0/3,M10.5.0/4" if POSIX conformance is required
and older timestamps need not be handled accurately.
.Pp
The
@ -203,7 +203,7 @@ 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 proleptic representation for such instants.
If nonempty, the TZ string must agree with the local time
If non-empty, 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
.Dq "WET0WEST,M3.5.0/1,M10.5.0"
@ -218,7 +218,7 @@ the earliest transition time.
For version-3-format timezone files, a TZ string (see
.Xr newtzset 3 )
may use the following POSIX.1-2024 extensions to POSIX.1-2017:
First, as in TZ="<\*-02>2<\*-01>,M3.5.0/\*-1,M10.5.0/0",
First, as in TZ="<\-02>2<\-01>,M3.5.0/\-1,M10.5.0/0",
the hours part of its transition times may be signed and range from
\-167 through 167 instead of being limited to unsigned values
from 0 through 24.
@ -275,7 +275,7 @@ 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,
.Dq "\*-" ,
.Dq "\-" ,
and
.Dq "+" .
This is for compatibility with POSIX requirements for
@ -300,16 +300,16 @@ through 60 instead of the usual 59; the UTC offset is unaffected.
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:
The goals of this section are to help:
.Bl -bullet
.It
to help TZif writers output files that avoid common
TZif writers output files that avoid common
pitfalls in older or buggy TZif readers,
.It
to help TZif readers avoid common pitfalls when reading
TZif readers avoid common pitfalls when reading
files generated by future TZif writers, and
.It
to help any future specification authors see what sort of
any future specification authors see what sort of
problems arise when the TZif format is changed.
.El
.Pp
@ -320,9 +320,9 @@ 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.
newer-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
workarounds as well as documenting other common bugs in
readers.
.Pp
Interoperability problems with TZif include the following:
@ -355,15 +355,15 @@ for two time zones east, e.g.,
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
as a partial workaround, a writer can substitute standard time
for the next time zone east \(en e.g.,
.Dq "AST4"
for permanent
Atlantic Standard Time (\-04).
.It
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 designed for version 2 or 3 and that require strict
conformance to RFC 9636 reject version 4 files whose leap second
tables are truncated at the start or end in expiration times.
.It
Some readers ignore the footer, and instead predict future
timestamps from the time type of the last transition.
@ -378,7 +378,7 @@ and even for current timestamps it can fail for settings like
TZ="Africa/Casablanca". This corresponds to a TZif file
containing explicit transitions through the year 2087,
followed by a footer containing the TZ string
.Dq <+01>\*-1 ,
.Dq <+01>\-1 ,
which should be used only for timestamps after the last
explicit transition.
.It
@ -389,7 +389,7 @@ As a partial workaround, a writer can output a dummy (no-op)
first transition at an early time.
.It
Some readers mishandle timestamps before the first
transition that has a timestamp not less than \-2**31.
transition that has a timestamp that is 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
@ -401,7 +401,7 @@ Some readers mishandle a transition if its timestamp has
the minimum possible signed 64-bit value.
Timestamps less than \-2**59 are not recommended.
.It
Some readers mishandle TZ strings that
Some readers mishandle proleptic TZ strings that
contain
.Dq "<"
or
@ -418,9 +418,9 @@ non-ASCII characters.
These characters are not recommended.
.It
Some readers may mishandle time zone abbreviations that
contain fewer than 3 or more than 6 characters, or that
contain fewer than 3 or more than 6 characters or that
contain ASCII characters other than alphanumerics,
.Dq "\*-",
.Dq "\-",
and
.Dq "+".
These abbreviations are not recommended.
@ -430,7 +430,7 @@ 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
.Dq "IST\*-1GMT0,M10.5.0,M3.5.0/1",
.Dq "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
@ -443,7 +443,7 @@ abbreviations correctly.
.It
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
For example, with UTC offset +01:23:45 and
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
@ -462,15 +462,15 @@ Developers of distributed applications should keep this
in mind if they need to deal with pre-1970 data.
.It
Some readers mishandle timestamps before the first
transition that has a nonnegative timestamp.
transition that has a non-negative timestamp.
Readers that do not support negative timestamps are likely to
be more prone to this problem.
.It
Some readers mishandle time zone abbreviations like
.Dq "\*-08"
.Dq "\-08"
that contain
.Dq "+" ,
.Dq "\*-" ,
.Dq "+",
.Dq "\-",
or digits.
.It
Some readers mishandle UT offsets that are out of the
@ -479,7 +479,7 @@ support locations like Kiritimati that are outside this
range.
.It
Some readers mishandle UT offsets in the range [\-3599, \-1]
seconds from UT, because they integer-divide the offset by
seconds from UT because they integer-divide the offset by
3600 to get 0 and then display the hour part as
.Dq "+00" .
.It
@ -498,8 +498,8 @@ of one hour, or of 15 minutes, or of 1 minute.
.%A P. Eggert
.%A K. Murchison
.%T "The Time Zone Information Format (TZif)"
.%R RFC 8536
.%D February 2019
.%U https://datatracker.ietf.org/doc/html/rfc8536
.%U https://doi.org/10.17487/RFC8536
.%R RFC 9636
.%D October 2024
.%U https://datatracker.ietf.org/doc/html/rfc9636
.%U https://doi.org/10.17487/RFC9636
.Re

View file

@ -28,7 +28,7 @@
#endif /* !defined TZDEFRULES */
/* See Internet RFC 8536 for more details about the following format. */
/* See Internet RFC 9636 for more details about the following format. */
/*
** Each file begins with. . .

View file

@ -4,8 +4,6 @@
.SH NAME
tzselect \- select a timezone
.SH SYNOPSIS
.ie \n(.g .ds - \f(CR-\fP
.el .ds - \-
.ds d " degrees
.ds m " minutes
.ds s " seconds
@ -20,15 +18,15 @@ tzselect \- select a timezone
.\}
.B tzselect
[
.B \*-c
.B \-c
.I coord
] [
.B \*-n
.B \-n
.I limit
] [
.B \*-\*-help
.B \-\-help
] [
.B \*-\*-version
.B \-\-version
]
.SH DESCRIPTION
The
@ -40,7 +38,7 @@ The output is suitable as a value for the TZ environment variable.
All interaction with the user is done via standard input and standard error.
.SH OPTIONS
.TP
.BI "\*-c " coord
.BI "\-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
@ -70,27 +68,27 @@ seconds, with any trailing fractions represent fractional minutes or
.I SS
is present) seconds. The decimal point is that of the current locale.
For example, in the (default) C locale,
.B "\*-c\ +40.689\*-074.045"
.B "\-c\ +40.689\-074.045"
specifies 40.689\*d\*_N, 74.045\*d\*_W,
.B "\*-c\ +4041.4\*-07402.7"
.B "\-c\ +4041.4\-07402.7"
specifies 40\*d\*_41.4\*m\*_N, 74\*d\*_2.7\*m\*_W, and
.B "\*-c\ +404121\*-0740240"
.B "\-c\ +404121\-0740240"
specifies 40\*d\*_41\*m\*_21\*s\*_N, 74\*d\*_2\*m\*_40\*s\*_W.
If
.I coord
is not one of the documented forms, the resulting behavior is unspecified.
.TP
.BI "\*-n " limit
.BI "\-n " limit
When
.B \*-c
.B \-c
is used, display the closest
.I limit
locations (default 10).
.TP
.B "\*-\*-help"
.B "\-\-help"
Output help information and exit.
.TP
.B "\*-\*-version"
.B "\-\-version"
Output version information and exit.
.SH "ENVIRONMENT VARIABLES"
.TP

View file

@ -1 +1 @@
2024b
2025b

View file

@ -28,6 +28,14 @@ The
program prints the current time in each
.Ar timezone
named on the command line.
A
.Ar timezone
of
.Li -
is treated as if it were
.Pa /dev/stdin ;
this can be used to pipe TZif data into
.Nm .
.Pp
The following options are available:
.Bl -tag -width indent
@ -106,7 +114,7 @@ then a line
where
.Ar string
is a double-quoted string giving the timezone, a second line
.Dq "\*- \*- \fIinterval\fP"
.Dq "\- \- \fIinterval\fP"
describing the time interval before the first transition if any, and
zero or more following lines
.Dq "\fIdate time interval\fP",
@ -138,11 +146,11 @@ the 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
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 and the time zone
abbreviation begins with
.Dq "-"
.Dq "\-"
or is
.Dq "zzz".
.Pp

View file

@ -14,10 +14,6 @@
#include "private.h"
#include <stdio.h>
#ifndef HAVE_SNPRINTF
# define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
#endif
#ifndef HAVE_LOCALTIME_R
# define HAVE_LOCALTIME_R 1
#endif
@ -148,17 +144,6 @@ sumsize(ptrdiff_t a, ptrdiff_t b)
size_overflow();
}
/* Return the size of of the string STR, including its trailing NUL.
Report an error and exit if this would exceed INDEX_MAX which means
pointer subtraction wouldn't work. */
static ptrdiff_t
xstrsize(char const *str)
{
size_t len = strlen(str);
if (len < INDEX_MAX)
return len + 1;
size_overflow();
}
/* Return a pointer to a newly allocated buffer of size SIZE, exiting
on failure. SIZE should be positive. */
@ -266,7 +251,7 @@ tzalloc(char const *val)
static ptrdiff_t fakeenv0size;
void *freeable = NULL;
char **env = fakeenv, **initial_environ;
ptrdiff_t valsize = xstrsize(val);
ptrdiff_t valsize = strlen(val) + 1;
if (fakeenv0size < valsize) {
char **e = environ, **to;
ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */
@ -427,7 +412,7 @@ saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp)
if (HAVE_LOCALTIME_RZ)
return ab;
else {
ptrdiff_t absize = xstrsize(ab);
ptrdiff_t absize = strlen(ab) + 1;
if (*bufalloc < absize) {
free(*buf);
@ -489,6 +474,7 @@ main(int argc, char *argv[])
register time_t cuthitime;
time_t now;
bool iflag = false;
size_t arglenmax = 0;
cutlotime = absolute_min_time;
cuthitime = absolute_max_time;
@ -588,15 +574,21 @@ main(int argc, char *argv[])
now = time(NULL);
now |= !now;
}
longest = 0;
for (i = optind; i < argc; i++) {
size_t arglen = strlen(argv[i]);
if (longest < arglen)
longest = min(arglen, INT_MAX);
if (arglenmax < arglen)
arglenmax = arglen;
}
if (!HAVE_SETENV && INDEX_MAX <= arglenmax)
size_overflow();
longest = min(arglenmax, INT_MAX - 2);
for (i = optind; i < argc; ++i) {
timezone_t tz = tzalloc(argv[i]);
/* Treat "-" as standard input on platforms with /dev/stdin.
It's not worth the bother of supporting "-" on other
platforms, as that would need temp files. */
timezone_t tz = tzalloc(strcmp(argv[i], "-") == 0
? "/dev/stdin" : argv[i]);
char const *ab;
time_t t;
struct tm tm, newtm;
@ -697,7 +689,7 @@ yeartot(intmax_t y)
return absolute_max_time;
seconds = diff400 * SECSPER400YEARS;
years = diff400 * 400;
} else {
} else {
seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
years = 1;
}
@ -928,13 +920,10 @@ showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
}
}
#if HAVE_SNPRINTF
# define my_snprintf snprintf
#else
/* On pre-C99 platforms, a snprintf substitute good enough for us. */
#if !HAVE_SNPRINTF
# include <stdarg.h>
/* A substitute for snprintf that is good enough for zdump. */
static int
ATTRIBUTE_FORMAT((printf, 3, 4)) static int
my_snprintf(char *s, size_t size, char const *format, ...)
{
int n;
@ -962,6 +951,7 @@ my_snprintf(char *s, size_t size, char const *format, ...)
va_end(args);
return n;
}
# define snprintf my_snprintf
#endif
/* Store into BUF, of size SIZE, a formatted local time taken from *TM.
@ -976,10 +966,10 @@ format_local_time(char *buf, ptrdiff_t size, struct tm const *tm)
{
int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
return (ss
? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
? snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
: mm
? my_snprintf(buf, size, "%02d:%02d", hh, mm)
: my_snprintf(buf, size, "%02d", hh));
? snprintf(buf, size, "%02d:%02d", hh, mm)
: snprintf(buf, size, "%02d", hh));
}
/* Store into BUF, of size SIZE, a formatted UT offset for the
@ -1014,10 +1004,10 @@ format_utc_offset(char *buf, ptrdiff_t size, struct tm const *tm, time_t t)
mm = off / 60 % 60;
hh = off / 60 / 60;
return (ss || 100 <= hh
? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
? snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
: mm
? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
: my_snprintf(buf, size, "%c%02ld", sign, hh));
? snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
: snprintf(buf, size, "%c%02ld", sign, hh));
}
/* Store into BUF (of size SIZE) a quoted string representation of P.
@ -1120,7 +1110,7 @@ istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
for (abp = ab; is_alpha(*abp); abp++)
continue;
len = (!*abp && *ab
? my_snprintf(b, s, "%s", ab)
? snprintf(b, s, "%s", ab)
: format_quoted_string(b, s, ab));
if (s <= len)
return false;
@ -1128,7 +1118,7 @@ istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
}
formatted_len
= (tm->tm_isdst
? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
? snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
: 0);
}
break;

View file

@ -112,13 +112,13 @@ Link \fItimezone\fP posixrules
If
.Ar timezone
is
.Dq "\*-"
.Dq "\-"
(the default), any already-existing link is removed.
.Pp
Unless
.Ar timezone
is
.Dq "\*-" ,
.Dq "\-" ,
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
@ -148,6 +148,10 @@ omits data intended for negative timestamps (i.e., before the Epoch), and
.Fl r @0/@2147483648
outputs data intended only for nonnegative timestamps that fit into
31-bit signed integers.
On platforms with GNU
.Nm date ,
.Dq "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
@ -366,7 +370,15 @@ separate script to further restrict in which
of years the rule would apply.
.It IN
Names the month in which the rule takes effect.
Month names may be abbreviated.
Month names may be abbreviated as mentioned previously;
for example, January can appear as
.Dq January ,
.Dq JANU
or
.Dq Ja ,
but not as
.Dq j
which would be ambiguous with both June and July.
.It ON
Gives the day on which the rule takes effect.
Recognized forms include:
@ -389,7 +401,12 @@ or a weekday name preceded by
.Dq "last"
(e.g.,
.Ql "lastSunday" )
may be abbreviated or spelled out in full.
may be abbreviated as mentioned previously,
e.g.,
.Dq Su
for Sunday and
.Dq lastsa
for the last Saturday.
There must be no white space characters within the
.Ar ON
field.
@ -540,7 +557,7 @@ field,
giving the amount of time to be added to local standard time
and whether the resulting time is standard or daylight saving.
Standard time applies if this field is
.Ql \*-
.Ql \-
or for timestamps occurring before any rule takes effect.
When an amount of time is given, only the sum of standard time and
this amount matters.

View file

@ -526,19 +526,19 @@ memcheck(void *ptr)
}
static void *
emalloc(size_t size)
xmalloc(size_t size)
{
return memcheck(malloc(size));
}
static void *
erealloc(void *ptr, size_t size)
xrealloc(void *ptr, size_t size)
{
return memcheck(realloc(ptr, size));
}
static char *
estrdup(char const *str)
xstrdup(char const *str)
{
return memcheck(strdup(str));
}
@ -567,7 +567,7 @@ growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
{
return (nitems < *nitems_alloc
? ptr
: erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
: xrealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
}
/*
@ -654,6 +654,8 @@ close_file(FILE *stream, char const *dir, char const *name,
char const *e = (ferror(stream) ? _("I/O error")
: fclose(stream) != 0 ? strerror(errno) : NULL);
if (e) {
if (name && *name == '/')
dir = NULL;
fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
dir ? dir : "", dir ? "/" : "",
name ? name : "", name ? ": " : "",
@ -961,6 +963,9 @@ static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH
| S_IWUSR);
static const char * tzdefault;
/* True if DIRECTORY ends in '/'. */
static bool directory_ends_in_slash;
/* -1 if the TZif output file should be slim, 0 if default, 1 if the
output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT
determines the default. */
@ -1166,6 +1171,7 @@ _("invalid file mode"));
return EXIT_FAILURE;
associate();
change_directory(directory);
directory_ends_in_slash = directory[strlen(directory) - 1] == '/';
catch_signals();
for (i = 0; i < nzones; i = j) {
/*
@ -1353,7 +1359,7 @@ random_dirent(char const **name, char **namealloc)
uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
if (!dst) {
dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
dst = xmalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
memcpy(dst, src, dirlen);
memcpy(dst + dirlen, prefix, prefixlen);
dst[dirlen + prefixlen + suffixlen] = '\0';
@ -1370,6 +1376,20 @@ random_dirent(char const **name, char **namealloc)
}
}
/* For diagnostics the directory, and file name relative to that
directory, respectively. A diagnostic routine can name FILENAME by
outputting diagdir(FILENAME), then diagslash(FILENAME), then FILENAME. */
static char const *
diagdir(char const *filename)
{
return *filename == '/' ? "" : directory;
}
static char const *
diagslash(char const *filename)
{
return &"/"[*filename == '/' || directory_ends_in_slash];
}
/* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
name of the temporary file that will eventually be renamed to
*OUTNAME. Assign the temporary file's name to both *OUTNAME and
@ -1406,8 +1426,9 @@ open_outfile(char const **outname, char **tempname)
} else if (fopen_errno == EEXIST)
random_dirent(outname, tempname);
else {
fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
progname, directory, *outname, strerror(fopen_errno));
fprintf(stderr, _("%s: Can't create %s%s%s: %s\n"),
progname, diagdir(*outname), diagslash(*outname), *outname,
strerror(fopen_errno));
exit(EXIT_FAILURE);
}
}
@ -1424,9 +1445,10 @@ rename_dest(char *tempname, char const *name)
if (tempname) {
if (rename(tempname, name) != 0) {
int rename_errno = errno;
(void)remove(tempname);
fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
progname, directory, name, strerror(rename_errno));
remove(tempname);
fprintf(stderr, _("%s: rename to %s%s%s: %s\n"),
progname, diagdir(name), diagslash(name), name,
strerror(rename_errno));
exit(EXIT_FAILURE);
}
free(tempname);
@ -1436,7 +1458,8 @@ rename_dest(char *tempname, char const *name)
/* 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. */
relative or absolute. Return a null pointer if the symlink contents
was not computed because LINKNAME is absolute but DIRECTORY is not. */
static char *
relname(char const *target, char const *linkname)
{
@ -1449,8 +1472,10 @@ relname(char const *target, char const *linkname)
size_t len = strlen(directory);
size_t lenslash = len + (len && directory[len - 1] != '/');
size_t targetsize = strlen(target) + 1;
if (*directory != '/')
return NULL;
linksize = size_sum(lenslash, targetsize);
f = result = emalloc(linksize);
f = result = xmalloc(linksize);
memcpy(result, directory, len);
result[len] = '/';
memcpy(result + lenslash, target, targetsize);
@ -1464,7 +1489,7 @@ relname(char const *target, char const *linkname)
dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
if (dotdotetcsize <= linksize) {
if (!result)
result = emalloc(dotdotetcsize);
result = xmalloc(dotdotetcsize);
for (i = 0; i < dotdots; i++)
memcpy(result + 3 * i, "../", 3);
memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
@ -1500,8 +1525,9 @@ dolink(char const *target, char const *linkname, bool staysymlink)
return;
else {
char const *e = strerror(errno);
fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
progname, directory, linkname, e);
fprintf(stderr, _("%s: Can't remove %s%s%s: %s\n"),
progname, diagdir(linkname), diagslash(linkname), linkname,
e);
exit(EXIT_FAILURE);
}
}
@ -1544,8 +1570,9 @@ dolink(char const *target, char const *linkname, bool staysymlink)
mkdirs(linkname, true);
linkdirs_made = true;
} else {
fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
progname, directory, target, directory, outname,
fprintf(stderr, _("%s: Can't link %s%s%s to %s%s%s: %s\n"),
progname, diagdir(target), diagslash(target), target,
diagdir(outname), diagslash(outname), outname,
strerror(link_errno));
exit(EXIT_FAILURE);
}
@ -1554,21 +1581,23 @@ dolink(char const *target, char const *linkname, bool staysymlink)
bool absolute = *target == '/';
char *linkalloc = absolute ? NULL : relname(target, linkname);
char const *contents = absolute ? target : linkalloc;
int symlink_errno;
int symlink_errno = -1;
while (true) {
if (symlink(contents, outname) == 0) {
symlink_errno = 0;
break;
if (contents) {
while (true) {
if (symlink(contents, outname) == 0) {
symlink_errno = 0;
break;
}
symlink_errno = errno;
if (symlink_errno == EEXIST)
random_dirent(&outname, &tempname);
else if (symlink_errno == ENOENT && !linkdirs_made) {
mkdirs(linkname, true);
linkdirs_made = true;
} else
break;
}
symlink_errno = errno;
if (symlink_errno == EEXIST)
random_dirent(&outname, &tempname);
else if (symlink_errno == ENOENT && !linkdirs_made) {
mkdirs(linkname, true);
linkdirs_made = true;
} else
break;
}
free(linkalloc);
if (symlink_errno == 0) {
@ -1581,8 +1610,8 @@ dolink(char const *target, char const *linkname, bool staysymlink)
fp = fopen(target, "rb");
if (!fp) {
char const *e = strerror(errno);
fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
progname, directory, target, e);
fprintf(stderr, _("%s: Can't read %s%s%s: %s\n"),
progname, diagdir(target), diagslash(target), target, e);
exit(EXIT_FAILURE);
}
tp = open_outfile(&outname, &tempname);
@ -1593,6 +1622,8 @@ dolink(char const *target, char const *linkname, bool staysymlink)
if (link_errno != ENOTSUP)
warning(_("copy used because hard link failed: %s"),
strerror(link_errno));
else if (symlink_errno < 0)
warning(_("copy used because symbolic link not obvious"));
else if (symlink_errno != ENOTSUP)
warning(_("copy used because symbolic link failed: %s"),
strerror(symlink_errno));
@ -1906,8 +1937,8 @@ inrule(char **fields, int nfields)
fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
fields[RF_TOD]))
return;
r.r_name = estrdup(fields[RF_NAME]);
r.r_abbrvar = estrdup(fields[RF_ABBRVAR]);
r.r_name = xstrdup(fields[RF_NAME]);
r.r_abbrvar = xstrdup(fields[RF_ABBRVAR]);
if (max_abbrvar_len < strlen(r.r_abbrvar))
max_abbrvar_len = strlen(r.r_abbrvar);
rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
@ -1990,7 +2021,8 @@ 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"));
if ((cp = strchr(fields[i_format], '%')) != 0) {
cp = strchr(fields[i_format], '%');
if (cp) {
if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/')) {
error(_("invalid abbreviation format"));
@ -2028,9 +2060,9 @@ inzsub(char **fields, int nfields, bool iscont)
return false;
}
}
z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]);
z.z_rule = estrdup(fields[i_rule]);
z.z_format = cp1 = estrdup(fields[i_format]);
z.z_name = iscont ? NULL : xstrdup(fields[ZF_NAME]);
z.z_rule = xstrdup(fields[i_rule]);
z.z_format = cp1 = xstrdup(fields[i_format]);
if (z.z_format_specifier == 'z') {
cp1[cp - fields[i_format]] = 's';
if (noise)
@ -2173,8 +2205,8 @@ inlink(char **fields, int nfields)
return;
l.l_filenum = filenum;
l.l_linenum = linenum;
l.l_target = estrdup(fields[LF_TARGET]);
l.l_linkname = estrdup(fields[LF_LINKNAME]);
l.l_target = xstrdup(fields[LF_TARGET]);
l.l_linkname = xstrdup(fields[LF_LINKNAME]);
links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
links[nlinks++] = l;
}
@ -2197,7 +2229,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
rp->r_month = lp->l_value;
rp->r_todisstd = false;
rp->r_todisut = false;
dp = estrdup(timep);
dp = xstrdup(timep);
if (*dp != '\0') {
ep = dp + strlen(dp) - 1;
switch (lowerit(*ep)) {
@ -2272,19 +2304,23 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
** Sun<=20
** Sun>=7
*/
dp = estrdup(dayp);
dp = xstrdup(dayp);
if ((lp = byword(dp, lasts)) != NULL) {
rp->r_dycode = DC_DOWLEQ;
rp->r_wday = lp->l_value;
rp->r_dayofmonth = len_months[1][rp->r_month];
} else {
if ((ep = strchr(dp, '<')) != 0)
rp->r_dycode = DC_DOWLEQ;
else if ((ep = strchr(dp, '>')) != 0)
rp->r_dycode = DC_DOWGEQ;
ep = strchr(dp, '<');
if (ep)
rp->r_dycode = DC_DOWLEQ;
else {
ep = strchr(dp, '>');
if (ep)
rp->r_dycode = DC_DOWGEQ;
else {
ep = dp;
rp->r_dycode = DC_DOM;
}
}
if (rp->r_dycode != DC_DOM) {
*ep++ = 0;
@ -2427,7 +2463,7 @@ writezone(const char *const name, const char *const string, char version,
/* Allocate the ATS and TYPES arrays via a single malloc,
as this is a bit faster. Do not malloc(0) if !timecnt,
as that might return NULL even on success. */
zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt,
zic_t *ats = xmalloc(align_to(size_product(timecnt + !timecnt,
sizeof *ats + 1),
alignof(zic_t)));
void *typesptr = ats + timecnt;
@ -2802,7 +2838,7 @@ writezone(const char *const name, const char *const string, char version,
if (thisleapexpiry) {
/* Append a no-op leap correction indicating when the leap
second table expires. Although this does not conform to
Internet RFC 8536, most clients seem to accept this and
Internet RFC 9636, most clients seem to accept this and
the plan is to amend the RFC to allow this in version 4
TZif files. */
puttzcodepass(leapexpires, fp, pass);
@ -3059,7 +3095,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
result[0] = '\0';
/* Internet RFC 8536 section 5.1 says to use an empty TZ string if
/* Internet RFC 9636 section 6.1 says to use an empty TZ string if
future timestamps are truncated. */
if (hi_time < max_time)
return -1;
@ -3187,9 +3223,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
startbuf = emalloc(max_abbr_len + 1);
ab = emalloc(max_abbr_len + 1);
envvar = emalloc(max_envvar_len + 1);
startbuf = xmalloc(max_abbr_len + 1);
ab = xmalloc(max_abbr_len + 1);
envvar = xmalloc(max_envvar_len + 1);
INITIALIZE(untiltime);
INITIALIZE(starttime);
/*
@ -3972,7 +4008,7 @@ mkdirs(char const *argname, bool ancestors)
if (Dflag)
return;
char *name = estrdup(argname);
char *name = xstrdup(argname);
char *cp = name;
/* On MS-Windows systems, do not worry about drive letters or