[PROPOSED] Clean up configuration of tzname etc.
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
participants (1)
-
Paul Eggert