[tz] [PROPOSED] Clean up configuration of tzname etc.
Paul Eggert
eggert at cs.ucla.edu
Fri Jun 9 00:50:08 UTC 2017
This documents a bit of software archaeology better.
tzname is standardized by POSIX, whereas timezone and daylight are
standardized by POSIX SI, so allow them to be configured separately.
* Makefile, NEWS, Theory: Document this.
* date.c, strftime.c, zdump.c (tzname): Remove decl;
private.h declares tzname as needed now.
* localtime.c (tzname, timezone, daylight, altzone): Also
define if TZ_TIME_T. However, define only if the relevant
other macro (HAVE_TZNAME, USG_COMPAT, ALTZONE) is set or defined.
(update_tzname_etc, settzname): Set tzname only if HAVE_TZNAME.
At the end, simplify ifdef by using TZ_TIME_T.
* newctime.3: Document optional vars and fields more carefully.
* private.h (USG_COMPAT): Set default from _XOPEN_VERSION.
All uses changed to assume it is 1 or 0.
(HAVE_TZNAME): Set default from that and from _POSIX_VERSION.
(TZ_TIME_T): New macro.
(daylight, timezone, tzname, altzone): Rename if needed to avoid
collision with standard library.
(tzname): Declare if (!HAVE_POSIX_DECLS || TZ_TIME_T) && HAVE_TZNAME.
(timezone, daylight): Declare if (!HAVE_POSIX_DECLS || TZ_TIME_T)
&& USG_COMPAT, not if not defined and !HAVE_POSIX_DECLS.
(altzone): Declare even if not defined.
(tzsetwall, offtime, timegm, timelocal, timeoff, time2posix)
(posix2time, posix2time_z, time2posix_z):
Use TZ_TIME_T instead of (defined time_tz).
* strftime.c (_fmt):
* zdump.c (abbr): Use tzname only if HAVE_TZNAME.
---
Makefile | 37 +++++++++++++++++++++++----------
NEWS | 8 +++++++
Theory | 6 +++---
date.c | 1 -
localtime.c | 34 ++++++++++++++++--------------
newctime.3 | 46 ++++++++++++++++++++++++++---------------
private.h | 69 ++++++++++++++++++++++++++++++++++++++++++++++---------------
strftime.c | 38 ++++++++++++++++------------------
zdump.c | 9 ++++----
9 files changed, 160 insertions(+), 88 deletions(-)
diff --git a/Makefile b/Makefile
index 476f259..e6d497d 100644
--- a/Makefile
+++ b/Makefile
@@ -164,6 +164,7 @@ LDLIBS=
# not needed by the main-program tz code, which is single-threaded.
# Append other compiler flags as needed, e.g., -pthread on GNU/Linux.
# -Dtime_tz=\"T\" to use T as the time_t type, rather than the system time_t
+# This is intended for internal use only; it mangles external names.
# -DTZ_DOMAIN=\"foo\" to use "foo" for gettext domain name; default is "tz"
# -DTZ_DOMAINDIR=\"/path\" to use "/path" for gettext directory;
# the default is system-supplied, typically "/usr/lib/locale"
@@ -199,12 +200,6 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
-Wno-address -Wno-format-nonliteral -Wno-sign-compare \
-Wno-type-limits -Wno-unused-parameter
#
-# If you want to use System V compatibility code, add
-# -DUSG_COMPAT
-# to the end of the "CFLAGS=" line. This arrange for "timezone" and "daylight"
-# variables to be kept up-to-date by the time conversion functions. Neither
-# "timezone" nor "daylight" is described in X3J11's work.
-#
# If your system has a "GMT offset" field in its "struct tm"s
# (or if you decide to add such a field in your system's "time.h" file),
# add the name to a define such as
@@ -216,6 +211,31 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
# and define NO_TM_ZONE to suppress any guessing. These two fields are not
# required by POSIX, but are widely available on GNU/Linux and BSD systems.
#
+# The next batch of options control support for external variables
+# exported by tzcode. In practice these variables are less useful
+# than TM_GMTOFF and TM_ZONE. However, most of them are standardized.
+# #
+# # To omit or support the external variable "tzname", add one of:
+# # -DHAVE_TZNAME=0
+# # -DHAVE_TZNAME=1
+# # to the "CFLAGS=" line. "tzname" is required by POSIX 1988 and later.
+# # If not defined, the code attempts to guess HAVE_TZNAME from other macros.
+# # Warning: unless time_tz is also defined, HAVE_TZNAME=1 can cause
+# # crashes when combined with some platforms' standard libraries,
+# # presumably due to memory allocation issues.
+# #
+# # To omit or support the external variables "timezone" and "daylight", add
+# # -DUSG_COMPAT=0
+# # -DUSG_COMPAT=1
+# # to the "CFLAGS=" line; "timezone" and "daylight" are inspired by
+# # Unix Systems Group code and are required by POSIX 2008 (with XSI) and later.
+# # If not defined, the code attempts to guess USG_COMPAT from other macros.
+# #
+# # To support the external variable "altzone", add
+# # -DALTZONE
+# # to the end of the "CFLAGS=" line; although "altzone" appeared in
+# # System V Release 3.1 it has not been standardized.
+#
# If you want functions that were inspired by early versions of X3J11's work,
# add
# -DSTD_INSPIRED
@@ -253,11 +273,6 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \
# -DALL_STATE
# to the end of the "CFLAGS=" line. Storage is obtained by calling malloc.
#
-# If you want an "altzone" variable (a la System V Release 3.1), add
-# -DALTZONE
-# to the end of the "CFLAGS=" line.
-# This variable is not described in X3J11's work.
-#
# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
# out by the National Institute of Standards and Technology
# which claims to test C and Posix conformance. If you want to pass PCTS, add
diff --git a/NEWS b/NEWS
index 99f5d64..44135ae 100644
--- a/NEWS
+++ b/NEWS
@@ -57,6 +57,14 @@ Unreleased, experimental changes
ordinary lines in leap second input. Formerly, zic sometimes
warned about this undocumented usage and handled it incorrectly.
+ The new macro HAVE_TZNAME governs whether the tzname external
+ variable is exported, instead of USG_COMPAT. USG_COMPAT now
+ governs only the external variables "timezone" and "daylight".
+ This change is needed because the three variables are not in the
+ same category: although POSIX requires tzname, it specifies the
+ other two variables as optional. Also, USG_COMPAT is now 1 or 0:
+ if not defined, the code attempts to guess it from other macros.
+
Several minor changes have been made to the code to make it a
bit easier to port to MS-Windows. (Thanks to Kees Dekker for
reporting the problems.)
diff --git a/Theory b/Theory
index 2bfadc6..8aeccb2 100644
--- a/Theory
+++ b/Theory
@@ -581,9 +581,9 @@ Points of interest to folks with other systems:
of GMT" value and a "daylight saving time in effect" flag) to a
time zone abbreviation, and we refuse to guess.
Programs that in the past used the timezone function may now examine
- tzname[localtime(&clock)->tm_isdst] to learn the correct time
- zone abbreviation to use. Alternatively, use
- localtime(&clock)->tm_zone if this has been enabled.
+ localtime(&clock)->tm_zone (if TM_ZONE is defined) or
+ tzname[localtime(&clock)->tm_isdst] (if HAVE_TZNAME is defined)
+ to learn the correct time zone abbreviation to use.
* The 4.2BSD gettimeofday function is not used in this package.
This formerly let users obtain the current UTC offset and DST flag,
diff --git a/date.c b/date.c
index 269825d..0d64b8a 100644
--- a/date.c
+++ b/date.c
@@ -45,7 +45,6 @@
#if !HAVE_POSIX_DECLS
extern char * optarg;
extern int optind;
-extern char * tzname[];
#endif
static int retval = EXIT_SUCCESS;
diff --git a/localtime.c b/localtime.c
index 8f103aa..b23fe43 100644
--- a/localtime.c
+++ b/localtime.c
@@ -188,20 +188,21 @@ static int lcl_is_set;
static struct tm tm;
-#if !HAVE_POSIX_DECLS
+#if !HAVE_POSIX_DECLS || TZ_TIME_T
+# if HAVE_TZNAME
char * tzname[2] = {
(char *) wildabbr,
(char *) wildabbr
};
-# ifdef USG_COMPAT
+# endif
+# if USG_COMPAT
long timezone;
int daylight;
# endif
-#endif
-
-#ifdef ALTZONE
+# ifdef ALTZONE
long altzone;
-#endif /* defined ALTZONE */
+# endif
+#endif
/* Initialize *S to a value based on GMTOFF, ISDST, and ABBRIND. */
static void
@@ -263,8 +264,10 @@ detzcode64(const char *const codep)
static void
update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp)
{
+#if HAVE_TZNAME
tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
-#ifdef USG_COMPAT
+#endif
+#if USG_COMPAT
if (!ttisp->tt_isdst)
timezone = - ttisp->tt_gmtoff;
#endif
@@ -280,16 +283,17 @@ settzname(void)
register struct state * const sp = lclptr;
register int i;
- tzname[0] = tzname[1] = (char *) wildabbr;
-#ifdef USG_COMPAT
+#if HAVE_TZNAME
+ tzname[0] = tzname[1] = (char *) (sp ? wildabbr : gmt);
+#endif
+#if USG_COMPAT
daylight = 0;
timezone = 0;
-#endif /* defined USG_COMPAT */
+#endif
#ifdef ALTZONE
altzone = 0;
#endif /* defined ALTZONE */
if (sp == NULL) {
- tzname[0] = tzname[1] = (char *) gmt;
return;
}
/*
@@ -304,10 +308,10 @@ settzname(void)
&sp->ttis[
sp->types[i]];
update_tzname_etc(sp, ttisp);
-#ifdef USG_COMPAT
+#if USG_COMPAT
if (ttisp->tt_isdst)
daylight = 1;
-#endif /* defined USG_COMPAT */
+#endif
}
}
@@ -2286,9 +2290,9 @@ posix2time(time_t t)
#endif /* defined STD_INSPIRED */
-#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
+#if TZ_TIME_T
-# ifndef USG_COMPAT
+# if !USG_COMPAT
# define daylight 0
# define timezone 0
# endif
diff --git a/newctime.3 b/newctime.3
index 169c919..8b89935 100644
--- a/newctime.3
+++ b/newctime.3
@@ -7,7 +7,7 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
.el ds - \-
.B #include <time.h>
.PP
-.B extern char *tzname[2];
+.BR "extern char *tzname[];" " /\(** (optional) \(**/"
.PP
.B char *ctime(time_t const *clock);
.PP
@@ -213,7 +213,7 @@ includes the following fields:
.RS
.PP
.nf
-.ta .5i +\w'long tm_gmtoff;\0\0'u
+.ta 2n +\w'long tm_gmtoff;nn'u
int tm_sec; /\(** seconds (0\*(en60) \(**/
int tm_min; /\(** minutes (0\*(en59) \(**/
int tm_hour; /\(** hours (0\*(en23) \(**/
@@ -223,21 +223,11 @@ includes the following fields:
int tm_wday; /\(** day of week (Sunday = 0) \(**/
int tm_yday; /\(** day of year (0\*(en365) \(**/
int tm_isdst; /\(** is summer time in effect? \(**/
- char \(**tm_zone; /\(** abbreviation of time zone name \(**/
- long tm_gmtoff; /\(** offset from UT in seconds \(**/
+ char \(**tm_zone; /\(** time zone abbreviation (optional) \(**/
+ long tm_gmtoff; /\(** offset from UT in seconds (optional) \(**/
.fi
.RE
.PP
-The
-.I tm_zone
-and
-.I tm_gmtoff
-fields exist, and are filled in, only if arrangements to do
-so were made when the library containing these functions was
-created.
-There is no guarantee that these fields will continue to exist
-in this form in future releases of this code.
-.PP
.I Tm_isdst
is non-zero if summer time is in effect.
.PP
@@ -246,6 +236,21 @@ is the offset (in seconds) of the time represented
from UT, with positive values indicating east
of the Prime Meridian.
The field's name is derived from Greenwich Mean Time, a precursor of UT.
+.PP
+In
+.B struct tm
+the
+.I tm_zone
+and
+.I tm_gmtoff
+fields exist, and are filled in, only if arrangements to do
+so were made when the library containing these functions was
+created.
+Similarly, the
+.B tzname
+variable is optional.
+There is no guarantee that these fields and this variable will
+continue to exist in this form in future releases of this code.
.SH FILES
.ta \w'/usr/local/etc/zoneinfo/posixrules\0\0'u
/usr/local/etc/zoneinfo time zone information directory
@@ -277,12 +282,19 @@ and
point to static data
overwritten by each call.
The
+.B tzname
+variable (once set) and the
.B tm_zone
field of a returned
.B "struct tm"
-points to a static array of characters, which
-can be overwritten by later calls to
-.IR tzset .
+both point to an array of characters that
+can be freed or overwritten by later calls to the functions
+.IR localtime ,
+.IR tzfree ,
+and
+.IR tzset ,
+if these functions affect the time zone information that specifies the
+abbreviation in question.
The remaining functions and data are thread-safe.
.PP
.IR Asctime ,
diff --git a/private.h b/private.h
index 1e61592..ad9fcb9 100644
--- a/private.h
+++ b/private.h
@@ -153,6 +153,22 @@
# endif
#endif
+#ifndef USG_COMPAT
+# ifndef _XOPEN_VERSION
+# define USG_COMPAT 0
+# else
+# define USG_COMPAT 1
+# endif
+#endif
+
+#ifndef HAVE_TZNAME
+# if _POSIX_VERSION < 198808 && !USG_COMPAT
+# define HAVE_TZNAME 0
+# else
+# define HAVE_TZNAME 1
+# endif
+#endif
+
#ifndef F_OK
#define F_OK 0
#endif /* !defined F_OK */
@@ -339,6 +355,12 @@ typedef unsigned long uintmax_t;
** typical platforms.
*/
#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
+# define TZ_TIME_T 1
+#else
+# define TZ_TIME_T 0
+#endif
+
+#if TZ_TIME_T
# ifdef LOCALTIME_IMPLEMENTATION
static time_t sys_time(time_t *x) { return time(x); }
# endif
@@ -393,6 +415,20 @@ typedef time_tz tz_time_t;
# define tzset tz_tzset
# undef tzsetwall
# define tzsetwall tz_tzsetwall
+# if HAVE_TZNAME
+# undef tzname
+# define tzname tz_tzname
+# endif
+# if USG_COMPAT
+# undef daylight
+# define daylight tz_daylight
+# undef timezone
+# define timezone tz_timezone
+# endif
+# ifdef ALTZONE
+# undef altzone
+# define altzone tz_altzone
+# endif
char *ctime(time_t const *);
char *ctime_r(time_t const *, char *);
@@ -422,18 +458,17 @@ extern char *asctime_r(struct tm const *restrict, char *restrict);
extern char **environ;
#endif
-#if !HAVE_POSIX_DECLS
-# ifdef USG_COMPAT
-# ifndef timezone
+#if TZ_TIME_T || !HAVE_POSIX_DECLS
+# if HAVE_TZNAME
+extern char *tzname[];
+# endif
+# if USG_COMPAT
extern long timezone;
-# endif
-# ifndef daylight
extern int daylight;
-# endif
# endif
#endif
-#if defined ALTZONE && !defined altzone
+#ifdef ALTZONE
extern long altzone;
#endif
@@ -443,25 +478,25 @@ extern long altzone;
*/
#ifdef STD_INSPIRED
-# if !defined tzsetwall || defined time_tz
+# if TZ_TIME_T || !defined tzsetwall
void tzsetwall(void);
# endif
-# if !defined offtime || defined time_tz
+# if TZ_TIME_T || !defined offtime
struct tm *offtime(time_t const *, long);
# endif
-# if !defined timegm || defined time_tz
+# if TZ_TIME_T || !defined timegm
time_t timegm(struct tm *);
# endif
-# if !defined timelocal || defined time_tz
+# if TZ_TIME_T || !defined timelocal
time_t timelocal(struct tm *);
# endif
-# if !defined timeoff || defined time_tz
+# if TZ_TIME_T || !defined timeoff
time_t timeoff(struct tm *, long);
# endif
-# if !defined time2posix || defined time_tz
+# if TZ_TIME_T || !defined time2posix
time_t time2posix(time_t);
# endif
-# if !defined posix2time || defined time_tz
+# if TZ_TIME_T || !defined posix2time
time_t posix2time(time_t);
# endif
#endif
@@ -495,10 +530,10 @@ time_t mktime_z(timezone_t restrict, struct tm *restrict);
timezone_t tzalloc(char const *);
void tzfree(timezone_t);
# ifdef STD_INSPIRED
-# if !defined posix2time_z || defined time_tz
+# if TZ_TIME_T || !defined posix2time_z
time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
# endif
-# if !defined time2posix_z || defined time_tz
+# if TZ_TIME_T || !defined time2posix_z
time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
# endif
# endif
@@ -649,7 +684,7 @@ char *ctime_r(time_t const *, char *);
** or
** isleap(a + b) == isleap(a % 400 + b % 400)
** This is true even if % means modulo rather than Fortran remainder
-** (which is allowed by C89 but not C99).
+** (which is allowed by C89 but not by C99 or later).
** We use this to avoid addition overflow problems.
*/
diff --git a/strftime.c b/strftime.c
index 4b9fd2c..d09a0db 100644
--- a/strftime.c
+++ b/strftime.c
@@ -73,7 +73,7 @@ static const struct lc_time_T C_time_locale = {
/*
** x_fmt
- ** C99 requires this format.
+ ** C99 and later require this format.
** Using just numbers (as here) makes Quakers happier;
** it's also compatible with SVR4.
*/
@@ -81,7 +81,7 @@ static const struct lc_time_T C_time_locale = {
/*
** c_fmt
- ** C99 requires this format.
+ ** C99 and later require this format.
** Previously this code used "%D %X", but we now conform to C99.
** Note that
** "%a %b %d %H:%M:%S %Y"
@@ -105,10 +105,6 @@ static char * _fmt(const char *, const struct tm *, char *, const char *,
int *);
static char * _yconv(int, int, bool, bool, char *, char const *);
-#if !HAVE_POSIX_DECLS
-extern char * tzname[];
-#endif
-
#ifndef YEAR_2000_NAME
#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
#endif /* !defined YEAR_2000_NAME */
@@ -223,7 +219,7 @@ label:
case 'E':
case 'O':
/*
- ** C99 locale modifiers.
+ ** Locale modifiers of C99 and later.
** The sequences
** %Ec %EC %Ex %EX %Ey %EY
** %Od %oe %OH %OI %Om %OM
@@ -483,19 +479,19 @@ label:
case 'Z':
#ifdef TM_ZONE
pt = _add(t->TM_ZONE, pt, ptlim);
-#else
+#elif HAVE_TZNAME
if (t->tm_isdst >= 0)
pt = _add(tzname[t->tm_isdst != 0],
pt, ptlim);
#endif
/*
- ** C99 says that %Z must be replaced by the
- ** empty string if the time zone is not
- ** determinable.
+ ** C99 and later say that %Z must be
+ ** replaced by the empty string if the
+ ** time zone is not determinable.
*/
continue;
case 'z':
-#if defined TM_GMTOFF || defined USG_COMPAT || defined ALTZONE
+#if defined TM_GMTOFF || USG_COMPAT || defined ALTZONE
{
long diff;
char const * sign;
@@ -505,7 +501,7 @@ label:
diff = t->TM_GMTOFF;
# else
/*
- ** C99 says that the UT offset must
+ ** C99 and later say that the UT offset must
** be computed by looking only at
** tm_isdst. This requirement is
** incorrect, since it means the code
@@ -513,20 +509,20 @@ label:
** altzone and timezone), and the
** magic might not have the correct
** offset. Doing things correctly is
- ** tricky and requires disobeying C99;
+ ** tricky and requires disobeying the standard;
** see GNU C strftime for details.
** For now, punt and conform to the
** standard, even though it's incorrect.
**
- ** C99 says that %z must be replaced by the
- ** empty string if the time zone is not
+ ** C99 and later that %z must be replaced by
+ ** the empty string if the time zone is not
** determinable, so output nothing if the
** appropriate variables are not available.
*/
if (t->tm_isdst < 0)
continue;
if (t->tm_isdst == 0)
-# ifdef USG_COMPAT
+# if USG_COMPAT
diff = -timezone;
# else
continue;
@@ -543,9 +539,11 @@ label:
#ifdef TM_ZONE
negative = t->TM_ZONE[0] == '-';
#else
- negative
- = (t->tm_isdst < 0
- || tzname[t->tm_isdst != 0][0] == '-');
+ negative = t->tm_isdst < 0;
+# if HAVE_TZNAME
+ if (tzname[t->tm_isdst != 0][0] == '-')
+ negative = true;
+# endif
#endif
}
if (negative) {
diff --git a/zdump.c b/zdump.c
index 7cde3d3..b69b1e9 100644
--- a/zdump.c
+++ b/zdump.c
@@ -77,7 +77,6 @@ extern int getopt(int argc, char * const argv[],
const char * options);
extern char * optarg;
extern int optind;
-extern char * tzname[];
#endif
/* The minimum and maximum finite time values. */
@@ -997,9 +996,11 @@ abbr(struct tm const *tmp)
#ifdef TM_ZONE
return tmp->TM_ZONE;
#else
- return (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst]
- ? tzname[0 < tmp->tm_isdst]
- : "");
+# if HAVE_TZNAME
+ if (0 <= tmp->tm_isdst && tzname[0 < tmp->tm_isdst])
+ return tzname[0 < tmp->tm_isdst];
+# endif
+ return "";
#endif
}
--
2.9.4
More information about the tz
mailing list