[tz] [PROPOSED PATCH 8/9] Add NetBSD-inspired functions for timezone_t objects.

Paul Eggert eggert at cs.ucla.edu
Mon Aug 25 06:59:52 UTC 2014


* Makefile, NEWS: Document this.
* localtime.c (NETBSD_INSPIRED_EXTERN): New macro.
(zoneinit): New function, with tzset_unlocked's internals.
(tzset_unlocked): Use it.
(tzalloc, tzfree) [NETBSD_INSPIRED]: New functions.
(localsub, gmtsub, time2sub, time2, time1, leapcorr):
New time zone argument.  All uses changed.
(localsub, gmtsub): Cast to char *, since the time zone argument
is a pointer-to-const.
(localtime_rz): New function, with localtime_tzset's internals.
(localtime_tzset): Use it.
(mktime_z): New function, with mktime's internals.
(mktime): Use it.
(leapcorr): Pass time_t by value, not by reference.
(time2posix_z): New function, with time2posix's internals.
(time2posix): Use it.  Omit unnecessary local.
(posix2time_z): New function, with posix2time's internals.
(posix2time): Use it.
* private.h (NETBSD_INSPIRED): Default to 1.
(localtime_rz, mktime_z, timezone_t, strftime, tzalloc, tzfree):
Define if NETBSD_INSPIRED is defined.
Use macros to avoid any clashes with <time.h>.
(posiztime_z, time2posix_z): Likewise, but only if
STD_INSPIRED is also defined.
---
 Makefile    |  11 ++++
 NEWS        |   8 +++
 localtime.c | 208 +++++++++++++++++++++++++++++++++++++++---------------------
 private.h   |  47 +++++++++++++-
 4 files changed, 201 insertions(+), 73 deletions(-)

diff --git a/Makefile b/Makefile
index a4a74e6..f4fb8cd 100644
--- a/Makefile
+++ b/Makefile
@@ -197,6 +197,17 @@ GCC_DEBUG_FLAGS = -Dlint -g3 -O3 -fno-common -fstrict-aliasing \
 # These functions may well disappear in future releases of the time
 # conversion package.
 #
+# If you don't want functions that were inspired by NetBSD, add
+#	-DNETBSD_INSPIRED=0
+# to the end of the "CFLAGS=" line.  Otherwise, the functions
+# "localtime_rz", "mktime_z", "tzalloc", and "tzfree" are added to the
+# time library, and if STD_INSPIRED is also defined the functions
+# "posix2time_z" and "time2posix_z" are added as well.
+# The functions ending in "_z" (or "_rz") are like their unsuffixed
+# (or suffixed-by-"_r") counterparts, except with an extra first
+# argument of opaque type timezone_t that specifies the time zone.
+# "tzalloc" allocates a timezone_t value, and "tzfree" frees it.
+#
 # If you want to allocate state structures in localtime, add
 #	-DALL_STATE
 # to the end of the "CFLAGS=" line.  Storage is obtained by calling malloc.
diff --git a/NEWS b/NEWS
index 8e4be29..ce49fd2 100644
--- a/NEWS
+++ b/NEWS
@@ -55,6 +55,14 @@ Unreleased, experimental changes
     bit cleaner and faster than plain localtime.  Compile with
     -DHAVE_LOCALTIME_R=0 and/or -DHAVE_TZSET=0 if your system lacks them.
 
+    Unless NETBSD_INSPIRED is defined to 0, the tz library now supplies
+    functions that let callers create and use objects representing time zones.
+    This is intended for applications that need to deal with many time
+    zones simultaneously, e.g., an application where each thread may be
+    in a different time zone.  The new functions are tzalloc, tzfree,
+    localtime_rz, mktime_z, and (if STD_INSPIRED is also defined)
+    posix2time_z and time2posix_z.
+
     The tz code now attempts to infer TM_GMTOFF and TM_ZONE if not
     already defined, to make it easier to configure on common platforms.
     Define NO_TM_GMTOFF and NO_TM_ZONE to suppress this.
diff --git a/localtime.c b/localtime.c
index e60842e..c68b575 100644
--- a/localtime.c
+++ b/localtime.c
@@ -28,6 +28,14 @@ static int lock(void) { return 0; }
 static void unlock(void) { }
 #endif
 
+/* NETBSD_INSPIRED_EXTERN functions are exported to callers if
+   NETBSD_INSPIRED is defined, and are private otherwise.  */
+#if NETBSD_INSPIRED
+# define NETBSD_INSPIRED_EXTERN
+#else
+# define NETBSD_INSPIRED_EXTERN static
+#endif
+
 #ifndef TZ_ABBR_MAX_LEN
 #define TZ_ABBR_MAX_LEN	16
 #endif /* !defined TZ_ABBR_MAX_LEN */
@@ -141,7 +149,8 @@ struct rule {
 #define DAY_OF_YEAR		1	/* n = day of year */
 #define MONTH_NTH_DAY_OF_WEEK	2	/* Mm.n.d = month, week, day of week */
 
-static struct tm *gmtsub(time_t const *, int_fast32_t, struct tm *);
+static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t,
+			 struct tm *);
 static bool increment_overflow(int *, int);
 static bool increment_overflow_time(time_t *, int_fast32_t);
 static bool normalize_overflow32(int_fast32_t *, int *, int);
@@ -1192,6 +1201,28 @@ tzsetwall(void)
 }
 #endif
 
+static struct state *
+zoneinit(struct state *sp, char const *name)
+{
+  if (sp) {
+    if (*name == '\0') {
+      /*
+      ** User wants it fast rather than right.
+      */
+      sp->leapcnt = 0;		/* so, we're off a little */
+      sp->timecnt = 0;
+      sp->typecnt = 0;
+      sp->ttis[0].tt_isdst = 0;
+      sp->ttis[0].tt_gmtoff = 0;
+      sp->ttis[0].tt_abbrind = 0;
+      strcpy(sp->chars, gmt);
+    } else if (! tzload(name, sp, true))
+      if (name[0] == ':' || ! tzparse(name, sp, false))
+	gmtload(sp);
+  }
+  return sp;
+}
+
 static void
 tzset_unlocked(void)
 {
@@ -1211,22 +1242,7 @@ tzset_unlocked(void)
   if (! lclptr)
     lclptr = malloc(sizeof *lclptr);
 #endif /* defined ALL_STATE */
-  if (lclptr) {
-    if (*name == '\0') {
-      /*
-      ** User wants it fast rather than right.
-      */
-      lclptr->leapcnt = 0;		/* so, we're off a little */
-      lclptr->timecnt = 0;
-      lclptr->typecnt = 0;
-      lclptr->ttis[0].tt_isdst = false;
-      lclptr->ttis[0].tt_gmtoff = 0;
-      lclptr->ttis[0].tt_abbrind = 0;
-      strcpy(lclptr->chars, gmt);
-    } else if (! tzload(name, lclptr, true))
-      if (name[0] == ':' || ! tzparse(name, lclptr, false))
-	gmtload(lclptr);
-  }
+  zoneinit(lclptr, name);
   settzname();
   lcl_is_set = shortname;
 }
@@ -1256,6 +1272,33 @@ gmtcheck(void)
   unlock();
 }
 
+#if NETBSD_INSPIRED
+
+timezone_t
+tzalloc(char const *name)
+{
+  timezone_t sp = malloc(sizeof *sp);
+  return zoneinit(sp, name);
+}
+
+void
+tzfree(timezone_t sp)
+{
+  free(sp);
+}
+
+/*
+** NetBSD 6.1.4 has ctime_rz, but omit it because POSIX says ctime and
+** ctime_r are obsolescent and have potential security problems that
+** ctime_rz would share.  Callers can instead use localtime_rz + strftime.
+**
+** NetBSD 6.1.4 has tzgetname, but omit it because it doesn't work
+** in zones with three or more time zone abbreviations.
+** Callers can instead use localtime_rz + strftime.
+*/
+
+#endif
+
 /*
 ** The easy way to behave "as if no library function calls" localtime
 ** is to not call it, so we drop its guts into "localsub", which can be
@@ -1267,18 +1310,16 @@ gmtcheck(void)
 
 /*ARGSUSED*/
 static struct tm *
-localsub(const time_t *const timep, const int_fast32_t offset,
+localsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
 	 struct tm *const tmp)
 {
-	register struct state *		sp;
 	register const struct ttinfo *	ttisp;
 	register int			i;
 	register struct tm *		result;
 	const time_t			t = *timep;
 
-	sp = lclptr;
 	if (sp == NULL)
-		return gmtsub(timep, offset, tmp);
+		return gmtsub(gmtptr, timep, offset, tmp);
 	if ((sp->goback && t < sp->ats[0]) ||
 		(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
 			time_t			newt = t;
@@ -1297,7 +1338,7 @@ localsub(const time_t *const timep, const int_fast32_t offset,
 			if (newt < sp->ats[0] ||
 				newt > sp->ats[sp->timecnt - 1])
 					return NULL;	/* "cannot happen" */
-			result = localsub(&newt, offset, tmp);
+			result = localsub(sp, &newt, offset, tmp);
 			if (result == tmp) {
 				register int_fast64_t newy;
 
@@ -1336,11 +1377,17 @@ localsub(const time_t *const timep, const int_fast32_t offset,
 	result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
 	tmp->tm_isdst = ttisp->tt_isdst;
 #ifdef TM_ZONE
-	tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+	tmp->TM_ZONE = (char *) &sp->chars[ttisp->tt_abbrind];
 #endif /* defined TM_ZONE */
 	return result;
 }
 
+NETBSD_INSPIRED_EXTERN struct tm *
+localtime_rz(struct state *sp, time_t const *timep, struct tm *tmp)
+{
+  return localsub(sp, timep, 0, tmp);
+}
+
 static struct tm *
 localtime_tzset(time_t const *timep, struct tm *tmp, bool skip_tzset)
 {
@@ -1351,7 +1398,7 @@ localtime_tzset(time_t const *timep, struct tm *tmp, bool skip_tzset)
   }
   if (!skip_tzset)
     tzset_unlocked();
-  tmp = localsub(timep, 0, tmp);
+  tmp = localtime_rz(lclptr, timep, tmp);
   unlock();
   return tmp;
 }
@@ -1362,10 +1409,6 @@ localtime(const time_t *const timep)
   return localtime_tzset(timep, &tm, 0);
 }
 
-/*
-** Re-entrant version of localtime.
-*/
-
 struct tm *
 localtime_r(const time_t *const timep, struct tm *tmp)
 {
@@ -1377,8 +1420,8 @@ localtime_r(const time_t *const timep, struct tm *tmp)
 */
 
 static struct tm *
-gmtsub(const time_t *const timep, const int_fast32_t offset,
-       struct tm *const tmp)
+gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset,
+       struct tm *tmp)
 {
 	register struct tm *	result;
 
@@ -1389,7 +1432,8 @@ gmtsub(const time_t *const timep, const int_fast32_t offset,
 	** "UT+xxxx" or "UT-xxxx" if offset is non-zero,
 	** but this is no time for a treasure hunt.
 	*/
-	tmp->TM_ZONE = offset ? wildabbr : gmtptr ? gmtptr->chars : gmt;
+	tmp->TM_ZONE = ((char *)
+			(offset ? wildabbr : gmtptr ? gmtptr->chars : gmt));
 #endif /* defined TM_ZONE */
 	return result;
 }
@@ -1408,7 +1452,7 @@ struct tm *
 gmtime_r(const time_t *const timep, struct tm *tmp)
 {
   gmtcheck();
-  tmp = gmtsub(timep, 0, tmp);
+  tmp = gmtsub(gmtptr, timep, 0, tmp);
   return tmp;
 }
 
@@ -1419,7 +1463,7 @@ offtime(const time_t *const timep, const long offset)
 {
   struct tm *tmp;
   gmtcheck();
-  tmp = gmtsub(timep, offset, &tm);
+  tmp = gmtsub(gmtptr, timep, offset, &tm);
   return tmp;
 }
 
@@ -1689,12 +1733,13 @@ tmcomp(register const struct tm *const atmp,
 
 static time_t
 time2sub(struct tm *const tmp,
-	 struct tm *(*const funcp)(const time_t *, int_fast32_t, struct tm *),
+	 struct tm *(*funcp)(struct state const *, time_t const *,
+			     int_fast32_t, struct tm *),
+	 struct state const *sp,
 	 const int_fast32_t offset,
 	 bool *okayp,
 	 bool do_norm_secs)
 {
-	register const struct state *	sp;
 	register int			dir;
 	register int			i, j;
 	register int			saved_seconds;
@@ -1791,7 +1836,7 @@ time2sub(struct tm *const tmp,
 			t = lo;
 		else if (t > hi)
 			t = hi;
-		if ((*funcp)(&t, offset, &mytm) == NULL) {
+		if (! funcp(sp, &t, offset, &mytm)) {
 			/*
 			** Assume that t is too extreme to be represented in
 			** a struct tm; arrange things so that it is less
@@ -1837,7 +1882,7 @@ time2sub(struct tm *const tmp,
 		  int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF;
 		  if (!increment_overflow_time(&altt, diff)) {
 		    struct tm alttm;
-		    if (funcp(&altt, offset, &alttm)
+		    if (funcp(sp, &altt, offset, &alttm)
 			&& alttm.tm_isdst == mytm.tm_isdst
 			&& alttm.TM_GMTOFF == yourtm.TM_GMTOFF
 			&& tmcomp(&alttm, &yourtm) == 0) {
@@ -1855,8 +1900,6 @@ time2sub(struct tm *const tmp,
 		** It's okay to guess wrong since the guess
 		** gets checked.
 		*/
-		sp = (const struct state *)
-			((funcp == localsub) ? lclptr : gmtptr);
 		if (sp == NULL)
 			return WRONG;
 		for (i = sp->typecnt - 1; i >= 0; --i) {
@@ -1867,7 +1910,7 @@ time2sub(struct tm *const tmp,
 					continue;
 				newt = t + sp->ttis[j].tt_gmtoff -
 					sp->ttis[i].tt_gmtoff;
-				if ((*funcp)(&newt, offset, &mytm) == NULL)
+				if (! funcp(sp, &newt, offset, &mytm))
 					continue;
 				if (tmcomp(&mytm, &yourtm) != 0)
 					continue;
@@ -1887,14 +1930,16 @@ label:
 	if ((newt < t) != (saved_seconds < 0))
 		return WRONG;
 	t = newt;
-	if ((*funcp)(&t, offset, tmp))
+	if (funcp(sp, &t, offset, tmp))
 		*okayp = true;
 	return t;
 }
 
 static time_t
 time2(struct tm * const	tmp,
-      struct tm * (*const funcp)(const time_t *, int_fast32_t, struct tm *),
+      struct tm *(*funcp)(struct state const *, time_t const *,
+			  int_fast32_t, struct tm *),
+      struct state const *sp,
       const int_fast32_t offset,
       bool *okayp)
 {
@@ -1905,17 +1950,18 @@ time2(struct tm * const	tmp,
 	** (in case tm_sec contains a value associated with a leap second).
 	** If that fails, try with normalization of seconds.
 	*/
-	t = time2sub(tmp, funcp, offset, okayp, false);
-	return *okayp ? t : time2sub(tmp, funcp, offset, okayp, true);
+	t = time2sub(tmp, funcp, sp, offset, okayp, false);
+	return *okayp ? t : time2sub(tmp, funcp, sp, offset, okayp, true);
 }
 
 static time_t
 time1(struct tm *const tmp,
-      struct tm *(*const funcp) (const time_t *, int_fast32_t, struct tm *),
+      struct tm *(*funcp) (struct state const *, time_t const *,
+			   int_fast32_t, struct tm *),
+      struct state const *sp,
       const int_fast32_t offset)
 {
 	register time_t			t;
-	register const struct state *	sp;
 	register int			samei, otheri;
 	register int			sameind, otherind;
 	register int			i;
@@ -1930,7 +1976,7 @@ time1(struct tm *const tmp,
 	}
 	if (tmp->tm_isdst > 1)
 		tmp->tm_isdst = 1;
-	t = time2(tmp, funcp, offset, &okay);
+	t = time2(tmp, funcp, sp, offset, &okay);
 	if (okay)
 		return t;
 	if (tmp->tm_isdst < 0)
@@ -1948,7 +1994,6 @@ time1(struct tm *const tmp,
 	** We try to divine the type they started from and adjust to the
 	** type they need.
 	*/
-	sp = (const struct state *) ((funcp == localsub) ?  lclptr : gmtptr);
 	if (sp == NULL)
 		return WRONG;
 	for (i = 0; i < sp->typecnt; ++i)
@@ -1970,7 +2015,7 @@ time1(struct tm *const tmp,
 			tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
 					sp->ttis[samei].tt_gmtoff;
 			tmp->tm_isdst = !tmp->tm_isdst;
-			t = time2(tmp, funcp, offset, &okay);
+			t = time2(tmp, funcp, sp, offset, &okay);
 			if (okay)
 				return t;
 			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
@@ -1981,6 +2026,12 @@ time1(struct tm *const tmp,
 	return WRONG;
 }
 
+NETBSD_INSPIRED_EXTERN time_t
+mktime_z(struct state *sp, struct tm *tmp)
+{
+  return time1(tmp, localsub, sp, 0);
+}
+
 time_t
 mktime(struct tm *const tmp)
 {
@@ -1991,7 +2042,7 @@ mktime(struct tm *const tmp)
     return -1;
   }
   tzset_unlocked();
-  t = time1(tmp, localsub, 0);
+  t = mktime_z(lclptr, tmp);
   unlock();
   return t;
 }
@@ -2019,7 +2070,7 @@ timeoff(struct tm *const tmp, const long offset)
   if (tmp)
     tmp->tm_isdst = 0;
   gmtcheck();
-  t = time1(tmp, gmtsub, offset);
+  t = time1(tmp, gmtsub, gmtptr, offset);
   return t;
 }
 
@@ -2040,26 +2091,30 @@ timeoff(struct tm *const tmp, const long offset)
 */
 
 static int_fast64_t
-leapcorr(time_t *timep)
+leapcorr(struct state const *sp, time_t t)
 {
-	register struct state *		sp;
-	register struct lsinfo *	lp;
+	register struct lsinfo const *	lp;
 	register int			i;
 
 	sp = lclptr;
 	i = sp->leapcnt;
 	while (--i >= 0) {
 		lp = &sp->lsis[i];
-		if (*timep >= lp->ls_trans)
+		if (t >= lp->ls_trans)
 			return lp->ls_corr;
 	}
 	return 0;
 }
 
+NETBSD_INSPIRED_EXTERN time_t ATTRIBUTE_PURE
+time2posix_z(struct state *sp, time_t t)
+{
+  return t - leapcorr(sp, t);
+}
+
 time_t
 time2posix(time_t t)
 {
-  time_t p;
   int err = lock();
   if (err) {
     errno = err;
@@ -2067,48 +2122,57 @@ time2posix(time_t t)
   }
   if (!lcl_is_set)
     tzset_unlocked();
-  p = t - leapcorr(&t);
+  if (lclptr)
+    t = time2posix_z(lclptr, t);
   unlock();
-  return p;
+  return t;
 }
 
-time_t
-posix2time(time_t t)
+NETBSD_INSPIRED_EXTERN time_t ATTRIBUTE_PURE
+posix2time_z(struct state *sp, time_t t)
 {
 	time_t	x;
 	time_t	y;
-	int err = lock();
-	if (err) {
-	  errno = err;
-	  return -1;
-	}
-	if (!lcl_is_set)
-	  tzset_unlocked();
 	/*
 	** For a positive leap second hit, the result
 	** is not unique. For a negative leap second
 	** hit, the corresponding time doesn't exist,
 	** so we return an adjacent second.
 	*/
-	x = t + leapcorr(&t);
-	y = x - leapcorr(&x);
+	x = t + leapcorr(sp, t);
+	y = x - leapcorr(sp, x);
 	if (y < t) {
 		do {
 			x++;
-			y = x - leapcorr(&x);
+			y = x - leapcorr(sp, x);
 		} while (y < t);
 		x -= y != t;
 	} else if (y > t) {
 		do {
 			--x;
-			y = x - leapcorr(&x);
+			y = x - leapcorr(sp, x);
 		} while (y > t);
 		x += y != t;
 	}
-	unlock();
 	return x;
 }
 
+time_t
+posix2time(time_t t)
+{
+  int err = lock();
+  if (err) {
+    errno = err;
+    return -1;
+  }
+  if (!lcl_is_set)
+    tzset_unlocked();
+  if (lclptr)
+    t = posix2time_z(lclptr, t);
+  unlock();
+  return t;
+}
+
 #endif /* defined STD_INSPIRED */
 
 #ifdef time_tz
diff --git a/private.h b/private.h
index 1858bae..00a2469 100644
--- a/private.h
+++ b/private.h
@@ -62,6 +62,10 @@
 #define HAVE_UTMPX_H		0
 #endif /* !defined HAVE_UTMPX_H */
 
+#ifndef NETBSD_INSPIRED
+# define NETBSD_INSPIRED 1
+#endif
+
 #if HAVE_INCOMPATIBLE_CTIME_R
 #define asctime_r _incompatible_asctime_r
 #define ctime_r _incompatible_ctime_r
@@ -74,12 +78,28 @@
 ** Nested includes
 */
 
+/* Avoid clashes with NetBSD by renaming NetBSD's declarations.  */
+#define localtime_rz sys_localtime_rz
+#define mktime_z sys_mktime_z
+#define posix2time_z sys_posix2time_z
+#define time2posix_z sys_time2posix_z
+#define timezone_t sys_timezone_t
+#define tzalloc sys_tzalloc
+#define tzfree sys_tzfree
+#include <time.h>
+#undef localtime_rz
+#undef mktime_z
+#undef posix2time_z
+#undef time2posix_z
+#undef timezone_t
+#undef tzalloc
+#undef tzfree
+
 #include "sys/types.h"	/* for time_t */
 #include "stdio.h"
 #include "errno.h"
 #include "string.h"
 #include "limits.h"	/* for CHAR_BIT et al. */
-#include "time.h"
 #include "stdlib.h"
 
 #if HAVE_GETTEXT
@@ -358,6 +378,31 @@ time_t posix2time(time_t);
 #endif
 
 /*
+** Define functions that are ABI compatible with NetBSD but have
+** better prototypes.  NetBSD 6.1.4 defines a pointer type timezone_t
+** and labors under the misconception that 'const timezone_t' is a
+** pointer to a constant.  This use of 'const' is ineffective, so it
+** is not done here.  What we call 'struct state' NetBSD calls
+** 'struct __state', but this is a private name so it doesn't matter.
+*/
+#if NETBSD_INSPIRED
+typedef struct state *timezone_t;
+struct tm *localtime_rz(timezone_t restrict, time_t const *restrict,
+			struct tm *restrict);
+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
+time_t posix2time_z(timezone_t, time_t) ATTRIBUTE_PURE;
+#  endif
+#  if !defined time2posix_z || defined time_tz
+time_t time2posix_z(timezone_t, time_t) ATTRIBUTE_PURE;
+#  endif
+# endif
+#endif
+
+/*
 ** Private function declarations.
 */
 
-- 
1.9.1



More information about the tz mailing list