mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-27 15:30:07 +00:00
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:
parent
57581acd95
commit
1f94147a79
14
SHARED-FILES
14
SHARED-FILES
@ -177,23 +177,25 @@ unicode:
|
||||
# The following files are shared with the upstream tzcode project and must be
|
||||
# updated regularly to stay in sync with the upstream releases.
|
||||
#
|
||||
# Update from tzcode 2017b.
|
||||
# Latest is 2018g:
|
||||
# https://mm.icann.org/pipermail/tz-announce/2018-October/000052.html
|
||||
# Currently synced to TZDB 2024a, announced and distributed here:
|
||||
# https://mm.icann.org/pipermail/tz-announce/2024-February/000081.html
|
||||
# https://data.iana.org/time-zones/releases/tzdb-2024a.tar.lz
|
||||
tzcode:
|
||||
timezone/private.h
|
||||
timezone/tzfile.h
|
||||
timezone/tzselect.ksh
|
||||
timezone/version
|
||||
timezone/zdump.c
|
||||
timezone/zic.c
|
||||
timezone/tzselect.ksh
|
||||
|
||||
# The following files are shared with the upstream tzdata project but is not
|
||||
# synchronized regularly. The data files themselves are used only for testing
|
||||
# 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.
|
||||
#
|
||||
# Currently synced to 2009i. Latest is 2018g.
|
||||
# https://mm.icann.org/pipermail/tz-announce/2018-October/000052.html
|
||||
# Currently synced to tzcode 2009i, announced and distributed here:
|
||||
# https://mm.icann.org/pipermail/tz/2009-June/040697.html
|
||||
# https://data.iana.org/time-zones/releases/tzdata2009i.tar.gz
|
||||
tzdata:
|
||||
timezone/africa
|
||||
timezone/antarctica
|
||||
|
@ -240,10 +240,6 @@
|
||||
|
||||
#ifdef _LIBC
|
||||
|
||||
/* The zic and zdump programs need these definitions. */
|
||||
|
||||
#define HAVE_STRERROR 1
|
||||
|
||||
/* The locale code needs these definitions. */
|
||||
|
||||
#define HAVE_REGEX 1
|
||||
|
@ -49,6 +49,13 @@ endif
|
||||
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)version.h: $(common-objpfx)config.make
|
||||
|
@ -17,6 +17,40 @@
|
||||
** 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
|
||||
** conversion package to increase confidence in the verification it provides.
|
||||
@ -39,24 +73,27 @@
|
||||
# define HAVE_DECL_ASCTIME_R 1
|
||||
#endif
|
||||
|
||||
#if !defined HAVE_GENERIC && defined __has_extension
|
||||
# if __has_extension(c_generic_selections)
|
||||
# define HAVE_GENERIC 1
|
||||
# else
|
||||
# define HAVE_GENERIC 0
|
||||
#if !defined HAVE__GENERIC && defined __has_extension
|
||||
# if !__has_extension(c_generic_selections)
|
||||
# define HAVE__GENERIC 0
|
||||
# endif
|
||||
#endif
|
||||
/* _Generic is buggy in pre-4.9 GCC. */
|
||||
#if !defined HAVE_GENERIC && defined __GNUC__
|
||||
# define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
|
||||
#if !defined HAVE__GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__
|
||||
# define HAVE__GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__))
|
||||
#endif
|
||||
#ifndef HAVE_GENERIC
|
||||
# define HAVE_GENERIC (201112 <= __STDC_VERSION__)
|
||||
#ifndef HAVE__GENERIC
|
||||
# define HAVE__GENERIC (201112 <= __STDC_VERSION__)
|
||||
#endif
|
||||
|
||||
#if !defined HAVE_GETTEXT && defined __has_include
|
||||
# if __has_include(<libintl.h>)
|
||||
# define HAVE_GETTEXT true
|
||||
# endif
|
||||
#endif
|
||||
#ifndef HAVE_GETTEXT
|
||||
#define HAVE_GETTEXT 0
|
||||
#endif /* !defined HAVE_GETTEXT */
|
||||
# define HAVE_GETTEXT false
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_INCOMPATIBLE_CTIME_R
|
||||
# define HAVE_INCOMPATIBLE_CTIME_R 0
|
||||
@ -66,41 +103,43 @@
|
||||
# define HAVE_LINK 1
|
||||
#endif /* !defined HAVE_LINK */
|
||||
|
||||
#ifndef HAVE_MALLOC_ERRNO
|
||||
# define HAVE_MALLOC_ERRNO 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_POSIX_DECLS
|
||||
# define HAVE_POSIX_DECLS 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STDBOOL_H
|
||||
#define HAVE_STDBOOL_H (199901 <= __STDC_VERSION__)
|
||||
#ifndef HAVE_SETENV
|
||||
# define HAVE_SETENV 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
# define HAVE_STRDUP 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRTOLL
|
||||
#define HAVE_STRTOLL 1
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SYMLINK
|
||||
# define HAVE_SYMLINK 1
|
||||
#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
|
||||
#define HAVE_SYS_STAT_H 1
|
||||
#endif /* !defined HAVE_SYS_STAT_H */
|
||||
|
||||
#ifndef HAVE_SYS_WAIT_H
|
||||
#define HAVE_SYS_WAIT_H 1
|
||||
#endif /* !defined HAVE_SYS_WAIT_H */
|
||||
# define HAVE_SYS_STAT_H true
|
||||
#endif
|
||||
|
||||
#if !defined HAVE_UNISTD_H && defined __has_include
|
||||
# if !__has_include(<unistd.h>)
|
||||
# define HAVE_UNISTD_H false
|
||||
# endif
|
||||
#endif
|
||||
#ifndef HAVE_UNISTD_H
|
||||
#define HAVE_UNISTD_H 1
|
||||
#endif /* !defined HAVE_UNISTD_H */
|
||||
|
||||
#ifndef HAVE_UTMPX_H
|
||||
#define HAVE_UTMPX_H 1
|
||||
#endif /* !defined HAVE_UTMPX_H */
|
||||
# define HAVE_UNISTD_H true
|
||||
#endif
|
||||
|
||||
#ifndef NETBSD_INSPIRED
|
||||
# define NETBSD_INSPIRED 1
|
||||
@ -118,15 +157,17 @@
|
||||
/* Enable strtoimax on pre-C99 Solaris 11. */
|
||||
#define __EXTENSIONS__ 1
|
||||
|
||||
/* To avoid having 'stat' fail unnecessarily with errno == EOVERFLOW,
|
||||
enable large files on GNUish systems ... */
|
||||
/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
|
||||
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
|
||||
# define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
/* ... and on AIX ... */
|
||||
#define _LARGE_FILES 1
|
||||
/* ... and enable large inode numbers on Mac OS X 10.5 and later. */
|
||||
#define _DARWIN_USE_64_BIT_INODE 1
|
||||
#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
|
||||
# define _TIME_BITS 64
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Nested includes
|
||||
@ -157,16 +198,29 @@
|
||||
#undef tzalloc
|
||||
#undef tzfree
|
||||
|
||||
#include <sys/types.h> /* for time_t */
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#if !PORT_TO_C89
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
#include <limits.h> /* for CHAR_BIT et al. */
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef EINVAL
|
||||
# define EINVAL ERANGE
|
||||
#endif
|
||||
|
||||
#ifndef ELOOP
|
||||
# define ELOOP EINVAL
|
||||
#endif
|
||||
#ifndef ENAMETOOLONG
|
||||
# define ENAMETOOLONG EINVAL
|
||||
#endif
|
||||
#ifndef ENOMEM
|
||||
# define ENOMEM EINVAL
|
||||
#endif
|
||||
#ifndef ENOTSUP
|
||||
# define ENOTSUP EINVAL
|
||||
#endif
|
||||
@ -218,15 +272,17 @@
|
||||
# define R_OK 4
|
||||
#endif /* !defined R_OK */
|
||||
|
||||
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
|
||||
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
|
||||
#if PORT_TO_C89
|
||||
|
||||
/*
|
||||
** Define HAVE_STDINT_H's default value here, rather than at the
|
||||
** 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.
|
||||
*/
|
||||
#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
|
||||
# define HAVE_STDINT_H \
|
||||
(199901 <= __STDC_VERSION__ \
|
||||
@ -246,36 +302,36 @@
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
# define LLONG_MAX __LONG_LONG_MAX__
|
||||
# endif
|
||||
# ifndef LLONG_MIN
|
||||
# define LLONG_MIN (-1 - LLONG_MAX)
|
||||
# endif
|
||||
# ifndef ULLONG_MAX
|
||||
# define ULLONG_MAX (LLONG_MAX * 2ull + 1)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef INT_FAST64_MAX
|
||||
# ifdef LLONG_MAX
|
||||
typedef long 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
|
||||
# if 1 <= LONG_MAX >> 31 >> 31
|
||||
typedef long int_fast64_t;
|
||||
# define INT_FAST64_MIN LONG_MIN
|
||||
# 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
|
||||
|
||||
#ifndef PRIdFAST64
|
||||
# if INT_FAST64_MAX == LLONG_MAX
|
||||
# define PRIdFAST64 "lld"
|
||||
# else
|
||||
# if INT_FAST64_MAX == LONG_MAX
|
||||
# define PRIdFAST64 "ld"
|
||||
# else
|
||||
# define PRIdFAST64 "lld"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@ -298,6 +354,9 @@ typedef int int_fast32_t;
|
||||
#ifndef INTMAX_MAX
|
||||
# ifdef LLONG_MAX
|
||||
typedef long long intmax_t;
|
||||
# ifndef HAVE_STRTOLL
|
||||
# define HAVE_STRTOLL true
|
||||
# endif
|
||||
# if HAVE_STRTOLL
|
||||
# define strtoimax strtoll
|
||||
# endif
|
||||
@ -321,66 +380,183 @@ typedef long intmax_t;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef UINT_FAST64_MAX
|
||||
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
|
||||
typedef unsigned long long uint_fast64_t;
|
||||
# else
|
||||
# if ULONG_MAX >> 31 >> 1 < 0xffffffff
|
||||
Please use a compiler that supports a 64-bit integer type (or wider);
|
||||
you may need to compile with "-DHAVE_STDINT_H".
|
||||
#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
|
||||
# if 3 <= ULONG_MAX >> 31 >> 31
|
||||
typedef unsigned long uint_fast64_t;
|
||||
# define UINT_FAST64_MAX ULONG_MAX
|
||||
# else
|
||||
/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */
|
||||
typedef unsigned long long uint_fast64_t;
|
||||
# define UINT_FAST64_MAX ULLONG_MAX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef UINTMAX_MAX
|
||||
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
|
||||
# ifdef ULLONG_MAX
|
||||
typedef unsigned long long uintmax_t;
|
||||
# define UINTMAX_MAX ULLONG_MAX
|
||||
# else
|
||||
typedef unsigned long uintmax_t;
|
||||
# define UINTMAX_MAX ULONG_MAX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef PRIuMAX
|
||||
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
|
||||
# ifdef ULLONG_MAX
|
||||
# define PRIuMAX "llu"
|
||||
# else
|
||||
# define PRIuMAX "lu"
|
||||
# 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
|
||||
# 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
|
||||
|
||||
#if 3 <= __GNUC__
|
||||
# define ATTRIBUTE_CONST __attribute__ ((const))
|
||||
# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__))
|
||||
# define ATTRIBUTE_PURE __attribute__ ((__pure__))
|
||||
# define ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec))
|
||||
# define ATTRIBUTE_MALLOC __attribute__((malloc))
|
||||
# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
|
||||
#else
|
||||
# define ATTRIBUTE_CONST /* empty */
|
||||
# define ATTRIBUTE_MALLOC /* empty */
|
||||
# define ATTRIBUTE_PURE /* empty */
|
||||
# define ATTRIBUTE_FORMAT(spec) /* empty */
|
||||
#endif
|
||||
|
||||
#if !defined _Noreturn && __STDC_VERSION__ < 201112
|
||||
# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__)
|
||||
# define _Noreturn __attribute__ ((__noreturn__))
|
||||
#if (defined __has_c_attribute \
|
||||
&& (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__))
|
||||
# define HAVE___HAS_C_ATTRIBUTE true
|
||||
#else
|
||||
# define _Noreturn
|
||||
# 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
|
||||
# define ATTRIBUTE_DEPRECATED /* empty */
|
||||
# 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 */
|
||||
#endif
|
||||
|
||||
@ -418,11 +594,12 @@ typedef unsigned long uintmax_t;
|
||||
# define TZ_TIME_T 0
|
||||
#endif
|
||||
|
||||
#if TZ_TIME_T
|
||||
# ifdef LOCALTIME_IMPLEMENTATION
|
||||
#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
|
||||
static time_t sys_time(time_t *x) { return time(x); }
|
||||
#endif
|
||||
|
||||
#if TZ_TIME_T
|
||||
|
||||
typedef time_tz tz_time_t;
|
||||
|
||||
# undef asctime
|
||||
@ -477,8 +654,6 @@ typedef time_tz tz_time_t;
|
||||
# define tzfree tz_tzfree
|
||||
# undef tzset
|
||||
# define tzset tz_tzset
|
||||
# undef tzsetwall
|
||||
# define tzsetwall tz_tzsetwall
|
||||
# if HAVE_STRFTIME_L
|
||||
# undef strftime_l
|
||||
# define strftime_l tz_strftime_l
|
||||
@ -498,11 +673,16 @@ typedef time_tz tz_time_t;
|
||||
# define altzone tz_altzone
|
||||
# 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 *ctime(time_t const *);
|
||||
DEPRECATED_IN_C23 char *ctime(time_t const *);
|
||||
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,
|
||||
struct tm const *restrict);
|
||||
# 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);
|
||||
time_t mktime(struct tm *);
|
||||
time_t time(time_t *);
|
||||
time_t timegm(struct tm *);
|
||||
void tzset(void);
|
||||
#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
|
||||
extern char *asctime_r(struct tm const *restrict, char *restrict);
|
||||
#endif
|
||||
@ -550,21 +745,18 @@ extern long altzone;
|
||||
** declarations if time_tz is defined.
|
||||
*/
|
||||
|
||||
#ifdef STD_INSPIRED
|
||||
# if TZ_TIME_T || !defined tzsetwall
|
||||
void tzsetwall(void);
|
||||
#ifndef STD_INSPIRED
|
||||
# define STD_INSPIRED 0
|
||||
#endif
|
||||
#if STD_INSPIRED
|
||||
# if TZ_TIME_T || !defined offtime
|
||||
struct tm *offtime(time_t const *, long);
|
||||
# endif
|
||||
# if TZ_TIME_T || !defined timegm
|
||||
time_t timegm(struct tm *);
|
||||
# endif
|
||||
# if TZ_TIME_T || !defined timelocal
|
||||
time_t timelocal(struct tm *);
|
||||
# endif
|
||||
# if TZ_TIME_T || !defined timeoff
|
||||
time_t timeoff(struct tm *, long);
|
||||
# define EXTERN_TIMEOFF
|
||||
# endif
|
||||
# if TZ_TIME_T || !defined time2posix
|
||||
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
|
||||
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 __APPLE__ && defined __MACH__))
|
||||
# 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);
|
||||
timezone_t tzalloc(char const *);
|
||||
void tzfree(timezone_t);
|
||||
# ifdef STD_INSPIRED
|
||||
# if STD_INSPIRED
|
||||
# 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
|
||||
# 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
|
||||
@ -616,18 +810,15 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
|
||||
** Finally, some convenience items.
|
||||
*/
|
||||
|
||||
#if HAVE_STDBOOL_H
|
||||
# include <stdbool.h>
|
||||
#else
|
||||
# define true 1
|
||||
# define false 0
|
||||
# define bool int
|
||||
#endif
|
||||
|
||||
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
|
||||
#define TYPE_BIT(type) (CHAR_BIT * (ptrdiff_t) sizeof(type))
|
||||
#define TYPE_SIGNED(type) (((type) -1) < 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
|
||||
B bits are used, and where the highest-order used bit is considered
|
||||
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))
|
||||
|
||||
/* 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.
|
||||
This implementation assumes no padding if time_t is signed and
|
||||
either the compiler lacks support for _Generic or time_t is not one
|
||||
of the standard signed integer types. */
|
||||
#if HAVE_GENERIC
|
||||
#if HAVE__GENERIC
|
||||
# define TIME_T_MIN \
|
||||
_Generic((time_t) 0, \
|
||||
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, \
|
||||
default: TIME_T_MAX_NO_PADDING) \
|
||||
: (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
|
||||
# define TIME_T_MIN TIME_T_MIN_NO_PADDING
|
||||
# define TIME_T_MAX TIME_T_MAX_NO_PADDING
|
||||
enum { SIGNED_PADDING_CHECK_NEEDED = true };
|
||||
#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.
|
||||
@ -685,10 +889,45 @@ time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
|
||||
# define INITIALIZE(x)
|
||||
#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
|
||||
# define UNINIT_TRAP 0
|
||||
#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...
|
||||
** '_(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
|
||||
#undef asctime_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 *);
|
||||
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
|
||||
|
||||
/* 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 MONSPERYEAR 12
|
||||
|
||||
#define TM_SUNDAY 0
|
||||
#define TM_MONDAY 1
|
||||
#define TM_TUESDAY 2
|
||||
#define TM_WEDNESDAY 3
|
||||
#define TM_THURSDAY 4
|
||||
#define TM_FRIDAY 5
|
||||
#define TM_SATURDAY 6
|
||||
#define DAYSPERREPEAT ((int_fast32_t) 400 * 365 + 100 - 4 + 1)
|
||||
#define SECSPERREPEAT ((int_fast64_t) DAYSPERREPEAT * SECSPERDAY)
|
||||
#define AVGSECSPERYEAR (SECSPERREPEAT / YEARSPERREPEAT)
|
||||
|
||||
#define TM_JANUARY 0
|
||||
#define TM_FEBRUARY 1
|
||||
#define TM_MARCH 2
|
||||
#define TM_APRIL 3
|
||||
#define TM_MAY 4
|
||||
#define TM_JUNE 5
|
||||
#define TM_JULY 6
|
||||
#define TM_AUGUST 7
|
||||
#define TM_SEPTEMBER 8
|
||||
#define TM_OCTOBER 9
|
||||
#define TM_NOVEMBER 10
|
||||
#define TM_DECEMBER 11
|
||||
/* How many years to generate (in zic.c) or search through (in localtime.c).
|
||||
This is two years larger than the obvious 400, to avoid edge cases.
|
||||
E.g., suppose a non-POSIX.1-2017 rule applies from 2012 on with transitions
|
||||
in March and September, plus one-off transitions in November 2013.
|
||||
If zic looked only at the last 400 years, it would set max_year=2413,
|
||||
with the intent that the 400 years 2014 through 2413 will be repeated.
|
||||
The last transition listed in the tzfile would be in 2413-09,
|
||||
less than 400 years after the last one-off transition in 2013-11.
|
||||
Two years is not overkill for localtime.c, as a one-year bump
|
||||
would mishandle 2023d's America/Ciudad_Juarez for November 2422. */
|
||||
enum { years_of_observations = YEARSPERREPEAT + 2 };
|
||||
|
||||
#define TM_YEAR_BASE 1900
|
||||
enum {
|
||||
TM_SUNDAY,
|
||||
TM_MONDAY,
|
||||
TM_TUESDAY,
|
||||
TM_WEDNESDAY,
|
||||
TM_THURSDAY,
|
||||
TM_FRIDAY,
|
||||
TM_SATURDAY
|
||||
};
|
||||
|
||||
#define EPOCH_YEAR 1970
|
||||
#define EPOCH_WDAY TM_THURSDAY
|
||||
enum {
|
||||
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))
|
||||
|
||||
@ -768,14 +1031,4 @@ char *ctime_r(time_t const *, char *);
|
||||
|
||||
#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 */
|
||||
|
@ -21,14 +21,6 @@
|
||||
** 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
|
||||
# define TZDEFRULES "posixrules"
|
||||
#endif /* !defined TZDEFRULES */
|
||||
@ -44,7 +36,7 @@
|
||||
|
||||
struct tzhead {
|
||||
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_ttisutcnt[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,
|
||||
** then a POSIX-TZ-environment-variable-style string for use in handling
|
||||
** instants after the last transition time stored in the file
|
||||
** (with nothing between the newlines if there is no POSIX representation for
|
||||
** such instants).
|
||||
** (with nothing between the newlines if there is no POSIX.1-2017
|
||||
** representation for such instants).
|
||||
**
|
||||
** 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.
|
||||
** 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
|
||||
@ -103,20 +95,24 @@ struct tzhead {
|
||||
*/
|
||||
|
||||
#ifndef TZ_MAX_TIMES
|
||||
/* This must be at least 242 for Europe/London with 'zic -b fat'. */
|
||||
# define TZ_MAX_TIMES 2000
|
||||
#endif /* !defined TZ_MAX_TIMES */
|
||||
|
||||
#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 */
|
||||
#endif /* !defined TZ_MAX_TYPES */
|
||||
|
||||
#ifndef TZ_MAX_CHARS
|
||||
/* 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) */
|
||||
#endif /* !defined TZ_MAX_CHARS */
|
||||
|
||||
#ifndef TZ_MAX_LEAPS
|
||||
/* 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 */
|
||||
|
||||
|
@ -10,7 +10,7 @@ REPORT_BUGS_TO=tz@iana.org
|
||||
|
||||
# Porting notes:
|
||||
#
|
||||
# This script requires a Posix-like shell and prefers the extension of a
|
||||
# This script requires a POSIX-like shell and prefers the extension of a
|
||||
# 'select' statement. The 'select' statement was introduced in the
|
||||
# Korn shell and is available in Bash and other shell implementations.
|
||||
# If your host lacks both Bash and the Korn shell, you can get their
|
||||
@ -18,34 +18,46 @@ REPORT_BUGS_TO=tz@iana.org
|
||||
#
|
||||
# Bash <https://www.gnu.org/software/bash/>
|
||||
# Korn Shell <http://www.kornshell.com/>
|
||||
# MirBSD Korn Shell <https://www.mirbsd.org/mksh.htm>
|
||||
# MirBSD Korn Shell <http://www.mirbsd.org/mksh.htm>
|
||||
#
|
||||
# For portability to Solaris 9 /bin/sh this script avoids some POSIX
|
||||
# features and common extensions, such as $(...) (which works sometimes
|
||||
# but not others), $((...)), and $10.
|
||||
# For portability to Solaris 10 /bin/sh (supported by Oracle through
|
||||
# January 2027) this script avoids some POSIX features and common
|
||||
# extensions, such as $(...), $((...)), ! CMD, unquoted ^, ${#ID},
|
||||
# ${ID##PAT}, ${ID%%PAT}, and $10. Although some of these constructs
|
||||
# work sometimes, it's simpler to avoid them entirely.
|
||||
#
|
||||
# This script also uses several features of modern awk programs.
|
||||
# If your host lacks awk, or has an old awk that does not conform to Posix,
|
||||
# you can use either of the following free programs instead:
|
||||
# This script also uses several features of POSIX awk.
|
||||
# If your host lacks awk, or has an old awk that does not conform to POSIX,
|
||||
# you can use any of the following free programs instead:
|
||||
#
|
||||
# Gawk (GNU awk) <https://www.gnu.org/software/gawk/>
|
||||
# mawk <https://invisible-island.net/mawk/>
|
||||
# nawk <https://github.com/onetrueawk/awk>
|
||||
#
|
||||
# Because 'awk "VAR=VALUE" ...' and 'awk -v "VAR=VALUE" ...' are not portable
|
||||
# if VALUE contains \, ", or newline, awk scripts in this file use:
|
||||
# awk 'BEGIN { VAR = substr(ARGV[1], 2); ARGV[1] = "" } ...' ="VALUE"
|
||||
# The substr avoids problems when VALUE is of the form X=Y and would be
|
||||
# misinterpreted as an assignment.
|
||||
|
||||
# This script does not want path expansion.
|
||||
set -f
|
||||
|
||||
# Specify default values for environment variables if they are unset.
|
||||
: ${AWK=awk}
|
||||
: ${TZDIR=`pwd`}
|
||||
: ${PWD=`pwd`}
|
||||
: ${TZDIR=$PWD}
|
||||
|
||||
# Output one argument as-is to standard output.
|
||||
# Output one argument as-is to standard output, with trailing newline.
|
||||
# Safer than 'echo', which can mishandle '\' or leading '-'.
|
||||
say() {
|
||||
printf '%s\n' "$1"
|
||||
}
|
||||
|
||||
# Check for awk Posix compliance.
|
||||
($AWK -v x=y 'BEGIN { exit 123 }') </dev/null >/dev/null 2>&1
|
||||
# Check for awk POSIX compliance.
|
||||
($AWK -v x=y 'BEGIN { exit 123 }') <>/dev/null >&0 2>&0
|
||||
[ $? = 123 ] || {
|
||||
say >&2 "$0: Sorry, your '$AWK' program is not Posix compatible."
|
||||
say >&2 "$0: Sorry, your '$AWK' program is not POSIX compatible."
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -79,14 +91,14 @@ Report bugs to $REPORT_BUGS_TO."
|
||||
|
||||
# Ask the user to select from the function's arguments,
|
||||
# and assign the selected argument to the variable 'select_result'.
|
||||
# Exit on EOF or I/O error. Use the shell's 'select' builtin if available,
|
||||
# falling back on a less-nice but portable substitute otherwise.
|
||||
# Exit on EOF or I/O error. Use the shell's nicer 'select' builtin if
|
||||
# available, falling back on a portable substitute otherwise.
|
||||
if
|
||||
case $BASH_VERSION in
|
||||
?*) :;;
|
||||
'')
|
||||
# '; exit' should be redundant, but Dash doesn't properly fail without it.
|
||||
(eval 'set --; select x; do break; done; exit') </dev/null 2>/dev/null
|
||||
(eval 'set --; select x; do break; done; exit') <>/dev/null 2>&0
|
||||
esac
|
||||
then
|
||||
# Do this inside 'eval', as otherwise the shell might exit when parsing it
|
||||
@ -101,19 +113,12 @@ then
|
||||
esac
|
||||
done || exit
|
||||
}
|
||||
|
||||
# Work around a bug in bash 1.14.7 and earlier, where $PS3 is sent to stdout.
|
||||
case $BASH_VERSION in
|
||||
[01].*)
|
||||
case `echo 1 | (select x in x; do break; done) 2>/dev/null` in
|
||||
?*) PS3=
|
||||
esac
|
||||
esac
|
||||
'
|
||||
else
|
||||
doselect() {
|
||||
# Field width of the prompt numbers.
|
||||
select_width=`expr $# : '.*'`
|
||||
print_nargs_length="BEGIN {print length(\"$#\");}"
|
||||
select_width=`$AWK "$print_nargs_length"`
|
||||
|
||||
select_i=
|
||||
|
||||
@ -124,14 +129,14 @@ else
|
||||
select_i=0
|
||||
for select_word
|
||||
do
|
||||
select_i=`expr $select_i + 1`
|
||||
select_i=`$AWK "BEGIN { print $select_i + 1 }"`
|
||||
printf >&2 "%${select_width}d) %s\\n" $select_i "$select_word"
|
||||
done;;
|
||||
*[!0-9]*)
|
||||
echo >&2 'Please enter a number in range.';;
|
||||
*)
|
||||
if test 1 -le $select_i && test $select_i -le $#; then
|
||||
shift `expr $select_i - 1`
|
||||
shift `$AWK "BEGIN { print $select_i - 1 }"`
|
||||
select_result=$1
|
||||
break
|
||||
fi
|
||||
@ -161,57 +166,124 @@ do
|
||||
-*)
|
||||
say >&2 "$0: -$opt$OPTARG: unknown option; try '$0 --help'"; exit 1;;
|
||||
*)
|
||||
say >&2 "$0: try '$0 --help'"; exit 1 ;;
|
||||
say >&2 "$0: try '$0 --help'"; exit 1
|
||||
esac
|
||||
done
|
||||
|
||||
shift `expr $OPTIND - 1`
|
||||
shift `$AWK "BEGIN { print $OPTIND - 1 }"`
|
||||
case $# in
|
||||
0) ;;
|
||||
*) say >&2 "$0: $1: unknown argument"; exit 1 ;;
|
||||
*) say >&2 "$0: $1: unknown argument"; exit 1
|
||||
esac
|
||||
|
||||
# Make sure the tables are readable.
|
||||
TZ_COUNTRY_TABLE=$TZDIR/iso3166.tab
|
||||
TZ_ZONE_TABLE=$TZDIR/$zonetabtype.tab
|
||||
for f in $TZ_COUNTRY_TABLE $TZ_ZONE_TABLE
|
||||
do
|
||||
<"$f" || {
|
||||
# translit=true to try transliteration.
|
||||
# This is false if U+12345 CUNEIFORM SIGN URU TIMES KI has length 1
|
||||
# which means awk (and presumably the shell) do not need transliteration.
|
||||
if $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) == 1 }'; then
|
||||
translit=true
|
||||
else
|
||||
translit=false
|
||||
fi
|
||||
|
||||
# Read into shell variable $1 the contents of file $2.
|
||||
# Convert to the current locale's encoding if possible,
|
||||
# as the shell aligns columns better that way.
|
||||
# If GNU iconv's //TRANSLIT does not work, fall back on POSIXish iconv;
|
||||
# if that does not work, fall back on 'cat'.
|
||||
read_file() {
|
||||
{ $translit && {
|
||||
eval "$1=\`(iconv -f UTF-8 -t //TRANSLIT) 2>/dev/null <\"\$2\"\`" ||
|
||||
eval "$1=\`(iconv -f UTF-8) 2>/dev/null <\"\$2\"\`"
|
||||
}; } ||
|
||||
eval "$1=\`cat <\"\$2\"\`" || {
|
||||
say >&2 "$0: time zone files are not set up correctly"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
# If the current locale does not support UTF-8, convert data to current
|
||||
# locale's format if possible, as the shell aligns columns better that way.
|
||||
# Check the UTF-8 of U+12345 CUNEIFORM SIGN URU TIMES KI.
|
||||
! $AWK 'BEGIN { u12345 = "\360\222\215\205"; exit length(u12345) != 1 }' &&
|
||||
{ tmp=`(mktemp -d) 2>/dev/null` || {
|
||||
tmp=${TMPDIR-/tmp}/tzselect.$$ &&
|
||||
(umask 77 && mkdir -- "$tmp")
|
||||
};} &&
|
||||
trap 'status=$?; rm -fr -- "$tmp"; exit $status' 0 HUP INT PIPE TERM &&
|
||||
(iconv -f UTF-8 -t //TRANSLIT <"$TZ_COUNTRY_TABLE" >$tmp/iso3166.tab) \
|
||||
2>/dev/null &&
|
||||
TZ_COUNTRY_TABLE=$tmp/iso3166.tab &&
|
||||
iconv -f UTF-8 -t //TRANSLIT <"$TZ_ZONE_TABLE" >$tmp/$zonetabtype.tab &&
|
||||
TZ_ZONE_TABLE=$tmp/$zonetabtype.tab
|
||||
}
|
||||
read_file TZ_COUNTRY_TABLE "$TZDIR/iso3166.tab"
|
||||
read_file TZ_ZONETABTYPE_TABLE "$TZDIR/$zonetabtype.tab"
|
||||
TZ_ZONENOW_TABLE=
|
||||
|
||||
newline='
|
||||
'
|
||||
IFS=$newline
|
||||
|
||||
|
||||
# Awk script to read a time zone table and output the same table,
|
||||
# with each column preceded by its distance from 'here'.
|
||||
output_distances='
|
||||
# Awk script to output a country list.
|
||||
output_country_list='
|
||||
BEGIN {
|
||||
continent_re = substr(ARGV[1], 2)
|
||||
TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
|
||||
TZ_ZONE_TABLE = substr(ARGV[3], 2)
|
||||
ARGV[1] = ARGV[2] = ARGV[3] = ""
|
||||
FS = "\t"
|
||||
while (getline <TZ_COUNTRY_TABLE)
|
||||
if ($0 ~ /^[^#]/)
|
||||
nlines = split(TZ_ZONE_TABLE, line, /\n/)
|
||||
for (iline = 1; iline <= nlines; iline++) {
|
||||
$0 = line[iline]
|
||||
commentary = $0 ~ /^#@/
|
||||
if (commentary) {
|
||||
if ($0 !~ /^#@/)
|
||||
continue
|
||||
col1ccs = substr($1, 3)
|
||||
conts = $2
|
||||
} else {
|
||||
col1ccs = $1
|
||||
conts = $3
|
||||
}
|
||||
ncc = split(col1ccs, cc, /,/)
|
||||
ncont = split(conts, cont, /,/)
|
||||
for (i = 1; i <= ncc; i++) {
|
||||
elsewhere = commentary
|
||||
for (ci = 1; ci <= ncont; ci++) {
|
||||
if (cont[ci] ~ continent_re) {
|
||||
if (!cc_seen[cc[i]]++)
|
||||
cc_list[++ccs] = cc[i]
|
||||
elsewhere = 0
|
||||
}
|
||||
}
|
||||
if (elsewhere)
|
||||
for (i = 1; i <= ncc; i++)
|
||||
cc_elsewhere[cc[i]] = 1
|
||||
}
|
||||
}
|
||||
nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 !~ /^#/)
|
||||
cc_name[$1] = $2
|
||||
}
|
||||
for (i = 1; i <= ccs; i++) {
|
||||
country = cc_list[i]
|
||||
if (cc_elsewhere[country])
|
||||
continue
|
||||
if (cc_name[country])
|
||||
country = cc_name[country]
|
||||
print country
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
# Awk script to process a time zone table and output the same table,
|
||||
# with each row preceded by its distance from 'here'.
|
||||
# If output_times is set, each row is instead preceded by its local time
|
||||
# and any apostrophes are escaped for the shell.
|
||||
output_distances_or_times='
|
||||
BEGIN {
|
||||
coord = substr(ARGV[1], 2)
|
||||
TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
|
||||
TZ_ZONE_TABLE = substr(ARGV[3], 2)
|
||||
ARGV[1] = ARGV[2] = ARGV[3] = ""
|
||||
FS = "\t"
|
||||
if (!output_times) {
|
||||
nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 ~ /^#/)
|
||||
continue
|
||||
country[$1] = $2
|
||||
}
|
||||
country["US"] = "US" # Otherwise the strings get too long.
|
||||
}
|
||||
}
|
||||
function abs(x) {
|
||||
return x < 0 ? -x : x;
|
||||
}
|
||||
@ -270,20 +342,41 @@ output_distances='
|
||||
BEGIN {
|
||||
coord_lat = convert_latitude(coord)
|
||||
coord_long = convert_longitude(coord)
|
||||
nlines = split(TZ_ZONE_TABLE, line, /\n/)
|
||||
for (h = 1; h <= nlines; h++) {
|
||||
$0 = line[h]
|
||||
if ($0 ~ /^#/)
|
||||
continue
|
||||
inline[inlines++] = $0
|
||||
ncc = split($1, cc, /,/)
|
||||
for (i = 1; i <= ncc; i++)
|
||||
cc_used[cc[i]]++
|
||||
}
|
||||
/^[^#]/ {
|
||||
here_lat = convert_latitude($2)
|
||||
here_long = convert_longitude($2)
|
||||
line = $1 "\t" $2 "\t" $3
|
||||
for (h = 0; h < inlines; h++) {
|
||||
$0 = inline[h]
|
||||
outline = $1 "\t" $2 "\t" $3
|
||||
sep = "\t"
|
||||
ncc = split($1, cc, /,/)
|
||||
split("", item_seen)
|
||||
item_seen[""] = 1
|
||||
for (i = 1; i <= ncc; i++) {
|
||||
line = line sep country[cc[i]]
|
||||
sep = ", "
|
||||
item = cc_used[cc[i]] <= 1 ? country[cc[i]] : $4
|
||||
if (item_seen[item]++)
|
||||
continue
|
||||
outline = outline sep item
|
||||
sep = "; "
|
||||
}
|
||||
if (output_times) {
|
||||
fmt = "TZ='\''%s'\'' date +'\''%d %%Y %%m %%d %%H:%%M %%a %%b\t%s'\''\n"
|
||||
gsub(/'\''/, "&\\\\&&", outline)
|
||||
printf fmt, $3, h, outline
|
||||
} else {
|
||||
here_lat = convert_latitude($2)
|
||||
here_long = convert_longitude($2)
|
||||
printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), \
|
||||
outline
|
||||
}
|
||||
}
|
||||
if (NF == 4)
|
||||
line = line " - " $4
|
||||
printf "%g\t%s\n", dist(coord_lat, coord_long, here_lat, here_long), line
|
||||
}
|
||||
'
|
||||
|
||||
@ -295,7 +388,10 @@ while
|
||||
|
||||
continent=
|
||||
country=
|
||||
country_result=
|
||||
region=
|
||||
time=
|
||||
TZ_ZONE_TABLE=$TZ_ZONETABTYPE_TABLE
|
||||
|
||||
case $coord in
|
||||
?*)
|
||||
@ -304,20 +400,36 @@ while
|
||||
|
||||
# Ask the user for continent or ocean.
|
||||
|
||||
echo >&2 'Please select a continent, ocean, "coord", or "TZ".'
|
||||
echo >&2 \
|
||||
'Please select a continent, ocean, "coord", "TZ", "time", or "now".'
|
||||
|
||||
quoted_continents=`
|
||||
$AWK '
|
||||
BEGIN { FS = "\t" }
|
||||
/^[^#]/ {
|
||||
entry = substr($3, 1, index($3, "/") - 1)
|
||||
function handle_entry(entry) {
|
||||
entry = substr(entry, 1, index(entry, "/") - 1)
|
||||
if (entry == "America")
|
||||
entry = entry "s"
|
||||
if (entry ~ /^(Arctic|Atlantic|Indian|Pacific)$/)
|
||||
entry = entry " Ocean"
|
||||
printf "'\''%s'\''\n", entry
|
||||
}
|
||||
' <"$TZ_ZONE_TABLE" |
|
||||
BEGIN {
|
||||
TZ_ZONETABTYPE_TABLE = substr(ARGV[1], 2)
|
||||
ARGV[1] = ""
|
||||
FS = "\t"
|
||||
nlines = split(TZ_ZONETABTYPE_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 ~ /^[^#]/)
|
||||
handle_entry($3)
|
||||
else if ($0 ~ /^#@/) {
|
||||
ncont = split($2, cont, /,/)
|
||||
for (ci = 1; ci <= ncont; ci++)
|
||||
handle_entry(cont[ci])
|
||||
}
|
||||
}
|
||||
}
|
||||
' ="$TZ_ZONETABTYPE_TABLE" |
|
||||
sort -u |
|
||||
tr '\n' ' '
|
||||
echo ''
|
||||
@ -326,18 +438,50 @@ while
|
||||
eval '
|
||||
doselect '"$quoted_continents"' \
|
||||
"coord - I want to use geographical coordinates." \
|
||||
"TZ - I want to specify the timezone using the Posix TZ format."
|
||||
"TZ - I want to specify the timezone using a POSIX.1-2017 TZ string." \
|
||||
"time - I know local time already." \
|
||||
"now - Like \"time\", but configure only for timestamps from now on."
|
||||
continent=$select_result
|
||||
case $continent in
|
||||
Americas) continent=America;;
|
||||
*" "*) continent=`expr "$continent" : '\''\([^ ]*\)'\''`
|
||||
*)
|
||||
# Get the first word of $continent. Path expansion is disabled
|
||||
# so this works even with "*", which should not happen.
|
||||
IFS=" "
|
||||
for continent in $continent ""; do break; done
|
||||
IFS=$newline;;
|
||||
esac
|
||||
case $zonetabtype,$continent in
|
||||
zonenow,*) ;;
|
||||
*,now)
|
||||
${TZ_ZONENOW_TABLE:+:} read_file TZ_ZONENOW_TABLE "$TZDIR/zonenow.tab"
|
||||
TZ_ZONE_TABLE=$TZ_ZONENOW_TABLE
|
||||
esac
|
||||
'
|
||||
esac
|
||||
|
||||
case $continent in
|
||||
TZ)
|
||||
# Ask the user for a Posix TZ string. Check that it conforms.
|
||||
# Ask the user for a POSIX.1-2017 TZ string. Check that it conforms.
|
||||
check_POSIX_TZ_string='
|
||||
BEGIN {
|
||||
tz = substr(ARGV[1], 2)
|
||||
ARGV[1] = ""
|
||||
tzname = ("(<[[:alnum:]+-][[:alnum:]+-][[:alnum:]+-]+>" \
|
||||
"|[[:alpha:]][[:alpha:]][[:alpha:]]+)")
|
||||
time = ("(2[0-4]|[0-1]?[0-9])" \
|
||||
"(:[0-5][0-9](:[0-5][0-9])?)?")
|
||||
offset = "[-+]?" time
|
||||
mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
|
||||
jdate = ("((J[1-9]|[0-9]|J?[1-9][0-9]" \
|
||||
"|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])")
|
||||
datetime = ",(" mdate "|" jdate ")(/" time ")?"
|
||||
tzpattern = ("^(:.*|" tzname offset "(" tzname \
|
||||
"(" offset ")?(" datetime datetime ")?)?)$")
|
||||
exit tz ~ tzpattern
|
||||
}
|
||||
'
|
||||
|
||||
while
|
||||
echo >&2 'Please enter the desired value' \
|
||||
'of the TZ environment variable.'
|
||||
@ -345,25 +489,12 @@ while
|
||||
'AEST and is 10 hours'
|
||||
echo >&2 'ahead (east) of Greenwich,' \
|
||||
'with no daylight saving time.'
|
||||
read TZ
|
||||
$AWK -v TZ="$TZ" 'BEGIN {
|
||||
tzname = "(<[[:alnum:]+-]{3,}>|[[:alpha:]]{3,})"
|
||||
time = "(2[0-4]|[0-1]?[0-9])" \
|
||||
"(:[0-5][0-9](:[0-5][0-9])?)?"
|
||||
offset = "[-+]?" time
|
||||
mdate = "M([1-9]|1[0-2])\\.[1-5]\\.[0-6]"
|
||||
jdate = "((J[1-9]|[0-9]|J?[1-9][0-9]" \
|
||||
"|J?[1-2][0-9][0-9])|J?3[0-5][0-9]|J?36[0-5])"
|
||||
datetime = ",(" mdate "|" jdate ")(/" time ")?"
|
||||
tzpattern = "^(:.*|" tzname offset "(" tzname \
|
||||
"(" offset ")?(" datetime datetime ")?)?)$"
|
||||
if (TZ ~ tzpattern) exit 1
|
||||
exit 0
|
||||
}'
|
||||
read tz
|
||||
$AWK "$check_POSIX_TZ_string" ="$tz"
|
||||
do
|
||||
say >&2 "'$TZ' is not a conforming Posix timezone string."
|
||||
say >&2 "'$tz' is not a conforming POSIX.1-2017 timezone string."
|
||||
done
|
||||
TZ_for_date=$TZ;;
|
||||
TZ_for_date=$tz;;
|
||||
*)
|
||||
case $continent in
|
||||
coord)
|
||||
@ -374,56 +505,155 @@ while
|
||||
echo >&2 'For example, +4042-07403 stands for'
|
||||
echo >&2 '40 degrees 42 minutes north,' \
|
||||
'74 degrees 3 minutes west.'
|
||||
read coord;;
|
||||
read coord
|
||||
esac
|
||||
distance_table=`$AWK \
|
||||
-v coord="$coord" \
|
||||
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
|
||||
"$output_distances" <"$TZ_ZONE_TABLE" |
|
||||
distance_table=`
|
||||
$AWK \
|
||||
"$output_distances_or_times" \
|
||||
="$coord" ="$TZ_COUNTRY_TABLE" ="$TZ_ZONE_TABLE" |
|
||||
sort -n |
|
||||
sed "${location_limit}q"
|
||||
$AWK "{print} NR == $location_limit { exit }"
|
||||
`
|
||||
regions=`say "$distance_table" | $AWK '
|
||||
BEGIN { FS = "\t" }
|
||||
{ print $NF }
|
||||
'`
|
||||
echo >&2 'Please select one of the following timezones,' \
|
||||
regions=`
|
||||
$AWK '
|
||||
BEGIN {
|
||||
distance_table = substr(ARGV[1], 2)
|
||||
ARGV[1] = ""
|
||||
nlines = split(distance_table, line, /\n/)
|
||||
for (nr = 1; nr <= nlines; nr++) {
|
||||
nf = split(line[nr], f, /\t/)
|
||||
print f[nf]
|
||||
}
|
||||
}
|
||||
' ="$distance_table"
|
||||
`
|
||||
echo >&2 'Please select one of the following timezones,'
|
||||
echo >&2 'listed roughly in increasing order' \
|
||||
"of distance from $coord".
|
||||
doselect $regions
|
||||
region=$select_result
|
||||
TZ=`say "$distance_table" | $AWK -v region="$region" '
|
||||
BEGIN { FS="\t" }
|
||||
$NF == region { print $4 }
|
||||
'`
|
||||
tz=`
|
||||
$AWK '
|
||||
BEGIN {
|
||||
distance_table = substr(ARGV[1], 2)
|
||||
region = substr(ARGV[2], 2)
|
||||
ARGV[1] = ARGV[2] = ""
|
||||
nlines = split(distance_table, line, /\n/)
|
||||
for (nr = 1; nr <= nlines; nr++) {
|
||||
nf = split(line[nr], f, /\t/)
|
||||
if (f[nf] == region)
|
||||
print f[4]
|
||||
}
|
||||
}
|
||||
' ="$distance_table" ="$region"
|
||||
`;;
|
||||
*)
|
||||
case $continent in
|
||||
now|time)
|
||||
minute_format='%a %b %d %H:%M'
|
||||
old_minute=`TZ=UTC0 date +"$minute_format"`
|
||||
for i in 1 2 3
|
||||
do
|
||||
time_table_command=`
|
||||
$AWK \
|
||||
-v output_times=1 \
|
||||
"$output_distances_or_times" \
|
||||
= = ="$TZ_ZONE_TABLE"
|
||||
`
|
||||
time_table=`eval "$time_table_command"`
|
||||
new_minute=`TZ=UTC0 date +"$minute_format"`
|
||||
case $old_minute in
|
||||
"$new_minute") break
|
||||
esac
|
||||
old_minute=$new_minute
|
||||
done
|
||||
echo >&2 "The system says Universal Time is $new_minute."
|
||||
echo >&2 "Assuming that's correct, what is the local time?"
|
||||
sorted_table=`say "$time_table" | sort -k2n -k2,5 -k1n` || {
|
||||
say >&2 "$0: cannot sort time table"
|
||||
exit 1
|
||||
}
|
||||
eval doselect `
|
||||
$AWK '
|
||||
BEGIN {
|
||||
sorted_table = substr(ARGV[1], 2)
|
||||
ARGV[1] = ""
|
||||
nlines = split(sorted_table, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
outline = $6 " " $7 " " $4 " " $5
|
||||
if (outline == oldline)
|
||||
continue
|
||||
oldline = outline
|
||||
gsub(/'\''/, "&\\\\&&", outline)
|
||||
printf "'\''%s'\''\n", outline
|
||||
}
|
||||
}
|
||||
' ="$sorted_table"
|
||||
`
|
||||
time=$select_result
|
||||
continent_re='^'
|
||||
zone_table=`
|
||||
$AWK '
|
||||
BEGIN {
|
||||
time = substr(ARGV[1], 2)
|
||||
time_table = substr(ARGV[2], 2)
|
||||
ARGV[1] = ARGV[2] = ""
|
||||
nlines = split(time_table, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($6 " " $7 " " $4 " " $5 == time) {
|
||||
sub(/[^\t]*\t/, "")
|
||||
print
|
||||
}
|
||||
}
|
||||
}
|
||||
' ="$time" ="$time_table"
|
||||
`
|
||||
countries=`
|
||||
$AWK \
|
||||
"$output_country_list" \
|
||||
="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" |
|
||||
sort -f
|
||||
`
|
||||
;;
|
||||
*)
|
||||
# Get list of names of countries in the continent or ocean.
|
||||
countries=`$AWK \
|
||||
-v continent="$continent" \
|
||||
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
|
||||
'
|
||||
BEGIN { FS = "\t" }
|
||||
/^#/ { next }
|
||||
$3 ~ ("^" continent "/") {
|
||||
ncc = split($1, cc, /,/)
|
||||
for (i = 1; i <= ncc; i++)
|
||||
if (!cc_seen[cc[i]]++) cc_list[++ccs] = cc[i]
|
||||
}
|
||||
END {
|
||||
while (getline <TZ_COUNTRY_TABLE) {
|
||||
if ($0 !~ /^#/) cc_name[$1] = $2
|
||||
}
|
||||
for (i = 1; i <= ccs; i++) {
|
||||
country = cc_list[i]
|
||||
if (cc_name[country]) {
|
||||
country = cc_name[country]
|
||||
}
|
||||
print country
|
||||
}
|
||||
}
|
||||
' <"$TZ_ZONE_TABLE" | sort -f`
|
||||
continent_re="^$continent/"
|
||||
zone_table=$TZ_ZONE_TABLE
|
||||
esac
|
||||
|
||||
# Get list of names of countries in the continent or ocean.
|
||||
countries=`
|
||||
$AWK \
|
||||
"$output_country_list" \
|
||||
="$continent_re" ="$TZ_COUNTRY_TABLE" ="$zone_table" |
|
||||
sort -f
|
||||
`
|
||||
# If all zone table entries have comments, and there are
|
||||
# at most 22 entries, asked based on those comments.
|
||||
# This fits the prompt onto old-fashioned 24-line screens.
|
||||
regions=`
|
||||
$AWK '
|
||||
BEGIN {
|
||||
TZ_ZONE_TABLE = substr(ARGV[1], 2)
|
||||
ARGV[1] = ""
|
||||
FS = "\t"
|
||||
nlines = split(TZ_ZONE_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 ~ /^[^#]/ && !missing_comment) {
|
||||
if ($4)
|
||||
comment[++inlines] = $4
|
||||
else
|
||||
missing_comment = 1
|
||||
}
|
||||
}
|
||||
if (!missing_comment && inlines <= 22)
|
||||
for (i = 1; i <= inlines; i++)
|
||||
print comment[i]
|
||||
}
|
||||
' ="$zone_table"
|
||||
`
|
||||
|
||||
# If there's more than one country, ask the user which one.
|
||||
case $countries in
|
||||
@ -431,6 +661,7 @@ while
|
||||
echo >&2 'Please select a country' \
|
||||
'whose clocks agree with yours.'
|
||||
doselect $countries
|
||||
country_result=$select_result
|
||||
country=$select_result;;
|
||||
*)
|
||||
country=$countries
|
||||
@ -438,58 +669,77 @@ while
|
||||
|
||||
|
||||
# Get list of timezones in the country.
|
||||
regions=`$AWK \
|
||||
-v country="$country" \
|
||||
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
|
||||
'
|
||||
regions=`
|
||||
$AWK '
|
||||
BEGIN {
|
||||
country = substr(ARGV[1], 2)
|
||||
TZ_COUNTRY_TABLE = substr(ARGV[2], 2)
|
||||
TZ_ZONE_TABLE = substr(ARGV[3], 2)
|
||||
ARGV[1] = ARGV[2] = ARGV[3] = ""
|
||||
FS = "\t"
|
||||
cc = country
|
||||
while (getline <TZ_COUNTRY_TABLE) {
|
||||
nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 !~ /^#/ && country == $2) {
|
||||
cc = $1
|
||||
break
|
||||
}
|
||||
}
|
||||
nlines = split(TZ_ZONE_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 ~ /^#/)
|
||||
continue
|
||||
if ($1 ~ cc)
|
||||
print $4
|
||||
}
|
||||
/^#/ { next }
|
||||
$1 ~ cc { print $4 }
|
||||
' <"$TZ_ZONE_TABLE"`
|
||||
|
||||
}
|
||||
' ="$country" ="$TZ_COUNTRY_TABLE" ="$zone_table"
|
||||
`
|
||||
|
||||
# If there's more than one region, ask the user which one.
|
||||
case $regions in
|
||||
*"$newline"*)
|
||||
echo >&2 'Please select one of the following timezones.'
|
||||
doselect $regions
|
||||
region=$select_result;;
|
||||
*)
|
||||
region=$regions
|
||||
region=$select_result
|
||||
esac
|
||||
|
||||
# Determine TZ from country and region.
|
||||
TZ=`$AWK \
|
||||
-v country="$country" \
|
||||
-v region="$region" \
|
||||
-v TZ_COUNTRY_TABLE="$TZ_COUNTRY_TABLE" \
|
||||
'
|
||||
# Determine tz from country and region.
|
||||
tz=`
|
||||
$AWK '
|
||||
BEGIN {
|
||||
country = substr(ARGV[1], 2)
|
||||
region = substr(ARGV[2], 2)
|
||||
TZ_COUNTRY_TABLE = substr(ARGV[3], 2)
|
||||
TZ_ZONE_TABLE = substr(ARGV[4], 2)
|
||||
ARGV[1] = ARGV[2] = ARGV[3] = ARGV[4] = ""
|
||||
FS = "\t"
|
||||
cc = country
|
||||
while (getline <TZ_COUNTRY_TABLE) {
|
||||
nlines = split(TZ_COUNTRY_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 !~ /^#/ && country == $2) {
|
||||
cc = $1
|
||||
break
|
||||
}
|
||||
}
|
||||
nlines = split(TZ_ZONE_TABLE, line, /\n/)
|
||||
for (i = 1; i <= nlines; i++) {
|
||||
$0 = line[i]
|
||||
if ($0 ~ /^#/)
|
||||
continue
|
||||
if ($1 ~ cc && ($4 == region || !region))
|
||||
print $3
|
||||
}
|
||||
/^#/ { next }
|
||||
$1 ~ cc && $4 == region { print $3 }
|
||||
' <"$TZ_ZONE_TABLE"`
|
||||
}
|
||||
' ="$country" ="$region" ="$TZ_COUNTRY_TABLE" ="$zone_table"
|
||||
`
|
||||
esac
|
||||
|
||||
# Make sure the corresponding zoneinfo file exists.
|
||||
TZ_for_date=$TZDIR/$TZ
|
||||
TZ_for_date=$TZDIR/$tz
|
||||
<"$TZ_for_date" || {
|
||||
say >&2 "$0: time zone files are not set up correctly"
|
||||
exit 1
|
||||
@ -506,32 +756,39 @@ while
|
||||
do
|
||||
TZdate=`LANG=C TZ="$TZ_for_date" date`
|
||||
UTdate=`LANG=C TZ=UTC0 date`
|
||||
TZsec=`expr "$TZdate" : '.*:\([0-5][0-9]\)'`
|
||||
UTsec=`expr "$UTdate" : '.*:\([0-5][0-9]\)'`
|
||||
case $TZsec in
|
||||
$UTsec)
|
||||
if $AWK '
|
||||
function getsecs(d) {
|
||||
return match(d, /.*:[0-5][0-9]/) ? substr(d, RLENGTH - 1, 2) : ""
|
||||
}
|
||||
BEGIN { exit getsecs(ARGV[1]) != getsecs(ARGV[2]) }
|
||||
' ="$TZdate" ="$UTdate"
|
||||
then
|
||||
extra_info="
|
||||
Selected time is now: $TZdate.
|
||||
Universal Time is now: $UTdate."
|
||||
break
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# Output TZ info and ask the user to confirm.
|
||||
|
||||
echo >&2 ""
|
||||
echo >&2 "The following information has been given:"
|
||||
echo >&2 "Based on the following information:"
|
||||
echo >&2 ""
|
||||
case $country%$region%$coord in
|
||||
?*%?*%) say >&2 " $country$newline $region";;
|
||||
?*%%) say >&2 " $country";;
|
||||
%?*%?*) say >&2 " coord $coord$newline $region";;
|
||||
%%?*) say >&2 " coord $coord";;
|
||||
*) say >&2 " TZ='$TZ'"
|
||||
case $time%$country_result%$region%$coord in
|
||||
?*%?*%?*%)
|
||||
say >&2 " $time$newline $country_result$newline $region";;
|
||||
?*%?*%%|?*%%?*%) say >&2 " $time$newline $country_result$region";;
|
||||
?*%%%) say >&2 " $time";;
|
||||
%?*%?*%) say >&2 " $country_result$newline $region";;
|
||||
%?*%%) say >&2 " $country_result";;
|
||||
%%?*%?*) say >&2 " coord $coord$newline $region";;
|
||||
%%%?*) say >&2 " coord $coord";;
|
||||
*) say >&2 " TZ='$tz'"
|
||||
esac
|
||||
say >&2 ""
|
||||
say >&2 "Therefore TZ='$TZ' will be used.$extra_info"
|
||||
say >&2 "TZ='$tz' will be used.$extra_info"
|
||||
say >&2 "Is the above information OK?"
|
||||
|
||||
doselect Yes No
|
||||
@ -543,8 +800,8 @@ do coord=
|
||||
done
|
||||
|
||||
case $SHELL in
|
||||
*csh) file=.login line="setenv TZ '$TZ'";;
|
||||
*) file=.profile line="TZ='$TZ'; export TZ"
|
||||
*csh) file=.login line="setenv TZ '$tz'";;
|
||||
*) file=.profile line="TZ='$tz'; export TZ"
|
||||
esac
|
||||
|
||||
test -t 1 && say >&2 "
|
||||
@ -555,4 +812,4 @@ to the file '$file' in your home directory; then log out and log in again.
|
||||
Here is that TZ value again, this time on standard output so that you
|
||||
can use the $0 command in shell scripts:"
|
||||
|
||||
say "$TZ"
|
||||
say "$tz"
|
||||
|
@ -1 +1 @@
|
||||
2020a
|
||||
2024a
|
||||
|
417
timezone/zdump.c
417
timezone/zdump.c
@ -15,7 +15,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef HAVE_SNPRINTF
|
||||
# define HAVE_SNPRINTF (199901 <= __STDC_VERSION__)
|
||||
# define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LOCALTIME_R
|
||||
@ -42,10 +42,6 @@
|
||||
# define ZDUMP_HI_YEAR 2500
|
||||
#endif /* !defined ZDUMP_HI_YEAR */
|
||||
|
||||
#ifndef MAX_STRING_LENGTH
|
||||
#define MAX_STRING_LENGTH 1024
|
||||
#endif /* !defined MAX_STRING_LENGTH */
|
||||
|
||||
#define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR)
|
||||
#define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY)
|
||||
#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \
|
||||
@ -88,22 +84,27 @@ static time_t const absolute_max_time =
|
||||
? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift))
|
||||
: -1);
|
||||
static int longest;
|
||||
static char * progname;
|
||||
static char const *progname;
|
||||
static bool warned;
|
||||
static bool errout;
|
||||
|
||||
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 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 showextrema(timezone_t, char *, time_t, struct tm *, time_t);
|
||||
static void showtrans(char const *, struct tm const *, time_t, char const *,
|
||||
char const *);
|
||||
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. */
|
||||
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
|
||||
/* Is C an ASCII digit? */
|
||||
static bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
/* Is A an alphabetic character in the C locale? */
|
||||
static bool
|
||||
@ -124,26 +125,49 @@ is_alpha(char a)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return A + B, exiting if the result would overflow. */
|
||||
static size_t
|
||||
sumsize(size_t a, size_t b)
|
||||
ATTRIBUTE_NORETURN static void
|
||||
size_overflow(void)
|
||||
{
|
||||
size_t sum = a + b;
|
||||
if (sum < a) {
|
||||
fprintf(stderr, "%s: size overflow\n", progname);
|
||||
fprintf(stderr, _("%s: size overflow\n"), progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Return A + B, exiting if the result would overflow either ptrdiff_t
|
||||
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
|
||||
on failure. SIZE should be nonzero. */
|
||||
static void * ATTRIBUTE_MALLOC
|
||||
xmalloc(size_t size)
|
||||
on failure. SIZE should be positive. */
|
||||
ATTRIBUTE_MALLOC static void *
|
||||
xmalloc(ptrdiff_t size)
|
||||
{
|
||||
void *p = malloc(size);
|
||||
if (!p) {
|
||||
perror(progname);
|
||||
fprintf(stderr, _("%s: Memory exhausted\n"), progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return p;
|
||||
@ -204,7 +228,7 @@ localtime_r(time_t *tp, struct tm *tmp)
|
||||
# undef localtime_rz
|
||||
# define localtime_rz zdump_localtime_rz
|
||||
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);
|
||||
}
|
||||
@ -227,33 +251,65 @@ mktime_z(timezone_t tz, struct tm *tmp)
|
||||
static timezone_t
|
||||
tzalloc(char const *val)
|
||||
{
|
||||
static char **fakeenv;
|
||||
char **env = fakeenv;
|
||||
char *env0;
|
||||
if (! env) {
|
||||
char **e = environ;
|
||||
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;
|
||||
# if HAVE_SETENV
|
||||
if (setenv("TZ", val, 1) != 0) {
|
||||
char const *e = strerror(errno);
|
||||
fprintf(stderr, _("%s: setenv: %s\n"), progname, e);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
env0 = xmalloc(sumsize(sizeof "TZ=", strlen(val)));
|
||||
env[0] = strcat(strcpy(env0, "TZ="), val);
|
||||
environ = fakeenv = env;
|
||||
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
|
||||
tzfree(timezone_t env)
|
||||
tzfree(ATTRIBUTE_MAYBE_UNUSED timezone_t initial_environ)
|
||||
{
|
||||
environ = env + 1;
|
||||
free(env[0]);
|
||||
# if !HAVE_SETENV
|
||||
environ = initial_environ;
|
||||
tzset();
|
||||
# endif
|
||||
}
|
||||
#endif /* ! USE_LOCALTIME_RZ */
|
||||
|
||||
@ -263,14 +319,28 @@ static void
|
||||
gmtzinit(void)
|
||||
{
|
||||
if (USE_LOCALTIME_RZ) {
|
||||
static char const utc[] = "UTC0";
|
||||
gmtz = tzalloc(utc);
|
||||
/* Try "GMT" first to find out whether this is one of the rare
|
||||
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) {
|
||||
perror(utc);
|
||||
static char const gmt0[] = "GMT0";
|
||||
gmtz = tzalloc(gmt0);
|
||||
if (!gmtz) {
|
||||
char const *e = strerror(errno);
|
||||
fprintf(stderr, _("%s: unknown timezone '%s': %s\n"),
|
||||
progname, gmt0, e);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert *TP to UT, storing the broken-down time into *TMP.
|
||||
Return TMP if successful, NULL otherwise. This is like gmtime_r(TP, TMP),
|
||||
@ -345,23 +415,23 @@ abbrok(const char *const abbrp, const char *const zone)
|
||||
|
||||
/* Return a time zone abbreviation. If the abbreviation needs to be
|
||||
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.
|
||||
Exit on memory allocation failure. */
|
||||
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);
|
||||
if (HAVE_LOCALTIME_RZ)
|
||||
return ab;
|
||||
else {
|
||||
size_t ablen = strlen(ab);
|
||||
if (*bufalloc <= ablen) {
|
||||
ptrdiff_t absize = xstrsize(ab);
|
||||
if (*bufalloc < absize) {
|
||||
free(*buf);
|
||||
|
||||
/* Make the new buffer at least twice as long as the old,
|
||||
to avoid O(N**2) behavior on repeated calls. */
|
||||
*bufalloc = sumsize(*bufalloc, ablen + 1);
|
||||
*bufalloc = sumsize(*bufalloc, absize);
|
||||
|
||||
*buf = xmalloc(*bufalloc);
|
||||
}
|
||||
@ -406,7 +476,7 @@ main(int argc, char *argv[])
|
||||
{
|
||||
/* These are static so that they're initially zero. */
|
||||
static char * abbrev;
|
||||
static size_t abbrevsize;
|
||||
static ptrdiff_t abbrevsize;
|
||||
|
||||
register int i;
|
||||
register bool vflag;
|
||||
@ -427,7 +497,7 @@ main(int argc, char *argv[])
|
||||
# endif /* defined TEXTDOMAINDIR */
|
||||
textdomain(TZ_DOMAIN);
|
||||
#endif /* HAVE_GETTEXT */
|
||||
progname = argv[0];
|
||||
progname = argv[0] ? argv[0] : "zdump";
|
||||
for (i = 1; i < argc; ++i)
|
||||
if (strcmp(argv[i], "--version") == 0) {
|
||||
printf("zdump %s%s\n", PKGVERSION, TZVERSION);
|
||||
@ -447,7 +517,7 @@ main(int argc, char *argv[])
|
||||
case -1:
|
||||
if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0))
|
||||
goto arg_processing_done;
|
||||
/* Fall through. */
|
||||
ATTRIBUTE_FALLTHROUGH;
|
||||
default:
|
||||
usage(stderr, EXIT_FAILURE);
|
||||
}
|
||||
@ -484,8 +554,8 @@ main(int argc, char *argv[])
|
||||
if (cuttimes != loend && !*loend) {
|
||||
hi = lo;
|
||||
if (hi < cuthitime) {
|
||||
if (hi < absolute_min_time)
|
||||
hi = absolute_min_time;
|
||||
if (hi < absolute_min_time + 1)
|
||||
hi = absolute_min_time + 1;
|
||||
cuthitime = hi;
|
||||
}
|
||||
} else if (cuttimes != loend && *loend == ','
|
||||
@ -497,8 +567,8 @@ main(int argc, char *argv[])
|
||||
cutlotime = lo;
|
||||
}
|
||||
if (hi < cuthitime) {
|
||||
if (hi < absolute_min_time)
|
||||
hi = absolute_min_time;
|
||||
if (hi < absolute_min_time + 1)
|
||||
hi = absolute_min_time + 1;
|
||||
cuthitime = hi;
|
||||
}
|
||||
} else {
|
||||
@ -510,14 +580,17 @@ main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
gmtzinit();
|
||||
INITIALIZE (now);
|
||||
if (! (iflag | vflag | Vflag))
|
||||
if (iflag | vflag | Vflag)
|
||||
now = 0;
|
||||
else {
|
||||
now = time(NULL);
|
||||
now |= !now;
|
||||
}
|
||||
longest = 0;
|
||||
for (i = optind; i < argc; i++) {
|
||||
size_t arglen = strlen(argv[i]);
|
||||
if (longest < arglen)
|
||||
longest = arglen < INT_MAX ? arglen : INT_MAX;
|
||||
longest = min(arglen, INT_MAX);
|
||||
}
|
||||
|
||||
for (i = optind; i < argc; ++i) {
|
||||
@ -527,10 +600,12 @@ main(int argc, char *argv[])
|
||||
struct tm tm, newtm;
|
||||
bool tm_ok;
|
||||
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;
|
||||
}
|
||||
if (! (iflag | vflag | Vflag)) {
|
||||
if (now) {
|
||||
show(tz, argv[i], now, false);
|
||||
tzfree(tz);
|
||||
continue;
|
||||
@ -539,12 +614,15 @@ main(int argc, char *argv[])
|
||||
t = absolute_min_time;
|
||||
if (! (iflag | Vflag)) {
|
||||
show(tz, argv[i], t, true);
|
||||
t += SECSPERDAY;
|
||||
show(tz, argv[i], t, true);
|
||||
if (my_localtime_rz(tz, &t, &tm) == NULL
|
||||
&& t < cutlotime) {
|
||||
time_t newt = cutlotime;
|
||||
if (my_localtime_rz(tz, &newt, &newtm) != NULL)
|
||||
showextrema(tz, argv[i], t, NULL, newt);
|
||||
}
|
||||
if (t < cutlotime)
|
||||
t = cutlotime;
|
||||
INITIALIZE (ab);
|
||||
}
|
||||
if (t + 1 < cutlotime)
|
||||
t = cutlotime - 1;
|
||||
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
|
||||
if (tm_ok) {
|
||||
ab = saveabbr(&abbrev, &abbrevsize, &tm);
|
||||
@ -552,19 +630,20 @@ main(int argc, char *argv[])
|
||||
showtrans("\nTZ=%f", &tm, t, ab, argv[i]);
|
||||
showtrans("-\t-\t%Q", &tm, t, ab, argv[i]);
|
||||
}
|
||||
}
|
||||
while (t < cuthitime) {
|
||||
} else
|
||||
ab = NULL;
|
||||
while (t < cuthitime - 1) {
|
||||
time_t newt = ((t < absolute_max_time - SECSPERDAY / 2
|
||||
&& t + SECSPERDAY / 2 < cuthitime)
|
||||
&& t + SECSPERDAY / 2 < cuthitime - 1)
|
||||
? t + SECSPERDAY / 2
|
||||
: cuthitime);
|
||||
: cuthitime - 1);
|
||||
struct tm *newtmp = localtime_rz(tz, &newt, &newtm);
|
||||
bool newtm_ok = newtmp != NULL;
|
||||
if (tm_ok != newtm_ok
|
||||
|| (tm_ok && (delta(&newtm, &tm) != newt - t
|
||||
|| (ab && (delta(&newtm, &tm) != newt - t
|
||||
|| newtm.tm_isdst != tm.tm_isdst
|
||||
|| strcmp(abbr(&newtm), ab) != 0))) {
|
||||
newt = hunt(tz, argv[i], t, newt);
|
||||
newt = hunt(tz, t, newt, false);
|
||||
newtmp = localtime_rz(tz, &newt, &newtm);
|
||||
newtm_ok = newtmp != NULL;
|
||||
if (iflag)
|
||||
@ -583,11 +662,15 @@ main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
if (! (iflag | Vflag)) {
|
||||
t = absolute_max_time;
|
||||
t -= SECSPERDAY;
|
||||
show(tz, argv[i], t, true);
|
||||
t += SECSPERDAY;
|
||||
show(tz, argv[i], t, true);
|
||||
time_t newt = absolute_max_time;
|
||||
t = cuthitime;
|
||||
if (t < newt) {
|
||||
struct tm *tmp = my_localtime_rz(tz, &t, &tm);
|
||||
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);
|
||||
}
|
||||
@ -640,36 +723,42 @@ yeartot(intmax_t y)
|
||||
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
|
||||
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 size_t loabsize;
|
||||
char const * ab;
|
||||
time_t t;
|
||||
static ptrdiff_t loabsize;
|
||||
struct tm lotm;
|
||||
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 tm_ok;
|
||||
char const *ab = lotm_ok ? saveabbr(&loab, &loabsize, &lotm) : NULL;
|
||||
|
||||
if (lotm_ok)
|
||||
ab = saveabbr(&loab, &loabsize, &lotm);
|
||||
for ( ; ; ) {
|
||||
time_t diff = hit - lot;
|
||||
if (diff < 2)
|
||||
/* T = average of LOT and HIT, rounding down.
|
||||
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;
|
||||
t = lot;
|
||||
t += diff / 2;
|
||||
if (t <= lot)
|
||||
++t;
|
||||
else if (t >= hit)
|
||||
--t;
|
||||
tm_ok = my_localtime_rz(tz, &t, &tm) != NULL;
|
||||
if (lotm_ok & tm_ok
|
||||
? (delta(&tm, &lotm) == t - lot
|
||||
&& tm.tm_isdst == lotm.tm_isdst
|
||||
&& strcmp(abbr(&tm), ab) == 0)
|
||||
: lotm_ok == tm_ok) {
|
||||
if (lotm_ok == tm_ok
|
||||
&& (only_ok
|
||||
|| (ab && tm.tm_isdst == lotm.tm_isdst
|
||||
&& delta(&tm, &lotm) == t - lot
|
||||
&& strcmp(abbr(&tm), ab) == 0))) {
|
||||
lot = t;
|
||||
if (tm_ok)
|
||||
lotm = tm;
|
||||
@ -685,11 +774,11 @@ hunt(timezone_t tz, char *name, time_t lot, time_t hit)
|
||||
static intmax_t
|
||||
delta_nonneg(struct tm *newp, struct tm *oldp)
|
||||
{
|
||||
register intmax_t result;
|
||||
register int tmy;
|
||||
|
||||
result = 0;
|
||||
for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
|
||||
intmax_t oldy = oldp->tm_year;
|
||||
int cycles = (newp->tm_year - oldy) / YEARSPERREPEAT;
|
||||
intmax_t sec = SECSPERREPEAT, result = cycles * sec;
|
||||
int tmy = oldp->tm_year + cycles * YEARSPERREPEAT;
|
||||
for ( ; tmy < newp->tm_year; ++tmy)
|
||||
result += DAYSPERNYEAR + isleap_sum(tmy, TM_YEAR_BASE);
|
||||
result += newp->tm_yday - oldp->tm_yday;
|
||||
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
|
||||
possibly nonnull result of an earlier call to my_gmtime_r. */
|
||||
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
|
||||
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);
|
||||
if (gmtmp == NULL) {
|
||||
printf(tformat(), t);
|
||||
printf(_(" (gmtime failed)"));
|
||||
} else {
|
||||
dumptime(gmtmp);
|
||||
printf(" UT");
|
||||
@ -771,8 +862,11 @@ show(timezone_t tz, char *zone, time_t t, bool v)
|
||||
printf(" = ");
|
||||
}
|
||||
tmp = my_localtime_rz(tz, &t, &tm);
|
||||
if (tmp == NULL) {
|
||||
printf(tformat(), t);
|
||||
printf(_(" (localtime failed)"));
|
||||
} else {
|
||||
dumptime(tmp);
|
||||
if (tmp != NULL) {
|
||||
if (*abbr(tmp) != '\0')
|
||||
printf(" %s", abbr(tmp));
|
||||
if (v) {
|
||||
@ -787,13 +881,58 @@ show(timezone_t tz, char *zone, time_t t, bool v)
|
||||
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
|
||||
# define my_snprintf snprintf
|
||||
#else
|
||||
# include <stdarg.h>
|
||||
|
||||
/* 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, ...)
|
||||
{
|
||||
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; do not overrun the output buffer. */
|
||||
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;
|
||||
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
|
||||
overrun the output buffer. */
|
||||
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);
|
||||
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
|
||||
length; the representation is not null terminated. Otherwise
|
||||
return SIZE, to indicate that BUF is too small. */
|
||||
static size_t
|
||||
format_quoted_string(char *buf, size_t size, char const *p)
|
||||
static ptrdiff_t
|
||||
format_quoted_string(char *buf, ptrdiff_t size, char const *p)
|
||||
{
|
||||
char *b = buf;
|
||||
size_t s = size;
|
||||
ptrdiff_t s = size;
|
||||
if (!s)
|
||||
return size;
|
||||
*b++ = '"', s--;
|
||||
@ -925,11 +1064,11 @@ format_quoted_string(char *buf, size_t size, char const *p)
|
||||
and omit any trailing tabs. */
|
||||
|
||||
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)
|
||||
{
|
||||
char *b = buf;
|
||||
size_t s = size;
|
||||
ptrdiff_t s = size;
|
||||
char const *f = time_fmt, *p;
|
||||
|
||||
for (p = f; ; p++)
|
||||
@ -938,9 +1077,9 @@ istrftime(char *buf, size_t size, char const *time_fmt,
|
||||
else if (!*p
|
||||
|| (*p == '%'
|
||||
&& (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) {
|
||||
size_t formatted_len;
|
||||
size_t f_prefix_len = p - f;
|
||||
size_t f_prefix_copy_size = p - f + 2;
|
||||
ptrdiff_t formatted_len;
|
||||
ptrdiff_t f_prefix_len = p - f;
|
||||
ptrdiff_t f_prefix_copy_size = sumsize(f_prefix_len, 2);
|
||||
char fbuf[100];
|
||||
bool oversized = sizeof fbuf <= f_prefix_copy_size;
|
||||
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;
|
||||
if (show_abbr) {
|
||||
char const *abp;
|
||||
size_t len;
|
||||
ptrdiff_t len;
|
||||
if (s <= 1)
|
||||
return false;
|
||||
*b++ = '\t', s--;
|
||||
@ -1011,7 +1150,7 @@ showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab,
|
||||
putchar('\n');
|
||||
} else {
|
||||
char stackbuf[1000];
|
||||
size_t size = sizeof stackbuf;
|
||||
ptrdiff_t size = sizeof stackbuf;
|
||||
char *buf = stackbuf;
|
||||
char *bufalloc = NULL;
|
||||
while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) {
|
||||
@ -1040,12 +1179,29 @@ abbr(struct tm const *tmp)
|
||||
|
||||
/*
|
||||
** 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 *
|
||||
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 (sizeof(time_t) == sizeof(intmax_t))
|
||||
return "%"PRIdMAX;
|
||||
@ -1076,33 +1232,24 @@ dumptime(register const struct tm *timeptr)
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
register const char * wn;
|
||||
register const char * mn;
|
||||
register int lead;
|
||||
register int trail;
|
||||
int DIVISOR = 10;
|
||||
|
||||
if (timeptr == NULL) {
|
||||
printf("NULL");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
** 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
|
||||
** 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 ",
|
||||
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_min, timeptr->tm_sec);
|
||||
#define DIVISOR 10
|
||||
trail = timeptr->tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
|
||||
lead = timeptr->tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
|
||||
trail / DIVISOR;
|
||||
|
2122
timezone/zic.c
2122
timezone/zic.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user