diff options
| author | Dag-Erling Smørgrav <des@FreeBSD.org> | 2026-09-13 13:18:34 +0000 |
|---|---|---|
| committer | Dag-Erling Smørgrav <des@FreeBSD.org> | 2026-09-13 13:18:34 +0000 |
| commit | 4d4fdb78dbcb075efa6bcd8f913a5bfb59246647 (patch) | |
| tree | 637a366d51fed7a1e52e276d74df19eb396a1a96 | |
| parent | 2479b6921a20c7fe260ba1b30c5ca7f666e692a6 (diff) | |
Import tzcode 2026dvendor/tzcode/tzcode2026dvendor/tzcode
| -rw-r--r-- | CONTRIBUTING | 2 | ||||
| -rw-r--r-- | Makefile | 19 | ||||
| -rw-r--r-- | NEWS | 115 | ||||
| -rw-r--r-- | README | 2 | ||||
| -rw-r--r-- | localtime.c | 397 | ||||
| -rw-r--r-- | private.h | 15 | ||||
| -rw-r--r-- | strftime.c | 9 | ||||
| -rw-r--r-- | theory.html | 63 | ||||
| -rw-r--r-- | tz-art.html | 14 | ||||
| -rw-r--r-- | tz-how-to.html | 18 | ||||
| -rw-r--r-- | tz-link.html | 118 | ||||
| -rw-r--r-- | tzfile.5.txt | 3 | ||||
| -rw-r--r-- | version | 2 | ||||
| -rw-r--r-- | zdump.c | 4 | ||||
| -rw-r--r-- | zic.8 | 43 | ||||
| -rw-r--r-- | zic.8.txt | 44 | ||||
| -rw-r--r-- | zic.c | 89 |
17 files changed, 530 insertions, 427 deletions
diff --git a/CONTRIBUTING b/CONTRIBUTING index c5fa803f7275..213ae67e962a 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -22,7 +22,7 @@ To email small changes, please run a POSIX shell command like For more-elaborate or possibly controversial changes, such as renaming, adding or removing zones, please read “Theory and pragmatics of the tz code and data” -<https://www.iana.org/time-zones/repository/theory.html>. +<https://data.iana.org/time-zones/theory.html> It is also good to browse the mailing list archives <https://lists.iana.org/hyperkitty/list/tz@iana.org/> for examples of patches that tend to work well. @@ -172,9 +172,9 @@ EXPIRES_LINE= 0 TZDATA_TEXT= leapseconds tzdata.zi -# For backward-compatibility links for old zone names, use +# For backward-compatibility links and zones for old names, use # BACKWARD= backward -# To omit these links, use +# To omit these links and zones, use # BACKWARD= BACKWARD= backward @@ -574,7 +574,7 @@ SAFE_CHAR= '[]'$(SAFE_CHARSET)'-]' # These non-alphabetic, non-ASCII printable characters are # used in commentary or in generated *.txt files # and are not likely to cause confusion. -UNUSUAL_OK_CHARSET= §«°±»½¾×–‘’“”•→−≤★⟨⟩⯪ +UNUSUAL_OK_CHARSET= §«°±»½¾¿×–‘’“”•→−≤★⟨⟩⯪ # Put this in a bracket expression to match spaces. s = [:space:] @@ -922,7 +922,7 @@ check_mild: check_web check_zishrink \ UTF8_LOCALE_MISSING = \ { test ! '$(UTF8_LOCALE)' \ || ! printf 'A\304\200B\n' \ - | LC_ALL='$(UTF8_LOCALE)' grep -q '^A.B$$' >/dev/null 2>&1 \ + | LC_ALL='$(UTF8_LOCALE)' grep -q '^A[[:alpha:]]B$$' >/dev/null 2>&1 \ || { export LC_ALL='$(UTF8_LOCALE)'; false; }; } character-set.ck: $(ENCHILADA) @@ -1006,11 +1006,11 @@ now.ck: checknow.awk date tzdata.zi zdump zic zone1970.tab zonenow.tab now=$(CHECK_NOW_TIMESTAMP) && \ future=$$(($(CHECK_NOW_FUTURE_SECS) + $$now)) && \ ./zdump -i -t $$now,$$future \ - $$(find "$$PWD/$@d"/????*/ -type f) \ + $$(find "$$PWD/$@d"/????*/ -type f -o -type l) \ >$@d/zdump-now.tab && \ ./zdump -i -t 0,$$future \ $$(find "$$PWD/$@d" -name Etc -prune \ - -o -type f ! -name '*.tab' -print) \ + -o \( -type f -o -type l \) ! -name '*.tab' -print) \ >$@d/zdump-1970.tab && \ $(AWK) \ -v now=$$now \ @@ -1018,7 +1018,12 @@ now.ck: checknow.awk date tzdata.zi zdump zic zone1970.tab zonenow.tab -v zdump_table=$@d/zdump-now.tab \ -f checknow.awk zonenow.tab $(AWK) \ - 'BEGIN {print "-\t-\tUTC"} /^Zone/ {print "-\t-\t" $$2}' \ + 'BEGIN { \ + SysVzone["EST5EDT"] = 1; SysVzone["CST6CDT"] = 1; \ + SysVzone["MST7MDT"] = 1; SysVzone["PST8PDT"] = 1; \ + print "-\t-\tUTC" \ + } \ + $$1 == "Zone" && !SysVzone[$$2] {print "-\t-\t" $$2}' \ $(PRIMARY_YDATA) backward factory | \ $(AWK) \ -v zdump_table=$@d/zdump-1970.tab \ @@ -1,5 +1,87 @@ News for the tz database +Release 2026d - 2026-09-11 15:21:07 -0700 + + Briefly: + Canada’s Northwest Territories moved to permanent -06 on 2026-08-21. + Obsolescent settings like TZ="EST5EDT" now conform better to POSIX. + Fix security, performance and porting bugs in zic and localtime. + + Changes to future timestamps + + Canada’s Northwest Territories will not fall back on 2026-11-01 + and will stay on -06 year-round, matching Alberta’s recent change. + Model this with its traditional abbreviation CST. Although the + change to permanent -06 legally took place on 2026-08-21, + temporarily model the change to occur on 2026-11-01 at 02:00 + for the same reason as other recent temporary hacks. (Caution: + see “NOTE FOR 2026b TEMPORARY HACK FOR CLDR AND CANADA” below.) + This affects only America/Inuvik as the rest of the territory is + covered by America/Edmonton, for which the equivalent change was + released in 2026c. + + Changes to past timestamps + + Colombia’s 1992-05-02 spring forward was at 00:00, not 24:00. + + Iran’s 1979-05-26 spring forward was at 00:00, not 24:00. + (Thanks to N.F. Hase.) + + The backward-compatibility names EST5EDT, CST6CDT, MST7MDT, and + PST8PDT now conform better to POSIX. For example, EST5EDT now + always uses the abbreviation "EST" for standard time (now always 5 + hours behind UT) and "EDT" for daylight saving time, whereas it + formerly had different UT offsets before standard time was + introduced and sometimes used abbreviations like "LMT", "EWT" and + "EPT", all contrary to POSIX. Also, though not required by POSIX + these names now use US federal rules rather than rules of places + like New York, reverting to 2024a behavior. This change affects + only timestamps before 1966-10-30 at 01:00 standard time. + + Other data changes + + The temporary hacks used for North American timekeeping changes + now work around a libstdc++ std::chrono bug in GCC 14.1-14.4, + 15.1-15.2, and 16.1; see GCC bug 124851. This data change does + not affect TZif files or timestamps. The change does not work + around the related but less serious GCC bugs 116110 and 124513. + These GCC bugs are all fixed in GCC 16.2. + + Changes to code + + zic now rejects Link targets that would have invalid names, and + more efficiently processes Expires, Leap and Rule lines with years + far in the past or future. (Thanks to Darren Carreras.) + + zic now ports to systems that report lack of link support via + EINVAL, ENOSYS or EPERM errno values. (Thanks to Tom Lane.) + + When tzset and related functions encounter a TZif file that is too + large for them, they now consistently fail instead of sometimes + silently ignoring excess parts of the file. + + localtime-related functions no longer mishandle extreme timestamps + when given TZif files holding some unlikely timezone histories. + (Problem reported by David Sarkisyan.) + + localtime-related functions no longer check the values of TZif + files’ standard/wall and UT/local indicators, which these + functions have not used since 2026a’s removal of the old + posixrules feature. + + tzcode has been ported to Haiku. + + localtime.c now works again by default on AIX and DragonFly BSD. + + zic now rejects ‘:’ and ‘\’ in Zone and Link names when running on + Microsoft Windows. (Problem reported by David Diaz.) + + Changes to documentation + + URLs for release tarballs in tz-link.html have been updated to + reflect their new canonical URLs on data.iana.org. + + Release 2026c - 2026-07-08 10:23:58 -0700 Briefly: @@ -15,7 +97,13 @@ Release 2026c - 2026-07-08 10:23:58 -0700 CST. Although the change to permanent -06 legally took place on 2026-06-18, temporarily model the change to occur on 2026-11-01 at 02:00 instead, for the same reason we introduced a similarly - temporary hack for British Columbia in 2026b. + temporary hack for British Columbia in 2026b. (Caution: see + “NOTE FOR 2026b TEMPORARY HACK FOR CLDR AND CANADA” below.) + + As the change affects both America/Edmonton and its backward + compatibility link, the obsolescent setting TZ="Canada/Mountain" + will now use the abbreviation CST for affected timestamps, + akin to TZ="Canada/Pacific" behavior introduced in 2026b. Although another TZDB release will likely be needed soon because Northwest Territories will likely follow Alberta, the legal @@ -67,6 +155,7 @@ Release 2026b - 2026-04-22 23:06:43 -0700 Briefly: British Columbia moved to permanent -07 on 2026-03-09. + zic no longer mishandles a last transition to a new time type. Some more overflow bugs have been fixed in zic. Changes to future timestamps @@ -79,9 +168,33 @@ Release 2026b - 2026-04-22 23:06:43 -0700 limitation in CLDR 48.1 (2026-01-08). This temporary hack is planned to be removed after CLDR is fixed. + To match longstanding practice for -07 standard time in North + America, such as for America/Dawson, America/Dawson_Creek, + America/Fort_Nelson and America/Whitehorse, affected BC + timestamps use the abbreviation MST. As the change affects both + America/Vancouver and its backward compatibility link, the + obsolescent setting TZ="Canada/Pacific" will now use the + abbreviation MST. Future TZDB releases may change the + abbreviation if something else becomes popular across areas + observing -07, and may change it to "-07" if no alphabetic + consensus emerges. + Changes to code zic no longer mishandles a last transition to a new time type. + NOTE FOR 2026b TEMPORARY HACK FOR CLDR AND CANADA: + This zic fix is needed for the temporary hack (mentioned above) + that works around the Canadian timekeeping bug in Unicode CLDR. + Without the fix, the temporary hack causes zic versions 2023d + through 2026a, in their default mode that generates slim output, + to generate a TZif file that violates Internet RFC 9636 §3.3. + The buggy file in turn causes some TZif readers, including tzcode + itself, to ignore America/Vancouver’s 2026-11-01 02:00 transition + from PDT (tm_isdst=1) to MST (tm_isdst=0). Although the buggy + file does not cause any known TZif reader to mishandle UT offsets, + caution is advised when using zic 2023d through 2026a to compile + data from more-recent tz releases. To work around this problem + when using these older zic versions, use ‘zic -b fat’. 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", @@ -10,7 +10,7 @@ locations around the globe. It is updated periodically to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules. -See <https://www.iana.org/time-zones/repository/tz-link.html> or the +See <https://data.iana.org/time-zones/tz-link.html> or the file tz-link.html for how to acquire the code and data. Once acquired, read the leading comments in the file ‘Makefile’ diff --git a/localtime.c b/localtime.c index be3fe1d5d8f4..7c06e3707a46 100644 --- a/localtime.c +++ b/localtime.c @@ -64,7 +64,7 @@ struct stat { char st_ctime, st_dev, st_ino; }; # endif # if THREAD_PREFER_SINGLE # ifndef HAVE___ISTHREADED -# if defined __FreeBSD__ || defined __OpenBSD__ +# if defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__ # define HAVE___ISTHREADED 1 # else # define HAVE___ISTHREADED 0 @@ -348,6 +348,10 @@ static int fstatat(int dd, char const *path, struct stat *st, int flags) # include <sys/auxv.h> # endif +/* Avoid clash if headers declare but libraries do not define issetugid. */ +# undef issetugid +# define issetugid localtime_issetugid + /* Return 1 if the process is privileged, 0 otherwise. */ static int issetugid(void) @@ -475,8 +479,6 @@ struct ttinfo { /* time type information */ -2**31 + 1 .. 2**31 - 1 */ desigidx_type tt_desigidx; /* abbreviation list index */ bool tt_isdst; /* used to set tm_isdst */ - bool tt_ttisstd; /* transition is std time */ - bool tt_ttisut; /* transition is UT */ }; struct lsinfo { /* leap second information */ @@ -505,7 +507,6 @@ struct state { int timecnt; int typecnt; int charcnt; - bool goback; bool goahead; time_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; @@ -569,12 +570,11 @@ struct rule { }; static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, - struct tm *); -static bool increment_overflow(int *, int); + struct tm *, time_t *); static bool increment_overflow_time(time_t *, int_fast32_2s); static int_fast32_2s leapcorr(struct state const *, time_t); static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, - struct tm *); + struct tm *, time_t *); static bool tzparse(char const *, struct state *, struct state const *); #ifndef ALL_STATE @@ -641,8 +641,6 @@ init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, s->tt_utoff = utoff; s->tt_isdst = isdst; s->tt_desigidx = desigidx; - s->tt_ttisstd = false; - s->tt_ttisut = false; } /* Return true if SP's time type I does not specify local time. */ @@ -838,10 +836,11 @@ tzfile_changed(int fd, struct stat *st) resolution if available, as this can help distinguish files on non-POSIX platforms where st_dev and st_ino are unreliable. */ struct timespec ctim; + /* Copy via members, as AIX 7.3 defaults to an incompatible st_ctim. */ + ctim.tv_sec = st->st_ctime; #if HAVE_STRUCT_STAT_ST_CTIM - ctim = st->st_ctim; + ctim.tv_nsec = st->st_ctim.tv_nsec; #else - ctim.tv_sec = st->st_ctime; ctim.tv_nsec = 0; #endif @@ -896,9 +895,19 @@ union local_storage { }; /* These tzload flags can be ORed together, and fit into 'char'. */ -enum { TZLOAD_FROMENV = 1 }; /* The TZ string came from the environment. */ -enum { TZLOAD_TZSTRING = 2 }; /* Read any newline-surrounded TZ string. */ -enum { TZLOAD_TZDIR_SUB = 4 }; /* TZ should be a file under TZDIR. */ + +/* TZLOAD_FROMENV means the TZ string is a process-wide setting either + taken from the TZ environment variable or inferred from TZ being unset. + When 0 <= tz_change_interval, TZLOAD_FROMENV lets us skip reanalysis + of a TZif file that did not change during the interval. */ +enum { TZLOAD_FROMENV = 1 }; + +/* Read any newline-surrounded TZ string. */ +enum { TZLOAD_TZSTRING = 2 }; + +/* TZ should be a file under TZDIR. */ +enum { TZLOAD_TZDIR_SUB = 4 }; + /* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS. Use **LSPP for temporary storage. Return 0 on @@ -923,7 +932,7 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, struct stat st; st.st_ctime = 0; - sp->goback = sp->goahead = false; + sp->goahead = false; if (! name) { name = TZDEFAULT; @@ -1198,30 +1207,9 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, } set_leapcount(sp, leapcnt); - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisstdcnt == 0) - ttisp->tt_ttisstd = false; - else { - if (*p != true && *p != false) - return EFTYPE; - ttisp->tt_ttisstd = *p++; - } - } - for (i = 0; i < sp->typecnt; ++i) { - register struct ttinfo * ttisp; - - ttisp = &sp->ttis[i]; - if (ttisutcnt == 0) - ttisp->tt_ttisut = false; - else { - if (*p != true && *p != false) - return EFTYPE; - ttisp->tt_ttisut = *p++; - } - } + /* Do not bother to validate standard/wall and UT/local + indicators, as they are no longer used here. */ + p += ttisstdcnt + ttisutcnt; } nread -= p - up->buf; @@ -1232,13 +1220,13 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, break; } if ((tzloadflags & TZLOAD_TZSTRING) && nread > 2 && - up->buf[0] == '\n' && up->buf[nread - 1] == '\n' && - sp->typecnt + 2 <= TZ_MAX_TYPES) { + up->buf[0] == '\n' && up->buf[nread - 1] == '\n') { struct state *ts = &lsp->u.st; up->buf[nread - 1] = '\0'; - if (tzparse(&up->buf[1], ts, sp)) { - + if (!tzparse(&up->buf[1], ts, sp)) + return EFTYPE; + else { /* Attempt to reuse existing abbreviations. Without this, America/Anchorage would consume 50 bytes for abbreviations, as @@ -1246,7 +1234,6 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, AHDT YST AKDT AKST) and ts->charcnt equals 10 (for AKST AKDT). Reusing means sp->charcnt can stay 40 in this example. */ - int gotabbr = 0; int charcnt = sp->charcnt; for (i = 0; i < ts->typecnt; i++) { char *tsabbr = ts->chars + ts->ttis[i].tt_desigidx; @@ -1254,22 +1241,25 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, for (j = 0; j < charcnt; j++) if (strcmp(sp->chars + j, tsabbr) == 0) { ts->ttis[i].tt_desigidx = j; - gotabbr++; break; } if (! (j < charcnt)) { int tsabbrlen = strnlen(tsabbr, TZ_MAX_CHARS - j); - if (j + tsabbrlen < TZ_MAX_CHARS) { + if (TZ_MAX_CHARS <= j + tsabbrlen) + return EOVERFLOW; + else { char *cp = sp->chars + j; cp = mempcpy(cp, tsabbr, tsabbrlen); *cp = '\0'; charcnt = j + tsabbrlen + 1; ts->ttis[i].tt_desigidx = j; - gotabbr++; } } } - if (gotabbr == ts->typecnt) { + + if (TZ_MAX_TYPES - sp->typecnt < ts->typecnt) + return EOVERFLOW; + else { sp->charcnt = charcnt; /* Ignore any trailing, no-op transitions generated @@ -1288,10 +1278,8 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, || (0 < sp->timecnt && t <= sp->ats[sp->timecnt - 1])) continue; - if (TZ_MAX_TIMES <= sp->timecnt) { - sp->goahead = false; - break; - } + if (TZ_MAX_TIMES <= sp->timecnt) + return EOVERFLOW; sp->ats[sp->timecnt] = t; sp->types[sp->timecnt] = (sp->typecnt + ts->types[i]); @@ -1334,10 +1322,6 @@ static const int mon_lengths[2][MONSPERYEAR] = { { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; -static const int year_lengths[2] = { - DAYSPERNYEAR, DAYSPERLYEAR -}; - /* Is C an ASCII digit? */ static bool is_digit(char c) @@ -1537,40 +1521,26 @@ getrule(const char *strp, register struct rule *const rulep) */ static int_fast32_t -transtime(const int year, register const struct rule *const rulep, +transtime(time_t year, register const struct rule *const rulep, const int_fast32_t offset) { + int d; /* Day of year (zero-origin). */ register bool leapyear; - register int_fast32_t value; - register int i; - int d, m1, yy0, yy1, yy2, dow; leapyear = isleap(year); - switch (rulep->r_type) { - case JULIAN_DAY: + if (rulep->r_type <= DAY_OF_YEAR) { /* ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap ** years. ** In non-leap years, or if the day number is 59 or less, just - ** add SECSPERDAY times the day number-1 to the time of - ** January 1, midnight, to get the day. - */ - value = (rulep->r_day - 1) * SECSPERDAY; - if (leapyear && rulep->r_day >= 60) - value += SECSPERDAY; - break; - - case DAY_OF_YEAR: - /* + ** use the day number - 1. + ** ** n - day of year. - ** Just add SECSPERDAY times the day number to the time of - ** January 1, midnight, to get the day. */ - value = rulep->r_day * SECSPERDAY; - break; - - case MONTH_NTH_DAY_OF_WEEK: + d = rulep->r_day - ((rulep->r_type < DAY_OF_YEAR) + & (!leapyear | (rulep->r_day <= 59))); + } else { /* ** Mm.n.d - nth "dth day" of month m. */ @@ -1579,14 +1549,16 @@ transtime(const int year, register const struct rule *const rulep, ** Use Zeller's Congruence to get day-of-week of first day of ** month. */ - m1 = (rulep->r_mon + 9) % 12 + 1; - yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; - yy1 = yy0 / 100; - yy2 = yy0 % 100; - dow = ((26 * m1 - 2) / 10 + - 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; - if (dow < 0) - dow += DAYSPERWEEK; + int i; + bool janfeb = rulep->r_mon <= 2; + int month = (rulep->r_mon + + (janfeb ? MONSPERYEAR : 0)); /* 3..14 */ + int adjustment = (TYPE_SIGNED(time_t) ? 0 : 400) - janfeb; + int ay_rem = (year + adjustment) % YEARSPERREPEAT; + int y = ay_rem + (ay_rem < 0 ? YEARSPERREPEAT : 0); + int dow = (((13 * (month + 1)) / 5 + + y + y / 4 - y / 100 + y / 400) + % DAYSPERWEEK); /* ** "dow" is the day-of-week of the first day of the month. Get @@ -1594,33 +1566,24 @@ transtime(const int year, register const struct rule *const rulep, ** month. */ d = rulep->r_day - dow; - if (d < 0) - d += DAYSPERWEEK; - for (i = 1; i < rulep->r_week; ++i) { - if (d + DAYSPERWEEK >= - mon_lengths[leapyear][rulep->r_mon - 1]) - break; - d += DAYSPERWEEK; - } + d += (rulep->r_week - (0 <= d)) * DAYSPERWEEK; + if (mon_lengths[leapyear][rulep->r_mon - 1] <= d) + d -= DAYSPERWEEK; /* ** "d" is the day-of-month (zero-origin) of the day we want. */ - value = d * SECSPERDAY; for (i = 0; i < rulep->r_mon - 1; ++i) - value += mon_lengths[leapyear][i] * SECSPERDAY; - break; - - default: unreachable(); + d += mon_lengths[leapyear][i]; } /* - ** "value" is the year-relative time of 00:00:00 UT on the day in + ** d is the origin-0 year-relative day in ** question. To get the year-relative time of the specified local ** time on that day, add the transition time and the current offset - ** from UT. + ** from UT to d * SECSPERDAY. */ - return value + rulep->r_time + offset; + return d * SECSPERDAY + rulep->r_time + offset; } /* @@ -1670,11 +1633,11 @@ tzparse(const char *name, struct state *sp, struct state const *basep) } } else set_leapcount(sp, 0); /* So, we're off a little. */ - sp->goback = sp->goahead = false; + sp->goahead = false; if (*name != '\0') { struct rule start, end; - int year, yearbeg, yearlim, timecnt; - time_t janfirst; + int timecnt; + time_t janfirst, repeatbeg, year, yearbeg, yearlim; int_fast32_t janoffset = 0; if (*name == '<') { @@ -1712,18 +1675,20 @@ tzparse(const char *name, struct state *sp, struct state const *basep) if (!name || *name) return false; sp->typecnt = 2; /* standard time and DST */ - /* - ** Two transitions per year, from EPOCH_YEAR forward. - */ + + /* Two transitions per year, from atlo forward, and going on + for years_of_observations past max(atlo, leaplo). */ init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); timecnt = 0; - janfirst = 0; - yearbeg = EPOCH_YEAR; + repeatbeg = (atlo / SECSPERREPEAT + + (atlo < 0 && 0 < atlo % SECSPERREPEAT)); + yearbeg = repeatbeg * YEARSPERREPEAT + EPOCH_YEAR; + janfirst = repeatbeg * SECSPERREPEAT; do { int_fast32_t yearsecs - = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + = year_days(yearbeg - 1) * SECSPERDAY; time_t janfirst1 = janfirst; yearbeg--; if (increment_overflow_time(&janfirst1, -yearsecs)) { @@ -1731,30 +1696,25 @@ tzparse(const char *name, struct state *sp, struct state const *basep) break; } janfirst = janfirst1; - } while (atlo < janfirst - && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + } while (atlo < janfirst); while (true) { int_fast32_t yearsecs - = year_lengths[isleap(yearbeg)] * SECSPERDAY; - int yearbeg1 = yearbeg; + = year_days(yearbeg) * SECSPERDAY; time_t janfirst1 = janfirst; if (increment_overflow_time(&janfirst1, yearsecs) - || increment_overflow(&yearbeg1, 1) || atlo <= janfirst1) break; - yearbeg = yearbeg1; + yearbeg++; janfirst = janfirst1; } - yearlim = yearbeg; - if (increment_overflow(&yearlim, years_of_observations)) - yearlim = INT_MAX; + yearlim = yearbeg + years_of_observations; for (year = yearbeg; year < yearlim; year++) { int_fast32_t starttime = transtime(year, &start, stdoffset), endtime = transtime(year, &end, dstoffset), - yearsecs = year_lengths[isleap(year)] * SECSPERDAY; + yearsecs = year_days(year) * SECSPERDAY; bool reversed = endtime < starttime; if (reversed) { int_fast32_t swap = starttime; @@ -1764,24 +1724,25 @@ tzparse(const char *name, struct state *sp, struct state const *basep) if (reversed || (starttime < endtime && endtime - starttime < yearsecs)) { - if (TZ_MAX_TIMES - 2 < timecnt) - break; - sp->ats[timecnt] = janfirst; - if (! increment_overflow_time(&sp->ats[timecnt], - janoffset + starttime) - && atlo <= sp->ats[timecnt]) + time_t at_added = TIME_T_MAX; + time_t at = janfirst; + if (! increment_overflow_time(&at, janoffset + starttime) + && atlo <= at) { + if (TZ_MAX_TIMES <= timecnt) + return false; + sp->ats[timecnt] = at_added = at; sp->types[timecnt++] = !reversed; - sp->ats[timecnt] = janfirst; - if (! increment_overflow_time(&sp->ats[timecnt], - janoffset + endtime) - && atlo <= sp->ats[timecnt]) { + } + at = janfirst; + if (! increment_overflow_time(&at, janoffset + endtime) + && atlo <= at) { + if (TZ_MAX_TIMES <= timecnt) + return false; + sp->ats[timecnt] = at_added = at; sp->types[timecnt++] = reversed; } - } - if (endtime < leaplo) { - yearlim = year; - if (increment_overflow(&yearlim, years_of_observations)) - yearlim = INT_MAX; + if (at_added < leaplo) + yearlim = year + years_of_observations; } if (increment_overflow_time(&janfirst, janoffset + yearsecs)) break; @@ -1792,7 +1753,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep) sp->ttis[0] = sp->ttis[1]; sp->typecnt = 1; /* Perpetual DST. */ } else if (years_of_observations <= year - yearbeg) - sp->goback = sp->goahead = true; + sp->goahead = true; } else { dstlen = 0; sp->typecnt = 1; /* only standard time */ @@ -1849,7 +1810,7 @@ zoneinit(struct state *sp, char const *name, char tzloadflags) sp->timecnt = 0; sp->typecnt = 0; sp->charcnt = 0; - sp->goback = sp->goahead = false; + sp->goahead = false; init_ttinfo(&sp->ttis[0], 0, false, 0); strcpy(sp->chars, utc); return 0; @@ -2069,12 +2030,15 @@ tzfree(timezone_t sp) ** since in that case tzset should have already done this step correctly. ** SETNAME's type is int_fast32_t for compatibility with gmtsub, ** but it is actually a boolean and its value should be 0 or 1. +** +** If LTM_YEAR, store the resulting year-1900 into *LTM_YEAR rather +** than into the default TMP->tm_year; this prevents year overflow. */ /*ARGSUSED*/ static struct tm * localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, - struct tm *const tmp) + struct tm *tmp, time_t *ltm_year) { register const struct ttinfo * ttisp; register int i; @@ -2083,52 +2047,39 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, if (sp == NULL) { /* Don't bother to set tzname etc.; tzset has already done it. */ - return gmtsub(gmtptr, timep, 0, tmp); + return gmtsub(gmtptr, timep, 0, tmp, ltm_year); } - if ((sp->goback && t < sp->ats[0]) || - (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - time_t newt; - register time_t seconds; - register time_t years; - - if (t < sp->ats[0]) - seconds = sp->ats[0] - t; - else seconds = t - sp->ats[sp->timecnt - 1]; - --seconds; - - /* Beware integer overflow, as SECONDS might - be close to the maximum time_t. */ - years = seconds / SECSPERREPEAT * YEARSPERREPEAT; - seconds = years * AVGSECSPERYEAR; - years += YEARSPERREPEAT; - if (t < sp->ats[0]) - newt = t + seconds + SECSPERREPEAT; - else - newt = t - seconds - SECSPERREPEAT; + if (sp->goahead && sp->ats[sp->timecnt - 1] < t) { + /* Avoid integer overflow when time_t is signed, by + using secs_div_2 twice; the full value would + always be even, so halving does not round. */ + time_t + tlo = sp->ats[sp->timecnt - 1], + diffyears = ((t / 2 - tlo / 2 + + ((t % 2 - tlo % 2 + 2) / 2 - 1)) + / (SECSPERREPEAT / 2) + * YEARSPERREPEAT), + years = diffyears + YEARSPERREPEAT, + secs_div_2 = (diffyears * (AVGSECSPERYEAR / 2) + + SECSPERREPEAT / 2), + newt = t - secs_div_2 - secs_div_2, + ryear; - if (newt < sp->ats[0] || - newt > sp->ats[sp->timecnt - 1]) - return NULL; /* "cannot happen" */ - result = localsub(sp, &newt, setname, tmp); + result = localsub(sp, &newt, setname, tmp, &ryear); if (result) { -# if defined ckd_add && defined ckd_sub - if (t < sp->ats[0] - ? ckd_sub(&result->tm_year, - result->tm_year, years) - : ckd_add(&result->tm_year, - result->tm_year, years)) + if (ltm_year) + *ltm_year = ryear + years; + else { +# ifdef ckd_add + if (ckd_add(&result->tm_year, ryear, years)) return NULL; # else - register int_fast64_t newy; - - newy = result->tm_year; - if (t < sp->ats[0]) - newy -= years; - else newy += years; + time_t newy = ryear + years; if (! (INT_MIN <= newy && newy <= INT_MAX)) return NULL; result->tm_year = newy; # endif + } } return result; } @@ -2152,9 +2103,9 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, ** To get (wrong) behavior that's compatible with System V Release 2.0 ** you'd replace the statement below with ** t += ttisp->tt_utoff; - ** timesub(&t, 0, sp, tmp); + ** timesub(&t, 0, sp, tmp, ltm_year); */ - result = timesub(&t, ttisp->tt_utoff, sp, tmp); + result = timesub(&t, ttisp->tt_utoff, sp, tmp, ltm_year); if (result) { result->tm_isdst = ttisp->tt_isdst; # ifdef TM_ZONE @@ -2204,7 +2155,7 @@ struct tm * localtime_rz(struct state *restrict sp, time_t const *restrict timep, struct tm *restrict tmp) { - return localsub(sp, timep, 0, tmp); + return localsub(sp, timep, 0, tmp, NULL); } # endif @@ -2219,7 +2170,7 @@ localtime_tzset(time_t const *timep, struct tm *tmp, bool setname) } if (0 <= tz_change_interval || setname || !lcl_is_set) tzset_unlocked(!err, false, now); - tmp = localsub(lclptr, timep, setname, tmp); + tmp = localsub(lclptr, timep, setname, tmp, NULL); unlock(!err); return tmp; } @@ -2246,11 +2197,11 @@ localtime_r(const time_t *restrict timep, struct tm *restrict tmp) static struct tm * gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep, - int_fast32_t offset, struct tm *tmp) + int_fast32_t offset, struct tm *tmp, time_t *ltm_year) { register struct tm * result; - result = timesub(timep, offset, gmtptr, tmp); + result = timesub(timep, offset, gmtptr, tmp, ltm_year); #ifdef TM_ZONE /* ** Could get fancy here and deliver something such as @@ -2273,7 +2224,7 @@ struct tm * gmtime_r(time_t const *restrict timep, struct tm *restrict tmp) { gmtcheck(); - return gmtsub(gmtptr, timep, 0, tmp); + return gmtsub(gmtptr, timep, 0, tmp, NULL); } struct tm * @@ -2294,7 +2245,7 @@ struct tm * offtime_r(time_t const *restrict timep, long offset, struct tm *restrict tmp) { gmtcheck(); - return gmtsub(gmtptr, timep, offset, tmp); + return gmtsub(gmtptr, timep, offset, tmp, NULL); } struct tm * @@ -2330,7 +2281,7 @@ leaps_thru_end_of(time_t y) static struct tm * timesub(const time_t *timep, int_fast32_t offset, - const struct state *sp, struct tm *tmp) + const struct state *sp, struct tm *tmp, time_t *ltm_year) { register time_t tdays; register const int * ip; @@ -2380,7 +2331,7 @@ timesub(const time_t *timep, int_fast32_t offset, idays += dayoff % DAYSPERREPEAT + 2 * DAYSPERREPEAT; idays %= DAYSPERREPEAT; /* Increase Y and decrease IDAYS until IDAYS is in range for Y. */ - while (year_lengths[isleap(y)] <= idays) { + while (year_days(y) <= idays) { int tdelta = idays / DAYSPERLYEAR; int_fast32_t ydelta = tdelta + !tdelta; time_t newy = y + ydelta; @@ -2392,29 +2343,34 @@ timesub(const time_t *timep, int_fast32_t offset, y = newy; } + if (ltm_year) { + *ltm_year = y - TM_YEAR_BASE; + } else { #ifdef ckd_add - if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) { - errno = EOVERFLOW; - return NULL; - } + if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) { + errno = EOVERFLOW; + return NULL; + } #else - if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) { - int signed_y = y; - tmp->tm_year = signed_y - TM_YEAR_BASE; - } else if ((!TYPE_SIGNED(time_t) || INT_MIN + TM_YEAR_BASE <= y) - && y - TM_YEAR_BASE <= INT_MAX) - tmp->tm_year = y - TM_YEAR_BASE; - else { - errno = EOVERFLOW; - return NULL; - } + if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) { + int signed_y = y; + tmp->tm_year = signed_y - TM_YEAR_BASE; + } else if ((!TYPE_SIGNED(time_t) || INT_MIN + TM_YEAR_BASE <= y) + && y - TM_YEAR_BASE <= INT_MAX) + tmp->tm_year = y - TM_YEAR_BASE; + else { + errno = EOVERFLOW; + return NULL; + } #endif + } tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. */ tmp->tm_wday = (TM_WDAY_BASE - + ((tmp->tm_year % DAYSPERWEEK) + + ((y % DAYSPERWEEK - TM_YEAR_BASE % DAYSPERWEEK) + % DAYSPERWEEK * (DAYSPERNYEAR % DAYSPERWEEK)) + leaps_thru_end_of(y - 1) - leaps_thru_end_of(TM_YEAR_BASE - 1) @@ -2460,27 +2416,6 @@ timesub(const time_t *timep, int_fast32_t offset, */ static bool -increment_overflow(int *ip, int j) -{ -#ifdef ckd_add - return ckd_add(ip, *ip, j); -#else - register int const i = *ip; - - /* - ** If i >= 0 there can only be overflow if i + j > INT_MAX - ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. - ** If i < 0 there can only be overflow if i + j < INT_MIN - ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. - */ - if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) - return true; - *ip += j; - return false; -#endif -} - -static bool increment_overflow_64(int *ip, int_fast64_t j) { #ifdef ckd_add @@ -2588,7 +2523,7 @@ mktmcpy(struct tm *dest, struct tm const *src) static time_t time2sub(struct tm *const tmp, struct tm *funcp(struct state const *, time_t const *, - int_fast32_t, struct tm *), + int_fast32_t, struct tm *, time_t *), struct state const *sp, const int_fast32_t offset, bool *okayp, @@ -2648,12 +2583,12 @@ time2sub(struct tm *const tmp, while (mday <= 0) { iinntt li = y - (yourtm.tm_mon <= 1); - mday += year_lengths[isleap(li)]; + mday += year_days(li); y--; } while (DAYSPERLYEAR < mday) { iinntt li = y + (1 < yourtm.tm_mon); - mday -= year_lengths[isleap(li)]; + mday -= year_days(li); y++; } yourtm.tm_mday = mday; @@ -2705,7 +2640,7 @@ time2sub(struct tm *const tmp, t = lo; else if (t > hi) t = hi; - if (! funcp(sp, &t, offset, &mytm)) { + if (! funcp(sp, &t, offset, &mytm, NULL)) { /* ** Assume that t is too extreme to be represented in ** a struct tm; arrange things so that it is less @@ -2761,7 +2696,7 @@ time2sub(struct tm *const tmp, # endif if (!v && !increment_overflow_time_64(&altt, offdiff)) { struct tm alttm; - if (funcp(sp, &altt, offset, &alttm) + if (funcp(sp, &altt, offset, &alttm, NULL) && alttm.tm_isdst == mytm.tm_isdst && alttm.TM_GMTOFF == yourtm.TM_GMTOFF && tmcomp(&alttm, &yourtm) == 0) { @@ -2795,7 +2730,7 @@ time2sub(struct tm *const tmp, utoff_diff(sp->ttis[j].tt_utoff, sp->ttis[i].tt_utoff))) continue; - if (! funcp(sp, &newt, offset, &mytm)) + if (! funcp(sp, &newt, offset, &mytm, NULL)) continue; if (tmcomp(&mytm, &yourtm) != 0) continue; @@ -2813,7 +2748,7 @@ time2sub(struct tm *const tmp, label: if (increment_overflow_time_iinntt(&t, saved_seconds)) return WRONG; - if (funcp(sp, &t, offset, tmp)) + if (funcp(sp, &t, offset, tmp, NULL)) *okayp = true; return t; } @@ -2821,7 +2756,7 @@ label: static time_t time2(struct tm * const tmp, struct tm *funcp(struct state const *, time_t const *, - int_fast32_t, struct tm *), + int_fast32_t, struct tm *, time_t *), struct state const *sp, const int_fast32_t offset, bool *okayp) @@ -2840,7 +2775,7 @@ time2(struct tm * const tmp, static time_t time1(struct tm *const tmp, struct tm *funcp(struct state const *, time_t const *, - int_fast32_t, struct tm *), + int_fast32_t, struct tm *, time_t *), struct state const *sp, const int_fast32_t offset) { diff --git a/private.h b/private.h index 7918082dca1b..f2861c94ff53 100644 --- a/private.h +++ b/private.h @@ -255,6 +255,9 @@ strnlen (char const *s, size_t maxlen) #ifndef ENOMEM # define ENOMEM EINVAL #endif +#ifndef ENOSYS +# define ENOSYS EINVAL +#endif #ifndef ENOTCAPABLE # define ENOTCAPABLE EINVAL #endif @@ -264,6 +267,9 @@ strnlen (char const *s, size_t maxlen) #ifndef EOVERFLOW # define EOVERFLOW EINVAL #endif +#ifndef EPERM +# define EPERM EINVAL +#endif #if HAVE_GETTEXT # include <libintl.h> @@ -305,6 +311,7 @@ extern int optind; #ifndef HAVE_ISSETUGID # if (defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || defined __DragonFly__ \ || (defined __linux__ && !defined __GLIBC__) /* Android, musl, etc. */ \ || (defined __APPLE__ && defined __MACH__) || defined __sun) # define HAVE_ISSETUGID 1 @@ -813,6 +820,7 @@ void tzset(void); # if (202311 <= __STDC_VERSION__ \ || defined __GLIBC__ || defined __tm_zone /* musl */ \ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || defined __DragonFly__ || defined __HAIKU__ \ || (defined __APPLE__ && defined __MACH__)) # define HAVE_DECL_TIMEGM 1 # else @@ -841,7 +849,8 @@ extern char **environ; #ifndef HAVE_MEMPCPY # if (defined mempcpy \ - || defined __FreeBSD__ || defined __NetBSD__ || defined __linux__) + || defined __FreeBSD__ || defined __NetBSD__ || defined __DragonFly__ \ + || defined __linux__) # define HAVE_MEMPCPY 1 # else # define HAVE_MEMPCPY 0 @@ -906,6 +915,7 @@ time_t posix2time(time_t); || defined __GLIBC__ \ || defined __tm_zone /* musl */ \ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || defined __DragonFly__ || defined __HAIKU__ \ || (defined __APPLE__ && defined __MACH__)) # if !defined TM_GMTOFF && !defined NO_TM_GMTOFF # define TM_GMTOFF tm_gmtoff @@ -1170,6 +1180,7 @@ enum { }; #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) +#define year_days(y) (DAYSPERNYEAR + isleap(y)) /* ** Since everything in isleap is modulo 400 (or a factor of 400), we know that @@ -1183,6 +1194,6 @@ enum { ** We use this to avoid addition overflow problems. */ -#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) +#define year_sum_days(a, b) (DAYSPERNYEAR + isleap((a) % 400 + (b) % 400)) #endif /* !defined PRIVATE_H */ diff --git a/strftime.c b/strftime.c index c249010561d1..a7d488499fdd 100644 --- a/strftime.c +++ b/strftime.c @@ -464,9 +464,7 @@ label: int bot; int top; - len = isleap_sum(year, base) ? - DAYSPERLYEAR : - DAYSPERNYEAR; + len = year_sum_days(year, base); /* ** What yday (-3 ... 3) does ** the ISO year begin on? @@ -493,9 +491,8 @@ label: break; } --base; - yday += isleap_sum(year, base) ? - DAYSPERLYEAR : - DAYSPERNYEAR; + yday += year_sum_days(year, + base); } #ifdef XPG4_1994_04_09 if ((w == 52 && diff --git a/theory.html b/theory.html index 8f76aaf0b3c8..d56e1a79df46 100644 --- a/theory.html +++ b/theory.html @@ -12,6 +12,7 @@ </head> <body> +<main> <h1>Theory and pragmatics of the <code><abbr>tz</abbr></code> code and data</h1> <nav> <ul> @@ -26,10 +27,10 @@ <li><a href="#leapsec">Leap seconds</a></li> <li><a href="#calendar">Calendrical issues</a></li> <li><a href="#planets">Time and time zones off earth</a></li> + <li><a href="#see-also">See also</a></li> </ul> </nav> -<section> <h2 id="scope">Scope of the <code><abbr>tz</abbr></code> database</h2> <p> The <a @@ -112,9 +113,6 @@ It does not always make sense to talk about a timezone’s “base offset”, which is not necessarily a single number. </p> -</section> - -<section> <h2 id="naming">Timezone identifiers</h2> <p> Each timezone has a name that uniquely identifies the timezone. @@ -345,8 +343,7 @@ These legacy names are mostly defined in the file "<code>etcetera</code>". Also, the file "<code>backward</code>" defines the legacy names <code>Etc/GMT0</code>, <code>Etc/GMT-0</code>, <code>Etc/GMT+0</code>, -<code>GMT0</code>, <code>GMT-0</code> and <code>GMT+0</code>, -and the file "<code>northamerica</code>" defines the legacy names +<code>GMT0</code>, <code>GMT-0</code>, <code>GMT+0</code>, <code>EST5EDT</code>, <code>CST6CDT</code>, <code>MST7MDT</code>, and <code>PST8PDT</code>. </li> @@ -387,11 +384,11 @@ and <code>zone.tab</code>. <p> The database defines each timezone name to be a zone, or a link to a zone. -The source file <code>backward</code> defines links for backward -compatibility; it does not define zones. +The source file <code>backward</code> defines links +and a few zones for backward compatibility. Although <code>backward</code> was originally designed to be optional, nowadays distributions typically use it -and no great weight should be attached to whether a link +and no great weight should be attached to whether a link or zone is defined in <code>backward</code> or in some other file. The source file <code>etcetera</code> defines names that may be useful on platforms that do not support proleptic <code>TZ</code> strings @@ -404,9 +401,7 @@ second information on platforms that support leap seconds. Another <code>etcetera</code> name, <code>GMT</code>, is used by older code releases. </p> -</section> -<section> <h2 id="abbreviations">Time zone abbreviations</h2> <p> When this package is installed, it generates time zone abbreviations @@ -610,9 +605,7 @@ Israel. To avoid ambiguity, use numeric <abbr>UT</abbr> offsets like <code>-</code>0600 instead of time zone abbreviations like CST. </p> -</section> -<section> <h2 id="accuracy">Accuracy of the <code><abbr>tz</abbr></code> database</h2> <p> The <code><abbr>tz</abbr></code> database is not authoritative, and it @@ -876,9 +869,7 @@ merely because two locations differ in <abbr>LMT</abbr> or transitioned to standard time at different dates. </p> -</section> -<section> <h2 id="functions">Time and date functions</h2> <p> The <code><abbr>tz</abbr></code> code contains time and date functions @@ -1284,9 +1275,7 @@ The vestigial <abbr>API</abbr>s are: much the better. </li> </ul> -</section> -<section> <h2 id="stability">Interface stability</h2> <p> The <code><abbr>tz</abbr></code> code and data supply the following interfaces: @@ -1353,9 +1342,7 @@ than Bangkok by putting <samp>Asia/Bangkok</samp> in the event’s record, the application should be robust in the presence of timezone splits between now and the future time. </p> -</section> -<section> <h2 id="leapsec">Leap seconds</h2> <p> Leap seconds were introduced in 1972 to accommodate the @@ -1430,9 +1417,7 @@ Alternatively, you can install just one set of <abbr>TZif</abbr> files in the first place; see the <code>REDO</code> variable in this package’s <a href="https://en.wikipedia.org/wiki/Makefile">makefile</a>. </p> -</section> -<section> <h2 id="calendar">Calendrical issues</h2> <p> Calendrical issues are a bit out of scope for a time zone database, @@ -1446,22 +1431,30 @@ Other information and sources are given in the file "<code>calendars</code>" in the <code><abbr>tz</abbr></code> distribution. They sometimes disagree. </p> -</section> -<section> <h2 id="planets">Time and time zones off Earth</h2> <p> -The European Space Agency is <a -href="https://www.esa.int/Applications/Satellite_navigation/Telling_time_on_the_Moon">considering</a> -the establishment of a reference timescale for the Moon, which has +Efforts are underway to establish a reference timescale for the Moon, which has days roughly equivalent to 29.5 Earth days, and where relativistic effects cause clocks to tick slightly faster than on Earth. -Also, <abbr title="National Aeronautics and Space Administration">NASA</abbr> -has been <a +The European Space Agency <a +href="https://www.esa.int/Applications/Satellite_navigation/Telling_time_on_the_Moon">began +considering</a> the problem in 2023, and in 2024 +<abbr title="National Aeronautics and Space Administration">NASA</abbr> +was <a href="https://bidenwhitehouse.archives.gov/wp-content/uploads/2024/04/Celestial-Time-Standardization-Policy.pdf">ordered</a> to consider the establishment of Coordinated Lunar Time (<abbr>LTC</abbr>). -It is not yet known whether the US and European efforts will result in -multiple timescales on the Moon. +In 2025 a team at China’s +<a href="https://en.wikipedia.org/wiki/Purple_Mountain_Observatory">Purple +Mountain Observatory</a> published +<a href="https://doi.org/10.1051/0004-6361/202557345">Lunar Time Ephemeris +440</a>, the first formal proposal for a standard +for Temps Coordonné Lunaire (<abbr>TCL</abbr>). +A <a href="https://www.bipm.org/documents/20126/284836054/CGPM-2026-Draft-Resolutions.pdf">draft +resolution of the 13–15 October 2026 General Conference on Weights +and Measures</a> recommends that the <a href="https://www.bipm.org/en/">Bureau +International des Poids et Mesures</a> collaborate with space agencies +and other organizations on this matter. </p> <p> @@ -1583,7 +1576,15 @@ Sources for time on other planets: (2016-01-20). </li> </ul> -</section> +</main> + +<aside> +<h2 id="see-also">See also</h2> +<ul> +<li><a href="tz-link.html">Time zone and daylight saving time data</a></li> +<li><a href="tz-art.html">Time and the arts</a></li> +</ul> +</aside> <footer> <hr> diff --git a/tz-art.html b/tz-art.html index 77c7983c49fd..57d64e5c53f9 100644 --- a/tz-art.html +++ b/tz-art.html @@ -9,6 +9,7 @@ ul {padding-left: 1.3rem;} <title>Time and the arts</title> </head> <body> +<main> <h1>Time and the arts</h1> <h2>Documentaries</h2> <ul> @@ -466,8 +467,10 @@ Unlike minimalism, it does not assume that the listener has plenty of time. <li> The webcomic <em>xkcd</em> has the strips “<a href="https://xkcd.com/673/">The Sun</a>” (2009-12-09), -“<a href="https://xkcd.com/1655/">Doomsday Clock</a>” (2016-03-14) and +“<a href="https://xkcd.com/1655/">Doomsday Clock</a>” (2016-03-14), “<a href="https://xkcd.com/2549/">Edge Cake</a>” (2021-12-01), +“<a href="https://xkcd.com/2867/">DateTime</a>” (2023-12-13), +and “<a href="https://xkcd.com/3272/">Time Change</a>” (2026-07-15), along with the panels “<a href="https://xkcd.com/448/">Good Morning</a>” (2008-07-11), “<a href="https://xkcd.com/1017/">Backward in Time</a>” (2012-02-14), @@ -482,8 +485,7 @@ along with the panels “<a href="https://xkcd.com/2266/">Leap Smearing</a>” (2020-02-10), “<a href="https://xkcd.com/2594/">Consensus Time</a>” (2022-03-16), “<a href="https://xkcd.com/2846/">Daylight Saving Choice</a>” (2023-10-25), -“<a href="https://xkcd.com/2854/">Date Line</a>” (2023-11-13), -and “<a href="https://xkcd.com/2867/">DateTime</a>” (2023-12-13). +and “<a href="https://xkcd.com/2854/">Date Line</a>” (2023-11-13). The related book <em>What If?</em> has an entry “<a href="https://what-if.xkcd.com/26/">Leap Seconds</a>” (2012-12-31). </li> @@ -611,10 +613,16 @@ but maybe in your time zone I haven’t finished yet. So stay tuned!” (Goldie Hawn, <em>Rowan & Martin’s Laugh-In</em> No. 65, 1970-03-09) </li> </ul> +</main> + +<aside> <h2>See also</h2> <ul> <li><a href="tz-link.html">Time zone and daylight saving time data</a></li> +<li><a href="theory.html">Theory and pragmatics of the +<code><abbr>tz</abbr></code> code and data</a></li> </ul> +</aside> <footer> <hr> diff --git a/tz-how-to.html b/tz-how-to.html index 82a6176bef25..5a7a4388fe1f 100644 --- a/tz-how-to.html +++ b/tz-how-to.html @@ -15,6 +15,7 @@ ul {padding-left: 1.3rem;} </style> </head> <body> +<main> <h1>How to Read the <a href="https://en.wikipedia.org/wiki/Tz_database">tz Database</a> Source Files</h1> <h2>by Bill Seymour</h2> @@ -415,17 +416,19 @@ Line. For example:</p> <pre> #Zone NAME STDOFF RULES FORMAT [UNTIL] -Zone America/Juneau 15:02:19 - LMT 1867 Oct 18 +Zone America/Juneau 15:02:19 - LMT 1867 Oct 19 0:31:13u -8:57:41 - LMT ... </pre> <p>When Alaska was purchased from Russia in 1867, the Date Line moved from the Alaska/Canada border to the Bering Strait; and the time in Alaska was then 24 hours earlier than it had -been. <code><aside></code>(6 October in the Julian calendar, -which Russia was still using then for religious reasons, was followed -by <i>a second instance of the same day with a different name</i>, 18 -October in the Gregorian calendar. Isn’t civil time +been. <code><aside></code>(This also means that, although the sun had set +on Friday 6 October and risen on Saturday 7 October in the Julian calendar, +which Russia was still using then for religious reasons, upon the transfer +Saturday afternoon became Friday afternoon, and then the sun set and rose once +more on <i>second instances of the same days with different names</i>, +Friday 18 and Saturday 19 October in the Gregorian calendar. Isn’t civil time wonderful? <code>8-)</code>)<code></aside></code></p> <p>The abbreviation, “LMT” stands for “local mean @@ -660,14 +663,15 @@ Zone Pacific/Honolulu -10:31:26 - LMT 1896 Jan 13 12:00 </table> <p>There will be a short quiz later. <code>8-)</code></p> +</main> +<footer> <hr> -<address> This web page is in the public domain, so clarified as of 2015-10-20 by Bill Seymour. <br> All suggestions and corrections will be welcome; all flames will be amusing. Mail to was at pobox dot com. -</address> +</footer> </body> </html> diff --git a/tz-link.html b/tz-link.html index 2a7da95e9d27..8f836909d7e8 100644 --- a/tz-link.html +++ b/tz-link.html @@ -11,6 +11,7 @@ ul {padding-left: 1.3rem;} </style> </head> <body> +<main> <h1>Time zone and daylight saving time data</h1> <p> <a href="https://en.wikipedia.org/wiki/Time_zone">Time zone</a> and @@ -61,7 +62,6 @@ area. </ul> </nav> -<section> <h2 id="tzdb">The <code><abbr title="time zone">tz</abbr></code> database</h2> <p> The <a href="https://en.wikipedia.org/wiki/Public_domain">public-domain</a> @@ -84,6 +84,7 @@ title="Berkeley Software Distribution">BSD</abbr></a>, <a href="https://netbsd.org">Net<abbr>BSD</abbr></a>, <a href="https://www.openbsd.org">Open<abbr>BSD</abbr></a>, <a href="https://www.chromium.org/chromium-os/">ChromiumOS</a>, +<a href="https://en.wikipedia.org/wiki/OpenHarmony">OpenHarmony</a>, <a href="https://cygwin.com">Cygwin</a>, <a href="https://mariadb.org">MariaDB</a>, <a href="https://musl.libc.org">musl libc</a>, @@ -133,9 +134,7 @@ Universal Time</a> (<abbr>UTC</abbr>). The database also records when daylight saving time was in use, along with some time zone abbreviations such as <abbr>EST</abbr> for Eastern Standard Time in the <abbr>US</abbr>.</p> -</section> -<section> <h2 id="download">Downloading the <code><abbr>tz</abbr></code> database</h2> <p> The following <a @@ -145,15 +144,15 @@ the latest release’s two to a <abbr>GNU</abbr>/Linux or similar host.</p> <pre><code>mkdir tzdb cd tzdb -<a href="https://www.gnu.org/software/wget/">wget</a> https://www.iana.org/time-zones/repository/tzcode-latest.tar.gz -wget https://www.iana.org/time-zones/repository/tzdata-latest.tar.gz +<a href="https://www.gnu.org/software/wget/">wget</a> https://data.iana.org/time-zones/tzcode-latest.tar.gz +wget https://data.iana.org/time-zones/tzdata-latest.tar.gz <a href="https://www.gnu.org/software/gzip/">gzip</a> -dc tzcode-latest.tar.gz | <a href="https://www.gnu.org/software/tar/">tar</a> -xf - gzip -dc tzdata-latest.tar.gz | tar -xf - </code></pre> <p>Alternatively, the following shell commands download the same release in a single-tarball format containing extra data useful for regression testing:</p> -<pre><code>wget <a href="https://www.iana.org/time-zones/repository/tzdb-latest.tar.lz">https://www.iana.org/time-zones/repository/tzdb-latest.tar.lz</a> +<pre><code>wget <a href="https://data.iana.org/time-zones/tzdb-latest.tar.lz">https://data.iana.org/time-zones/tzdb-latest.tar.lz</a> <a href="https://www.nongnu.org/lzip/">lzip</a> -dc tzdb-latest.tar.lz | tar -xf - </code></pre> <p>These commands use convenience links to the latest release @@ -183,7 +182,7 @@ title="Hypertext Transfer Protocol Secure">HTTPS</abbr></a>, <a href="https://en.wikipedia.org/wiki/Rsync"><abbr title="remote sync">rsync</abbr></a>, and <a href="https://en.wikipedia.org/wiki/FTP"><abbr -title="File Transfer Protocol">FTP</abbr></a>. +title="File Transfer Protocol">FTP</abbr></a>.</p> <p>Alternatively, a development repository of code and data can be retrieved from <a href="https://github.com">GitHub</a> via the shell command:</p> @@ -205,9 +204,7 @@ Time Zone Information Format (<abbr>TZif</abbr>)</a> The code also lets you read a <abbr>TZif</abbr> file and interpret timestamps for that location.</p> -</section> -<section> <h2 id="changes">Changes to the <code><abbr>tz</abbr></code> database</h2> <p> The <code><abbr>tz</abbr></code> code and data @@ -234,7 +231,10 @@ the process by tailoring the generic instructions in the <code><abbr>tz</abbr> README</code> file and installing the latest data yourself. System-specific instructions for installing the latest <code><abbr>tz</abbr></code> data have also been published -for <a href="https://www.ibm.com/support/pages/aix-time-zone-olson-tzdata-updates"><abbr>AIX</abbr></a>, +for <a href="https://community.ibm.com/community/user/blogs/ravindra-shinde/2024/12/13/time-zone-update-tool-tz"><abbr>AIX</abbr></a> +(and +<a href="https://www.ibm.com/support/pages/aix-time-zone-olson-tzdata-updates">older +<abbr>AIX</abbr></a>), <a href="https://source.android.com/docs/core/permissions/timezone-rules">Android</a>, <a @@ -276,16 +276,14 @@ found in <a href="theory.html">Theory and pragmatics of the <a href="https://a0.github.io/a0-tzmigration/">A0 TimeZone Migration</a> displays changes between recent <code><abbr>tzdb</abbr></code> versions. </p> -</section> -<section> <h2 id="coordinating">Coordinating with governments and distributors</h2> <p> As discussed in “<a href="https://www.icann.org/en/blogs/details/how-time-zones-are-coordinated-13-03-2023-en">How Time Zones Are Coordinated</a>”, the time zone database relies on collaboration among governments, the time zone database volunteer -community, and data distributors downstream. +community, and data distributors downstream.</p> <p> If your government plans to change its time zone boundaries or daylight saving rules, please send email as described in @@ -326,9 +324,7 @@ The shorter the notice, the more likely clock problems will arise; see “<a href="https://codeofmatt.com/on-the-timing-of-time-zone-changes/">On the Timing of Time Zone Changes</a>” for examples. </p> -</section> -<section> <h2 id="commentary">Commentary on the <code><abbr>tz</abbr></code> database</h2> <ul> <li>The article @@ -344,9 +340,7 @@ database’s style.</li> <li><a href="https://doi.org/10.1145/3340301.3341125">What time is it: managing time in the internet</a> analyzes the database longitudinally.</li> </ul> -</section> -<section> <h2 id="web">Web sites using recent versions of the <code><abbr>tz</abbr></code> database</h2> <p> @@ -380,9 +374,7 @@ calculates the current time difference between locations.</li> <li><a href="https://www.wx-now.com">Weather Now</a> and <a href="https://www.thetimenow.com">The Time Now</a> list the weather too.</li> </ul> -</section> -<section> <h2 id="protocols">Network protocols for <code><abbr>tz</abbr></code> data</h2> <ul> <li><a href="https://www.rfc-editor.org/info/rfc7808/">Time Zone @@ -405,9 +397,7 @@ title="Extensible Markup Language">XML</abbr></a> format, and a variant uses <a href="https://www.json.org/json-en.html"><abbr title="JavaScript Object Notation">JSON</abbr></a> format.</li> </ul> -</section> -<section> <h2 id="compilers">Other <code><abbr>tz</abbr></code> compilers</h2> <p>Although some of these do not fully support <code><abbr>tz</abbr></code> data, in recent <code><abbr>tzdb</abbr></code> @@ -416,7 +406,7 @@ running the command <code>make rearguard_tarballs</code> and compiling from the resulting tarballs instead.</p> <ul> <li><a href="https://github.com/libical">Libical</a> -contains <code>vzic</code>, <a +contains <code>vzic</code>, a <a href="https://en.wikipedia.org/wiki/C_(programming_language)">C</a> program that compiles <code><abbr>tz</abbr></code> source into iCalendar-compatible VTIMEZONE files. @@ -425,6 +415,23 @@ available under the <a href="https://www.gnu.org/copyleft/gpl.html"><abbr>GNU</abbr> General Public License (<abbr title="General Public License">GPL</abbr>)</a>.</li> +<li>The <a id="TZDP" +href="https://howardhinnant.github.io/date/tz.html">Time Zone +Database Parser</a> contains a +<a href="https://en.wikipedia.org/wiki/C++">C++</a> runtime parser +supporting the <a +href="https://en.cppreference.com/cpp/chrono#Time_zone"><code>std::chrono::time_zone</code> API</a> +that is standardized in +<a href="https://en.wikipedia.org/wiki/C%2B%2B20">C++20</a> and later. +It is freely available under the +<a href="https://en.wikipedia.org/wiki/MIT_License"><abbr +title="Massachusetts Institute of Technology">MIT</abbr> license</a>. +This approach is used by the <code>std::chrono::time_zone</code> implementation +of <a href="https://gcc.gnu.org/"><abbr +title="the GNU Compiler Collection">GCC</abbr></a> +<code>libstdc++</code>, which is available under the GPL combined +with the <a href="https://www.gnu.org/licenses/gcc-exception.html">GCC +Runtime Library Exception</a>.</li> <li><a href="https://metacpan.org/release/DateTime-TimeZone">DateTime::TimeZone</a> contains a script <code>parse_olson</code> that compiles @@ -436,14 +443,6 @@ available under both the <abbr>GPL</abbr> and the Perl Artistic License. DateTime::TimeZone also contains a script <code>tests_from_zdump</code> that generates test cases for each clock transition in the <code><abbr>tz</abbr></code> database.</li> -<li>The <a href="https://howardhinnant.github.io/date/tz.html">Time Zone -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/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> <li><a id="ICU" href="https://icu.unicode.org">International Components for Unicode (<abbr>ICU</abbr>)</a> contains C/C++ and <a href="https://en.wikipedia.org/wiki/Java_(programming_language)">Java</a> @@ -498,6 +497,7 @@ License">LGPL</abbr>)</a>.</li> <li><abbr>ICU</abbr> (mentioned <a href="#ICU">above</a>) contains compilers and Java-based libraries.</li> </ul> +</li> <li><a href="https://nodatime.org">Noda Time – Date and time <abbr>API</abbr> for .NET</a> is like Joda-Time and Time4J, but for the .NET framework instead of Java. @@ -549,6 +549,7 @@ objects</a> let programs access an abstract view of <code><abbr>tzdb</abbr></code> data, and are designed to replace <a href="https://codeofmatt.com/javascript-date-type-is-horribly-broken/">JavaScript’s problematic <code>Date</code> objects</a> when working with dates and times. +</li> <li><a href="https://github.com/JuliaTime">JuliaTime</a> contains a compiler from <code><abbr>tz</abbr></code> source into <a href="https://julialang.org">Julia</a>. It is freely available @@ -587,9 +588,7 @@ source into text files, along with a runtime that can read those files. Tcl is freely available under a <abbr>BSD</abbr>-style license.</li> </ul> -</section> -<section> <h2 id="TZif">Other <abbr>TZif</abbr> readers</h2> <ul> <li>The <a @@ -604,6 +603,17 @@ a <abbr>TZif</abbr> file reader written in C that creates a <code>GTimeZone</code> object representing sets of <abbr>UT</abbr> offsets. It is freely available under the <abbr>LGPL</abbr>.</li> +<li>The <a href="https://en.wikipedia.org/wiki/Musl">musl</a> C library +also has an <abbr>TZif</abbr> reader. +It is freely available under the <abbr>MIT</abbr> license.</li> +<li>The Time Zone Database Parser for C++, mentioned <a href="#TZDP">above,</a> +can be configured to read TZif binary files. +A similar approach is taken in +<a href="https://www.llvm.org/"><abbr>LLVM</abbr></a> +<code>std::chrono::time_zone</code>, which is available +under the Apache License with +<a href="https://llvm.org/docs/DeveloperPolicy.html#open-source-licensing-terms">LLVM +Exceptions</a>.</li> <li>The <a href="https://github.com/bloomberg/bde/wiki">BDE Standard Library</a>’s <code>baltzo::TimeZoneUtil</code> component contains a C++ @@ -659,15 +669,20 @@ converts <abbr>TZif</abbr> data into <abbr>JSON</abbr>-format data suitable for use in its JavaScript library for time zone conversion. Dates before 1970 are not supported.</li> +<li>The <a href="https://crates.io/crates/jiff">jiff</a> datetime library +for <a href="https://rust-lang.org/">Rust</a> either compiles +<code><abbr>tz</abbr></code> source into Rust, +or consults <abbr>TZif</abbr> data at runtime. +It is in the public domain. +It intends to supplant the soft-deprecated +<a href="https://crates.io/crates/chrono-tz">Chrono-TZ</a>.</li> <li>The <a href="https://hackage.haskell.org/package/timezone-olson">timezone-olson</a> package contains <a href="https://www.haskell.org">Haskell</a> code that parses and uses <abbr>TZif</abbr> data. It is freely available under a <abbr>BSD</abbr>-style license.</li> </ul> -</section> -<section> <h2 id="software">Other <code><abbr>tz</abbr></code>-based time zone software</h2> <ul> <li><a href="https://foxclocks.org">FoxClocks</a> @@ -684,7 +699,10 @@ data (mentioned <a href="#CLDR">below</a>) used by the <a href="https://en.wikipedia.org/wiki/Windows_Runtime">Windows Runtime</a> / <a href="https://en.wikipedia.org/wiki/Universal_Windows_Platform">Universal Windows Platform</a> classes <a href="https://learn.microsoft.com/en-us/uwp/api/Windows.Globalization.DateTimeFormatting.DateTimeFormatter"><code>DateTimeFormatter</code></a> and -<a href="https://learn.microsoft.com/en-us/uwp/api/windows.globalization.calendar"><code>Calendar</code></a>. +<a href="https://learn.microsoft.com/en-us/uwp/api/windows.globalization.calendar"><code>Calendar</code></a>, +and with updates discussed in the +<a href="https://techcommunity.microsoft.com/category/windows/blog/dstblog">Daylight +Saving Time & Time Zone Blog</a>. <a id="System.TimeZoneInfo" href="https://learn.microsoft.com/en-us/archive/blogs/bclteam/exploring-windows-time-zones-with-system-timezoneinfo-josh-free">Exploring Windows Time Zones with <code>System.TimeZoneInfo</code></a> describes @@ -700,16 +718,14 @@ to <code><abbr>tz</abbr></code> names. These mappings can be performed programmatically via the <a href="https://github.com/mattjohnsonpint/TimeZoneConverter">TimeZoneConverter</a> .NET library, -or the ICU Java and C++ libraries mentioned <a href="#ICU">above</a>. +or the ICU Java and C++ libraries mentioned <a href="#ICU">above</a>.</li> <li><a href="https://www.oracle.com/java/">Oracle Java</a> contains a copy of a subset of a recent <code><abbr>tz</abbr></code> database in a Java-specific format.</li> </ul> -</section> -<section> <h2 id="other-dbs">Other time zone databases</h2> <ul> <li><a href="https://www.astro.com/atlas">Time-zone Atlas</a> @@ -733,9 +749,7 @@ Schedules Information Manual</a> of the International Air Transport Association gives current time zone rules for airports served by commercial aviation.</li> </ul> -</section> -<section> <h2 id="maps">Maps</h2> <ul> <li><a href="https://www.worldtimezone.com">World Time Zone Map @@ -757,9 +771,7 @@ World Factbook</em></a>, formerly published by the Although the maps’ pictorial quality is good, the maps do not indicate daylight saving time.</li> </ul> -</section> -<section> <h2 id="boundaries">Time zone boundaries</h2> <p>Geographical boundaries between timezones are available from several <a @@ -810,9 +822,7 @@ for land and territorial waters only). A captain can change ship’s clocks any time after entering a new time zone; midnight changes are common.</li> </ul> -</section> -<section> <h2 id="civil">Civil time concepts and history</h2> <ul> <li><a href="https://www.nist.gov/pml/time-and-frequency-division/popular-links/walk-through-time">A @@ -835,9 +845,7 @@ time zone boundary.</li> <li><a href="https://statoids.com/tconcept.html">Basic Time Zone Concepts</a> discusses terminological issues behind time zones.</li> </ul> -</section> -<section> <h2 id="national">National histories of legal time</h2> <dl> <dt>Australia</dt> @@ -935,9 +943,7 @@ Navy (SOHMA) publishes an annual <a href="https://sohma.armada.mil.uy/index.php/servicios/datos-astronomicos" hreflang="es">almanac (in Spanish)</a>.</dd> </dl> -</section> -<section> <h2 id="costs">Costs and benefits of time shifts</h2> <p>Various sources argue for and against daylight saving time and time zone shifts, and many scientific studies have been conducted. This @@ -1016,9 +1022,7 @@ 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> -<section> <h2 id="precision">Precision timekeeping</h2> <ul> <li><a @@ -1053,7 +1057,7 @@ a client’s time zone and daylight saving settings automatically.</li> Scales</a> describes astronomical time scales like <abbr title="Terrestrial Dynamic Time">TDT</abbr>, <abbr title="Geocentric Coordinate Time">TCG</abbr>, and -<abbr title="Barycentric Dynamic Time">TDB</abbr>. +<abbr title="Barycentric Dynamic Time">TDB</abbr>.</li> <li>The <a href="https://www.iau.org"><abbr title="International Astronomical Union">IAU</abbr></a>’s <a href="https://www.iausofa.org"><abbr @@ -1170,9 +1174,7 @@ However, there is still no consensus on whether this is the best way to replace leap seconds. </li> </ul> -</section> -<section> <h2 id="notation">Time notation</h2> <ul> <li>The <a id="CLDR" href="https://cldr.unicode.org">Unicode Common Locale Data @@ -1183,7 +1185,7 @@ contains French translations for “Eastern European Summer Time”, <a href="https://unicode.org/cldr/charts/latest/by_type/">by-type charts</a> show these values for many locales. Data values are available in both <abbr title="Locale Data Markup Language">LDML</abbr> -(an <abbr>XML</abbr> format) and <abbr>JSON</abbr>. +(an <abbr>XML</abbr> format) and <abbr>JSON</abbr>.</li> <li> <a href="https://www.cl.cam.ac.uk/~mgk25/iso-time.html">A summary of the international standard date and time notation</a> covers @@ -1209,7 +1211,7 @@ An extension, <a href="https://www.rfc-editor.org/info/rfc9557/">Date and Time on the Internet: Timestamps with Additional Information</a> (Internet <abbr>RFC</abbr> 9557) extends this profile to let you specify the <code><abbr>tzdb</abbr></code> timezone of a timestamp -via suffixes like <code>[Asia/Tokyo]</code>. +via suffixes like <code>[Asia/Tokyo]</code>.</li> <li> <a href="https://web.archive.org/web/20190130042457/https://www.hackcraft.net/web/datetime/">Date & Time Formats on the Web</a> surveys web- and Internet-oriented date and time @@ -1243,16 +1245,16 @@ any future changes to the rules. One should never set local time is nine hours ahead of <abbr>UT</abbr> and the time zone is called “<abbr>GMT</abbr>”.</li> </ul> -</section> +</main> -<section> +<aside> <h2 id="see-also">See also</h2> <ul> <li><a href="theory.html">Theory and pragmatics of the <code><abbr>tz</abbr></code> code and data</a></li> <li><a href="tz-art.html">Time and the arts</a></li> </ul> -</section> +</aside> <footer> <hr> diff --git a/tzfile.5.txt b/tzfile.5.txt index d812b0ea6003..00c2e62d398d 100644 --- a/tzfile.5.txt +++ b/tzfile.5.txt @@ -402,7 +402,6 @@ SEE ALSO time(2), localtime(3), tzset(3), tzselect(8), zdump(8), zic(8). Olson A, Eggert P, Murchison K. The Time Zone Information Format - (TZif). October 2024. Internet RFC 9636 ⟨https://www.rfc-editor.org/ - rfc/rfc9636⟩ doi:10.17487/RFC9636 ⟨https://doi.org/10.17487/RFC9636⟩. + (TZif). October 2024. Internet RFC 9636 doi:10.17487/RFC9636. Time Zone Database tzfile(5) @@ -1 +1 @@ -2026c +2026d @@ -719,7 +719,7 @@ delta_nonneg(struct tm *newp, struct tm *oldp) intmax_t sec = SECSPERREPEAT, result = cycles * sec; int tmy = oldp->tm_year + cycles * YEARSPERREPEAT; for ( ; tmy < newp->tm_year; ++tmy) - result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); + result += year_sum_days(tmy, TM_YEAR_BASE); result += newp->tm_yday - oldp->tm_yday; result *= HOURSPERDAY; result += newp->tm_hour - oldp->tm_hour; @@ -746,7 +746,7 @@ adjusted_yday(struct tm const *a, struct tm const *b) { int yday = a->tm_yday; if (b->tm_year < a->tm_year) - yday += 365 + isleap_sum(b->tm_year, TM_YEAR_BASE); + yday += year_sum_days(b->tm_year, TM_YEAR_BASE); return yday; } #endif @@ -330,12 +330,8 @@ for the POSIX Portable Character Set (PPCS) .UE and the encoding's non-unibyte characters should consist entirely of -non-PPCS bytes. Non-PPCS characters typically occur only in comments: -although output file names and time zone abbreviations can contain -nearly any character, other software will work better if these are -limited to the restricted syntax described under the -.B \-v -option. +non-PPCS bytes. +Non-PPCS characters typically occur only in comments. .PP Input lines are made up of fields. Fields are separated from one another by one or more white space characters. @@ -350,15 +346,16 @@ Any line that is blank (after comment stripping) is ignored. Nonblank lines are expected to be of one of three types: rule lines, zone lines, and link lines. .PP -Names must be in English and are case insensitive. +Keywords are in English and are case insensitive. +They are not reserved words. They appear in several contexts, and include month and weekday names -and keywords such as +and other words such as .BR "maximum" , .BR "only" , .BR "Rolling" , and .BR "Zone" . -A name can be abbreviated by omitting all but an initial prefix; any +A keyword can be abbreviated by omitting all but an initial prefix; any abbreviation must be unambiguous in context. .PP A rule line has the form @@ -586,11 +583,26 @@ The name of the timezone. This is the name used in creating the time conversion information file for the timezone. It should not contain a file name component +that is empty or is .q ".\&" or .q ".." ; a file name component is a maximal substring that does not contain .q "/" . +Names can be further restricted by the operating system or file system. +Most systems reject a name +.IB A / B +if there is another name +.IR A , +some reject names differing only in case, +and some reject names containing special characters like +.q ":" +and +.q "\e" . +Other software will work better if names are +limited to the restricted syntax described under the +.B \-v +option. .TP .B STDOFF The amount of time to add to UT to get standard time, @@ -737,8 +749,16 @@ A link line has the form .ta \w'Link\0\0'u +\w'Europe/Istanbul\0\0'u Link TARGET LINK-NAME .sp +.fi +where +.B TARGET +and +.B LINK-NAME +both have the same syntax as a zone line's +.BR NAME . For example: .sp +.nf .ti +2 Link Europe/Istanbul Asia/Istanbul .sp @@ -752,10 +772,7 @@ field in some zone line or as the field in some link line. The .B LINK-NAME -field is used as an alternative name for that zone; -it has the same syntax as a zone line's -.B NAME -field. +field is an alternative name for that zone. Links can chain together, although the behavior is unspecified if a chain of one or more links does not terminate in a Zone name. A link line can appear before the line that defines the link target. diff --git a/zic.8.txt b/zic.8.txt index 77b5c62f6878..945a810dcb50 100644 --- a/zic.8.txt +++ b/zic.8.txt @@ -186,13 +186,9 @@ FILES zero or more lines, each ending in a newline byte and containing at most 2048 bytes counting the newline, and without any NUL bytes. The input text's encoding is typically UTF-8 or ASCII; it should have a - unibyte representation for the POSIX Portable Character Set (PPCS) - ⟨https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap06 - .html⟩ and the encoding's non-unibyte characters should consist - entirely of non-PPCS bytes. Non-PPCS characters typically occur only - in comments: although output file names and time zone abbreviations can - contain nearly any character, other software will work better if these - are limited to the restricted syntax described under the -v option. + unibyte representation for the POSIX Portable Character Set (PPCS) and + the encoding's non-unibyte characters should consist entirely of non- + PPCS bytes. Non-PPCS characters typically occur only in comments. Input lines are made up of fields. Fields are separated from one another by one or more white space characters. The white space @@ -206,11 +202,11 @@ FILES lines are expected to be of one of three types: rule lines, zone lines, and link lines. - Names must be in English and are case insensitive. They appear in - several contexts, and include month and weekday names and keywords such - as maximum, only, Rolling, and Zone. A name can be abbreviated by - omitting all but an initial prefix; any abbreviation must be - unambiguous in context. + Keywords are in English and are case insensitive. They are not + reserved words. They appear in several contexts, and include month and + weekday names and other words such as maximum, only, Rolling, and Zone. + A keyword can be abbreviated by omitting all but an initial prefix; any + abbreviation must be unambiguous in context. A rule line has the form @@ -330,8 +326,14 @@ FILES NAME The name of the timezone. This is the name used in creating the time conversion information file for the timezone. It should - not contain a file name component “.” or “..”; a file name - component is a maximal substring that does not contain “/”. + not contain a file name component that is empty or is “.” or + “..”; a file name component is a maximal substring that does not + contain “/”. Names can be further restricted by the operating + system or file system. Most systems reject a name A/B if there + is another name A, some reject names differing only in case, and + some reject names containing special characters like “:” and + “\”. Other software will work better if names are limited to + the restricted syntax described under the -v option. STDOFF The amount of time to add to UT to get standard time, without any adjustment for daylight saving. This field has the same @@ -418,17 +420,17 @@ FILES Link TARGET LINK-NAME - For example: + where TARGET and LINK-NAME both have the same syntax as a zone line's + NAME. For example: Link Europe/Istanbul Asia/Istanbul The TARGET field should appear as the NAME field in some zone line or - as the LINK-NAME field in some link line. The LINK-NAME field is used - as an alternative name for that zone; it has the same syntax as a zone - line's NAME field. Links can chain together, although the behavior is - unspecified if a chain of one or more links does not terminate in a - Zone name. A link line can appear before the line that defines the - link target. For example: + as the LINK-NAME field in some link line. The LINK-NAME field is an + alternative name for that zone. Links can chain together, although the + behavior is unspecified if a chain of one or more links does not + terminate in a Zone name. A link line can appear before the line that + defines the link target. For example: Link Greenwich G_M_T Link Etc/GMT Greenwich @@ -528,10 +528,6 @@ static const int len_months[2][MONSPERYEAR] = { { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; -static const int len_years[2] = { - DAYSPERNYEAR, DAYSPERLYEAR -}; - static struct attype { zic_t at; bool dontmerge; @@ -783,6 +779,7 @@ arg2num(char const *arg, int base, unsigned long maxval, char const *msgid) #ifndef HAVE_SETMODE # if (defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || defined __DragonFly__ \ || (defined __APPLE__ && defined __MACH__)) # define HAVE_SETMODE 1 # else @@ -1467,6 +1464,12 @@ namecheck(const char *name) register char const *component = name; for (cp = name; *cp; cp++) { unsigned char c = *cp; +#if defined _WIN32 || defined __CYGWIN__ + if (c == ':' || c == '\\') { + error(N_("file name '%s' contains '%c'"), name, c); + return false; + } +#endif if (noise && !strchr(benign, c)) { warning((strchr(printable_and_not_benign, c) ? N_("file name '%s' contains byte '%c'") @@ -1781,8 +1784,20 @@ dolink(char const *target, char const *linkname, bool staysymlink) break; } link_errno = errno; + /* When hard links are not supported, some MS-Windows file system + drivers fail with EINVAL, contrary to the intent of + MS-FSA 42.0 (2025) section 2.1.5.15.7. */ + if (link_errno == EINVAL) + link_errno = ENOTSUP; } #endif + /* On platforms like AIX, the Linux kernel, macOS, and Solaris, + link/linkat can fail with ENOSYS or EPERM if the file system + does not support hard links, or if other problems occur. + It is too much trouble to suss out the other problems. */ + if (link_errno == ENOSYS || link_errno == EPERM) + link_errno = ENOTSUP; + if (link_errno == EXDEV || link_errno == ENOTSUP) break; @@ -1849,7 +1864,8 @@ dolink(char const *target, char const *linkname, bool staysymlink) strerror(link_errno)); else if (symlink_errno < 0) warning(N_("copy used because symbolic link not obvious")); - else if (symlink_errno != ENOTSUP) + else if (symlink_errno != ENOSYS && symlink_errno != ENOTSUP + && symlink_errno != EPERM) warning(N_("copy used because symbolic link failed: %s"), strerror(symlink_errno)); } @@ -2307,18 +2323,13 @@ inzsub(char **fields, int nfields, bool iscont) static zic_t getleapdatetime(char **fields, bool expire_line) { - register const char * cp; register const struct lookup * lp; - register zic_t i, j; zic_t year; - int month, day; - zic_t dayoff, tod; zic_t t; char xs; + struct rule r; - dayoff = 0; - cp = fields[LP_YEAR]; - if (sscanf(cp, "%"SCNdZIC"%c", &year, &xs) != 1) { + if (sscanf(fields[LP_YEAR], "%"SCNdZIC"%c", &year, &xs) != 1) { /* ** Leapin' Lizards! */ @@ -2332,38 +2343,20 @@ getleapdatetime(char **fields, bool expire_line) leapminyear = year; leapseen = true; } - j = EPOCH_YEAR; - while (j != year) { - if (year > j) { - i = len_years[isleap(j)]; - ++j; - } else { - --j; - i = -len_years[isleap(j)]; - } - dayoff = oadd(dayoff, i); - } if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) { error(N_("invalid month name")); return -1; } - month = lp->l_value; - j = TM_JANUARY; - while (j != month) { - i = len_months[isleap(year)][j]; - dayoff = oadd(dayoff, i); - ++j; - } - cp = fields[LP_DAY]; - if (sscanf(cp, "%d%c", &day, &xs) != 1 || - day <= 0 || day > len_months[isleap(year)][month]) { + r.r_month = lp->l_value; + if (! (sscanf(fields[LP_DAY], "%d%c", &r.r_dayofmonth, &xs) == 1 + && 0 < r.r_dayofmonth + && r.r_dayofmonth <= len_months[isleap(year)][r.r_month])) { error(N_("invalid day of month")); return -1; } - dayoff = oadd(dayoff, day - 1); - t = omul(dayoff, SECSPERDAY); - tod = gethms(fields[LP_TIME], N_("invalid time of day")); - t = tadd(t, tod); + r.r_dycode = DC_DOM; + r.r_tod = gethms(fields[LP_TIME], N_("invalid time of day")); + t = rpytime(&r, year); if (t < 0) error(N_("leap second precedes Epoch")); return t; @@ -2419,6 +2412,8 @@ inlink(char **fields, int nfields) error(N_("blank TARGET field on Link line")); return; } + if (! namecheck(fields[LF_TARGET])) + return; if (! namecheck(fields[LF_LINKNAME])) return; l.l_filenum = filenum; @@ -3552,10 +3547,13 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) } else defaulttype = type; } else { - zic_t year; - for (year = min_year; year <= max_year; ++year) { + zic_t year, next_year; + bool try_next_year = true; + for (year = min_year; try_next_year; year = next_year) { if (useuntil && year > zp->z_untilrule.r_hiyear) break; + try_next_year = false; + next_year = max_year; /* ** Mark which rules to do in the current year. ** For those to do, calculate rpytime(rp, year); @@ -3567,6 +3565,17 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) struct rule *rp = &zp->z_rules[j]; eats(zp->z_filenum, zp->z_linenum, rp->r_filenum, rp->r_linenum); + + /* Keep track of the earliest year after YEAR + in which some rule <= J applies. */ + if (year < rp->r_hiyear) { + zic_t next = max(year + 1, rp->r_loyear); + if (next <= next_year) { + next_year = next; + try_next_year = true; + } + } + rp->r_todo = year >= rp->r_loyear && year <= rp->r_hiyear; if (rp->r_todo) { @@ -4147,7 +4156,7 @@ rpytime(const struct rule *rp, zic_t wantedy) wantedy = y + (yrem + 2 * YEARSPERREPEAT) % YEARSPERREPEAT; while (wantedy != y) { - i = len_years[isleap(y)]; + i = year_days(y); dayoff = oadd(dayoff, i); y++; } |
