timezone: sync to TZDB 2024a

Sync tzselect, zdump, zic to TZDB 2024a.
This patch incorporates the following TZDB source code changes,
listed roughly in descending order of importance.

  zic now supports links to links, needed for future tzdata
  zic now defaults to '-b slim'
  zic now updates output files atomically
  zic has new options -R, -l -, -p -
  zic -r now uses -00 for unspecified timestamps
  zdump now uses [lo,hi) for both -c and -t
  Fix several integer overflow bugs
  zic now checks input bytes more carefully
  Simplify and fix new TZDIR setup
  Default time_t to 64 bits on glibc 2.34+ 32-bit
  zic now generates TZ strings that conform to POSIX when all-year DST
  zic -v now shows extreme-int tm_year transitions
  Fix zic bug in last time type of Asia/Gaza etc.
  Fix zic bug with Palestine after 2075
  Fix bug uncovered by recent change to Iran history
  Fix 'zic -b fat' bug with Port Moresby 32-bit data
  Fix zic bug with -r @X where X is deduced from TZ
  Fix bug with zic -r cutoff before 1st transition
  Fix leap second expiry and truncation
  Fix zic bug on Linux 2.6.16 and 2.6.17
  Fix bug with 'zic -d /a/b/c' if /a is unwriteable
  Don't mistruncate TZif files at leap seconds
  Fix zdump undefined behavior if !USE_LTZ
  zdump -v reports localtime+gmtime failures better
  Fix zdump diagnostic for missing timezone
  Don't assume nonempty argv
  Port better to C23
  Do not assume negative >> behavior
  I18nize zdump a bit better
  Port zdump to right_only installations
  New tzselect menu option 'now'
  tzselect can now use current time to help choose
  Improve tzselect behavior for Turkey etc.
  tzselect: do not create temporary files
  tzselect: work around mawk bug with {2,}
  tzselect: Port to POSIX awk, which prohibits -v newlines
  Do not use empty RE in tzselect
  Don't set TZ in tzselect
  Avoid sed, expr in tzselect
  tzselect: Fix problems with spaces in TZDIR
  Improve tzselect diagnostics
  Remove zic workaround for Qt bug 53071
  Remove zic support for "min" in Rule lines
  Remove zic support for zic -y, Rule TYPEs, pacificnew
  Remove tzselect workaround for Bash 1.14.7 bug

* SHARED-FILES: Update to match current sync.
* config.h.in (HAVE_STRERROR): Remove; no longer needed.
* timezone/Makefile ($(objpfx)zic.o): Depend on tzdir.h.
($(objpfx)tzdir.h): New rule to build a placeholder.
* timezone/private.h, timezone/tzfile.h, timezone/version:
* timezone/zdump.c, timezone/zic.c: Copy verbatim from TZDB 2024a.
This commit is contained in:
Paul Eggert 2024-04-06 23:39:53 -07:00
parent 57581acd95
commit 1f94147a79
9 changed files with 2686 additions and 1554 deletions

View File

@ -177,23 +177,25 @@ unicode:
# The following files are shared with the upstream tzcode project and must be # The following files are shared with the upstream tzcode project and must be
# updated regularly to stay in sync with the upstream releases. # updated regularly to stay in sync with the upstream releases.
# #
# Update from tzcode 2017b. # Currently synced to TZDB 2024a, announced and distributed here:
# Latest is 2018g: # https://mm.icann.org/pipermail/tz-announce/2024-February/000081.html
# https://mm.icann.org/pipermail/tz-announce/2018-October/000052.html # https://data.iana.org/time-zones/releases/tzdb-2024a.tar.lz
tzcode: tzcode:
timezone/private.h timezone/private.h
timezone/tzfile.h timezone/tzfile.h
timezone/tzselect.ksh
timezone/version
timezone/zdump.c timezone/zdump.c
timezone/zic.c timezone/zic.c
timezone/tzselect.ksh
# The following files are shared with the upstream tzdata project but is not # The following files are shared with the upstream tzdata project but is not
# synchronized regularly. The data files themselves are used only for testing # synchronized regularly. The data files themselves are used only for testing
# purposes and their data is never used to generate any output. We synchronize # purposes and their data is never used to generate any output. We synchronize
# them only to stay on top of newer data that might help with testing. # them only to stay on top of newer data that might help with testing.
# #
# Currently synced to 2009i. Latest is 2018g. # Currently synced to tzcode 2009i, announced and distributed here:
# https://mm.icann.org/pipermail/tz-announce/2018-October/000052.html # https://mm.icann.org/pipermail/tz/2009-June/040697.html
# https://data.iana.org/time-zones/releases/tzdata2009i.tar.gz
tzdata: tzdata:
timezone/africa timezone/africa
timezone/antarctica timezone/antarctica

View File

@ -240,10 +240,6 @@
#ifdef _LIBC #ifdef _LIBC
/* The zic and zdump programs need these definitions. */
#define HAVE_STRERROR 1
/* The locale code needs these definitions. */ /* The locale code needs these definitions. */
#define HAVE_REGEX 1 #define HAVE_REGEX 1

View File

@ -49,6 +49,13 @@ endif
include ../Rules include ../Rules
$(objpfx)zic.o: $(objpfx)tzdir.h
# In glibc this file is an empty placeholder,
# as tz-cflags defines TZDEFAULT and TZDIR.
$(objpfx)tzdir.h:
> $@
$(objpfx)zic.o $(objpfx)zdump.o: $(objpfx)version.h $(objpfx)zic.o $(objpfx)zdump.o: $(objpfx)version.h
$(objpfx)version.h: $(common-objpfx)config.make $(objpfx)version.h: $(common-objpfx)config.make

View File

@ -17,6 +17,40 @@
** Thank you! ** Thank you!
*/ */
/* PORT_TO_C89 means the code should work even if the underlying
compiler and library support only C89 plus C99's 'long long'
and perhaps a few other extensions to C89. SUPPORT_C89 means the
tzcode library should support C89 callers in addition to the usual
support for C99-and-later callers; however, C89 support can trigger
latent bugs in C99-and-later callers. These macros are obsolescent,
and the plan is to remove them along with any code needed only when
they are nonzero. A good time to do that might be in the year 2029
because RHEL 7 (whose GCC defaults to C89) extended life cycle
support (ELS) is scheduled to end on 2028-06-30. */
#ifndef PORT_TO_C89
# define PORT_TO_C89 0
#endif
#ifndef SUPPORT_C89
# define SUPPORT_C89 0
#endif
#ifndef __STDC_VERSION__
# define __STDC_VERSION__ 0
#endif
/* Define true, false and bool if they don't work out of the box. */
#if PORT_TO_C89 && __STDC_VERSION__ < 199901
# define true 1
# define false 0
# define bool int
#elif __STDC_VERSION__ < 202311
# include <stdbool.h>
#endif
#if __STDC_VERSION__ < 202311
# define static_assert(cond) extern int static_assert_check[(cond) ? 1 : -1]
#endif
/* /*
** zdump has been made independent of the rest of the time ** zdump has been made independent of the rest of the time
** conversion package to increase confidence in the verification it provides. ** conversion package to increase confidence in the verification it provides.
@ -36,79 +70,84 @@
*/ */
#ifndef HAVE_DECL_ASCTIME_R #ifndef HAVE_DECL_ASCTIME_R
#define HAVE_DECL_ASCTIME_R 1 # define HAVE_DECL_ASCTIME_R 1
#endif #endif
#if !defined HAVE_GENERIC && defined __has_extension #if !defined HAVE__GENERIC && defined __has_extension
# if __has_extension(c_generic_selections) # if !__has_extension(c_generic_selections)
# define HAVE_GENERIC 1 # define HAVE__GENERIC 0
# else
# define HAVE_GENERIC 0
# endif # endif
#endif #endif
/* _Generic is buggy in pre-4.9 GCC. */ /* _Generic is buggy in pre-4.9 GCC. */
#if !defined HAVE_GENERIC && defined __GNUC__ #if !defined HAVE__GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__
# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__)) # define HAVE__GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
#endif #endif
#ifndef HAVE_GENERIC #ifndef HAVE__GENERIC
# define HAVE_GENERIC (201112 <= __STDC_VERSION__) # define HAVE__GENERIC (201112 <= __STDC_VERSION__)
#endif #endif
#if !defined HAVE_GETTEXT && defined __has_include
# if __has_include(<libintl.h>)
# define HAVE_GETTEXT true
# endif
#endif
#ifndef HAVE_GETTEXT #ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0 # define HAVE_GETTEXT false
#endif /* !defined HAVE_GETTEXT */ #endif
#ifndef HAVE_INCOMPATIBLE_CTIME_R #ifndef HAVE_INCOMPATIBLE_CTIME_R
#define HAVE_INCOMPATIBLE_CTIME_R 0 # define HAVE_INCOMPATIBLE_CTIME_R 0
#endif #endif
#ifndef HAVE_LINK #ifndef HAVE_LINK
#define HAVE_LINK 1 # define HAVE_LINK 1
#endif /* !defined HAVE_LINK */ #endif /* !defined HAVE_LINK */
#ifndef HAVE_POSIX_DECLS #ifndef HAVE_MALLOC_ERRNO
#define HAVE_POSIX_DECLS 1 # define HAVE_MALLOC_ERRNO 1
#endif #endif
#ifndef HAVE_STDBOOL_H #ifndef HAVE_POSIX_DECLS
#define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__) # define HAVE_POSIX_DECLS 1
#endif
#ifndef HAVE_SETENV
# define HAVE_SETENV 1
#endif #endif
#ifndef HAVE_STRDUP #ifndef HAVE_STRDUP
#define HAVE_STRDUP 1 # define HAVE_STRDUP 1
#endif
#ifndef HAVE_STRTOLL
#define HAVE_STRTOLL 1
#endif #endif
#ifndef HAVE_SYMLINK #ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1 # define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */ #endif /* !defined HAVE_SYMLINK */
#if !defined HAVE_SYS_STAT_H && defined __has_include
# if !__has_include(<sys/stat.h>)
# define HAVE_SYS_STAT_H false
# endif
#endif
#ifndef HAVE_SYS_STAT_H #ifndef HAVE_SYS_STAT_H
#define HAVE_SYS_STAT_H 1 # define HAVE_SYS_STAT_H true
#endif /* !defined HAVE_SYS_STAT_H */ #endif
#ifndef HAVE_SYS_WAIT_H
#define HAVE_SYS_WAIT_H 1
#endif /* !defined HAVE_SYS_WAIT_H */
#if !defined HAVE_UNISTD_H && defined __has_include
# if !__has_include(<unistd.h>)
# define HAVE_UNISTD_H false
# endif
#endif
#ifndef HAVE_UNISTD_H #ifndef HAVE_UNISTD_H
#define HAVE_UNISTD_H 1 # define HAVE_UNISTD_H true
#endif /* !defined HAVE_UNISTD_H */ #endif
#ifndef HAVE_UTMPX_H
#define HAVE_UTMPX_H 1
#endif /* !defined HAVE_UTMPX_H */
#ifndef NETBSD_INSPIRED #ifndef NETBSD_INSPIRED
# define NETBSD_INSPIRED 1 # define NETBSD_INSPIRED 1
#endif #endif
#if HAVE_INCOMPATIBLE_CTIME_R #if HAVE_INCOMPATIBLE_CTIME_R
#define asctime_r _incompatible_asctime_r # define asctime_r _incompatible_asctime_r
#define ctime_r _incompatible_ctime_r # define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */ #endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */ /* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
@ -118,15 +157,17 @@
/* Enable strtoimax on pre-C99 Solaris 11. */ /* Enable strtoimax on pre-C99 Solaris 11. */
#define __EXTENSIONS__ 1 #define __EXTENSIONS__ 1
/* To avoid having 'stat' fail unnecessarily with errno == EOVERFLOW, /* On GNUish systems where time_t might be 32 or 64 bits, use 64.
enable large files on GNUish systems ... */ On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
setting _TIME_BITS to 64 does not work. The code does not
otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
use off_t or functions like 'stat' that depend on off_t. */
#ifndef _FILE_OFFSET_BITS #ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64 # define _FILE_OFFSET_BITS 64
#endif #endif
/* ... and on AIX ... */ #if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
#define _LARGE_FILES 1 # define _TIME_BITS 64
/* ... and enable large inode numbers on Mac OS X 10.5 and later. */ #endif
#define _DARWIN_USE_64_BIT_INODE 1
/* /*
** Nested includes ** Nested includes
@ -157,16 +198,29 @@
#undef tzalloc #undef tzalloc
#undef tzfree #undef tzfree
#include <sys/types.h> /* for time_t */ #include <stddef.h>
#include <string.h> #include <string.h>
#if !PORT_TO_C89
# include <inttypes.h>
#endif
#include <limits.h> /* for CHAR_BIT et al. */ #include <limits.h> /* for CHAR_BIT et al. */
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#ifndef EINVAL
# define EINVAL ERANGE
#endif
#ifndef ELOOP
# define ELOOP EINVAL
#endif
#ifndef ENAMETOOLONG #ifndef ENAMETOOLONG
# define ENAMETOOLONG EINVAL # define ENAMETOOLONG EINVAL
#endif #endif
#ifndef ENOMEM
# define ENOMEM EINVAL
#endif
#ifndef ENOTSUP #ifndef ENOTSUP
# define ENOTSUP EINVAL # define ENOTSUP EINVAL
#endif #endif
@ -175,11 +229,11 @@
#endif #endif
#if HAVE_GETTEXT #if HAVE_GETTEXT
#include <libintl.h> # include <libintl.h>
#endif /* HAVE_GETTEXT */ #endif /* HAVE_GETTEXT */
#if HAVE_UNISTD_H #if HAVE_UNISTD_H
#include <unistd.h> /* for R_OK, and other POSIX goodness */ # include <unistd.h> /* for R_OK, and other POSIX goodness */
#endif /* HAVE_UNISTD_H */ #endif /* HAVE_UNISTD_H */
#ifndef HAVE_STRFTIME_L #ifndef HAVE_STRFTIME_L
@ -215,27 +269,29 @@
#endif #endif
#ifndef R_OK #ifndef R_OK
#define R_OK 4 # define R_OK 4
#endif /* !defined R_OK */ #endif /* !defined R_OK */
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ #if PORT_TO_C89
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
/* /*
** Define HAVE_STDINT_H's default value here, rather than at the ** Define HAVE_STDINT_H's default value here, rather than at the
** start, since __GLIBC__ and INTMAX_MAX's values depend on ** start, since __GLIBC__ and INTMAX_MAX's values depend on
** previously-included files. glibc 2.1 and Solaris 10 and later have ** previously included files. glibc 2.1 and Solaris 10 and later have
** stdint.h, even with pre-C99 compilers. ** stdint.h, even with pre-C99 compilers.
*/ */
#if !defined HAVE_STDINT_H && defined __has_include
# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
#endif
#ifndef HAVE_STDINT_H #ifndef HAVE_STDINT_H
#define HAVE_STDINT_H \ # define HAVE_STDINT_H \
(199901 <= __STDC_VERSION__ \ (199901 <= __STDC_VERSION__ \
|| 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \ || 2 < __GLIBC__ + (1 <= __GLIBC_MINOR__) \
|| __CYGWIN__ || INTMAX_MAX) || __CYGWIN__ || INTMAX_MAX)
#endif /* !defined HAVE_STDINT_H */ #endif /* !defined HAVE_STDINT_H */
#if HAVE_STDINT_H #if HAVE_STDINT_H
#include <stdint.h> # include <stdint.h>
#endif /* !HAVE_STDINT_H */ #endif /* !HAVE_STDINT_H */
#ifndef HAVE_INTTYPES_H #ifndef HAVE_INTTYPES_H
@ -246,36 +302,36 @@
#endif #endif
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
#ifdef __LONG_LONG_MAX__ #if defined __LONG_LONG_MAX__ && !defined __STRICT_ANSI__
# ifndef LLONG_MAX # ifndef LLONG_MAX
# define LLONG_MAX __LONG_LONG_MAX__ # define LLONG_MAX __LONG_LONG_MAX__
# endif # endif
# ifndef LLONG_MIN # ifndef LLONG_MIN
# define LLONG_MIN (-1 - LLONG_MAX) # define LLONG_MIN (-1 - LLONG_MAX)
# endif # endif
# ifndef ULLONG_MAX
# define ULLONG_MAX (LLONG_MAX * 2ull + 1)
# endif
#endif #endif
#ifndef INT_FAST64_MAX #ifndef INT_FAST64_MAX
# ifdef LLONG_MAX # if 1 <= LONG_MAX >> 31 >> 31
typedef long long int_fast64_t; typedef long int_fast64_t;
# define INT_FAST64_MIN LLONG_MIN
# define INT_FAST64_MAX LLONG_MAX
# else
# if LONG_MAX >> 31 < 0xffffffff
Please use a compiler that supports a 64-bit integer type (or wider);
you may need to compile with "-DHAVE_STDINT_H".
# endif
typedef long int_fast64_t;
# define INT_FAST64_MIN LONG_MIN # define INT_FAST64_MIN LONG_MIN
# define INT_FAST64_MAX LONG_MAX # define INT_FAST64_MAX LONG_MAX
# else
/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
typedef long long int_fast64_t;
# define INT_FAST64_MIN LLONG_MIN
# define INT_FAST64_MAX LLONG_MAX
# endif # endif
#endif #endif
#ifndef PRIdFAST64 #ifndef PRIdFAST64
# if INT_FAST64_MAX == LLONG_MAX # if INT_FAST64_MAX == LONG_MAX
# define PRIdFAST64 "lld"
# else
# define PRIdFAST64 "ld" # define PRIdFAST64 "ld"
# else
# define PRIdFAST64 "lld"
# endif # endif
#endif #endif
@ -298,6 +354,9 @@ typedef int int_fast32_t;
#ifndef INTMAX_MAX #ifndef INTMAX_MAX
# ifdef LLONG_MAX # ifdef LLONG_MAX
typedef long long intmax_t; typedef long long intmax_t;
# ifndef HAVE_STRTOLL
# define HAVE_STRTOLL true
# endif
# if HAVE_STRTOLL # if HAVE_STRTOLL
# define strtoimax strtoll # define strtoimax strtoll
# endif # endif
@ -321,66 +380,183 @@ typedef long intmax_t;
# endif # endif
#endif #endif
#ifndef PTRDIFF_MAX
# define PTRDIFF_MAX MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t))
#endif
#ifndef UINT_FAST32_MAX
typedef unsigned long uint_fast32_t;
#endif
#ifndef UINT_FAST64_MAX #ifndef UINT_FAST64_MAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ # if 3 <= ULONG_MAX >> 31 >> 31
typedef unsigned long long uint_fast64_t; typedef unsigned long uint_fast64_t;
# define UINT_FAST64_MAX ULONG_MAX
# else # else
# if ULONG_MAX >> 31 >> 1 < 0xffffffff /* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
Please use a compiler that supports a 64-bit integer type (or wider); typedef unsigned long long uint_fast64_t;
you may need to compile with "-DHAVE_STDINT_H". # define UINT_FAST64_MAX ULLONG_MAX
# endif
typedef unsigned long uint_fast64_t;
# endif # endif
#endif #endif
#ifndef UINTMAX_MAX #ifndef UINTMAX_MAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ # ifdef ULLONG_MAX
typedef unsigned long long uintmax_t; typedef unsigned long long uintmax_t;
# define UINTMAX_MAX ULLONG_MAX
# else # else
typedef unsigned long uintmax_t; typedef unsigned long uintmax_t;
# define UINTMAX_MAX ULONG_MAX
# endif # endif
#endif #endif
#ifndef PRIuMAX #ifndef PRIuMAX
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ # ifdef ULLONG_MAX
# define PRIuMAX "llu" # define PRIuMAX "llu"
# else # else
# define PRIuMAX "lu" # define PRIuMAX "lu"
# endif # endif
#endif #endif
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif /* !defined INT32_MAX */
#ifndef INT32_MIN
#define INT32_MIN (-1 - INT32_MAX)
#endif /* !defined INT32_MIN */
#ifndef SIZE_MAX #ifndef SIZE_MAX
#define SIZE_MAX ((size_t) -1) # define SIZE_MAX ((size_t) -1)
#endif
#endif /* PORT_TO_C89 */
/* The maximum size of any created object, as a signed integer.
Although the C standard does not outright prohibit larger objects,
behavior is undefined if the result of pointer subtraction does not
fit into ptrdiff_t, and the code assumes in several places that
pointer subtraction works. As a practical matter it's OK to not
support objects larger than this. */
#define INDEX_MAX ((ptrdiff_t) min(PTRDIFF_MAX, SIZE_MAX))
/* Support ckd_add, ckd_sub, ckd_mul on C23 or recent-enough GCC-like
hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
#if !defined HAVE_STDCKDINT_H && defined __has_include
# if __has_include(<stdckdint.h>)
# define HAVE_STDCKDINT_H true
# endif
#endif
#ifdef HAVE_STDCKDINT_H
# if HAVE_STDCKDINT_H
# include <stdckdint.h>
# endif
#elif defined __EDG__
/* Do nothing, to work around EDG bug <https://bugs.gnu.org/53256>. */
#elif defined __has_builtin
# if __has_builtin(__builtin_add_overflow)
# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
# endif
# if __has_builtin(__builtin_sub_overflow)
# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
# endif
# if __has_builtin(__builtin_mul_overflow)
# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
# endif
#elif 7 <= __GNUC__
# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r)
# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r)
# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r)
#endif #endif
#if 3 <= __GNUC__ #if 3 <= __GNUC__
# define ATTRIBUTE_CONST __attribute__ ((const)) # define ATTRIBUTE_MALLOC __attribute__((malloc))
# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) # define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
# define ATTRIBUTE_PURE __attribute__ ((__pure__))
# define ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
#else #else
# define ATTRIBUTE_CONST /* empty */
# define ATTRIBUTE_MALLOC /* empty */ # define ATTRIBUTE_MALLOC /* empty */
# define ATTRIBUTE_PURE /* empty */
# define ATTRIBUTE_FORMAT(spec) /* empty */ # define ATTRIBUTE_FORMAT(spec) /* empty */
#endif #endif
#if !defined _Noreturn && __STDC_VERSION__ < 201112 #if (defined __has_c_attribute \
# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__) && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__))
# define _Noreturn __attribute__ ((__noreturn__)) # define HAVE___HAS_C_ATTRIBUTE true
#else
# define HAVE___HAS_C_ATTRIBUTE false
#endif
#if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(deprecated)
# define ATTRIBUTE_DEPRECATED [[deprecated]]
# endif
#endif
#ifndef ATTRIBUTE_DEPRECATED
# if 3 < __GNUC__ + (2 <= __GNUC_MINOR__)
# define ATTRIBUTE_DEPRECATED __attribute__((deprecated))
# else # else
# define _Noreturn # define ATTRIBUTE_DEPRECATED /* empty */
# endif # endif
#endif #endif
#if __STDC_VERSION__ < 199901 && !defined restrict #if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(fallthrough)
# define ATTRIBUTE_FALLTHROUGH [[fallthrough]]
# endif
#endif
#ifndef ATTRIBUTE_FALLTHROUGH
# if 7 <= __GNUC__
# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough))
# else
# define ATTRIBUTE_FALLTHROUGH ((void) 0)
# endif
#endif
#if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(maybe_unused)
# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]]
# endif
#endif
#ifndef ATTRIBUTE_MAYBE_UNUSED
# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused))
# else
# define ATTRIBUTE_MAYBE_UNUSED /* empty */
# endif
#endif
#if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(noreturn)
# define ATTRIBUTE_NORETURN [[noreturn]]
# endif
#endif
#ifndef ATTRIBUTE_NORETURN
# if 201112 <= __STDC_VERSION__
# define ATTRIBUTE_NORETURN _Noreturn
# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
# define ATTRIBUTE_NORETURN __attribute__((noreturn))
# else
# define ATTRIBUTE_NORETURN /* empty */
# endif
#endif
#if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(reproducible)
# define ATTRIBUTE_REPRODUCIBLE [[reproducible]]
# endif
#endif
#ifndef ATTRIBUTE_REPRODUCIBLE
# if 3 <= __GNUC__
# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure))
# else
# define ATTRIBUTE_REPRODUCIBLE /* empty */
# endif
#endif
#if HAVE___HAS_C_ATTRIBUTE
# if __has_c_attribute(unsequenced)
# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
# endif
#endif
#ifndef ATTRIBUTE_UNSEQUENCED
# if 3 <= __GNUC__
# define ATTRIBUTE_UNSEQUENCED __attribute__((const))
# else
# define ATTRIBUTE_UNSEQUENCED /* empty */
# endif
#endif
#if (__STDC_VERSION__ < 199901 && !defined restrict \
&& (PORT_TO_C89 || defined _MSC_VER))
# define restrict /* empty */ # define restrict /* empty */
#endif #endif
@ -418,10 +594,11 @@ typedef unsigned long uintmax_t;
# define TZ_TIME_T 0 # define TZ_TIME_T 0
#endif #endif
#if TZ_TIME_T #if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
# ifdef LOCALTIME_IMPLEMENTATION
static time_t sys_time(time_t *x) { return time(x); } static time_t sys_time(time_t *x) { return time(x); }
# endif #endif
#if TZ_TIME_T
typedef time_tz tz_time_t; typedef time_tz tz_time_t;
@ -477,8 +654,6 @@ typedef time_tz tz_time_t;
# define tzfree tz_tzfree # define tzfree tz_tzfree
# undef tzset # undef tzset
# define tzset tz_tzset # define tzset tz_tzset
# undef tzsetwall
# define tzsetwall tz_tzsetwall
# if HAVE_STRFTIME_L # if HAVE_STRFTIME_L
# undef strftime_l # undef strftime_l
# define strftime_l tz_strftime_l # define strftime_l tz_strftime_l
@ -498,11 +673,16 @@ typedef time_tz tz_time_t;
# define altzone tz_altzone # define altzone tz_altzone
# endif # endif
char *asctime(struct tm const *); # if __STDC_VERSION__ < 202311
# define DEPRECATED_IN_C23 /* empty */
# else
# define DEPRECATED_IN_C23 ATTRIBUTE_DEPRECATED
# endif
DEPRECATED_IN_C23 char *asctime(struct tm const *);
char *asctime_r(struct tm const *restrict, char *restrict); char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime(time_t const *); DEPRECATED_IN_C23 char *ctime(time_t const *);
char *ctime_r(time_t const *, char *); char *ctime_r(time_t const *, char *);
double difftime(time_t, time_t) ATTRIBUTE_CONST; ATTRIBUTE_UNSEQUENCED double difftime(time_t, time_t);
size_t strftime(char *restrict, size_t, char const *restrict, size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict); struct tm const *restrict);
# if HAVE_STRFTIME_L # if HAVE_STRFTIME_L
@ -515,9 +695,24 @@ struct tm *localtime(time_t const *);
struct tm *localtime_r(time_t const *restrict, struct tm *restrict); struct tm *localtime_r(time_t const *restrict, struct tm *restrict);
time_t mktime(struct tm *); time_t mktime(struct tm *);
time_t time(time_t *); time_t time(time_t *);
time_t timegm(struct tm *);
void tzset(void); void tzset(void);
#endif #endif
#ifndef HAVE_DECL_TIMEGM
# if (202311 <= __STDC_VERSION__ \
|| defined __GLIBC__ || defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
# define HAVE_DECL_TIMEGM true
# else
# define HAVE_DECL_TIMEGM false
# endif
#endif
#if !HAVE_DECL_TIMEGM && !defined timegm
time_t timegm(struct tm *);
#endif
#if !HAVE_DECL_ASCTIME_R && !defined asctime_r #if !HAVE_DECL_ASCTIME_R && !defined asctime_r
extern char *asctime_r(struct tm const *restrict, char *restrict); extern char *asctime_r(struct tm const *restrict, char *restrict);
#endif #endif
@ -550,21 +745,18 @@ extern long altzone;
** declarations if time_tz is defined. ** declarations if time_tz is defined.
*/ */
#ifdef STD_INSPIRED #ifndef STD_INSPIRED
# if TZ_TIME_T || !defined tzsetwall # define STD_INSPIRED 0
void tzsetwall(void); #endif
# endif #if STD_INSPIRED
# if TZ_TIME_T || !defined offtime # if TZ_TIME_T || !defined offtime
struct tm *offtime(time_t const *, long); struct tm *offtime(time_t const *, long);
# endif # endif
# if TZ_TIME_T || !defined timegm
time_t timegm(struct tm *);
# endif
# if TZ_TIME_T || !defined timelocal # if TZ_TIME_T || !defined timelocal
time_t timelocal(struct tm *); time_t timelocal(struct tm *);
# endif # endif
# if TZ_TIME_T || !defined timeoff # if TZ_TIME_T || !defined timeoff
time_t timeoff(struct tm *, long); # define EXTERN_TIMEOFF
# endif # endif
# if TZ_TIME_T || !defined time2posix # if TZ_TIME_T || !defined time2posix
time_t time2posix(time_t); time_t time2posix(time_t);
@ -576,7 +768,9 @@ time_t posix2time(time_t);
/* Infer TM_ZONE on systems where this information is known, but suppress /* Infer TM_ZONE on systems where this information is known, but suppress
guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */
#if (defined __GLIBC__ \ #if (200809 < _POSIX_VERSION \
|| defined __GLIBC__ \
|| defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__)) || (defined __APPLE__ && defined __MACH__))
# if !defined TM_GMTOFF && !defined NO_TM_GMTOFF # if !defined TM_GMTOFF && !defined NO_TM_GMTOFF
@ -602,12 +796,12 @@ struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
time_t mktime_z(timezone_t restrict, struct tm *restrict); time_t mktime_z(timezone_t restrict, struct tm *restrict);
timezone_t tzalloc(char const *); timezone_t tzalloc(char const *);
void tzfree(timezone_t); void tzfree(timezone_t);
# ifdef STD_INSPIRED # if STD_INSPIRED
# if TZ_TIME_T || !defined posix2time_z # if TZ_TIME_T || !defined posix2time_z
time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE; ATTRIBUTE_REPRODUCIBLE time_t posix2time_z(timezone_t, time_t);
# endif # endif
# if TZ_TIME_T || !defined time2posix_z # if TZ_TIME_T || !defined time2posix_z
time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE; ATTRIBUTE_REPRODUCIBLE time_t time2posix_z(timezone_t, time_t);
# endif # endif
# endif # endif
#endif #endif
@ -616,18 +810,15 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
** Finally, some convenience items. ** Finally, some convenience items.
*/ */
#if HAVE_STDBOOL_H #define TYPE_BIT(type) (CHAR_BIT * (ptrdiff_t) sizeof(type))
# include <stdbool.h>
#else
# define true 1
# define false 0
# define bool int
#endif
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
#define TYPE_SIGNED(type) (((type) -1) < 0) #define TYPE_SIGNED(type) (((type) -1) < 0)
#define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0) #define TWOS_COMPLEMENT(t) ((t) ~ (t) 0 < 0)
/* Minimum and maximum of two values. Use lower case to avoid
naming clashes with standard include files. */
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
/* Max and min values of the integer type T, of which only the bottom /* Max and min values of the integer type T, of which only the bottom
B bits are used, and where the highest-order used bit is considered B bits are used, and where the highest-order used bit is considered
to be a sign bit if T is signed. */ to be a sign bit if T is signed. */
@ -642,12 +833,12 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
#define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t)) #define TIME_T_MAX_NO_PADDING MAXVAL(time_t, TYPE_BIT(time_t))
/* The extreme time values. These are macros, not constants, so that /* The extreme time values. These are macros, not constants, so that
any portability problem occur only when compiling .c files that use any portability problems occur only when compiling .c files that use
the macros, which is safer for applications that need only zdump and zic. the macros, which is safer for applications that need only zdump and zic.
This implementation assumes no padding if time_t is signed and This implementation assumes no padding if time_t is signed and
either the compiler lacks support for _Generic or time_t is not one either the compiler lacks support for _Generic or time_t is not one
of the standard signed integer types. */ of the standard signed integer types. */
#if HAVE_GENERIC #if HAVE__GENERIC
# define TIME_T_MIN \ # define TIME_T_MIN \
_Generic((time_t) 0, \ _Generic((time_t) 0, \
signed char: SCHAR_MIN, short: SHRT_MIN, \ signed char: SCHAR_MIN, short: SHRT_MIN, \
@ -660,10 +851,23 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \ int: INT_MAX, long: LONG_MAX, long long: LLONG_MAX, \
default: TIME_T_MAX_NO_PADDING) \ default: TIME_T_MAX_NO_PADDING) \
: (time_t) -1) : (time_t) -1)
enum { SIGNED_PADDING_CHECK_NEEDED
= _Generic((time_t) 0,
signed char: false, short: false,
int: false, long: false, long long: false,
default: true) };
#else #else
# define TIME_T_MIN TIME_T_MIN_NO_PADDING # define TIME_T_MIN TIME_T_MIN_NO_PADDING
# define TIME_T_MAX TIME_T_MAX_NO_PADDING # define TIME_T_MAX TIME_T_MAX_NO_PADDING
enum { SIGNED_PADDING_CHECK_NEEDED = true };
#endif #endif
/* Try to check the padding assumptions. Although TIME_T_MAX and the
following check can both have undefined behavior on oddball
platforms due to shifts exceeding widths of signed integers, these
platforms' compilers are likely to diagnose these issues in integer
constant expressions, so it shouldn't hurt to check statically. */
static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
|| TIME_T_MAX >> (TYPE_BIT(time_t) - 2) == 1);
/* /*
** 302 / 1000 is log10(2.0) rounded up. ** 302 / 1000 is log10(2.0) rounded up.
@ -685,10 +889,45 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
# define INITIALIZE(x) # define INITIALIZE(x)
#endif #endif
/* Whether memory access must strictly follow the C standard.
If 0, it's OK to read uninitialized storage so long as the value is
not relied upon. Defining it to 0 lets mktime access parts of
struct tm that might be uninitialized, as a heuristic when the
standard doesn't say what to return and when tm_gmtoff can help
mktime likely infer a better value. */
#ifndef UNINIT_TRAP #ifndef UNINIT_TRAP
# define UNINIT_TRAP 0 # define UNINIT_TRAP 0
#endif #endif
/* localtime.c sometimes needs access to timeoff if it is not already public.
tz_private_timeoff should be used only by localtime.c. */
#if (!defined EXTERN_TIMEOFF \
&& defined TM_GMTOFF && (200809 < _POSIX_VERSION || ! UNINIT_TRAP))
# ifndef timeoff
# define timeoff tz_private_timeoff
# endif
# define EXTERN_TIMEOFF
#endif
#ifdef EXTERN_TIMEOFF
time_t timeoff(struct tm *, long);
#endif
#ifdef DEBUG
# undef unreachable
# define unreachable() abort()
#elif !defined unreachable
# ifdef __has_builtin
# if __has_builtin(__builtin_unreachable)
# define unreachable() __builtin_unreachable()
# endif
# elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
# define unreachable() __builtin_unreachable()
# endif
# ifndef unreachable
# define unreachable() ((void) 0)
# endif
#endif
/* /*
** For the benefit of GNU folk... ** For the benefit of GNU folk...
** '_(MSGID)' uses the current locale's message library string for MSGID. ** '_(MSGID)' uses the current locale's message library string for MSGID.
@ -708,49 +947,73 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
#if HAVE_INCOMPATIBLE_CTIME_R #if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r #undef asctime_r
#undef ctime_r #undef ctime_r
char *asctime_r(struct tm const *, char *); char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime_r(time_t const *, char *); char *ctime_r(time_t const *, char *);
#endif /* HAVE_INCOMPATIBLE_CTIME_R */ #endif /* HAVE_INCOMPATIBLE_CTIME_R */
/* Handy macros that are independent of tzfile implementation. */ /* Handy macros that are independent of tzfile implementation. */
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */ enum {
SECSPERMIN = 60,
MINSPERHOUR = 60,
SECSPERHOUR = SECSPERMIN * MINSPERHOUR,
HOURSPERDAY = 24,
DAYSPERWEEK = 7,
DAYSPERNYEAR = 365,
DAYSPERLYEAR = DAYSPERNYEAR + 1,
MONSPERYEAR = 12,
YEARSPERREPEAT = 400 /* years before a Gregorian repeat */
};
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define TM_SUNDAY 0 #define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
#define TM_MONDAY 1 #define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
#define TM_TUESDAY 2 #define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_JANUARY 0 /* How many years to generate (in zic.c) or search through (in localtime.c).
#define TM_FEBRUARY 1 This is two years larger than the obvious 400, to avoid edge cases.
#define TM_MARCH 2 E.g., suppose a non-POSIX.1-2017 rule applies from 2012 on with transitions
#define TM_APRIL 3 in March and September, plus one-off transitions in November 2013.
#define TM_MAY 4 If zic looked only at the last 400 years, it would set max_year=2413,
#define TM_JUNE 5 with the intent that the 400 years 2014 through 2413 will be repeated.
#define TM_JULY 6 The last transition listed in the tzfile would be in 2413-09,
#define TM_AUGUST 7 less than 400 years after the last one-off transition in 2013-11.
#define TM_SEPTEMBER 8 Two years is not overkill for localtime.c, as a one-year bump
#define TM_OCTOBER 9 would mishandle 2023d's America/Ciudad_Juarez for November 2422. */
#define TM_NOVEMBER 10 enum { years_of_observations = YEARSPERREPEAT + 2 };
#define TM_DECEMBER 11
#define TM_YEAR_BASE 1900 enum {
TM_SUNDAY,
TM_MONDAY,
TM_TUESDAY,
TM_WEDNESDAY,
TM_THURSDAY,
TM_FRIDAY,
TM_SATURDAY
};
#define EPOCH_YEAR 1970 enum {
#define EPOCH_WDAY TM_THURSDAY TM_JANUARY,
TM_FEBRUARY,
TM_MARCH,
TM_APRIL,
TM_MAY,
TM_JUNE,
TM_JULY,
TM_AUGUST,
TM_SEPTEMBER,
TM_OCTOBER,
TM_NOVEMBER,
TM_DECEMBER
};
enum {
TM_YEAR_BASE = 1900,
TM_WDAY_BASE = TM_MONDAY,
EPOCH_YEAR = 1970,
EPOCH_WDAY = TM_THURSDAY
};
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
@ -768,14 +1031,4 @@ char *ctime_r(time_t const *, char *);
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400) #define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
/*
** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
*/
#define AVGSECSPERYEAR 31556952L
#define SECSPERREPEAT \
((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined PRIVATE_H */ #endif /* !defined PRIVATE_H */

View File

@ -21,16 +21,8 @@
** Information about time zone files. ** Information about time zone files.
*/ */
#ifndef TZDIR
#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
#define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES #ifndef TZDEFRULES
#define TZDEFRULES "posixrules" # define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */ #endif /* !defined TZDEFRULES */
@ -44,7 +36,7 @@
struct tzhead { struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */ char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ char tzh_version[1]; /* '\0' or '2'-'4' as of 2021 */
char tzh_reserved[15]; /* reserved; must be zero */ char tzh_reserved[15]; /* reserved; must be zero */
char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
@ -86,11 +78,11 @@ struct tzhead {
** time uses 8 rather than 4 chars, ** time uses 8 rather than 4 chars,
** then a POSIX-TZ-environment-variable-style string for use in handling ** then a POSIX-TZ-environment-variable-style string for use in handling
** instants after the last transition time stored in the file ** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for ** (with nothing between the newlines if there is no POSIX.1-2017
** such instants). ** representation for such instants).
** **
** If tz_version is '3' or greater, the above is extended as follows. ** If tz_version is '3' or greater, the above is extended as follows.
** First, the POSIX TZ string's hour offset may range from -167 ** First, the TZ string's hour offset may range from -167
** through 167 as compared to the POSIX-required 0 through 24. ** through 167 as compared to the POSIX-required 0 through 24.
** Second, its DST start time may be January 1 at 00:00 and its stop ** Second, its DST start time may be January 1 at 00:00 and its stop
** time December 31 at 24:00 plus the difference between DST and ** time December 31 at 24:00 plus the difference between DST and
@ -103,21 +95,25 @@ struct tzhead {
*/ */
#ifndef TZ_MAX_TIMES #ifndef TZ_MAX_TIMES
#define TZ_MAX_TIMES 2000 /* This must be at least 242 for Europe/London with 'zic -b fat'. */
# define TZ_MAX_TIMES 2000
#endif /* !defined TZ_MAX_TIMES */ #endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES #ifndef TZ_MAX_TYPES
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ /* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ # define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined TZ_MAX_TYPES */ #endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS #ifndef TZ_MAX_CHARS
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ /* This must be at least 40 for America/Anchorage. */
# define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */ /* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */ #endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS #ifndef TZ_MAX_LEAPS
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ /* This must be at least 27 for leap seconds from 1972 through mid-2023.
There's a plan to discontinue leap seconds by 2035. */
# define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */ #endif /* !defined TZ_MAX_LEAPS */
#endif /* !defined TZFILE_H */ #endif /* !defined TZFILE_H */

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
2020a 2024a

View File

@ -15,7 +15,7 @@
#include <stdio.h> #include <stdio.h>
#ifndef HAVE_SNPRINTF #ifndef HAVE_SNPRINTF
# define HAVE_SNPRINTF (199901 <= __STDC_VERSION__) # define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
#endif #endif
#ifndef HAVE_LOCALTIME_R #ifndef HAVE_LOCALTIME_R
@ -35,17 +35,13 @@
#endif #endif
#ifndef ZDUMP_LO_YEAR #ifndef ZDUMP_LO_YEAR
#define ZDUMP_LO_YEAR (-500) # define ZDUMP_LO_YEAR (-500)
#endif /* !defined ZDUMP_LO_YEAR */ #endif /* !defined ZDUMP_LO_YEAR */
#ifndef ZDUMP_HI_YEAR #ifndef ZDUMP_HI_YEAR
#define ZDUMP_HI_YEAR 2500 # define ZDUMP_HI_YEAR 2500
#endif /* !defined ZDUMP_HI_YEAR */ #endif /* !defined ZDUMP_HI_YEAR */
#ifndef MAX_STRING_LENGTH
#define MAX_STRING_LENGTH 1024
#endif /* !defined MAX_STRING_LENGTH */
#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ #define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
@ -61,7 +57,7 @@
enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 };
#if HAVE_GETTEXT #if HAVE_GETTEXT
#include <locale.h> /* for setlocale */ # include <locale.h> /* for setlocale */
#endif /* HAVE_GETTEXT */ #endif /* HAVE_GETTEXT */
#if ! HAVE_LOCALTIME_RZ #if ! HAVE_LOCALTIME_RZ
@ -77,7 +73,7 @@ extern int optind;
#endif #endif
/* The minimum and maximum finite time values. */ /* The minimum and maximum finite time values. */
enum { atime_shift = CHAR_BIT * sizeof (time_t) - 2 }; enum { atime_shift = CHAR_BIT * sizeof(time_t) - 2 };
static time_t const absolute_min_time = static time_t const absolute_min_time =
((time_t) -1 < 0 ((time_t) -1 < 0
? (- ((time_t) ~ (time_t) 0 < 0) ? (- ((time_t) ~ (time_t) 0 < 0)
@ -88,22 +84,27 @@ static time_t const absolute_max_time =
? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
: -1); : -1);
static int longest; static int longest;
static char * progname; static char const *progname;
static bool warned; static bool warned;
static bool errout; static bool errout;
static char const *abbr(struct tm const *); static char const *abbr(struct tm const *);
static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE; ATTRIBUTE_REPRODUCIBLE static intmax_t delta(struct tm *, struct tm *);
static void dumptime(struct tm const *); static void dumptime(struct tm const *);
static time_t hunt(timezone_t, char *, time_t, time_t); static time_t hunt(timezone_t, time_t, time_t, bool);
static void show(timezone_t, char *, time_t, bool); static void show(timezone_t, char *, time_t, bool);
static void showextrema(timezone_t, char *, time_t, struct tm *, time_t);
static void showtrans(char const *, struct tm const *, time_t, char const *, static void showtrans(char const *, struct tm const *, time_t, char const *,
char const *); char const *);
static const char *tformat(void); static const char *tformat(void);
static time_t yeartot(intmax_t) ATTRIBUTE_PURE; ATTRIBUTE_REPRODUCIBLE static time_t yeartot(intmax_t);
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */ /* Is C an ASCII digit? */
#define is_digit(c) ((unsigned)(c) - '0' <= 9) static bool
is_digit(char c)
{
return '0' <= c && c <= '9';
}
/* Is A an alphabetic character in the C locale? */ /* Is A an alphabetic character in the C locale? */
static bool static bool
@ -124,26 +125,49 @@ is_alpha(char a)
} }
} }
/* Return A + B, exiting if the result would overflow. */ ATTRIBUTE_NORETURN static void
static size_t size_overflow(void)
sumsize(size_t a, size_t b)
{ {
size_t sum = a + b; fprintf(stderr, _("%s: size overflow\n"), progname);
if (sum < a) { exit(EXIT_FAILURE);
fprintf(stderr, "%s: size overflow\n", progname); }
exit(EXIT_FAILURE);
} /* Return A + B, exiting if the result would overflow either ptrdiff_t
return sum; or size_t. A and B are both nonnegative. */
ATTRIBUTE_REPRODUCIBLE static ptrdiff_t
sumsize(ptrdiff_t a, ptrdiff_t b)
{
#ifdef ckd_add
ptrdiff_t sum;
if (!ckd_add(&sum, a, b) && sum <= INDEX_MAX)
return sum;
#else
if (a <= INDEX_MAX && b <= INDEX_MAX - a)
return a + b;
#endif
size_overflow();
}
/* Return the size of of the string STR, including its trailing NUL.
Report an error and exit if this would exceed INDEX_MAX which means
pointer subtraction wouldn't work. */
static ptrdiff_t
xstrsize(char const *str)
{
size_t len = strlen(str);
if (len < INDEX_MAX)
return len + 1;
size_overflow();
} }
/* Return a pointer to a newly allocated buffer of size SIZE, exiting /* Return a pointer to a newly allocated buffer of size SIZE, exiting
on failure. SIZE should be nonzero. */ on failure. SIZE should be positive. */
static void * ATTRIBUTE_MALLOC ATTRIBUTE_MALLOC static void *
xmalloc(size_t size) xmalloc(ptrdiff_t size)
{ {
void *p = malloc(size); void *p = malloc(size);
if (!p) { if (!p) {
perror(progname); fprintf(stderr, _("%s: Memory exhausted\n"), progname);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
return p; return p;
@ -204,7 +228,7 @@ localtime_r(time_t *tp, struct tm *tmp)
# undef localtime_rz # undef localtime_rz
# define localtime_rz zdump_localtime_rz # define localtime_rz zdump_localtime_rz
static struct tm * static struct tm *
localtime_rz(timezone_t rz, time_t *tp, struct tm *tmp) localtime_rz(ATTRIBUTE_MAYBE_UNUSED timezone_t rz, time_t *tp, struct tm *tmp)
{ {
return localtime_r(tp, tmp); return localtime_r(tp, tmp);
} }
@ -227,33 +251,65 @@ mktime_z(timezone_t tz, struct tm *tmp)
static timezone_t static timezone_t
tzalloc(char const *val) tzalloc(char const *val)
{ {
static char **fakeenv; # if HAVE_SETENV
char **env = fakeenv; if (setenv("TZ", val, 1) != 0) {
char *env0; char const *e = strerror(errno);
if (! env) { fprintf(stderr, _("%s: setenv: %s\n"), progname, e);
char **e = environ; exit(EXIT_FAILURE);
int to;
while (*e++)
continue;
env = xmalloc(sumsize(sizeof *environ,
(e - environ) * sizeof *environ));
to = 1;
for (e = environ; (env[to] = *e); e++)
to += strncmp(*e, "TZ=", 3) != 0;
} }
env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
env[0] = strcat(strcpy(env0, "TZ="), val);
environ = fakeenv = env;
tzset(); tzset();
return env; return &optarg; /* Any valid non-null char ** will do. */
# else
enum { TZeqlen = 3 };
static char const TZeq[TZeqlen] = "TZ=";
static char **fakeenv;
static ptrdiff_t fakeenv0size;
void *freeable = NULL;
char **env = fakeenv, **initial_environ;
ptrdiff_t valsize = xstrsize(val);
if (fakeenv0size < valsize) {
char **e = environ, **to;
ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */
while (*e++) {
# ifdef ckd_add
if (ckd_add(&initial_nenvptrs, initial_nenvptrs, 1)
|| INDEX_MAX < initial_nenvptrs)
size_overflow();
# else
if (initial_nenvptrs == INDEX_MAX / sizeof *environ)
size_overflow();
initial_nenvptrs++;
# endif
}
fakeenv0size = sumsize(valsize, valsize);
fakeenv0size = max(fakeenv0size, 64);
freeable = env;
fakeenv = env =
xmalloc(sumsize(sumsize(sizeof *environ,
initial_nenvptrs * sizeof *environ),
sumsize(TZeqlen, fakeenv0size)));
to = env + 1;
for (e = environ; (*to = *e); e++)
to += strncmp(*e, TZeq, TZeqlen) != 0;
env[0] = memcpy(to + 1, TZeq, TZeqlen);
}
memcpy(env[0] + TZeqlen, val, valsize);
initial_environ = environ;
environ = env;
tzset();
free(freeable);
return initial_environ;
# endif
} }
static void static void
tzfree(timezone_t env) tzfree(ATTRIBUTE_MAYBE_UNUSED timezone_t initial_environ)
{ {
environ = env + 1; # if !HAVE_SETENV
free(env[0]); environ = initial_environ;
tzset();
# endif
} }
#endif /* ! USE_LOCALTIME_RZ */ #endif /* ! USE_LOCALTIME_RZ */
@ -263,11 +319,25 @@ static void
gmtzinit(void) gmtzinit(void)
{ {
if (USE_LOCALTIME_RZ) { if (USE_LOCALTIME_RZ) {
static char const utc[] = "UTC0"; /* Try "GMT" first to find out whether this is one of the rare
gmtz = tzalloc(utc); platforms where time_t counts leap seconds; this works due to
the "Zone GMT 0 - GMT" line in the "etcetera" file. If "GMT"
fails, fall back on "GMT0" which might be similar due to the
"Link GMT GMT0" line in the "backward" file, and which
should work on all POSIX platforms. The rest of zdump does not
use the "GMT" abbreviation that comes from this setting, so it
is OK to use "GMT" here rather than the modern "UTC" which
would not work on platforms that omit the "backward" file. */
gmtz = tzalloc("GMT");
if (!gmtz) { if (!gmtz) {
perror(utc); static char const gmt0[] = "GMT0";
exit(EXIT_FAILURE); gmtz = tzalloc(gmt0);
if (!gmtz) {
char const *e = strerror(errno);
fprintf(stderr, _("%s: unknown timezone '%s': %s\n"),
progname, gmt0, e);
exit(EXIT_FAILURE);
}
} }
} }
} }
@ -345,23 +415,23 @@ abbrok(const char *const abbrp, const char *const zone)
/* Return a time zone abbreviation. If the abbreviation needs to be /* Return a time zone abbreviation. If the abbreviation needs to be
saved, use *BUF (of size *BUFALLOC) to save it, and return the saved, use *BUF (of size *BUFALLOC) to save it, and return the
abbreviation in the possibly-reallocated *BUF. Otherwise, just abbreviation in the possibly reallocated *BUF. Otherwise, just
return the abbreviation. Get the abbreviation from TMP. return the abbreviation. Get the abbreviation from TMP.
Exit on memory allocation failure. */ Exit on memory allocation failure. */
static char const * static char const *
saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp) saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp)
{ {
char const *ab = abbr(tmp); char const *ab = abbr(tmp);
if (HAVE_LOCALTIME_RZ) if (HAVE_LOCALTIME_RZ)
return ab; return ab;
else { else {
size_t ablen = strlen(ab); ptrdiff_t absize = xstrsize(ab);
if (*bufalloc <= ablen) { if (*bufalloc < absize) {
free(*buf); free(*buf);
/* Make the new buffer at least twice as long as the old, /* Make the new buffer at least twice as long as the old,
to avoid O(N**2) behavior on repeated calls. */ to avoid O(N**2) behavior on repeated calls. */
*bufalloc = sumsize(*bufalloc, ablen + 1); *bufalloc = sumsize(*bufalloc, absize);
*buf = xmalloc(*bufalloc); *buf = xmalloc(*bufalloc);
} }
@ -406,7 +476,7 @@ main(int argc, char *argv[])
{ {
/* These are static so that they're initially zero. */ /* These are static so that they're initially zero. */
static char * abbrev; static char * abbrev;
static size_t abbrevsize; static ptrdiff_t abbrevsize;
register int i; register int i;
register bool vflag; register bool vflag;
@ -422,12 +492,12 @@ main(int argc, char *argv[])
cuthitime = absolute_max_time; cuthitime = absolute_max_time;
#if HAVE_GETTEXT #if HAVE_GETTEXT
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
#ifdef TZ_DOMAINDIR # ifdef TZ_DOMAINDIR
bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR); bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
#endif /* defined TEXTDOMAINDIR */ # endif /* defined TEXTDOMAINDIR */
textdomain(TZ_DOMAIN); textdomain(TZ_DOMAIN);
#endif /* HAVE_GETTEXT */ #endif /* HAVE_GETTEXT */
progname = argv[0]; progname = argv[0] ? argv[0] : "zdump";
for (i = 1; i < argc; ++i) for (i = 1; i < argc; ++i)
if (strcmp(argv[i], "--version") == 0) { if (strcmp(argv[i], "--version") == 0) {
printf("zdump %s%s\n", PKGVERSION, TZVERSION); printf("zdump %s%s\n", PKGVERSION, TZVERSION);
@ -447,7 +517,7 @@ main(int argc, char *argv[])
case -1: case -1:
if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
goto arg_processing_done; goto arg_processing_done;
/* Fall through. */ ATTRIBUTE_FALLTHROUGH;
default: default:
usage(stderr, EXIT_FAILURE); usage(stderr, EXIT_FAILURE);
} }
@ -484,8 +554,8 @@ main(int argc, char *argv[])
if (cuttimes != loend && !*loend) { if (cuttimes != loend && !*loend) {
hi = lo; hi = lo;
if (hi < cuthitime) { if (hi < cuthitime) {
if (hi < absolute_min_time) if (hi < absolute_min_time + 1)
hi = absolute_min_time; hi = absolute_min_time + 1;
cuthitime = hi; cuthitime = hi;
} }
} else if (cuttimes != loend && *loend == ',' } else if (cuttimes != loend && *loend == ','
@ -497,8 +567,8 @@ main(int argc, char *argv[])
cutlotime = lo; cutlotime = lo;
} }
if (hi < cuthitime) { if (hi < cuthitime) {
if (hi < absolute_min_time) if (hi < absolute_min_time + 1)
hi = absolute_min_time; hi = absolute_min_time + 1;
cuthitime = hi; cuthitime = hi;
} }
} else { } else {
@ -510,14 +580,17 @@ main(int argc, char *argv[])
} }
} }
gmtzinit(); gmtzinit();
INITIALIZE (now); if (iflag | vflag | Vflag)
if (! (iflag | vflag | Vflag)) now = 0;
else {
now = time(NULL); now = time(NULL);
now |= !now;
}
longest = 0; longest = 0;
for (i = optind; i < argc; i++) { for (i = optind; i < argc; i++) {
size_t arglen = strlen(argv[i]); size_t arglen = strlen(argv[i]);
if (longest < arglen) if (longest < arglen)
longest = arglen < INT_MAX ? arglen : INT_MAX; longest = min(arglen, INT_MAX);
} }
for (i = optind; i < argc; ++i) { for (i = optind; i < argc; ++i) {
@ -527,10 +600,12 @@ main(int argc, char *argv[])
struct tm tm, newtm; struct tm tm, newtm;
bool tm_ok; bool tm_ok;
if (!tz) { if (!tz) {
perror(argv[i]); char const *e = strerror(errno);
fprintf(stderr, _("%s: unknown timezone '%s': %s\n"),
progname, argv[i], e);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (! (iflag | vflag | Vflag)) { if (now) {
show(tz, argv[i], now, false); show(tz, argv[i], now, false);
tzfree(tz); tzfree(tz);
continue; continue;
@ -539,12 +614,15 @@ main(int argc, char *argv[])
t = absolute_min_time; t = absolute_min_time;
if (! (iflag | Vflag)) { if (! (iflag | Vflag)) {
show(tz, argv[i], t, true); show(tz, argv[i], t, true);
t += SECSPERDAY; if (my_localtime_rz(tz, &t, &tm) == NULL
show(tz, argv[i], t, true); && t < cutlotime) {
time_t newt = cutlotime;
if (my_localtime_rz(tz, &newt, &newtm) != NULL)
showextrema(tz, argv[i], t, NULL, newt);
}
} }
if (t < cutlotime) if (t + 1 < cutlotime)
t = cutlotime; t = cutlotime - 1;
INITIALIZE (ab);
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL; tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
if (tm_ok) { if (tm_ok) {
ab = saveabbr(&abbrev, &abbrevsize, &tm); ab = saveabbr(&abbrev, &abbrevsize, &tm);
@ -552,19 +630,20 @@ main(int argc, char *argv[])
showtrans("\nTZ=%f", &tm, t, ab, argv[i]); showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
showtrans("-\t-\t%Q", &tm, t, ab, argv[i]); showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
} }
} } else
while (t < cuthitime) { ab = NULL;
while (t < cuthitime - 1) {
time_t newt = ((t < absolute_max_time - SECSPERDAY / 2 time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
&& t + SECSPERDAY / 2 < cuthitime) && t + SECSPERDAY / 2 < cuthitime - 1)
? t + SECSPERDAY / 2 ? t + SECSPERDAY / 2
: cuthitime); : cuthitime - 1);
struct tm *newtmp = localtime_rz(tz, &newt, &newtm); struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
bool newtm_ok = newtmp != NULL; bool newtm_ok = newtmp != NULL;
if (tm_ok != newtm_ok if (tm_ok != newtm_ok
|| (tm_ok && (delta(&newtm, &tm) != newt - t || (ab && (delta(&newtm, &tm) != newt - t
|| newtm.tm_isdst != tm.tm_isdst || newtm.tm_isdst != tm.tm_isdst
|| strcmp(abbr(&newtm), ab) != 0))) { || strcmp(abbr(&newtm), ab) != 0))) {
newt = hunt(tz, argv[i], t, newt); newt = hunt(tz, t, newt, false);
newtmp = localtime_rz(tz, &newt, &newtm); newtmp = localtime_rz(tz, &newt, &newtm);
newtm_ok = newtmp != NULL; newtm_ok = newtmp != NULL;
if (iflag) if (iflag)
@ -583,11 +662,15 @@ main(int argc, char *argv[])
} }
} }
if (! (iflag | Vflag)) { if (! (iflag | Vflag)) {
t = absolute_max_time; time_t newt = absolute_max_time;
t -= SECSPERDAY; t = cuthitime;
show(tz, argv[i], t, true); if (t < newt) {
t += SECSPERDAY; struct tm *tmp = my_localtime_rz(tz, &t, &tm);
show(tz, argv[i], t, true); if (tmp != NULL
&& my_localtime_rz(tz, &newt, &newtm) == NULL)
showextrema(tz, argv[i], t, tmp, newt);
}
show(tz, argv[i], absolute_max_time, true);
} }
tzfree(tz); tzfree(tz);
} }
@ -640,36 +723,42 @@ yeartot(intmax_t y)
return t; return t;
} }
/* Search for a discontinuity in timezone TZ, in the
timestamps ranging from LOT through HIT. LOT and HIT disagree
about some aspect of timezone. If ONLY_OK, search only for
definedness changes, i.e., localtime succeeds on one side of the
transition but fails on the other side. Return the timestamp just
before the transition from LOT's settings. */
static time_t static time_t
hunt(timezone_t tz, char *name, time_t lot, time_t hit) hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok)
{ {
static char * loab; static char * loab;
static size_t loabsize; static ptrdiff_t loabsize;
char const * ab;
time_t t;
struct tm lotm; struct tm lotm;
struct tm tm; struct tm tm;
/* Convert LOT into a broken-down time here, even though our
caller already did that. On platforms without TM_ZONE,
tzname may have been altered since our caller broke down
LOT, and tzname needs to be changed back. */
bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL; bool lotm_ok = my_localtime_rz(tz, &lot, &lotm) != NULL;
bool tm_ok; bool tm_ok;
char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL;
if (lotm_ok)
ab = saveabbr(&loab, &loabsize, &lotm);
for ( ; ; ) { for ( ; ; ) {
time_t diff = hit - lot; /* T = average of LOT and HIT, rounding down.
if (diff < 2) Avoid overflow. */
int rem_sum = lot % 2 + hit % 2;
time_t t = (rem_sum == 2) - (rem_sum < 0) + lot / 2 + hit / 2;
if (t == lot)
break; break;
t = lot;
t += diff / 2;
if (t <= lot)
++t;
else if (t >= hit)
--t;
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL; tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
if (lotm_ok & tm_ok if (lotm_ok == tm_ok
? (delta(&tm, &lotm) == t - lot && (only_ok
&& tm.tm_isdst == lotm.tm_isdst || (ab && tm.tm_isdst == lotm.tm_isdst
&& strcmp(abbr(&tm), ab) == 0) && delta(&tm, &lotm) == t - lot
: lotm_ok == tm_ok) { && strcmp(abbr(&tm), ab) == 0))) {
lot = t; lot = t;
if (tm_ok) if (tm_ok)
lotm = tm; lotm = tm;
@ -685,11 +774,11 @@ hunt(timezone_t tz, char *name, time_t lot, time_t hit)
static intmax_t static intmax_t
delta_nonneg(struct tm *newp, struct tm *oldp) delta_nonneg(struct tm *newp, struct tm *oldp)
{ {
register intmax_t result; intmax_t oldy = oldp->tm_year;
register int tmy; int cycles = (newp->tm_year - oldy) / YEARSPERREPEAT;
intmax_t sec = SECSPERREPEAT, result = cycles * sec;
result = 0; int tmy = oldp->tm_year + cycles * YEARSPERREPEAT;
for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy) for ( ; tmy < newp->tm_year; ++tmy)
result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE); result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
result += newp->tm_yday - oldp->tm_yday; result += newp->tm_yday - oldp->tm_yday;
result *= HOURSPERDAY; result *= HOURSPERDAY;
@ -730,7 +819,8 @@ adjusted_yday(struct tm const *a, struct tm const *b)
my_gmtime_r and use its result instead of B. Otherwise, B is the my_gmtime_r and use its result instead of B. Otherwise, B is the
possibly nonnull result of an earlier call to my_gmtime_r. */ possibly nonnull result of an earlier call to my_gmtime_r. */
static long static long
gmtoff(struct tm const *a, time_t *t, struct tm const *b) gmtoff(struct tm const *a, ATTRIBUTE_MAYBE_UNUSED time_t *t,
ATTRIBUTE_MAYBE_UNUSED struct tm const *b)
{ {
#ifdef TM_GMTOFF #ifdef TM_GMTOFF
return a->TM_GMTOFF; return a->TM_GMTOFF;
@ -764,6 +854,7 @@ show(timezone_t tz, char *zone, time_t t, bool v)
gmtmp = my_gmtime_r(&t, &gmtm); gmtmp = my_gmtime_r(&t, &gmtm);
if (gmtmp == NULL) { if (gmtmp == NULL) {
printf(tformat(), t); printf(tformat(), t);
printf(_(" (gmtime failed)"));
} else { } else {
dumptime(gmtmp); dumptime(gmtmp);
printf(" UT"); printf(" UT");
@ -771,8 +862,11 @@ show(timezone_t tz, char *zone, time_t t, bool v)
printf(" = "); printf(" = ");
} }
tmp = my_localtime_rz(tz, &t, &tm); tmp = my_localtime_rz(tz, &t, &tm);
dumptime(tmp); if (tmp == NULL) {
if (tmp != NULL) { printf(tformat(), t);
printf(_(" (localtime failed)"));
} else {
dumptime(tmp);
if (*abbr(tmp) != '\0') if (*abbr(tmp) != '\0')
printf(" %s", abbr(tmp)); printf(" %s", abbr(tmp));
if (v) { if (v) {
@ -787,13 +881,58 @@ show(timezone_t tz, char *zone, time_t t, bool v)
abbrok(abbr(tmp), zone); abbrok(abbr(tmp), zone);
} }
/* Show timestamps just before and just after a transition between
defined and undefined (or vice versa) in either localtime or
gmtime. These transitions are for timezone TZ with name ZONE, in
the range from LO (with broken-down time LOTMP if that is nonnull)
through HI. LO and HI disagree on definedness. */
static void
showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
{
struct tm localtm[2], gmtm[2];
time_t t, boundary = hunt(tz, lo, hi, true);
bool old = false;
hi = (SECSPERDAY < hi - boundary
? boundary + SECSPERDAY
: hi + (hi < TIME_T_MAX));
if (SECSPERDAY < boundary - lo) {
lo = boundary - SECSPERDAY;
lotmp = my_localtime_rz(tz, &lo, &localtm[old]);
}
if (lotmp)
localtm[old] = *lotmp;
else
localtm[old].tm_sec = -1;
if (! my_gmtime_r(&lo, &gmtm[old]))
gmtm[old].tm_sec = -1;
/* Search sequentially for definedness transitions. Although this
could be sped up by refining 'hunt' to search for either
localtime or gmtime definedness transitions, it hardly seems
worth the trouble. */
for (t = lo + 1; t < hi; t++) {
bool new = !old;
if (! my_localtime_rz(tz, &t, &localtm[new]))
localtm[new].tm_sec = -1;
if (! my_gmtime_r(&t, &gmtm[new]))
gmtm[new].tm_sec = -1;
if (((localtm[old].tm_sec < 0) != (localtm[new].tm_sec < 0))
| ((gmtm[old].tm_sec < 0) != (gmtm[new].tm_sec < 0))) {
show(tz, zone, t - 1, true);
show(tz, zone, t, true);
}
old = new;
}
}
#if HAVE_SNPRINTF #if HAVE_SNPRINTF
# define my_snprintf snprintf # define my_snprintf snprintf
#else #else
# include <stdarg.h> # include <stdarg.h>
/* A substitute for snprintf that is good enough for zdump. */ /* A substitute for snprintf that is good enough for zdump. */
static int ATTRIBUTE_FORMAT((printf, 3, 4)) ATTRIBUTE_FORMAT((printf, 3, 4)) static int
my_snprintf(char *s, size_t size, char const *format, ...) my_snprintf(char *s, size_t size, char const *format, ...)
{ {
int n; int n;
@ -831,7 +970,7 @@ my_snprintf(char *s, size_t size, char const *format, ...)
fit, return the length that the string would have been if it had fit, return the length that the string would have been if it had
fit; do not overrun the output buffer. */ fit; do not overrun the output buffer. */
static int static int
format_local_time(char *buf, size_t size, struct tm const *tm) format_local_time(char *buf, ptrdiff_t size, struct tm const *tm)
{ {
int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour; int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
return (ss return (ss
@ -854,7 +993,7 @@ format_local_time(char *buf, size_t size, struct tm const *tm)
the length that the string would have been if it had fit; do not the length that the string would have been if it had fit; do not
overrun the output buffer. */ overrun the output buffer. */
static int static int
format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t) format_utc_offset(char *buf, ptrdiff_t size, struct tm const *tm, time_t t)
{ {
long off = gmtoff(tm, &t, NULL); long off = gmtoff(tm, &t, NULL);
char sign = ((off < 0 char sign = ((off < 0
@ -883,11 +1022,11 @@ format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t)
If the representation's length is less than SIZE, return the If the representation's length is less than SIZE, return the
length; the representation is not null terminated. Otherwise length; the representation is not null terminated. Otherwise
return SIZE, to indicate that BUF is too small. */ return SIZE, to indicate that BUF is too small. */
static size_t static ptrdiff_t
format_quoted_string(char *buf, size_t size, char const *p) format_quoted_string(char *buf, ptrdiff_t size, char const *p)
{ {
char *b = buf; char *b = buf;
size_t s = size; ptrdiff_t s = size;
if (!s) if (!s)
return size; return size;
*b++ = '"', s--; *b++ = '"', s--;
@ -925,11 +1064,11 @@ format_quoted_string(char *buf, size_t size, char const *p)
and omit any trailing tabs. */ and omit any trailing tabs. */
static bool static bool
istrftime(char *buf, size_t size, char const *time_fmt, istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
struct tm const *tm, time_t t, char const *ab, char const *zone_name) struct tm const *tm, time_t t, char const *ab, char const *zone_name)
{ {
char *b = buf; char *b = buf;
size_t s = size; ptrdiff_t s = size;
char const *f = time_fmt, *p; char const *f = time_fmt, *p;
for (p = f; ; p++) for (p = f; ; p++)
@ -938,9 +1077,9 @@ istrftime(char *buf, size_t size, char const *time_fmt,
else if (!*p else if (!*p
|| (*p == '%' || (*p == '%'
&& (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) { && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
size_t formatted_len; ptrdiff_t formatted_len;
size_t f_prefix_len = p - f; ptrdiff_t f_prefix_len = p - f;
size_t f_prefix_copy_size = p - f + 2; ptrdiff_t f_prefix_copy_size = sumsize(f_prefix_len, 2);
char fbuf[100]; char fbuf[100];
bool oversized = sizeof fbuf <= f_prefix_copy_size; bool oversized = sizeof fbuf <= f_prefix_copy_size;
char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf; char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf;
@ -972,7 +1111,7 @@ istrftime(char *buf, size_t size, char const *time_fmt,
b += offlen, s -= offlen; b += offlen, s -= offlen;
if (show_abbr) { if (show_abbr) {
char const *abp; char const *abp;
size_t len; ptrdiff_t len;
if (s <= 1) if (s <= 1)
return false; return false;
*b++ = '\t', s--; *b++ = '\t', s--;
@ -1011,7 +1150,7 @@ showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
putchar('\n'); putchar('\n');
} else { } else {
char stackbuf[1000]; char stackbuf[1000];
size_t size = sizeof stackbuf; ptrdiff_t size = sizeof stackbuf;
char *buf = stackbuf; char *buf = stackbuf;
char *bufalloc = NULL; char *bufalloc = NULL;
while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) { while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
@ -1040,28 +1179,45 @@ abbr(struct tm const *tmp)
/* /*
** The code below can fail on certain theoretical systems; ** The code below can fail on certain theoretical systems;
** it works on all known real-world systems as of 2004-12-30. ** it works on all known real-world systems as of 2022-01-25.
*/ */
static const char * static const char *
tformat(void) tformat(void)
{ {
#if HAVE__GENERIC
/* C11-style _Generic is more likely to return the correct
format when distinct types have the same size. */
char const *fmt =
_Generic(+ (time_t) 0,
int: "%d", long: "%ld", long long: "%lld",
unsigned: "%u", unsigned long: "%lu",
unsigned long long: "%llu",
default: NULL);
if (fmt)
return fmt;
fmt = _Generic((time_t) 0,
intmax_t: "%"PRIdMAX, uintmax_t: "%"PRIuMAX,
default: NULL);
if (fmt)
return fmt;
#endif
if (0 > (time_t) -1) { /* signed */ if (0 > (time_t) -1) { /* signed */
if (sizeof (time_t) == sizeof (intmax_t)) if (sizeof(time_t) == sizeof(intmax_t))
return "%"PRIdMAX; return "%"PRIdMAX;
if (sizeof (time_t) > sizeof (long)) if (sizeof(time_t) > sizeof(long))
return "%lld"; return "%lld";
if (sizeof (time_t) > sizeof (int)) if (sizeof(time_t) > sizeof(int))
return "%ld"; return "%ld";
return "%d"; return "%d";
} }
#ifdef PRIuMAX #ifdef PRIuMAX
if (sizeof (time_t) == sizeof (uintmax_t)) if (sizeof(time_t) == sizeof(uintmax_t))
return "%"PRIuMAX; return "%"PRIuMAX;
#endif #endif
if (sizeof (time_t) > sizeof (unsigned long)) if (sizeof(time_t) > sizeof(unsigned long))
return "%llu"; return "%llu";
if (sizeof (time_t) > sizeof (unsigned int)) if (sizeof(time_t) > sizeof(unsigned int))
return "%lu"; return "%lu";
return "%u"; return "%u";
} }
@ -1076,33 +1232,24 @@ dumptime(register const struct tm *timeptr)
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
}; };
register const char * wn;
register const char * mn;
register int lead; register int lead;
register int trail; register int trail;
int DIVISOR = 10;
if (timeptr == NULL) {
printf("NULL");
return;
}
/* /*
** The packaged localtime_rz and gmtime_r never put out-of-range ** The packaged localtime_rz and gmtime_r never put out-of-range
** values in tm_wday or tm_mon, but since this code might be compiled ** values in tm_wday or tm_mon, but since this code might be compiled
** with other (perhaps experimental) versions, paranoia is in order. ** with other (perhaps experimental) versions, paranoia is in order.
*/ */
if (timeptr->tm_wday < 0 || timeptr->tm_wday >=
(int) (sizeof wday_name / sizeof wday_name[0]))
wn = "???";
else wn = wday_name[timeptr->tm_wday];
if (timeptr->tm_mon < 0 || timeptr->tm_mon >=
(int) (sizeof mon_name / sizeof mon_name[0]))
mn = "???";
else mn = mon_name[timeptr->tm_mon];
printf("%s %s%3d %.2d:%.2d:%.2d ", printf("%s %s%3d %.2d:%.2d:%.2d ",
wn, mn, ((0 <= timeptr->tm_wday
&& timeptr->tm_wday < sizeof wday_name / sizeof wday_name[0])
? wday_name[timeptr->tm_wday] : "???"),
((0 <= timeptr->tm_mon
&& timeptr->tm_mon < sizeof mon_name / sizeof mon_name[0])
? mon_name[timeptr->tm_mon] : "???"),
timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec); timeptr->tm_min, timeptr->tm_sec);
#define DIVISOR 10
trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR; trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR + lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
trail / DIVISOR; trail / DIVISOR;

File diff suppressed because it is too large Load Diff