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

Paul Eggert eggert at cs.ucla.edu
Tue Aug 26 21:24:03 UTC 2014


Thanks very much for that review.

lennox at cs.columbia.edu wrote:
> These functions aren't documented yet, as far as I can tell?

I had been lazy, and had taken the lead from the STD_INSPIRED functions, 
which aren't documented....  But you're right, the new functions should 
be, or at least the ones that aren't STD_INSPIRED.

> Since NetBSD doesn't define what a NULL argument to tzalloc does, could I
> suggest that it indeed mean /etc/localtime?

As Alan mentioned NetBSD-current says NULL means UTC, but your idea is 
better and Alan likes it too so let's do that.

> I'd also suggest that tzalloc should fail for unknown time zone names,
> rather than silently returning GMT.

Yes, that sounds good too.

Proposed further patches attached for all the above.
-------------- next part --------------
From 636f5e942c8f543e8aea00f83ffbfd1ec2d3fc91 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Tue, 26 Aug 2014 12:01:30 -0700
Subject: [PROPOSED PATCH 1/4] tzalloc(NULL) now acts like tzset() does when TZ
 is unset.

Suggested by Jonathan Lennox in:
http://mm.icann.org/pipermail/tz/2014-August/021529.html
* localtime.c (tzalloc): Treat NULL argument like tzsetwall.
(tzsetwall_unlocked): Remove, replacing with ...
(tzsetlcl): New function, which merges the old tzsetwall and
tzset_unlocked.
(zoneinit): Treat a null NAME like tzsetwall.
(tzsetwall, tzset_unlocked): Rewrite to use tzsetlcl.
* NEWS: Document this.
---
 NEWS        |  4 ++--
 localtime.c | 73 ++++++++++++++++++++++++-------------------------------------
 2 files changed, 30 insertions(+), 47 deletions(-)

diff --git a/NEWS b/NEWS
index b8ef455..db9e46f 100644
--- a/NEWS
+++ b/NEWS
@@ -58,8 +58,8 @@ Unreleased, experimental changes
     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.  (Thanks to Alan Barrett for
-    helping to debug this.)
+    posix2time_z and time2posix_z.  (Thanks to Alan Barrett and
+    Jonathan Lennox for helping to debug this.)
 
     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.
diff --git a/localtime.c b/localtime.c
index aee343e..e294a9e 100644
--- a/localtime.c
+++ b/localtime.c
@@ -1174,42 +1174,11 @@ gmtload(struct state *const sp)
 		tzparse(gmt, sp, true);
 }
 
-static void
-tzsetwall_unlocked(void)
-{
-  if (lcl_is_set < 0)
-    return;
-#ifdef ALL_STATE
-  if (! lclptr)
-    lclptr = malloc(sizeof *lclptr);
-#endif
-  if (lclptr && ! tzload(NULL, lclptr, true))
-    gmtload(lclptr);
-  settzname();
-  lcl_is_set = -1;
-}
-
-#ifdef STD_INSPIRED
-void
-tzsetwall(void)
-{
-  if (lock() != 0)
-    return;
-  tzsetwall_unlocked();
-  unlock();
-}
-#endif
-
 static struct state *
 zoneinit(struct state *sp, char const *name)
 {
   if (sp) {
-    if (! name
-	|| (name[0]
-	    && ! (tzload(name, sp, true)
-		  || (name[0] != ':' && tzparse(name, sp, false)))))
-      gmtload(sp);
-    else if (! name[0]) {
+    if (name && ! name[0]) {
       /*
       ** User wants it fast rather than right.
       */
@@ -1220,25 +1189,22 @@ zoneinit(struct state *sp, char const *name)
       sp->ttis[0].tt_gmtoff = 0;
       sp->ttis[0].tt_abbrind = 0;
       strcpy(sp->chars, gmt);
-    }
+    } else if (! (tzload(name, sp, true)
+		  || (name && name[0] != ':' && tzparse(name, sp, false))))
+      gmtload(sp);
   }
   return sp;
 }
 
 static void
-tzset_unlocked(void)
+tzsetlcl(char const *name)
 {
-  bool shortname;
-  register char const *name = getenv("TZ");
-
-  if (!name) {
-    tzsetwall_unlocked();
-    return;
-  }
-  shortname = strlen(name) < sizeof lcl_TZname;
-  if (0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
+  int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
+  if (lcl < 0
+      ? lcl_is_set < 0
+      : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
     return;
-  if (shortname)
+  if (0 < lcl)
     strcpy(lcl_TZname, name);
 #ifdef ALL_STATE
   if (! lclptr)
@@ -1246,7 +1212,24 @@ tzset_unlocked(void)
 #endif /* defined ALL_STATE */
   zoneinit(lclptr, name);
   settzname();
-  lcl_is_set = shortname;
+  lcl_is_set = lcl;
+}
+
+#ifdef STD_INSPIRED
+void
+tzsetwall(void)
+{
+  if (lock() != 0)
+    return;
+  tzsetlcl(NULL);
+  unlock();
+}
+#endif
+
+static void
+tzset_unlocked(void)
+{
+  return tzsetlcl(getenv("TZ"));
 }
 
 void
-- 
1.9.1
-------------- next part --------------
From 3230f367b09e5b9476253176fa51123e90acb5d2 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Tue, 26 Aug 2014 12:48:16 -0700
Subject: [PROPOSED PATCH 2/4] * localtime.c (zoneinit, tzalloc): Return NULL
 on failure.

Suggested by Jonathan Lennox in:
http://mm.icann.org/pipermail/tz/2014-August/021529.html
---
 localtime.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/localtime.c b/localtime.c
index e294a9e..2434167 100644
--- a/localtime.c
+++ b/localtime.c
@@ -1191,7 +1191,7 @@ zoneinit(struct state *sp, char const *name)
       strcpy(sp->chars, gmt);
     } else if (! (tzload(name, sp, true)
 		  || (name && name[0] != ':' && tzparse(name, sp, false))))
-      gmtload(sp);
+      return NULL;
   }
   return sp;
 }
@@ -1264,7 +1264,10 @@ timezone_t
 tzalloc(char const *name)
 {
   timezone_t sp = malloc(sizeof *sp);
-  return zoneinit(sp, name);
+  timezone_t tp = sp ? zoneinit(sp, name) : sp;
+  if (!tp)
+    free(sp);
+  return tp;
 }
 
 void
-- 
1.9.1
-------------- next part --------------
From 9b7dd12d564527191b0d2a7c66bbadace15653e2 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Tue, 26 Aug 2014 12:49:39 -0700
Subject: [PROPOSED PATCH 3/4] * zdump.c (main): Mention "FOO" if
 tzalloc("FOO") fails.

* NEWS: Document this.
---
 NEWS    | 3 ++-
 zdump.c | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index db9e46f..3ebef9e 100644
--- a/NEWS
+++ b/NEWS
@@ -83,7 +83,8 @@ Unreleased, experimental changes
     To build zdump with the system library, use 'make CFLAGS=-DUSE_LTZ=0
     TZDOBJS=zdump.o CHECK_TIME_T_ALTERNATIVES='.
 
-    zdump now uses localtime_rz if available, as it's significantly faster.
+    zdump now uses localtime_rz if available, as it's significantly faster,
+    and it can help zdump better diagnose invalid time zone names.
     Define HAVE_LOCALTIME_RZ to 0 to suppress this.  HAVE_LOCALTIME_TZ
     defaults to 1 if NETBSD_INSPIRED && USE_LTZ.  When localtime_rz is
     not available, zdump now uses localtime_r and tzset if available,
diff --git a/zdump.c b/zdump.c
index 6fdd579..2d82fe5 100644
--- a/zdump.c
+++ b/zdump.c
@@ -634,7 +634,7 @@ main(int argc, char *argv[])
 		timezone_t tz = tzalloc(argv[i]);
 		char const *ab;
 		if (!tz) {
-		  perror("tzalloc");
+		  perror(argv[i]);
 		  return EXIT_FAILURE;
 		}
 		if (! (vflag | Vflag)) {
-- 
1.9.1
-------------- next part --------------
From 2423b5b4a37da406d405649a4f0019c7343f76ca Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Tue, 26 Aug 2014 14:10:08 -0700
Subject: [PROPOSED PATCH 4/4] Add documentation for the core NetBSD-inspired
 functions.

Lack of documentation reported by Jonathan Lennox in:
http://mm.icann.org/pipermail/tz/2014-August/021529.html
* newtzset.3: Document tzalloc and tzfree.
* newctime.3: Document localtime_rz and mktime_z.
* NEWS: Mention this.
---
 NEWS       |  6 ++++--
 newctime.3 | 42 +++++++++++++++++++++++++++++++++--------
 newtzset.3 | 64 ++++++++++++++++++++++++++++++++++++++++++++------------------
 3 files changed, 84 insertions(+), 28 deletions(-)

diff --git a/NEWS b/NEWS
index 3ebef9e..68d0ae3 100644
--- a/NEWS
+++ b/NEWS
@@ -136,8 +136,10 @@ Unreleased, experimental changes
     suggesting a CONTRIBUTING file, and to Tony Finch and Walter Harms
     for debugging it.)
 
-    The man pages have been updated to use function prototypes, and
-    to document thread-safe variants like localtime_r.
+    The man pages have been updated to use function prototypes,
+    to document thread-safe variants like localtime_r, and to document
+    the NetBSD-inspired functions tzalloc, tzfree, localtime_rz, and
+    mktime_z.
 
     The fields in Link lines have been renamed to be more descriptive
     and more like the parameters of 'ln'.  LINK-FROM has become TARGET,
diff --git a/newctime.3 b/newctime.3
index accefa5..6667e0d 100644
--- a/newctime.3
+++ b/newctime.3
@@ -25,6 +25,10 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
 .B "struct tm *localtime_r(time_t const *restrict clock,"
 .B "    struct tm *restrict result);"
 .PP
+.B "struct tm *localtime_rz(timezone_t restrict zone,"
+.B "    time_t const *restrict clock,"
+.B "    struct tm *restrict result);"
+.PP
 .B struct tm *gmtime(time_t const *clock);
 .PP
 .B "struct tm *gmtime_r(time_t const *restrict clock,"
@@ -32,6 +36,9 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
 .PP
 .B time_t mktime(struct tm *tm);
 .PP
+.B "time_t mktime_z(timezone_t restrict zone,"
+.B "    struct tm *restrict tm);"
+.PP
 .B cc ... \*-ltz
 .fi
 .SH DESCRIPTION
@@ -110,14 +117,6 @@ structure to a string,
 as shown in the above example,
 and returns a pointer to the string.
 .PP
-.IR Ctime_r ,
-.IR localtime_r ,
-.IR gmtime_r ,
-and
-.I asctime_r
-are like their unsuffixed counterparts, except that they accept an
-additional argument specifying where to store the result if successful.
-.PP
 .I Mktime
 converts the broken-down time,
 expressed as local time,
@@ -175,6 +174,33 @@ returns the difference between two calendar times,
 .IR time0 ),
 expressed in seconds.
 .PP
+.IR Ctime_r ,
+.IR localtime_r ,
+.IR gmtime_r ,
+and
+.I asctime_r
+are like their unsuffixed counterparts, except that they accept an
+additional argument specifying where to store the result if successful.
+.PP
+.IR Localtime_rz
+and
+.I mktime_z
+are like their unsuffixed counterparts, except that they accept an
+extra initial
+.B zone
+argument specifying the time zone to be used for conversion.
+If
+.B zone
+is null, UTC is used; otherwise,
+.B zone
+should be have been allocated by
+.I tzalloc
+and should not be freed until after all uses (e.g., by calls to
+.IR strftime )
+of the filled-in
+.B tm_zone
+fields.
+.PP
 Declarations of all the functions and externals, and the
 .q "tm"
 structure,
diff --git a/newtzset.3 b/newtzset.3
index 51c4197..68905a5 100644
--- a/newtzset.3
+++ b/newtzset.3
@@ -7,6 +7,10 @@ tzset \- initialize time conversion information
 .el ds - \-
 .B #include <time.h>
 .PP
+.B timezone_t tzalloc(char const *TZ);
+.PP
+.B void tzfree(timezone_t tz);
+.PP
 .B void tzset(void);
 .PP
 .B cc ... \*-ltz
@@ -21,30 +25,61 @@ tzset \- initialize time conversion information
 .de q
 \\$3\*(lq\\$1\*(rq\\$2
 ..
-.I Tzset
-uses the value of the environment variable
+.I Tzalloc
+allocates and returns a time zone object described by
+.BR TZ .
+If
 .B TZ
-to set time conversion information used by
-.IR localtime .
+is not a valid time zone description, or if the object cannot be allocated,
+.I tzalloc
+returns a null pointer and sets
+.BR errno .
+.PP
+.I Tzfree
+frees a time zone object
+.BR tz ,
+which should have been successfully allocated by
+.IR tzalloc .
+This invalidates any
+.B tm_zone
+pointers that
+.B tz
+was used to set.
+.PP
+.I Tzset
+acts like
+.BR tzalloc(getenv("TZ")) ,
+except it saves any resulting time zone object into internal
+storage that is accessed by
+.IR localtime ,
+.IR localtime_r ,
+and
+.IR mktime .
+The anonymous shared time zone object is freed by the next call to
+.IR tzset .
+If the implied call to
+.B tzalloc
+fails,
+.I tzset
+falls back on UTC.
+.PP
 If
 .B TZ
-does not appear in the environment,
-the best available approximation to local wall clock time, as specified
-by the
+is null, the best available approximation to local wall
+clock time, as specified by the
 .IR tzfile (5)-format
 file
 .B localtime
-in the system time conversion information directory, is used by
-.IR localtime .
+in the system time conversion information directory, is used.
 If
 .B TZ
-appears in the environment but its value is a null string,
+is the empty string,
 Universal Time (UT) is used, with the abbreviation "UTC"
 and without leap second correction; please see
 .IR newctime (3)
 for more about UT, UTC, and leap seconds.  If
 .B TZ
-appears in the environment and its value is not a null string:
+is nonnull and nonempty:
 .IP
 if the value begins with a colon, it is used as a pathname of a file
 from which to read the time conversion information;
@@ -267,13 +302,6 @@ For compatibility with System V Release 3.1, a semicolon
 may be used to separate the
 .I rule
 from the rest of the specification.
-.PP
-If the
-.B TZ
-environment variable does not specify a
-.IR tzfile (5)-format
-and cannot be interpreted as a direct specification,
-UTC is used.
 .SH FILES
 .ta \w'/usr/local/etc/zoneinfo/posixrules\0\0'u
 /usr/local/etc/zoneinfo	time zone information directory
-- 
1.9.1


More information about the tz mailing list