Proposal: API for thread-safe time zone functions

Jonathan Lennox lennox at
Thu Jun 7 19:04:44 UTC 2001

On Thursday, June 7 2001, "Garrett Wollman" wrote to "Jonathan Lennox, tz at" saying:

> <<On Thu, 7 Jun 2001 12:09:33 -0400 (EDT), Jonathan Lennox <lennox at> said:
> > Three functions manipulate a struct tz:
> >   newtz -- create a time zone object
> Poor choice of name.  I believe that POSIX now requires any new
> interfaces to use namespace prefixes to avoid taking away more
> application namespace.

Okay.  Marcus Kuhn's proposal used tz_*, so I'd be willing to go with that.

> >   #include <time.h>
> ...probably a different header file should be used as well.

It would be, in practice, at first, clearly.  But presumably if this
actually got accepted by any standards body they'd want it to go into

> >   It has one externally-visible element:
> >   tz_name,
> A structure of this sort should be opaque.  Define accessor functions,
> not the members of the structure.  

I think, on reflection, that the publically-visible tz_name field was
unnecessary, so I'll drop it.  It was intended to reflect 'extern char
*tzname[2]', but that's really a backward-compatibility hack anyway.  The
tm_zone and tm_gmtoff fields of struct tm are much more

> Unfortunately, POSIX is rather too
> fond of defining foo_t typedefs for things (like pointers to opaque
> structures) which should not be typedef'ed, so `struct tz' would
> probably get transmogrified by standards committees into `timezone_t'
> or some similar nonsense.

timezone_t is fine.

> As I think about it, I think a better alternative would be parallel to
> how setlocale() works:
> 	If tzname is a null pointer, the return value shall represent
> 	Coordinated Universal Time (UTC).  If tzname is a string of
> 	length zero, the return value shall represent the same
> 	timezone as is chosen by tzset().

We want there to be a way of getting the system time -- the equivalent of 
'unsetenv("TZ"); tzset();'.

That said, I suppose defining the interface such that tzset() doesn't depend
on "TZ" is a good idea.  (I had thought that tzset() and "TZ" were both
POSIX, but is one of them ANSI?)

> >   If the system defines the tm_zone field of struct tm, this function
> >   invalidates the strings pointed to by the tm_zone field of all struct tm
> >   values created by localtime_z called with this tzobj.
> The result of accessing any freed memory is undefined, so such
> language is not necessary and would probably reduce standardizability.

I think you missed my point here.  My point is that mktime_z() fills in the
tm.tm_zone field of struct tm, which is a char *.  Since there's no function
tm_free(), you can't dynamically allocate the tm.tm_zone.  Thus, either we
need to have tm_zone be a static buffer, or we need to have it point to some
memory defined elsewhere.

The natural thing to do -- reflecting most closely, I think, what the
current tzcode does -- is to have a private (e.g.) tz._tz_zonenames[] field
of struct tz, and then have tm.tm_zone point to the appropriate
tz._tz_zonename field.  However, you'd want _tz_zonename to be freed when
you call tz_free(), which would invalidate the pointer inside the tm.

The other possibility would be to deprecate tm.tm_zone and leave it always
NULL, with the new functions, since it's badly designed from the point of
view of localization anyway.  We'd say that if you want the time zone name,
you should call strftime with an appropriate format string.

> >   #include <time.h>
> >   struct tz* duptz(const struct tz* tzobj);
> I'm not sure how really useful this interface is.  In many other
> places in C and POSIX we define opaque structures without any sort of
> ``duplicate'' mechanism, and leave the application to do reference
> counting if it so wishes.  (Viz., the `FILE *' interfaces.)

The idea was to make it easy to write a copy constructor for a C++ wrapper
to the object, but yes, a reference-counting implementation would be easy

> >   struct tm *
> >   localtime_z(const time_t *clock, struct tm *result, const struct tz *tz);
> >   Equivalent to localtime_r() or gmtime_r(), in the time zone represented by
> >   tz, except that tz->tz_name is not modified.
> There doesn't seem to me to be any benefit in this restriction, and a
> program which is adopting this interface may well need to interact or
> be linked with libraries developed to the old interface.  If you
> eliminate this restriction, then you no longer need to duplicate the
> strftime() interfacem, since the returned `struct tm' contains all the
> necessary information.

I'm not quite sure I follow what your point was, here.  The point I was
making is that localtime() and localtime_r are defined to modify the
appropriate (is_dst'th) field of 'extern char *tzname[2]', but I was saying
that localtime_z doesn't do this, or the equivalent of it.

> >   char *
> >   ctime_z(const time_t *clock, char *buf, const struct tz *tz);
> >   char *
> >   asctime_z(const struct tm *tm, char *buf, const struct tz *tz);
> As Joseph Myers pointed out, these interfaces are redundant with
> strftime() and an appropriate format specifier.

Yes, okay.  (Would it be worthwhile having a #define for the asctime/ctime

> Don't forget that for
> C99 you'll want to add an appropriate `restrict' qualifier or two.


> > Name
> >   tzuse -- use time zone object in current thread.
> > Synopsis
> >   #include <time.h>
> >   void tzuse(const struct tz *tz);
> Rather than having a specific function, one might instead define a
> specific pre-instantiated key such that one can call
> `pthread_setspecific(PTHREAD_DEFAULT_TIMEZONE, tz)' with the
> consequences you describe.  This makes it possible for the application
> to find out what the current timezone is, simply by calling
> `pthread_getspecific(PTHREAD_DEFAULT_TIMEZONE)'.  You would need to
> specify what the thread-termination consequences are.

Hm, interesting.  I suppose that would work, given that that's the natural
way to implement it.

Jonathan Lennox
lennox at

More information about the tz mailing list