Proposal: API for thread-safe time zone functions

Garrett Wollman wollman at khavrinen.lcs.mit.edu
Thu Jun 7 17:44:11 UTC 2001


<<On Thu, 7 Jun 2001 12:09:33 -0400 (EDT), Jonathan Lennox <lennox at cs.columbia.edu> 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.

>   #include <time.h>

...probably a different header file should be used as well.

>   tzname is a pointer to a string representing
>   the name of the allocated time zone.

Should allow a null pointer to represent whatever the default (as used
in tzset()) would be.

>   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.  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.

>   If tzname is NULL, the returned struct tz describes the local wall-clock
>   time, as best as it is known by the local system.

Oops...  Should reorder this description.

>   If tzname is the empty string "", the returned struct tz describes
>   Coordinated Universal Time (UTC).  (The POSIX rules also allow UTC to be
>   represented with the string "GMT0".)

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().

>      This interface is designed so that newtz(getenv("TZ")) will return an
>      object describing the default time zone object that non-thread-aware
>      versions of the time functions will use by default, provided TZ (if
>      set) is set to a valid time zone name.

I would suggest that requiring applications to check the ennvironment
-- or even assuming that there is a meaningful environment, which
there may not be in some profiles -- is probably a bad idea and
potentially prone to error.  (Consider: getenv("ZT") would be a common
typo which would not be recognized by a compiler.)

>   #include <time.h>
>   void freetz(struct tz* tzobj);

The same namespace comments apply here as well.

>   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.

>   #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.)

>   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.

>   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.  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.

-GAWollman




More information about the tz mailing list