1
0
mirror of https://git.FreeBSD.org/src.git synced 2026-06-02 11:24:32 +00:00

Import tzcode 2026b

This commit is contained in:
Dag-Erling Smørgrav
2026-04-24 21:54:03 +02:00
parent cd59570c18
commit b17ccc1f45
5 changed files with 166 additions and 68 deletions
+45 -8
View File
@@ -1,10 +1,47 @@
News for the tz database
Release 2026b - 2026-04-22 23:06:43 -0700
Briefly:
British Columbia moved to permanent -07 on 2026-03-09.
Some more overflow bugs have been fixed in zic.
Changes to future timestamps
British Columbias 2026-03-08 spring forward was its last
foreseeable clock change, as it moved to permanent -07 thereafter.
(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
planned to be removed after CLDR is fixed.
Changes to code
zic no longer mishandles a last transition to a new time type.
zic no longer overflows a buffer when generating a TZ string like
"PST-167:59:58PDT-167:59:59,M11.5.6/-167:59:59,M12.5.6/-167:59:59",
which can occur with adversarial input. (Thanks to Naveed Khan.)
zic no longer generates a longer TZif file than necessary when
an earlier time zone abbreviation is a suffix of a later one.
As a nice side effect, zic no longer overflows a buffer when given
a long series of abbreviations, each a suffix of the next.
(Buffer overflow reported by Arthur Chan.)
zic no longer overflows an int when processing input like Zone
Ouch 2147483648:00:00 - LMT. The int overflow can lead to buffer
overflow in adversarial cases. (Thanks to Naveed Khan.)
zic now checks for signals more often.
Release 2026a - 2026-03-01 22:59:49 -0800
Briefly:
Moldova has used EU transition times since 2022.
The "right" TZif files are no longer installed by default.
The right TZif files are no longer installed by default.
-DTZ_RUNTIME_LEAPS=0 disables runtime support for leap seconds.
TZif files are no longer limited to 50 bytes of abbreviations.
zic is no longer limited to 50 leap seconds.
@@ -25,23 +62,23 @@ Release 2026a - 2026-03-01 22:59:49 -0800
The Makefile no longer by default installs an alternate set
of TZif files for system clocks that count leap seconds.
Install with 'make REDO=posix_right' to get the old default,
Install with make REDO=posix_right to get the old default,
which is rarely used in major downstream distributions.
If your system clock counts leap seconds (contrary to POSIX),
it is better to install with 'make REDO=right_only'.
it is better to install with make REDO=right_only.
This change does not affect the leapseconds file, which is still
installed as before.
The Makefile's POSIXRULES option, which was declared obsolete in
release 2019b, has been removed. The Makefile's build procedure
The Makefiles POSIXRULES option, which was declared obsolete in
release 2019b, has been removed. The Makefiles build procedure
thus no longer optionally installs the obsolete posixrules file.
Changes to code
Compiling with the new option -DTZ_RUNTIME_LEAPS=0 disables
runtime support for leap seconds. Although this conforms to
POSIX, shrinks tzcode's attack surface, and is more efficient,
it fails to support Internet RFC 9636's leap seconds.
POSIX, shrinks tzcodes attack surface, and is more efficient,
it fails to support Internet RFC 9636s leap seconds.
zic now can generate, and localtime.c can now use, TZif files that
hold up to 256 bytes of abbreviations, counting trailing NULs.
@@ -51,7 +88,7 @@ Release 2026a - 2026-03-01 22:59:49 -0800
zic -L can now generate TZif files with more than 50 leap seconds.
This helps test TZif readers not limited to 50 leap seconds, as
tzcode's localtime.c is; it has little immediate need for
tzcodes localtime.c is; it has little immediate need for
practical timekeeping as there have been only 27 leap seconds and
possibly there will be no more, due to planned changes to UTC.
zic -v warns if its output exceeds the old 50-second limit.
+1 -1
View File
@@ -446,7 +446,7 @@ Includes the song “Does Anybody Really Know What Time It Is?”.
</li>
<li>
Emanuele Arciuli,
<a href="https://neumarecords.org/ols/products/william-duckworth-the-time-curve-preludes"><em>The Time Curve Preludes</em></a> (2023).
<a href="https://williamduckworth.bandcamp.com/album/the-time-curve-preludes"><em>The Time Curve Preludes</em></a> (2023).
Neuma 174, 44:46.
The title piece, composed by
<a href="https://en.wikipedia.org/wiki/William_Duckworth_(composer)">William
+15 -7
View File
@@ -441,7 +441,7 @@ transition in the <code><abbr>tz</abbr></code> database.</li>
Database Parser</a> is a
<a href="https://en.wikipedia.org/wiki/C++">C++</a> parser and
runtime library with a <a
href="https://en.cppreference.com/w/cpp/chrono.html"><code>std::chrono</code> API</a>
href="https://en.cppreference.com/cpp/chrono"><code>std::chrono</code> API</a>
that is a standard part of C++.
It is freely available under the
<abbr title="Massachusetts Institute of Technology">MIT</abbr> license.</li>
@@ -1135,8 +1135,7 @@ Network Time Protocol Best Current Practices</a>
applications requiring accurate <abbr>UTC</abbr> or civil time,
and is intended for use only in single, well-controlled environments.</li>
<li>The <a
href="https://pairlist6.pair.net/mailman/listinfo/leapsecs">Leap
Second Discussion List</a> covers <a
href="https://groups.io/g/LEAPSECS">LEAPSECS List</a> covers <a
href="https://gge.ext.unb.ca/Resources/gpsworld.november99.pdf">McCarthy
and Klepczynskis 1999 proposal to discontinue leap seconds</a>,
discussed further in
@@ -1146,21 +1145,30 @@ leap second: its history and possible future</a>.
might be redefined
without Leap Seconds</a> gives pointers on this
contentious issue.
The General Conference on Weights and Measures
The General Conference on Weights and Measures (CGPM)
<a href="https://www.bipm.org/en/cgpm-2022/resolution-4">decided in 2022</a>
to discontinue the use of leap seconds by 2035, and requested that no
discontinuous adjustments be made to UTC for at least a century.
The World Radiocommunication Conference <a
href="https://www.itu.int/dms_pub/itu-r/opb/act/R-ACT-WRC.15-2023-PDF-E.pdf">resolved
in 2023</a> to cooperate with this process. One proposal to implement this
in 2023</a> to cooperate with this process. A draft <a
href="https://www.bipm.org/documents/d/guest/cgpm-2026-draft-resolutions">Resolution
C to make continuous UTC effective on 2027-05-20</a>,
and thereby discontinue leap seconds,
has been scheduled for the 28th CGPM starting 2026-10-13 in Paris.
One proposal to implement this
would replace leap seconds with seven 13-second leap smears occurring once per
decade until 2100, with leap smears after that gradually increasing in size.
See:
<ul>
<li>Levine J. <a href="https://www.preprints.org/manuscript/202406.0043">A
<li>Levine J. <a href="https://tf.nist.gov/general/pdf/3242.pdf">A
proposal to change the leap-second adjustments to
coordinated universal time</a>. <em>Metrologia.</em> 2024;61(5):055002. doi:<a
href="https://doi.org/10.1088/1681-7575/ad6266">10.1088/1681-7575/ad6266</a>.</li>
href="https://doi.org/10.1088/1681-7575/ad6266">10.1088/1681-7575/ad6266</a>
with followups in doi:<a
href="https://doi.org/10.1088/1681-7575/ade314">10.1088/1681-7575/ade314</a>
and doi:<a
href="https://doi.org/10.1088/1681-7575/ade315">10.1088/1681-7575/ade315</a>.</li>
</ul>
However, there is still no consensus on whether this is the best way
to replace leap seconds.
+1 -1
View File
@@ -1 +1 @@
2026a
2026b
+104 -51
View File
@@ -253,11 +253,13 @@ symlink(char const *target, char const *linkname)
(errno = ENOTSUP, -1)
#endif
static int addabbr(char[TZ_MAX_CHARS], int *, char const *);
static void addtt(zic_t starttime, int type);
static int addtype(zic_t, char const *, bool, bool, bool);
static void leapadd(zic_t, int, int);
static void adjleap(void);
static void associate(void);
static void 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);
@@ -270,11 +272,11 @@ static void inrule(char ** fields, int nfields);
static bool inzcont(char ** fields, int nfields);
static bool inzone(char ** fields, int nfields);
static bool inzsub(char **, int, bool);
static int itssymlink(char const *, int *);
static bool is_alpha(char a);
static int itssymlink(char const *, int *);
static void leapadd(zic_t, int, int);
static char lowerit(char);
static void mkdirs(char const *, bool);
static void newabbr(const char * abbr);
static zic_t oadd(zic_t t1, zic_t t2);
static zic_t omul(zic_t, zic_t);
static void outzone(const struct zone * zp, ptrdiff_t ntzones);
@@ -704,6 +706,7 @@ eat(int fnum, lineno num)
ATTRIBUTE_FORMAT((printf, 1, 0)) static void
verror(const char *const string, va_list args)
{
check_for_signal();
/*
** Match the format of "cc" to allow sh users to
** zic ... 2>&1 | error -t "*" -v
@@ -1074,6 +1077,7 @@ make_links(void)
warning(_("link %s targeting link %s"),
links[i].l_linkname, links[i].l_target);
}
check_for_signal();
}
}
@@ -1391,6 +1395,7 @@ main(int argc, char **argv)
for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
continue;
outzone(&zones[i], j - i);
check_for_signal();
}
make_links();
if (lcltime != NULL) {
@@ -1486,9 +1491,11 @@ get_rand_u64(void)
static int nwords;
if (!nwords) {
ssize_t s;
do
for (;; check_for_signal()) {
s = getrandom(entropy_buffer, sizeof entropy_buffer, 0);
while (s < 0 && errno == EINTR);
if (! (s < 0 && errno == EINTR))
break;
}
nwords = s < 0 ? -1 : s / sizeof *entropy_buffer;
}
@@ -1516,7 +1523,7 @@ get_rand_u64(void)
rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1,
r = 0, rmax = 0;
do {
for (;; check_for_signal()) {
uint_fast64_t rmax1 = rmax;
if (rmod) {
/* Avoid signed integer overflow on theoretical platforms
@@ -1527,7 +1534,9 @@ get_rand_u64(void)
rmax1 = nrand * rmax1 + rand_max;
r = nrand * r + rand();
rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX;
} while (rmax < UINT_FAST64_MAX);
if (UINT_FAST64_MAX <= rmax)
break;
}
return r;
}
@@ -1574,9 +1583,11 @@ random_dirent(char const **name, char **namealloc)
*name = *namealloc = dst;
}
do
for (;; check_for_signal()) {
r = get_rand_u64();
while (unfair_min <= r);
if (r < unfair_min)
break;
}
for (i = 0; i < suffixlen; i++) {
dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen];
@@ -1611,7 +1622,7 @@ open_outfile(char const **outname, char **tempname)
if (!*tempname)
random_dirent(outname, tempname);
while (true) {
for (;; check_for_signal()) {
int oflags = O_WRONLY | O_BINARY | O_CREAT | O_EXCL;
int fd = open(*outname, oflags, creat_perms);
int err;
@@ -1725,8 +1736,6 @@ dolink(char const *target, char const *linkname, bool staysymlink)
char const *outname = linkname;
int targetissym = -2, linknameissym = -2;
check_for_signal();
if (strcmp(target, "-") == 0) {
if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR)
return;
@@ -1739,7 +1748,7 @@ dolink(char const *target, char const *linkname, bool staysymlink)
}
}
while (true) {
for (;; check_for_signal()) {
if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW)
== 0) {
link_errno = 0;
@@ -1791,7 +1800,7 @@ dolink(char const *target, char const *linkname, bool staysymlink)
int symlink_errno = -1;
if (contents) {
while (true) {
for (;; check_for_signal()) {
if (symlink(contents, outname) == 0) {
symlink_errno = 0;
break;
@@ -1822,7 +1831,7 @@ dolink(char const *target, char const *linkname, bool staysymlink)
exit(EXIT_FAILURE);
}
tp = open_outfile(&outname, &tempname);
while ((c = getc(fp)) != EOF)
for (; (c = getc(fp)) != EOF; check_for_signal())
putc(c, tp);
close_file(tp, directory, linkname, tempname);
close_file(fp, directory, target, NULL);
@@ -1946,7 +1955,7 @@ static bool
inputline(FILE *fp, char *buf, ptrdiff_t bufsize)
{
ptrdiff_t linelen = 0, ch;
while ((ch = getc(fp)) != '\n') {
for (; (ch = getc(fp)) != '\n'; check_for_signal()) {
if (ch < 0) {
if (ferror(fp)) {
error(_("input error"));
@@ -2033,6 +2042,7 @@ infile(int fnum, char const *name)
default: unreachable();
}
}
check_for_signal();
}
close_file(fp, NULL, filename(fnum), NULL);
if (wantcont)
@@ -2927,30 +2937,27 @@ writezone(const char *const name, const char *const string, char version,
: i == thisdefaulttype ? old0 : i]
= thistypecnt++;
for (i = 0; i < sizeof indmap / sizeof indmap[0]; ++i)
indmap[i] = -1;
thischarcnt = stdcnt = utcnt = 0;
for (i = old0; i < typecnt; i++) {
register char * thisabbr;
if (omittype[i])
continue;
if (ttisstds[i])
stdcnt = thistypecnt;
if (ttisuts[i])
utcnt = thistypecnt;
if (indmap[desigidx[i]] >= 0)
continue;
thisabbr = &chars[desigidx[i]];
for (j = 0; j < thischarcnt; ++j)
if (strcmp(&thischars[j], thisabbr) == 0)
break;
if (j == thischarcnt) {
strcpy(&thischars[thischarcnt], thisabbr);
thischarcnt += strlen(thisabbr) + 1;
}
indmap[desigidx[i]] = j;
addabbr(thischars, &thischarcnt, &chars[desigidx[i]]);
}
/* Now that all abbrevs have been added to THISCHARS,
it is safe to set INDMAP without worrying about
whether the abbrevs might move later. */
for (i = 0; i < TZ_MAX_CHARS; i++)
indmap[i] = -1;
for (i = old0; i < typecnt; i++)
if (!omittype[i] && indmap[desigidx[i]] < 0)
indmap[desigidx[i]] = addabbr(thischars, &thischarcnt,
&chars[desigidx[i]]);
if (pass == 1 && !want_bloat()) {
hicut = thisleapexpiry = false;
pretranstype = -1;
@@ -3173,11 +3180,11 @@ stringoffset(char *result, zic_t offset)
offset /= SECSPERMIN;
minutes = offset % MINSPERHOUR;
offset /= MINSPERHOUR;
hours = offset;
if (hours >= HOURSPERDAY * DAYSPERWEEK) {
if (offset >= HOURSPERDAY * DAYSPERWEEK) {
result[0] = '\0';
return 0;
}
hours = offset;
len += sprintf(result + len, "%d", hours);
if (minutes != 0 || seconds != 0) {
len += sprintf(result + len, ":%02d", minutes);
@@ -3416,12 +3423,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
int nonTZlimtype = -1;
zic_t max_year0;
int defaulttype = -1;
int max_stringoffset_len = sizeof "-167:59:59" - 1;
int max_comma_stringrule_len = (sizeof ",M12.5.6/" - 1
+ max_stringoffset_len);
check_for_signal();
/* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND. */
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
max_envvar_len = 2 * (max_abbr_len + max_stringoffset_len
+ max_comma_stringrule_len);
startbuf = xmalloc(max_abbr_len + 1);
ab = xmalloc(max_abbr_len + 1);
@@ -3522,7 +3533,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
startttisut);
if (usestart) {
addtt(starttime, type);
if (useuntil && nonTZlimtime < starttime) {
if (nonTZlimtime < starttime) {
nonTZlimtime = starttime;
nonTZlimtype = type;
}
@@ -3769,6 +3780,7 @@ static int
addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
{
register int i, j;
int charcnt0;
/* RFC 9636 section 3.2 specifies this range for utoff. */
if (! (-TWO_31_MINUS_1 <= utoff && utoff <= TWO_31_MINUS_1)) {
@@ -3778,12 +3790,18 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut)
if (!want_bloat())
ttisstd = ttisut = false;
for (j = 0; j < charcnt; ++j)
if (strcmp(&chars[j], abbr) == 0)
break;
if (j == charcnt)
newabbr(abbr);
else {
checkabbr(abbr);
charcnt0 = charcnt;
j = addabbr(chars, &charcnt, abbr);
if (charcnt0 < charcnt) {
/* If an abbreviation was inserted, increment indexes no
earlier than the insert by the size of the insertion,
so that they continue to point to the same contents. */
for (i = 0; i < typecnt; i++)
if (j <= desigidx[i])
desigidx[i] += charcnt - charcnt0;
} else {
/* If there's already an entry, return its index. */
for (i = 0; i < typecnt; i++)
if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i]
@@ -4168,10 +4186,8 @@ will not work with pre-2004 versions of zic"));
}
static void
newabbr(const char *string)
checkabbr(char const *string)
{
register int i;
if (strcmp(string, GRANDPARENTED) != 0) {
register const char * cp;
const char * mp;
@@ -4190,13 +4206,50 @@ mp = _("time zone abbreviation differs from POSIX standard");
if (mp != NULL)
warning("%s (%s)", mp, string);
}
i = strnlen(string, TZ_MAX_CHARS - charcnt) + 1;
if (charcnt + i > TZ_MAX_CHARS) {
error(_("too many, or too long, time zone abbreviations"));
exit(EXIT_FAILURE);
}
strcpy(&chars[charcnt], string);
charcnt += i;
}
/* Put into CHS, which currently contains *PNCHS bytes containing
NUL-terminated abbreviations none of which are suffixes of another,
the abbreviation ABBR including its trailing NUL.
If ABBR does not already appear in CHS,
possibly as a suffix of an existing abbreviation,
add ABBR to CHS, remove from CHS any abbreviation
that is a suffix of ABBR, and increment *PNCHS accordingly.
Return the index of ABBR after any modifications to CHS are made.
If all abbreviations have already been added, this function
lets the caller look up the index of an existing abbreviation. */
static int
addabbr(char chs[TZ_MAX_CHARS], int *pnchs, char const *abbr)
{
int nchs = *pnchs;
int alen = strlen(abbr), nchs_incr = alen + 1;
int i;
for (i = 0; i < nchs; ) {
int clen = strlen(&chs[i]);
if (alen <= clen) {
/* If ABBR is a suffix of an abbreviation in CHS,
return the index of ABBR in CHS. */
int isuff = i + (clen - alen);
if (memcmp(&chs[isuff], abbr, alen) == 0)
return isuff;
} else if (memcmp(&chs[i], &abbr[alen - clen], clen) == 0) {
/* An abbreviation in CHS is a substring of ABBR.
Replace it with ABBR, instead of the more-common
actions of appending ABBR or doing nothing. */
nchs_incr = alen - clen;
break;
}
i += clen + 1;
}
if (TZ_MAX_CHARS < nchs + nchs_incr) {
error(_("too many, or too long, time zone abbreviations"));
exit(EXIT_FAILURE);
}
memmove(&chs[i + nchs_incr], &chs[i], nchs - i);
memcpy(&chs[i], abbr, nchs_incr);
*pnchs = nchs + nchs_incr;
return i;
}
/* Ensure that the directories of ARGNAME exist, by making any missing