git: 967a49a21a27 - main - Update tzcode to 2025b
- Reply: Dag-Erling_Smørgrav : "Re: git: 967a49a21a27 - main - Update tzcode to 2025b"
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 27 Aug 2025 18:40:41 UTC
The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=967a49a21a27380ba1c545c746b4f1badabefd77 commit 967a49a21a27380ba1c545c746b4f1badabefd77 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2025-08-25 12:06:59 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2025-08-27 18:40:09 +0000 Update tzcode to 2025b MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D52103 --- contrib/tzcode/Makefile | 32 +-- contrib/tzcode/NEWS | 109 +++++++- contrib/tzcode/asctime.c | 122 +++++---- contrib/tzcode/date.1 | 122 ++------- contrib/tzcode/localtime.c | 585 +++++++++++++++++++++++++++---------------- contrib/tzcode/newctime.3 | 152 ++++++----- contrib/tzcode/newstrftime.3 | 86 ++++--- contrib/tzcode/newtzset.3 | 24 +- contrib/tzcode/private.h | 119 ++++++--- contrib/tzcode/strftime.c | 83 ++++-- contrib/tzcode/theory.html | 40 ++- contrib/tzcode/tz-link.html | 45 ++-- contrib/tzcode/tzfile.5 | 70 +++--- contrib/tzcode/tzfile.h | 2 +- contrib/tzcode/tzselect.8 | 26 +- contrib/tzcode/version | 2 +- contrib/tzcode/zdump.8 | 14 +- contrib/tzcode/zdump.c | 62 ++--- contrib/tzcode/zic.8 | 27 +- contrib/tzcode/zic.c | 142 +++++++---- 20 files changed, 1117 insertions(+), 747 deletions(-) diff --git a/contrib/tzcode/Makefile b/contrib/tzcode/Makefile index 0087b4596515..2130582c2deb 100644 --- a/contrib/tzcode/Makefile +++ b/contrib/tzcode/Makefile @@ -137,7 +137,7 @@ TIME_T_ALTERNATIVES_TAIL = int_least32_t.ck uint_least32_t.ck \ uint_least64_t.ck # What kind of TZif data files to generate. (TZif is the binary time -# zone data format that zic generates; see Internet RFC 8536.) +# zone data format that zic generates; see Internet RFC 9636.) # If you want only POSIX time, with time values interpreted as # seconds since the epoch (not counting leap seconds), use # REDO= posix_only @@ -255,6 +255,7 @@ LDLIBS= # -DHAVE_UNISTD_H=0 if <unistd.h> does not work* # -DHAVE_UTMPX_H=0 if <utmpx.h> does not work* # -Dlocale_t=XXX if your system uses XXX instead of locale_t +# -DMKTIME_MIGHT_OVERFLOW if mktime might fail due to time_t overflow # -DPORT_TO_C89 if tzcode should also run on mostly-C89 platforms+ # Typically it is better to use a later standard. For example, # with GCC 4.9.4 (2016), prefer '-std=gnu11' to '-DPORT_TO_C89'. @@ -262,7 +263,7 @@ LDLIBS= # feature (integers at least 64 bits wide) and maybe more. # -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers # with external linkage, e.g., applications cannot define 'localtime'. -# -Dssize_t=long on hosts like MS-Windows that lack ssize_t +# -Dssize_t=int on hosts like MS-Windows that lack ssize_t # -DSUPPORT_C89=0 if the tzcode library should not support C89 callers # Although -DSUPPORT_C89=0 might work around latent bugs in callers, # it does not conform to POSIX. @@ -285,7 +286,7 @@ LDLIBS= # This mishandles some past timestamps, as US DST rules have changed. # It also mishandles settings like TZ='EET-2EEST' for eastern Europe, # as Europe and US DST rules differ. -# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 255) +# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 254) # -DUNINIT_TRAP if reading uninitialized storage can cause problems # other than simply getting garbage data # -DUSE_LTZ=0 to build zdump with the system time zone library @@ -319,7 +320,8 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \ $(GCC_INSTRUMENT) \ -Wall -Wextra \ -Walloc-size-larger-than=100000 -Warray-bounds=2 \ - -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wdate-time \ + -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wcast-qual \ + -Wdate-time \ -Wdeclaration-after-statement -Wdouble-promotion \ -Wduplicated-branches -Wduplicated-cond -Wflex-array-member-not-at-end \ -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \ @@ -336,7 +338,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \ -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure \ -Wtrampolines -Wundef -Wunused-macros -Wuse-after-free=3 \ -Wvariadic-macros -Wvla -Wwrite-strings \ - -Wno-format-nonliteral -Wno-sign-compare + -Wno-format-nonliteral -Wno-sign-compare -Wno-type-limits # # If your system has a "GMT offset" field in its "struct tm"s # (or if you decide to add such a field in your system's "time.h" file), @@ -614,8 +616,8 @@ TZS_YEAR= 2050 TZS_CUTOFF_FLAG= -c $(TZS_YEAR) TZS= to$(TZS_YEAR).tzs TZS_NEW= to$(TZS_YEAR)new.tzs -TZS_DEPS= $(YDATA) asctime.c localtime.c \ - private.h tzfile.h zdump.c zic.c +TZS_DEPS= $(YDATA) localtime.c private.h \ + strftime.c tzfile.h zdump.c zic.c TZDATA_DIST = $(COMMON) $(DATA) $(MISC) # EIGHT_YARDS is just a yard short of the whole ENCHILADA. EIGHT_YARDS = $(TZDATA_DIST) $(DOCS) $(SOURCES) tzdata.zi @@ -855,10 +857,10 @@ tzselect: tzselect.ksh version chmod +x $@.out mv $@.out $@ -check: check_mild back.ck +check: check_mild back.ck now.ck check_mild: check_web check_zishrink \ character-set.ck white-space.ck links.ck mainguard.ck \ - name-lengths.ck now.ck slashed-abbrs.ck sorted.ck \ + name-lengths.ck slashed-abbrs.ck sorted.ck \ tables.ck ziguard.ck tzs.ck # True if UTF8_LOCALE does not work; @@ -1103,7 +1105,7 @@ set-timestamps.out: $(EIGHT_YARDS) touch -md @1 test.out; then \ rm -f test.out && \ for file in $$files; do \ - if git diff --quiet $$file; then \ + if git diff --quiet HEAD $$file; then \ time=$$(TZ=UTC0 git log -1 \ --format='tformat:%cd' \ --date='format:%Y-%m-%dT%H:%M:%SZ' \ @@ -1354,13 +1356,13 @@ long-long.ck unsigned.ck: $(VERSION_DEPS) zonenames: tzdata.zi @$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi -asctime.o: private.h tzfile.h +asctime.o: private.h date.o: private.h difftime.o: private.h -localtime.o: private.h tzfile.h tzdir.h -strftime.o: private.h tzfile.h -zdump.o: version.h -zic.o: private.h tzfile.h tzdir.h version.h +localtime.o: private.h tzdir.h tzfile.h +strftime.o: localtime.c private.h tzdir.h tzfile.h +zdump.o: private.h version.h +zic.o: private.h tzdir.h tzfile.h version.h .PHONY: ALL INSTALL all .PHONY: check check_mild check_time_t_alternatives diff --git a/contrib/tzcode/NEWS b/contrib/tzcode/NEWS index 83b8b8c8d39c..8c0771641ef0 100644 --- a/contrib/tzcode/NEWS +++ b/contrib/tzcode/NEWS @@ -1,5 +1,108 @@ News for the tz database +Release 2025b - 2025-03-22 13:40:46 -0700 + + Briefly: + New zone for Aysén Region in Chile which moves from -04/-03 to -03. + + Changes to future timestamps + + Chile's Aysén Region moves from -04/-03 to -03 year-round, joining + Magallanes Region. The region will not change its clocks on + 2025-04-05 at 24:00, diverging from America/Santiago and creating a + new zone America/Coyhaique. (Thanks to Yonathan Dossow.) Model + this as a change to standard offset effective 2025-03-20. + + Changes to past timestamps + + Iran switched from +04 to +0330 on 1978-11-10 at 24:00, not at + year end. (Thanks to Roozbeh Pournader.) + + Changes to code + + 'zic -l TIMEZONE -d . -l /some/other/file/system' no longer + attempts to create an incorrect symlink, and no longer has a + read buffer underflow. (Problem reported by Evgeniy Gorbanev.) + + +Release 2025a - 2025-01-15 10:47:24 -0800 + + Briefly: + Paraguay adopted permanent -03 starting spring 2024. + Improve pre-1991 data for the Philippines. + Etc/Unknown is now reserved. + + Changes to future timestamps + + Paraguay stopped changing its clocks after the spring-forward + transition on 2024-10-06, so it is now permanently at -03. + (Thanks to Heitor David Pinto and Even Scharning.) + This affects timestamps starting 2025-03-22, as well as the + obsolescent tm_isdst flags starting 2024-10-15. + + Changes to past timestamps + + Correct timestamps for the Philippines before 1900, and from 1937 + through 1990. (Thanks to P Chan for the heads-up and citations.) + This includes adjusting local mean time before 1899; fixing + transitions in September 1899, January 1937, and June 1954; adding + transitions in December 1941, November 1945, March and September + 1977, and May and July 1990; and removing incorrect transitions in + March and September 1978. + + Changes to data + + Add zone1970.tab lines for the Concordia and Eyre Bird Observatory + research stations. (Thanks to Derick Rethans and Jule Dabars.) + + Changes to code + + strftime %s now generates the correct numeric string even when the + represented number does not fit into time_t. This is better than + generating the numeric equivalent of (time_t) -1, as strftime did + in TZDB releases 96a (when %s was introduced) through 2020a and in + releases 2022b through 2024b. It is also better than failing and + returning 0, as strftime did in releases 2020b through 2022a. + + strftime now outputs an invalid conversion specifier as-is, + instead of eliding the leading '%', which confused debugging. + + An invalid TZ now generates the time zone abbreviation "-00", not + "UTC", to help the user see that an error has occurred. (Thanks + to Arthur David Olson for suggesting a "wrong result".) + + mktime and timeoff no longer incorrectly fail merely because a + struct tm component near INT_MIN or INT_MAX overflows when a + lower-order component carries into it. + + TZNAME_MAXIMUM, the maximum number of bytes in a proleptic TZ + string's time zone abbreviation, now defaults to 254 not 255. + This helps reduce the size of internal state from 25480 to 21384 + on common platforms. This change should not be a problem, as + nobody uses such long "abbreviations" and the longstanding tzcode + maximum was 16 until release 2023a. For those who prefer no + arbitrary limits, you can now specify TZNAME_MAXIMUM values up to + PTRDIFF_MAX, a limit forced by C anyway; formerly tzcode silently + misbehaved unless TZNAME_MAXIMUM was less than INT_MAX. + + tzset and related functions no longer leak a file descriptor if + another thread forks or execs at about the same time and if the + platform has O_CLOFORK and O_CLOEXEC respectively. Also, the + functions no longer let a TZif file become a controlling terminal. + + 'zdump -' now reads TZif data from /dev/stdin. + (From a question by Arthur David Olson.) + + Changes to documentation + + The name Etc/Unknown is now reserved: it will not be used by TZDB. + This is for compatibility with CLDR, which uses the string + "Etc/Unknown" for an unknown or invalid timezone. (Thanks to + Justin Grant, Mark Davis, and Guy Harris.) + + Cite Internet RFC 9636, which obsoletes RFC 8536 for TZif format. + + Release 2024b - 2024-09-04 12:27:47 -0700 Briefly: @@ -116,7 +219,7 @@ Release 2024b - 2024-09-04 12:27:47 -0700 Changes to commentary Commentary about historical transitions in Portugal and her former - colonies has been expanded with links to many relevant legislation. + colonies has been expanded with links to relevant legislation. (Thanks to Tim Parenti.) @@ -204,10 +307,10 @@ Release 2023d - 2023-12-21 20:02:24 -0800 changing its time zone from -01/+00 to -02/-01 at the same moment as the spring-forward transition. Its clocks will therefore not spring forward as previously scheduled. The time zone change - reverts to its common practice before 1981. + reverts to its common practice before 1981. (Thanks to Jule Dabars.) Fix predictions for DST transitions in Palestine in 2072-2075, - correcting a typo introduced in 2023a. + correcting a typo introduced in 2023a. (Thanks to Jule Dabars.) Changes to past and future timestamps diff --git a/contrib/tzcode/asctime.c b/contrib/tzcode/asctime.c index ebb90a1cc84d..1977a2272896 100644 --- a/contrib/tzcode/asctime.c +++ b/contrib/tzcode/asctime.c @@ -7,6 +7,7 @@ /* ** Avoid the temptation to punt entirely to strftime; +** strftime can behave badly when tm components are out of range, and ** the output of strftime is supposed to be locale specific ** whereas the output of asctime is supposed to be constant. */ @@ -18,27 +19,6 @@ #include "un-namespace.h" #include <stdio.h> -/* -** All years associated with 32-bit time_t values are exactly four digits long; -** some years associated with 64-bit time_t values are not. -** Vintage programs are coded for years that are always four digits long -** and may assume that the newline always lands in the same place. -** For years that are less than four digits, we pad the output with -** leading zeroes to get the newline in the traditional place. -** The -4 ensures that we get four characters of output even if -** we call a strftime variant that produces fewer characters for some years. -** This conforms to recent ISO C and POSIX standards, which say behavior -** is undefined when the year is less than 1000 or greater than 9999. -*/ -static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n"; -/* -** For years that are more than four digits we put extra spaces before the year -** so that code trying to overwrite the newline won't end up overwriting -** a digit within a year and truncating the year (operating on the assumption -** that no output is better than wrong output). -*/ -static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n"; - enum { STD_ASCTIME_BUF_SIZE = 26 }; /* ** Big enough for something such as @@ -52,14 +32,24 @@ enum { STD_ASCTIME_BUF_SIZE = 26 }; */ static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1]; -/* A similar buffer for ctime. - C89 requires that they be the same buffer. - This requirement was removed in C99, so support it only if requested, - as support is more likely to lead to bugs in badly written programs. */ -#if SUPPORT_C89 -# define buf_ctime buf_asctime -#else -static char buf_ctime[sizeof buf_asctime]; +/* On pre-C99 platforms, a snprintf substitute good enough for us. */ +#if !HAVE_SNPRINTF +# include <stdarg.h> +ATTRIBUTE_FORMAT((printf, 3, 4)) static int +my_snprintf(char *s, size_t size, char const *format, ...) +{ + int n; + va_list args; + char stackbuf[sizeof buf_asctime]; + va_start(args, format); + n = vsprintf(stackbuf, format, args); + va_end (args); + if (0 <= n && n < size) + memcpy (s, stackbuf, n + 1); + return n; +} +# undef snprintf +# define snprintf my_snprintf #endif /* Publish asctime_r and ctime_r only when supporting older POSIX. */ @@ -84,14 +74,19 @@ asctime_r(struct tm const *restrict timeptr, char *restrict buf) "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - const char * wn; - const char * mn; - char year[INT_STRLEN_MAXIMUM(int) + 2]; - char result[sizeof buf_asctime]; + register const char * wn; + register const char * mn; + int year, mday, hour, min, sec; + long long_TM_YEAR_BASE = TM_YEAR_BASE; + size_t bufsize = (buf == buf_asctime + ? sizeof buf_asctime : STD_ASCTIME_BUF_SIZE); if (timeptr == NULL) { + strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); + /* Set errno now, since strcpy might change it in + POSIX.1-2017 and earlier. */ errno = EINVAL; - return strcpy(buf, "??? ??? ?? ??:??:?? ????\n"); + return buf; } if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK) wn = "???"; @@ -99,25 +94,41 @@ asctime_r(struct tm const *restrict timeptr, char *restrict buf) if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR) mn = "???"; else mn = mon_name[timeptr->tm_mon]; - /* - ** Use strftime's %Y to generate the year, to avoid overflow problems - ** when computing timeptr->tm_year + TM_YEAR_BASE. - ** Assume that strftime is unaffected by other out-of-range members - ** (e.g., timeptr->tm_mday) when processing "%Y". - */ - strftime(year, sizeof year, "%Y", timeptr); - /* - ** We avoid using snprintf since it's not available on all systems. - */ - sprintf(result, - ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B), - wn, mn, - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec, - year); - if (strlen(result) < STD_ASCTIME_BUF_SIZE - || buf == buf_ctime || buf == buf_asctime) - return strcpy(buf, result); + + year = timeptr->tm_year; + mday = timeptr->tm_mday; + hour = timeptr->tm_hour; + min = timeptr->tm_min; + sec = timeptr->tm_sec; + + /* Vintage programs are coded for years that are always four bytes long + and may assume that the newline always lands in the same place. + For years that are less than four bytes, pad the output with + leading zeroes to get the newline in the traditional place. + For years longer than four bytes, put extra spaces before the year + so that vintage code trying to overwrite the newline + won't overwrite a digit within a year and truncate the year, + using the principle that no output is better than wrong output. + This conforms to ISO C and POSIX standards, which say behavior + is undefined when the year is less than 1000 or greater than 9999. + + Also, avoid overflow when formatting tm_year + TM_YEAR_BASE. */ + + if ((year <= LONG_MAX - TM_YEAR_BASE + ? snprintf (buf, bufsize, + ((-999 - TM_YEAR_BASE <= year + && year <= 9999 - TM_YEAR_BASE) + ? "%s %s%3d %.2d:%.2d:%.2d %04ld\n" + : "%s %s%3d %.2d:%.2d:%.2d %ld\n"), + wn, mn, mday, hour, min, sec, + year + long_TM_YEAR_BASE) + : snprintf (buf, bufsize, + "%s %s%3d %.2d:%.2d:%.2d %d%d\n", + wn, mn, mday, hour, min, sec, + year / 10 + TM_YEAR_BASE / 10, + year % 10)) + < bufsize) + return buf; else { errno = EOVERFLOW; return NULL; @@ -142,5 +153,8 @@ ctime_r(const time_t *timep, char *buf) char * ctime(const time_t *timep) { - return ctime_r(timep, buf_ctime); + /* Do not call localtime_r, as C23 requires ctime to initialize the + static storage that localtime updates. */ + struct tm *tmp = localtime(timep); + return tmp ? asctime(tmp) : NULL; } diff --git a/contrib/tzcode/date.1 b/contrib/tzcode/date.1 index 01907bc76e2c..3a02e7c2e08a 100644 --- a/contrib/tzcode/date.1 +++ b/contrib/tzcode/date.1 @@ -6,15 +6,13 @@ date \- show and set date and time .SH SYNOPSIS .if n .nh .if n .na -.ie \n(.g .ds - \f(CR-\fP -.el .ds - \- .B date [ -.B \*-u +.B \-u ] [ -.B \*-c +.B \-c ] [ -.B \*-r +.B \-r .I seconds ] [ .BI + format @@ -35,7 +33,7 @@ command without arguments writes the date and time to the standard output in the form .ce 1 -Wed Mar 8 14:54:40 EST 1989 +Sat Mar 8 14:54:40 EST 2025 .br with .B EST @@ -49,99 +47,24 @@ If a command-line argument starts with a plus sign (\c .q "\fB+\fP" ), the rest of the argument is used as a .I format -that controls what appears in the output. -In the format, when a percent sign (\c -.q "\fB%\fP" -appears, -it and the character after it are not output, -but rather identify part of the date or time -to be output in a particular way -(or identify a special character to output): -.nf -.sp -.if t .in +.5i -.if n .in +2 -.ta \w'%M\0\0'u +\w'Wed Mar 8 14:54:40 EST 1989\0\0'u - Sample output Explanation -%a Wed Abbreviated weekday name* -%A Wednesday Full weekday name* -%b Mar Abbreviated month name* -%B March Full month name* -%c Wed Mar 08 14:54:40 1989 Date and time* -%C 19 Century -%d 08 Day of month (always two digits) -%D 03/08/89 Month/day/year (eight characters) -%e 8 Day of month (leading zero blanked) -%h Mar Abbreviated month name* -%H 14 24-hour-clock hour (two digits) -%I 02 12-hour-clock hour (two digits) -%j 067 Julian day number (three digits) -%k 2 12-hour-clock hour (leading zero blanked) -%l 14 24-hour-clock hour (leading zero blanked) -%m 03 Month number (two digits) -%M 54 Minute (two digits) -%n \\n newline character -%p PM AM/PM designation -%r 02:54:40 PM Hour:minute:second AM/PM designation -%R 14:54 Hour:minute -%S 40 Second (two digits) -%t \\t tab character -%T 14:54:40 Hour:minute:second -%U 10 Sunday-based week number (two digits) -%w 3 Day number (one digit, Sunday is 0) -%W 10 Monday-based week number (two digits) -%x 03/08/89 Date* -%X 14:54:40 Time* -%y 89 Last two digits of year -%Y 1989 Year in full -%z -0500 Numeric time zone -%Z EST Time zone abbreviation -%+ Wed Mar 8 14:54:40 EST 1989 Default output format* -.if t .in -.5i -.if n .in -2 -* The exact output depends on the locale. -.sp -.fi -If a character other than one of those shown above appears after -a percent sign in the format, -that following character is output. -All other characters in the format are copied unchanged to the output; -a newline character is always added at the end of the output. -.PP -In Sunday-based week numbering, -the first Sunday of the year begins week 1; -days preceding it are part of -.q "week 0" . -In Monday-based week numbering, -the first Monday of the year begins week 1. -.PP -To set the date, use a command line argument with one of the following forms: -.nf -.if t .in +.5i -.if n .in +2 -.ta \w'198903081454\0'u -1454 24-hour-clock hours (first two digits) and minutes -081454 Month day (first two digits), hours, and minutes -03081454 Month (two digits, January is 01), month day, hours, minutes -8903081454 Year, month, month day, hours, minutes -0308145489 Month, month day, hours, minutes, year - (on System V-compatible systems) -030814541989 Month, month day, hours, minutes, four-digit year -198903081454 Four-digit year, month, month day, hours, minutes -.if t .in -.5i -.if n .in -2 -.fi -If the century, year, month, or month day is not given, -the current value is used. -Any of the above forms may be followed by a period and two digits that give -the seconds part of the new time; if no seconds are given, zero is assumed. +that is processed by +.BR strftime (3) +to determine what to output; +a newline character is appended. +For example, the shell command: +.ce 1 +date +"%Y\-%m\-%d %H:%M:%S %z" +.br +outputs a line like +.q "2025\-03\-08 14:54:40 \-0500" +instead. .PP These options are available: .TP -.BR \*-u " or " \*-c +.BR \-u " or " \-c Use Universal Time when setting and showing the date and time. .TP -.BI "\*-r " seconds +.BI "\-r " seconds Output the date that corresponds to .I seconds past the epoch of 1970-01-01 00:00:00 UTC, where @@ -149,16 +72,13 @@ past the epoch of 1970-01-01 00:00:00 UTC, where should be an integer, either decimal, octal (leading 0), or hexadecimal (leading 0x), preceded by an optional sign. .SH FILES -.ta \w'/usr/share/zoneinfo/posixrules\0\0'u +.ta \w'/usr/share/zoneinfo/Etc/UTC\0\0'u /etc/localtime local timezone file .br /usr/lib/locale/\f2L\fP/LC_TIME description of time locale \f2L\fP .br /usr/share/zoneinfo timezone directory .br -/usr/share/zoneinfo/posixrules default DST rules (obsolete) -.br -/usr/share/zoneinfo/GMT for UTC leap seconds -.PP -If /usr/share/zoneinfo/GMT is absent, -UTC leap seconds are loaded from /usr/share/zoneinfo/GMT0 if present. +/usr/share/zoneinfo/Etc/UTC for UTC leap seconds +.SH SEE ALSO +.BR strftime (3). diff --git a/contrib/tzcode/localtime.c b/contrib/tzcode/localtime.c index 0fe7f1ed3f64..a80d422f2955 100644 --- a/contrib/tzcode/localtime.c +++ b/contrib/tzcode/localtime.c @@ -34,6 +34,14 @@ int __tz_change_interval = DETECT_TZ_CHANGES_INTERVAL; #include "un-namespace.h" #endif /* __FreeBSD__ */ +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if !defined S_ISREG && defined S_IFREG +/* Ancient UNIX or recent MS-Windows. */ +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + #if defined THREAD_SAFE && THREAD_SAFE # include <pthread.h> #ifdef __FreeBSD__ @@ -48,6 +56,73 @@ static int lock(void) { return 0; } static void unlock(void) { } #endif +/* Unless intptr_t is missing, pacify gcc -Wcast-qual on char const * exprs. + Use this carefully, as the casts disable type checking. + This is a macro so that it can be used in static initializers. */ +#ifdef INTPTR_MAX +# define UNCONST(a) ((char *) (intptr_t) (a)) +#else +# define UNCONST(a) ((char *) (a)) +#endif + +/* A signed type wider than int, so that we can add 1900 + tm_mon/12 to tm_year + without overflow. The static_assert checks that it is indeed wider + than int; if this fails on your platform please let us know. */ +#if INT_MAX < LONG_MAX +typedef long iinntt; +# define IINNTT_MIN LONG_MIN +# define IINNTT_MAX LONG_MAX +#elif INT_MAX < LLONG_MAX +typedef long long iinntt; +# define IINNTT_MIN LLONG_MIN +# define IINNTT_MAX LLONG_MAX +#else +typedef intmax_t iinntt; +# define IINNTT_MIN INTMAX_MIN +# define IINNTT_MAX INTMAX_MAX +#endif +static_assert(IINNTT_MIN < INT_MIN && INT_MAX < IINNTT_MAX); + +/* On platforms where offtime or mktime might overflow, + strftime.c defines USE_TIMEX_T to be true and includes us. + This tells us to #define time_t to an internal type timex_t that is + wide enough so that strftime %s never suffers from integer overflow, + and to #define offtime (if TM_GMTOFF is defined) or mktime (otherwise) + to a static function that returns the redefined time_t. + It also tells us to define only data and code needed + to support the offtime or mktime variant. */ +#ifndef USE_TIMEX_T +# define USE_TIMEX_T false +#endif +#if USE_TIMEX_T +# undef TIME_T_MIN +# undef TIME_T_MAX +# undef time_t +# define time_t timex_t +# if MKTIME_FITS_IN(LONG_MIN, LONG_MAX) +typedef long timex_t; +# define TIME_T_MIN LONG_MIN +# define TIME_T_MAX LONG_MAX +# elif MKTIME_FITS_IN(LLONG_MIN, LLONG_MAX) +typedef long long timex_t; +# define TIME_T_MIN LLONG_MIN +# define TIME_T_MAX LLONG_MAX +# else +typedef intmax_t timex_t; +# define TIME_T_MIN INTMAX_MIN +# define TIME_T_MAX INTMAX_MAX +# endif + +# ifdef TM_GMTOFF +# undef timeoff +# define timeoff timex_timeoff +# undef EXTERN_TIMEOFF +# else +# undef mktime +# define mktime timex_mktime +# endif +#endif + #ifndef TZ_ABBR_CHAR_SET # define TZ_ABBR_CHAR_SET \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" @@ -57,12 +132,23 @@ static void unlock(void) { } # define TZ_ABBR_ERR_CHAR '_' #endif /* !defined TZ_ABBR_ERR_CHAR */ -/* -** Support non-POSIX platforms that distinguish between text and binary files. -*/ +/* Port to platforms that lack some O_* flags. Unless otherwise + specified, the flags are standardized by POSIX. */ #ifndef O_BINARY -# define O_BINARY 0 +# define O_BINARY 0 /* MS-Windows */ +#endif +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 +#endif +#ifndef O_CLOFORK +# define O_CLOFORK 0 +#endif +#ifndef O_IGNORE_CTTY +# define O_IGNORE_CTTY 0 /* GNU/Hurd */ +#endif +#ifndef O_NOCTTY +# define O_NOCTTY 0 #endif #ifndef WILDABBR @@ -91,7 +177,10 @@ static void unlock(void) { } static const char wildabbr[] = WILDABBR; static char const etc_utc[] = "Etc/UTC"; + +#if !USE_TIMEX_T || defined TM_ZONE || !defined TM_GMTOFF static char const *utc = etc_utc + sizeof "Etc/" - 1; +#endif /* ** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. @@ -103,10 +192,31 @@ static char const *utc = etc_utc + sizeof "Etc/" - 1; # define TZDEFRULESTRING ",M3.2.0,M11.1.0" #endif +/* Limit to time zone abbreviation length in proleptic TZ strings. + This is distinct from TZ_MAX_CHARS, which limits TZif file contents. + It defaults to 254, not 255, so that desigidx_type can be an unsigned char. + unsigned char suffices for TZif files, so the only reason to increase + TZNAME_MAXIMUM is to support TZ strings specifying abbreviations + longer than 254 bytes. There is little reason to do that, though, + as strings that long are hardly "abbreviations". */ +#ifndef TZNAME_MAXIMUM +# define TZNAME_MAXIMUM 254 +#endif + +#if TZNAME_MAXIMUM < UCHAR_MAX +typedef unsigned char desigidx_type; +#elif TZNAME_MAXIMUM < INT_MAX +typedef int desigidx_type; +#elif TZNAME_MAXIMUM < PTRDIFF_MAX +typedef ptrdiff_t desigidx_type; +#else +# error "TZNAME_MAXIMUM too large" +#endif + struct ttinfo { /* time type information */ - int_fast32_t tt_utoff; /* UT offset in seconds */ + int_least32_t tt_utoff; /* UT offset in seconds */ + desigidx_type tt_desigidx; /* abbreviation list index */ bool tt_isdst; /* used to set tm_isdst */ - int tt_desigidx; /* abbreviation list index */ bool tt_ttisstd; /* transition is std time */ bool tt_ttisut; /* transition is UT */ }; @@ -125,12 +235,6 @@ static char const UNSPEC[] = "-00"; for ttunspecified to work without crashing. */ enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 }; -/* Limit to time zone abbreviation length in proleptic TZ strings. - This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */ -#ifndef TZNAME_MAXIMUM -# define TZNAME_MAXIMUM 255 -#endif - /* A representation of the contents of a TZif file. Ideally this would have no size limits; the following sizes should suffice for practical use. This struct should not be too large, as instances @@ -173,7 +277,6 @@ static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, static bool increment_overflow(int *, int); static bool increment_overflow_time(time_t *, int_fast32_t); static int_fast32_t leapcorr(struct state const *, time_t); -static bool normalize_overflow32(int_fast32_t *, int *, int); static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, struct tm *); static bool tzparse(char const *, struct state *, struct state const *); @@ -194,8 +297,10 @@ static struct state *const gmtptr = &gmtmem; # define TZ_STRLEN_MAX 255 #endif /* !defined TZ_STRLEN_MAX */ +#if !USE_TIMEX_T || !defined TM_GMTOFF static char lcl_TZname[TZ_STRLEN_MAX + 1]; static int lcl_is_set; +#endif #ifdef __FreeBSD__ static pthread_once_t gmt_once = PTHREAD_ONCE_INIT; static pthread_once_t gmtime_once = PTHREAD_ONCE_INIT; @@ -221,27 +326,29 @@ static int localtime_key_error; ** trigger latent bugs in programs. */ -#if SUPPORT_C89 +#if !USE_TIMEX_T + +# if SUPPORT_C89 static struct tm tm; #endif -#if 2 <= HAVE_TZNAME + TZ_TIME_T -char * tzname[2] = { - (char *) wildabbr, - (char *) wildabbr -}; -#endif -#if 2 <= USG_COMPAT + TZ_TIME_T +# if 2 <= HAVE_TZNAME + TZ_TIME_T +char *tzname[2] = { UNCONST(wildabbr), UNCONST(wildabbr) }; +# endif +# if 2 <= USG_COMPAT + TZ_TIME_T long timezone; int daylight; -#endif -#if 2 <= ALTZONE + TZ_TIME_T +# endif +# if 2 <= ALTZONE + TZ_TIME_T long altzone; +# endif + #endif /* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */ static void -init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx) +init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, + desigidx_type desigidx) { s->tt_utoff = utoff; s->tt_isdst = isdst; @@ -305,20 +412,22 @@ detzcode64(const char *const codep) return result; } +#if !USE_TIMEX_T || !defined TM_GMTOFF + static void update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp) { -#if HAVE_TZNAME - tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx]; -#endif -#if USG_COMPAT +# if HAVE_TZNAME + tzname[ttisp->tt_isdst] = UNCONST(&sp->chars[ttisp->tt_desigidx]); +# endif +# if USG_COMPAT if (!ttisp->tt_isdst) timezone = - ttisp->tt_utoff; -#endif -#if ALTZONE +# endif +# if ALTZONE if (ttisp->tt_isdst) altzone = - ttisp->tt_utoff; -#endif +# endif } /* If STDDST_MASK indicates that SP's TYPE provides useful info, @@ -349,18 +458,18 @@ settzname(void) When STDDST_MASK becomes zero we can stop looking. */ int stddst_mask = 0; -#if HAVE_TZNAME - tzname[0] = tzname[1] = (char *) (sp ? wildabbr : utc); +# if HAVE_TZNAME + tzname[0] = tzname[1] = UNCONST(sp ? wildabbr : utc); stddst_mask = 3; -#endif -#if USG_COMPAT +# endif +# if USG_COMPAT timezone = 0; stddst_mask = 3; -#endif -#if ALTZONE +# endif +# if ALTZONE altzone = 0; stddst_mask |= 2; -#endif +# endif /* ** And to get the latest time zone abbreviations into tzname. . . */ @@ -370,9 +479,9 @@ settzname(void) for (i = sp->typecnt - 1; stddst_mask && 0 <= i; i--) stddst_mask = may_update_tzname_etc(stddst_mask, sp, i); } -#if USG_COMPAT +# if USG_COMPAT daylight = stddst_mask >> 1 ^ 1; -#endif +# endif } /* Replace bogus characters in time zone abbreviations. @@ -399,6 +508,8 @@ scrub_abbrs(struct state *sp) return 0; } +#endif + #ifdef DETECT_TZ_CHANGES /* * Check whether either the time zone name or the file it refers to has @@ -473,11 +584,15 @@ union local_storage { #endif /* !__FreeBSD__ */ }; -/* Load tz data from the file named NAME into *SP. Read extended - format if DOEXTEND. Use *LSP for temporary storage. Return 0 on +/* 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. */ + +/* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS. + Use *LSP for temporary storage. Return 0 on success, an errno value on failure. */ static int -tzloadbody(char const *name, struct state *sp, bool doextend, +tzloadbody(char const *name, struct state *sp, char tzloadflags, union local_storage *lsp) { register int i; @@ -485,7 +600,9 @@ tzloadbody(char const *name, struct state *sp, bool doextend, register int stored; register ssize_t nread; #ifdef __FreeBSD__ - int serrno; + struct stat sb; + const char *relname; + int dd, serrno; #else /* !__FreeBSD__ */ register bool doaccess; #endif /* !__FreeBSD__ */ @@ -533,45 +650,75 @@ tzloadbody(char const *name, struct state *sp, bool doextend, name = lsp->fullname; } - if (doaccess && access(name, R_OK) != 0) - return errno; - fid = _open(name, O_RDONLY | O_BINARY); + if (doaccess && (tzloadflags & TZLOAD_FROMENV)) { + /* Check for security violations and for devices whose mere + opening could have unwanted side effects. Although these + checks are racy, they're better than nothing and there is + no portable way to fix the races. */ + if (access(name, R_OK) < 0) + return errno; +#ifdef S_ISREG + { + struct stat st; + if (stat(name, &st) < 0) + return errno; + if (!S_ISREG(st.st_mode)) + return EINVAL; + } +#endif *** 2836 LINES SKIPPED ***