s helps test TZif readers not limited to 50 leap seconds, as - tzcode's localtime.c is; it has little immediate need for + tzcode’s 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. diff --git a/contrib/tzcode/tz-art.html b/contrib/tzcode/tz-art.html index 1bd1bf781166..7870a6187390 100644 --- a/contrib/tzcode/tz-art.html +++ b/contrib/tzcode/tz-art.html @@ -446,7 +446,7 @@ Includes the song “Does Anybody Really Know What Time It Is?”.
  • Emanuele Arciuli, -The Time Curve Preludes (2023). +The Time Curve Preludes (2023). Neuma 174, 44:46. The title piece, composed by William diff --git a/contrib/tzcode/tz-link.html b/contrib/tzcode/tz-link.html index 5f1989f014b3..4d2c12b09452 100644 --- a/contrib/tzcode/tz-link.html +++ b/contrib/tzcode/tz-link.html @@ -441,7 +441,7 @@ transition in the tz database.
  • Database Parser is a C++ parser and runtime library with a std::chrono API +href="https://en.cppreference.com/cpp/chrono">std::chrono API that is a standard part of C++. It is freely available under the MIT license. @@ -1135,8 +1135,7 @@ Network Time Protocol Best Current Practices applications requiring accurate UTC or civil time, and is intended for use only in single, well-controlled environments.
  • The Leap -Second Discussion List covers LEAPSECS List covers McCarthy and Klepczynski’s 1999 proposal to discontinue leap seconds, discussed further in @@ -1146,21 +1145,30 @@ leap second: its history and possible future. might be redefined without Leap Seconds gives pointers on this contentious issue. -The General Conference on Weights and Measures +The General Conference on Weights and Measures (CGPM) decided in 2022 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 resolved -in 2023 to cooperate with this process. One proposal to implement this +in 2023 to cooperate with this process. A draft Resolution +C to make continuous UTC effective on 2027-05-20, +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: However, there is still no consensus on whether this is the best way to replace leap seconds. diff --git a/contrib/tzcode/version b/contrib/tzcode/version index 5d9126009e7f..75d34ee38931 100644 --- a/contrib/tzcode/version +++ b/contrib/tzcode/version @@ -1 +1 @@ -2026a +2026b diff --git a/contrib/tzcode/zic.c b/contrib/tzcode/zic.c index d1084b4a25e9..f4c1aa6d54e6 100644 --- a/contrib/tzcode/zic.c +++ b/contrib/tzcode/zic.c @@ -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; + } if (s < 0) nwords = -1; @@ -1519,7 +1526,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 @@ -1530,7 +1537,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; } @@ -1577,9 +1586,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]; @@ -1622,7 +1633,7 @@ open_outfile(char const **outname, char **tempname) exit(EXIT_FAILURE); } - 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; @@ -1736,8 +1747,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; @@ -1750,7 +1759,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; @@ -1802,7 +1811,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; @@ -1833,7 +1842,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); @@ -1957,7 +1966,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")); @@ -2044,6 +2053,7 @@ infile(int fnum, char const *name) default: unreachable(); } } + check_for_signal(); } close_file(fp, NULL, filename(fnum), NULL); if (wantcont) @@ -2657,7 +2667,6 @@ writezone(const char *const name, const char *const string, char version, { register FILE * fp; register ptrdiff_t i, j; - register size_t u; register int pass; char *tempname = NULL; char const *outname = name; @@ -2939,30 +2948,27 @@ writezone(const char *const name, const char *const string, char version, : i == thisdefaulttype ? old0 : i] = thistypecnt++; - for (u = 0; u < sizeof indmap / sizeof indmap[0]; ++u) - indmap[u] = -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; @@ -3185,11 +3191,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); @@ -3428,12 +3434,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); @@ -3534,7 +3544,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; } @@ -3781,6 +3791,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)) { @@ -3790,12 +3801,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] @@ -4180,10 +4197,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; @@ -4202,13 +4217,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