tz
Threads by month
- ----- 2026 -----
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1999 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1998 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1997 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1996 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1995 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1994 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1993 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1992 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1991 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1990 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1989 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1988 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1987 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 1986 -----
- December
- November
- 10 participants
- 7500 discussions
June 19, 2001
-----Original Message-----
From: Mathew Englander
Sent: Monday, June 18, 2001 5:33 PM
To: Olson, Arthur David (NCI)
Subject: Nunavut time zones - pointer to proclamation
In case you aren't aware, there is a proclamation establishing 3 time zones
for Nunavut in the Canada Gazette, Part II, June 6 2001, on pp. 1066-67.
This is available in pdf and ascii formats on the Internet. For the pdf
file, go to http://canada.gc.ca/gazette/part2/pdf/g2-13512.pdf and then
scroll to page 78 of 99.
1
0
I've finished a first cut (call it beta 1?) of a tzcode-based implementation
of my thread-safe time zone API.
The API as defined here was fairly straightforward to implement. Only a few
changes were necessary.
* The most noticable change is that the type representing a time zone
object is now 'struct tzinfo', not 'struct timezone'. I discovered that
'struct timezone' is the type of the deprecated second argument to BSD
gettimeofday().
* Other than that, I clarified some things, notably the fact that tzname and
the other externs aren't defined when you're using pthread_settz(). (I
could have implemented them using the trick used for thread-safe errno,
but it seemed like too much trouble for a badly-defined API.)
The attached patch also contains code to make the 'normal' time functions
thread-safe. This is based on FreeBSD's changes to tzcode to make it
thread-safe, but rather than sticking #ifdef's directly into the code, as
FreeBSD does, I instead implemented them as macros. You can control your
thread model based on a Makefile setting.
The only part of the thread-safety API that isn't currently implemented in
my patch is that the error return value of strftime_z() is still the same as
that of strftime(), rather than using the snprintf() error return style.
This requires annoyingly intrusive changes to tzcode's strftime(), so I've
put it off for now.
Attached, find two files:
1. The revised (v3) API proposal.
2. A patch to tzcode2001c implementing the proposal.
Comments are very welcome!
A proposal for thread-safe time zone information. A set of extensions to
ISO C (99) and IEEE POSIX (200x).
by Jonathan Lennox, Columbia University.
Version 3.
Summary:
A new data type is defined, 'struct tzinfo', that represents the time zone
information for a particular region. Versions of the ISO C time conversion
functions are defined that take this data type as an argument.
Definitions:
Time zone: The set of information necessary to correctly convert
bidirectionally between a time_t and a struct tm, for a particular
geographic region.
Wall clock time: The system's best approximation of the local time in its
physical location, independent of the physical location of any user.
Functions defined in this proposal:
Functions manipulating struct tzinfo values:
Name
tz_prep -- prepare a time zone object, based on a descriptive string.
Synopsis
#include <time.h>
int tz_prep(struct tzinfo** tz, const char *tzstring);
Description
The tz_prep() function creates a new time zone object, corresponding to the
given time zone name. tzstring is a pointer to a string, or NULL.
The two defined values of tzstring are NULL, representing the system's
best approximation of its wall clock time, and the zero-length string "",
representing the system's best approximation of Coordinated Universal Time
(UTC).
On successful completion, *tz will contain a pointer to a struct tzinfo,
contains all necessary information to represent times in the specified
time zone. It has no externally-visible elements.
POSIX extension:
In a POSIX-based environment, the syntax of tzstring is the same as
that of the "TZ" environment variable, including the
implementation-defined values beginning with ':'.
Return Value
On successful completion, tz_prep() returns a value of 0 and fills in *tz.
On failure, an error number is returned and no resources are allocated.
Errors
The tz_prep() function shall fail if:
ENOMEM
Not enough memory is available to create the time zone object.
ENOENT
No known time zone corresponds to tzname.
The tz_prep() function may fail if:
* There is a problem with the system's configuration (such as an on-disk
time zone database) which caused retrieval of the time zone information
to fail unexpectedly. In this case any appropriate error number may be
returned.
Rationale and commentary
Systems which define extensions beyond POSIX (such as the Olson time
zone names) for the "TZ" environment variable should support the same
extensions for tzstring.
Under the POSIX rules, strings such as "UTC0" can also represent UTC, but
the empty string "" is the portable representation. This representation
is also correctly locale-independent -- "UTC0" in fr_FR generates times
with the incorrect time zone abbreviation "UTC", whereas "" in that locale
should use the correct time zone abbreviation "TUC" if the time zone code
correctly supports locales.
This interface is designed so that tz_prep(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.
Question for discussion: should there be an ISO C way of saying "the
timezone which localtime() and mktime() would use by default"? Currently
this is only possible for POSIX, using thee tz_prep(getenv("TZ")) idiom
mentioned above.
Name
tz_free -- Free resources allocated for a time zone object
Synopsis
#include <time.h>
void tz_free(struct tzinfo* tzobj);
Description
The tz_free() function frees the resources allocated for a time zone
object returned by a call to tz_prep().
Return Value
None.
Errors
None.
Rationale and commentary
None needed.
Functions using struct tzinfo to manipulate time values:
Name
time_make - derive a time_t from a struct tm, in a specified time zone.
Synopsis
#include <time.h>
int time_make(time_t *clock, struct tm *tm, const struct tzinfo *tz);
Description
This function interprets the broken-down time in *tm as a local time in
the timezone specified in *tz, and writes the corresponding value
into *clock, using the same encoding as that of the values returned by the
'time' function.
The original values of the tm_wday and tm_yday components of *tm are
ignored, and the original values of the other components are not
restricted to their normal ranges. (A positive or zero value for tm_isdst
causes time_make() to presume initially that summer time (for example,
Daylight Saving Time) is or is not in effect for the specified time,
respectively. A negative value for tm_isdst causes the time_make()
function to attempt to divine whether summer time is in effect for the
specified time.)
On successful completion, the values of the tm_wday and tm_yday components
of *tm are set appropriately, and the other components are set to
represent the specified calendar time, but with their values forced to
their normal ranges; the final value of tm_mday is not set until tm_mon
and tm_year are determined.
Returns
time_make returns 0 on sucessful completion, sets *clock, and normalizes
*tm. On failure, it returns an error number and leaves the values of
*clock and *tm undefined.
Errors
time_make() shall fail if:
ERANGE
*tm does not represent a time representable by a time_t value.
time_make() may fail if:
EINVAL
*tm does not represent a possible time in the timezone *tz. (For
instance, a leap-forward interval.)
Rationale and commentary
This function is the generalization of the ISO C function mktime() and the
BSD/tzcode function timegm().
The name and calling conventions of this function are inspired by Markus
Kuhn's proposed xtime_make(). struct xtime is a much more sophisticated
and better-defined representation of time than time_t; this proposal is
designed to be less ambitious while still leaving room for these future
improvements.
Following Kuhn, this function corrects a flaw of mktime()/timegm(). Those
functions use the value (time_t)-1 to represent an error return status.
However, this value can also be a correct translation of a struct tm
representing the time December 31, 1969, 23:59:59 GMT (assuming POSIX
time_t's). This function, instead, uses an out-of-band method to indicate
error conditions, leaving the entire time_t space free to represent
valid values.
Given an impossible time value for the time zone (e.g. one that occurs
during a leap-forward interval), implemenetations have a choice of failing
and returning EINVAL, or normalizing *tm to a nearby valid time.
Name
time_breakup - derive a struct tm from a time_t, in a specified time
zone.
Synopsis
#include <time.h>
int time_breakup(struct tm *result, const time_t *clock,
const struct tzinfo *tz);
Description
Fill in *result with the values corresponding to *clock in the time zone
*tz.
Return value
time_breakup() returns 0, and fills in *result, always.
Errors
None.
Rationale and commentary
This function is the generalization of the ISO C functions localtime() and
gmtime(), and the POSIX functions localtime_r() and gmtime_r().
It is inspired by Markus Kuhn's xtime_breakup().
The function assumes that a 'struct tzinfo' will be able to do something
reasonable over the whole range of a time_t. This isn't necessarily
true; it might be sensible to define an ERANGE error return value?
This function omits the localtime side effect that it sets the value of
the global variable char *tzname[2].
It's not clear how best to handle the BSD/tzcode tm_zone field of struct
tm. This is defined as a char*, but since there is no tm_free() function,
the data it points to cannot be dynamically allocated by time_breakup().
One possibility would be to have tm_zone point into data stored within
'struct tz', but then it would be invalidated when tz_free() was called.
A second possibility would be to define tm_zone as char
tm_zone[TZNAME_MAX], but this would break binary compatibility. A third
possibility would be to deprecate tm_zone, and have time_breakup leave it
NULL. Zone names can be obtained by calling strftime with a "%Z" format
string (assuming POSIX strftime).
Name
strftime_z - generate a text representation of a time value, in a given
time zone.
Synopsis
#include <time.h>
size_t
strftime_z(char * restrict buf, size_t maxsize,
const char * restrict format,
const struct tm * restrict timeptr,
const struct tzinfo * restrict tz);
Description
strftime_z() formats the information from 'timeptr' into the buffer 'buf'
according to the format specified by 'format' and the system locale.
The format specified by 'format' is the same as that of strftime().
POSIX note: All POSIX extensions to strftime() apply to strftime_z() as
well.
Return value
On successful completion, strftime_z fills in 'buf' and returns the number
of bytes converted. On failure, strftime_z returns the number of bytes of
buffer that would be required to fully perform the conversion (including
the terminating NUL), and leaves 'buf' in an indeterminate state.
Errors
None.
Rationale and commentary
This function is a generalization of strftime().
The return value of this function has changed from strftime(), however.
strftime() returns 0 if the buffer is not large enough. This function,
instead, follows the example of ISO C 99's snprintf(), which allows an
appropriately-sized buffer to be allocated in one step after a failure,
rather than requiring a binary search. The error status can be easily
checked by checking (ret <= maxbuf).
The formatting strings of ISO C strftime() are not affected by the time
zone, so this function is strictly speaking not necessary in a pure-C
environment. POSIX strftime(), however, has the '%z' and '%Z'
conversions, which require knowledge of the relevant time zone.
It has been argued that this function is redundant, because time_breakup()
could embed time zone information into struct tm (either in extension
fields or private fields), and thus the standard POSIX strftime could
suffice to convert struct tm's in a thread-safe way. However, this is
only true for struct tm's created from time_breakup() or the mktime()
family. Creation of struct tm values "by hand" is still fairly common,
and a time zone specification is needed to print these.
Name
strftime_zl - generate a text representation of a time value, in a given
time zone and locale.
Synopsis
#include <time.h>
size_t
strftime_zl(char * restrict buf, size_t maxsize,
const char * restrict format,
const struct tm * restrict timeptr,
const struct tzinfo * restrict tz,
const locale_t * restrict l);
Description
(This is an extension based on Ulrich Drepper's thread-aware locale
proposal; see rationale.)
strftime_z() formats the information from 'timeptr' into the buffer 'buf'
according to the format specified by 'format' and the locale specified by
'l'.
Other than the source of locale information, this is in all ways identical
to strftime_z.
Rationale and commentary
This function follows Ulrich Drepper's thread-safe locale proposal; it is
the combination of his strftime_l and strftime_z defined above. See
<http://www.cygnus.com/~drepper/tllocale.ps.bz2> for details. This
function should only be implemented if the rest of Drepper's proposal is
as well.
The _zl suffix is ugly. More natural would be to define a single function
which takes all the necessary thread-safe data as arguments, but I don't
want to force implementation of this proposal to be blocked on Drepper's.
Thread-support functions
Name
pthread_settz -- set time zone object for the current POSIX thread.
Synopsis
#include <time.h>
#include <pthread.h>
int pthread_settz(const struct tzinfo *tz);
Description
(This function is only relevant in a POSIX system with the Threads
option.)
pthread_settz() sets the time zone object to be used for time functions
called from the current POSIX thread. The time zone setting for all other
threads remains the same.
Once pthread_settz() has been called, all calls to the functions
localtime(), localtime_r(), ctime(), ctime_r(), asctime(), asctime_r(),
mktime(), and strftime() in the thread from which it was called will use
the specified time zone rather than the process-wide time zone.
If pthread_settz() is called with a NULL pointer as its argument, the
current thread will again use the global time zone object.
pthread_settz() leaves the values of the global variable tzname, and the
XSI global variables daylight and timezone, undefined.
Return value
Upon successful completion, pthread_settz() returns a value of zero.
Otherwise, an error code is returned to indicate an error.
Errors
pthread_settz() may fail if:
ENOMEM
Insufficient memory exists to store the time zone information for the
thread.
EAGAIN
The system lacked the necessary resources to associate the time zone
information with the thread.
Rationale and commentary
This function allows existing code which uses the ISO C APIs to work
correctly, unmodified, in a threaded environment. The extended parts of
the POSIX APIs may not work correctly unmodified.
This function would typically be implemented as a wrapper around
pthread_setspecific().
It's not clear if a parallel pthread_gettz() is also needed.
General rationale and commentary
Thanks to many comments from the people of the tz(a)elsie.nci.nih.gov
mailing list.
Much inspiration for this work was drawn from Markus Kuhn's proposed
extended time APIs for ISO C 200x, and from Ulrich Drepper's proposed
thread-aware locale functions.
Markus Kuhn's proposed time zone API defined an additional time zone
manipulation function tz_jump(), which gets the next or previous
discontinuity in a given time zone. This is a potentially useful
function, but it was omitted here so as to keep the API definitions
modest. (Additionally, it should probably wait on the proper definition
of an extended time type, to avoid unnecessary redundancy.)
Time zone specific versions of asctime[_r] and ctime[_r] were omitted, as
they can be constructed trivially out of the functions defined here.
A time zone specific version of strptime is not necessary. The only
zone-aware function of strptime that I know of is the BSD extension to
scan "%Z"; POSIX 200x does not define this, and it generally doesn't seem
to work very well as zone abbreviations are ambiguous. (BSD strptime only
recognizes the current local timezone, in standard and summer forms, and
"GMT". Even this is not necessarily unambiguous: consider for example the
Australian "EST" meaning both "Eastern Standard Time" and "Eastern Summer
Time".)
wcsftime_z and wcsftime_zl functions are needed, analogous to strftime_z
and strftime_zl for wide strings. Their definitions are obvious analogies
to the existing functions.
Changes from Version 2:
The structure was renamed to 'struct tzinfo' from 'struct timezone',
because the latter structure is already defined on BSD systems. (It is
the deprecated second argument to gettimeofday().)
Clarified that pthread_settz() leaves the state of global variables
undefined.
Corrected incorrect errno value in the time_make() rationale.
Changes from Version 1:
The time-zone creation and destruction functions were renamed from newtz()
and freetz() to tz_prep() and tz_free(). This puts them in a name
space, and aligns them with Markus Kuhn's functions. (These functions are
slightly different from Kuhn's, though, in that they are defined to return
errno values on errors.)
tzdup() was dropped as not useful.
mktime_z() and localtime_z() were renamed to time_make() and
time_breakup(), by analogy with Kuhn's xtime_* functions.
Zone-specific versions of asctime(), ctime(), and strptime() were dropped
as unnecessary.
The text was greatly clarified to make clear the distinctions between ISO
C extensions, POSIX extensions, and rationale and commentary.
The externally-visible tz_name elements of struct tz were dropped.
The strftime_zl function was defined.
diff -crN -x *~ ../tzcode2001c/Makefile thread-safe-2/Makefile
*** ../tzcode2001c/Makefile Tue Jun 5 13:48:21 2001
--- thread-safe-2/Makefile Mon Jun 18 15:49:47 2001
***************
*** 83,88 ****
--- 83,89 ----
# Non-default libraries needed to link.
# Add -lintl if you want to use `gettext' on Solaris.
+ # Add -lpthread if you're compiling with non-weak POSIX threads in libpthread.
LDLIBS=
# Add the following to the end of the "CFLAGS=" line as needed.
***************
*** 211,216 ****
--- 212,220 ----
CFLAGS=
+ # The threading model for this system. "dummy" or "posix"
+ THREADMODEL= dummy
+
# If you want zic's -s option used when installing, uncomment the next line
# ZFLAGS= -s
***************
*** 242,260 ****
cc= cc
CC= $(cc) -DTZDIR=\"$(TZDIR)\"
! TZCSRCS= zic.c localtime.c asctime.c scheck.c ialloc.c
! TZCOBJS= zic.o localtime.o asctime.o scheck.o ialloc.o
! TZDSRCS= zdump.c localtime.c asctime.c ialloc.c
! TZDOBJS= zdump.o localtime.o asctime.o ialloc.o
! DATESRCS= date.c localtime.c logwtmp.c strftime.c asctime.c
! DATEOBJS= date.o localtime.o logwtmp.o strftime.o asctime.o
! LIBSRCS= localtime.c asctime.c difftime.c
! LIBOBJS= localtime.o asctime.o difftime.o
! HEADERS= tzfile.h private.h
NONLIBSRCS= zic.c zdump.c scheck.c ialloc.c
NEWUCBSRCS= date.c logwtmp.c strftime.c
! SOURCES= $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS) tzselect.ksh
MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
tzfile.5 tzselect.8 zic.8 zdump.8
DOCS= README Theory $(MANS) date.1 Makefile
PRIMARY_YDATA= africa antarctica asia australasia \
--- 246,274 ----
cc= cc
CC= $(cc) -DTZDIR=\"$(TZDIR)\"
! THREADSRC= tzthread-$(THREADMODEL).c
! THREADOBJ= tzthread-$(THREADMODEL).o
! THREADHDR= tzthread-$(THREADMODEL).h
!
! CFLAGS += -DTZTHREAD_HEADER_H=\"$(THREADHDR)\"
!
! TZCSRCS= zic.c localtime.c $(THREADSRC) asctime.c scheck.c ialloc.c
! TZCOBJS= zic.o localtime.o $(THREADOBJ) asctime.o scheck.o ialloc.o
! TZDSRCS= zdump.c localtime.c $(THREADSRC) asctime.c ialloc.c
! TZDOBJS= zdump.o localtime.o $(THREADOBJ) asctime.o ialloc.o
! DATESRCS= date.c localtime.c $(THREADSRC) logwtmp.c strftime.c asctime.c
! DATEOBJS= date.o localtime.o $(THREADOBJ) logwtmp.o strftime.o asctime.o
! LIBSRCS= localtime.c $(THREADSRC) asctime.c difftime.c
! LIBOBJS= localtime.o $(THREADOBJ) asctime.o difftime.o
! HEADERS= tzfile.h private.h struct_tzinfo.h tzthread-dummy.h \
! tzthread-posix.h tztimeext.h
! VARLIBSRCS= tzthread-dummy.c tzthread-posix.c
NONLIBSRCS= zic.c zdump.c scheck.c ialloc.c
NEWUCBSRCS= date.c logwtmp.c strftime.c
! SOURCES= $(HEADERS) $(LIBSRCS) $(VARLIBSRCS) $(NONLIBSRCS) \
! $(NEWUCBSRCS) tzselect.ksh
MANS= newctime.3 newstrftime.3 newtzset.3 time2posix.3 \
+ tz_prep.3 time_make.3 pthread_settz.3 \
tzfile.5 tzselect.8 zic.8 zdump.8
DOCS= README Theory $(MANS) date.1 Makefile
PRIMARY_YDATA= africa antarctica asia australasia \
***************
*** 353,359 ****
ar r ,lib.a logwtmp.o
if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
then ranlib ,lib.a ; fi
! $(CC) $(CFLAGS) date.o localtime.o asctime.o strftime.o \
$(LDLIBS) -lc ,lib.a -o $@
rm -f ,lib.a
--- 367,373 ----
ar r ,lib.a logwtmp.o
if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
then ranlib ,lib.a ; fi
! $(CC) $(CFLAGS) date.o localtime.o $(THREADOBJ) asctime.o strftime.o \
$(LDLIBS) -lc ,lib.a -o $@
rm -f ,lib.a
***************
*** 396,404 ****
date.o: private.h
difftime.o: private.h
ialloc.o: private.h
! localtime.o: private.h tzfile.h
scheck.o: private.h
! strftime.o: tzfile.h
zic.o: private.h tzfile.h
.KEEP_STATE:
--- 410,419 ----
date.o: private.h
difftime.o: private.h
ialloc.o: private.h
! localtime.o: private.h tzfile.h tztimeext.h struct_tzinfo.h $(THREADHDR)
scheck.o: private.h
! strftime.o: tzfile.h private.h struct_tzinfo.h
zic.o: private.h tzfile.h
+ $(THREADOBJ): private.h tzfile.h struct_tzinfo.h $(THREADHDR)
.KEEP_STATE:
diff -crN -x *~ ../tzcode2001c/asctime.c thread-safe-2/asctime.c
*** ../tzcode2001c/asctime.c Tue Jun 5 13:49:48 2001
--- thread-safe-2/asctime.c Mon Jun 18 15:46:04 2001
***************
*** 14,19 ****
--- 14,21 ----
#include "private.h"
#include "tzfile.h"
+ #include TZTHREAD_HEADER_H
+
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12.
*/
***************
*** 60,74 ****
asctime(timeptr)
register const struct tm * timeptr;
{
! /*
! ** Big enough for something such as
! ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
! ** (two three-character abbreviations, five strings denoting integers,
! ** three explicit spaces, two explicit colons, a newline,
! ** and a trailing ASCII nul).
! */
! static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
! 3 + 2 + 1 + 1];
return asctime_r(timeptr, result);
}
--- 62,68 ----
asctime(timeptr)
register const struct tm * timeptr;
{
! char *result = GET_ASCTIME_BUF();
return asctime_r(timeptr, result);
}
diff -crN -x *~ ../tzcode2001c/localtime.c thread-safe-2/localtime.c
*** ../tzcode2001c/localtime.c Tue Jun 5 13:59:55 2001
--- thread-safe-2/localtime.c Mon Jun 18 16:26:37 2001
***************
*** 21,26 ****
--- 21,32 ----
#include "tzfile.h"
#include "fcntl.h"
+ #include "struct_tzinfo.h"
+
+ #include "tztimeext.h"
+
+ #include TZTHREAD_HEADER_H
+
/*
** SunOS 4.1.1 headers lack O_BINARY.
*/
***************
*** 70,110 ****
#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
#endif /* !defined TZDEFDST */
- struct ttinfo { /* time type information */
- long tt_gmtoff; /* UTC offset in seconds */
- int tt_isdst; /* used to set tm_isdst */
- int tt_abbrind; /* abbreviation list index */
- int tt_ttisstd; /* TRUE if transition is std time */
- int tt_ttisgmt; /* TRUE if transition is UTC */
- };
-
- struct lsinfo { /* leap second information */
- time_t ls_trans; /* transition time */
- long ls_corr; /* correction to apply */
- };
-
- #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
-
- #ifdef TZNAME_MAX
- #define MY_TZNAME_MAX TZNAME_MAX
- #endif /* defined TZNAME_MAX */
- #ifndef TZNAME_MAX
- #define MY_TZNAME_MAX 255
- #endif /* !defined TZNAME_MAX */
-
- struct state {
- int leapcnt;
- int timecnt;
- int typecnt;
- int charcnt;
- time_t ats[TZ_MAX_TIMES];
- unsigned char types[TZ_MAX_TIMES];
- struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
- (2 * (MY_TZNAME_MAX + 1)))];
- struct lsinfo lsis[TZ_MAX_LEAPS];
- };
-
struct rule {
int r_type; /* type of rule--see below */
int r_day; /* day number of rule */
--- 76,81 ----
***************
*** 128,175 ****
static const char * getsecs P((const char * strp, long * secsp));
static const char * getoffset P((const char * strp, long * offsetp));
static const char * getrule P((const char * strp, struct rule * rulep));
! static void gmtload P((struct state * sp));
! static void gmtsub P((const time_t * timep, long offset,
! struct tm * tmp));
! static void localsub P((const time_t * timep, long offset,
! struct tm * tmp));
static int increment_overflow P((int * number, int delta));
static int normalize_overflow P((int * tensptr, int * unitsptr,
int base));
! static void settzname P((void));
! static time_t time1 P((struct tm * tmp,
! void(*funcp) P((const time_t *,
! long, struct tm *)),
long offset));
! static time_t time2 P((struct tm *tmp,
! void(*funcp) P((const time_t *,
! long, struct tm*)),
! long offset, int * okayp));
! static time_t time2sub P((struct tm *tmp,
! void(*funcp) P((const time_t *,
! long, struct tm*)),
! long offset, int * okayp, int do_norm_secs));
static void timesub P((const time_t * timep, long offset,
! const struct state * sp, struct tm * tmp));
static int tmcomp P((const struct tm * atmp,
const struct tm * btmp));
static time_t transtime P((time_t janfirst, int year,
const struct rule * rulep, long offset));
! static int tzload P((const char * name, struct state * sp));
! static int tzparse P((const char * name, struct state * sp,
int lastditch));
- #ifdef ALL_STATE
- static struct state * lclptr;
- static struct state * gmtptr;
- #endif /* defined ALL_STATE */
-
- #ifndef ALL_STATE
- static struct state lclmem;
- static struct state gmtmem;
- #define lclptr (&lclmem)
- #define gmtptr (&gmtmem)
- #endif /* State Farm */
#ifndef TZ_STRLEN_MAX
#define TZ_STRLEN_MAX 255
--- 99,145 ----
static const char * getsecs P((const char * strp, long * secsp));
static const char * getoffset P((const char * strp, long * offsetp));
static const char * getrule P((const char * strp, struct rule * rulep));
! static void gmtload P((struct tzinfo * sp));
! static void nulltzsub P((const time_t * timep,
! long offset, struct tm * tmp));
! static void tzsub P((const time_t * timep,
! const struct tzinfo *tz,
! long offset, struct tm * tmp, int));
static int increment_overflow P((int * number, int delta));
static int normalize_overflow P((int * tensptr, int * unitsptr,
int base));
! static void settzname P((const struct tzinfo * const));
! static int time1 P((time_t * timep,
! struct tm * tmp,
! const struct tzinfo * tz,
! long offset,
! int accomodate_gap));
! static int time2 P((time_t * timep,
! struct tm *tmp,
! const struct tzinfo * tz,
long offset));
! static int time2sub P((time_t * timep,
! struct tm *tmp,
! const struct tzinfo * tz,
! long offset, int do_norm_secs));
static void timesub P((const time_t * timep, long offset,
! const struct tzinfo * sp, struct tm * tmp));
static int tmcomp P((const struct tm * atmp,
const struct tm * btmp));
static time_t transtime P((time_t janfirst, int year,
const struct rule * rulep, long offset));
! static int tzload P((const char * name, struct tzinfo * sp));
! static int tzparse P((const char * name, struct tzinfo * sp,
int lastditch));
+ static void tzsetwall_basic P((struct tzinfo * const sp));
+ static void tzset_basic P((struct tzinfo * const sp));
+ #ifndef STD_INSPIRED
+ static void tzsetwall P((void));
+ #endif
+ #ifdef STD_INSPIRED
+ static long leapcorr P((time_t *, const struct tzinfo *));
+ #endif
#ifndef TZ_STRLEN_MAX
#define TZ_STRLEN_MAX 255
***************
*** 184,189 ****
--- 154,164 ----
wildabbr
};
+ #define MIN_TIME (TYPE_SIGNED(time_t) ? \
+ (((time_t) 1) << (TYPE_BIT(time_t) - 1)) : 0)
+
+ #define MAX_TIME (~MIN_TIME)
+
/*
** Section 4.12.3 of X3.159-1989 requires that
** Except for the strftime function, these functions [asctime,
***************
*** 192,199 ****
** Thanks to Paul Eggert (eggert(a)twinsun.com) for noting this.
*/
- static struct tm tm;
-
#ifdef USG_COMPAT
time_t timezone = 0;
int daylight = 0;
--- 167,172 ----
***************
*** 217,225 ****
}
static void
! settzname P((void))
{
- register struct state * const sp = lclptr;
register int i;
tzname[0] = wildabbr;
--- 190,198 ----
}
static void
! settzname(sp)
! const struct tzinfo * const sp;
{
register int i;
tzname[0] = wildabbr;
***************
*** 231,242 ****
#ifdef ALTZONE
altzone = 0;
#endif /* defined ALTZONE */
- #ifdef ALL_STATE
if (sp == NULL) {
tzname[0] = tzname[1] = gmt;
return;
}
- #endif /* defined ALL_STATE */
for (i = 0; i < sp->typecnt; ++i) {
register const struct ttinfo * const ttisp = &sp->ttis[i];
--- 204,213 ----
***************
*** 269,275 ****
static int
tzload(name, sp)
register const char * name;
! register struct state * const sp;
{
register const char * p;
register int i;
--- 240,246 ----
static int
tzload(name, sp)
register const char * name;
! register struct tzinfo * const sp;
{
register const char * p;
register int i;
***************
*** 698,704 ****
static int
tzparse(name, sp, lastditch)
const char * name;
! register struct state * const sp;
const int lastditch;
{
const char * stdname;
--- 669,675 ----
static int
tzparse(name, sp, lastditch)
const char * name;
! register struct tzinfo * const sp;
const int lastditch;
{
const char * stdname;
***************
*** 910,921 ****
static void
gmtload(sp)
! struct state * const sp;
{
if (tzload(gmt, sp) != 0)
(void) tzparse(gmt, sp, TRUE);
}
#ifndef STD_INSPIRED
/*
** A non-static declaration of tzsetwall in a system header file
--- 881,934 ----
static void
gmtload(sp)
! struct tzinfo * const sp;
{
if (tzload(gmt, sp) != 0)
(void) tzparse(gmt, sp, TRUE);
}
+
+ static void
+ gmtset(sp)
+ struct tzinfo * const sp;
+ {
+ /*
+ ** Fast-path the common-case (no locking needed once gmt is set).
+ */
+ if (gmt_is_set) {
+ return;
+ }
+
+ LOCK_GMTPTR();
+ /*
+ ** Another thread might have set it, so check gmt_is_set again.
+ */
+ if (!gmt_is_set) {
+ gmt_is_set = TRUE;
+ if (sp != NULL)
+ gmtload(sp);
+ }
+ UNLOCK_GMTPTR();
+ }
+
+
+ static void
+ tzsetwall_basic(sp)
+ struct tzinfo * const sp;
+ {
+ if (lcl_is_set < 0)
+ return;
+ lcl_is_set = -1;
+
+ if (sp == NULL) {
+ settzname(NULL); /* all we can do */
+ return;
+ }
+ if (tzload((char *) NULL, sp) != 0)
+ gmtload(sp);
+ settzname(sp);
+ }
+
#ifndef STD_INSPIRED
/*
** A non-static declaration of tzsetwall in a system header file
***************
*** 926,957 ****
void
tzsetwall P((void))
{
! if (lcl_is_set < 0)
! return;
! lcl_is_set = -1;
! #ifdef ALL_STATE
! if (lclptr == NULL) {
! lclptr = (struct state *) malloc(sizeof *lclptr);
! if (lclptr == NULL) {
! settzname(); /* all we can do */
! return;
! }
}
! #endif /* defined ALL_STATE */
! if (tzload((char *) NULL, lclptr) != 0)
! gmtload(lclptr);
! settzname();
}
! void
! tzset P((void))
{
register const char * name;
name = getenv("TZ");
if (name == NULL) {
! tzsetwall();
return;
}
--- 939,967 ----
void
tzsetwall P((void))
{
! struct tzinfo * sp;
! int process_wide;
! LOCK_LCLPTR();
! sp = GET_LCLPTR(&process_wide);
! if (!process_wide) {
! UNLOCK_LCLPTR();
! return;
}
!
! tzsetwall_basic(sp);
! UNLOCK_LCLPTR();
}
! static void
! tzset_basic(lclptr)
! struct tzinfo * const lclptr;
{
register const char * name;
name = getenv("TZ");
if (name == NULL) {
! tzsetwall_basic(lclptr);
return;
}
***************
*** 961,975 ****
if (lcl_is_set)
(void) strcpy(lcl_TZname, name);
- #ifdef ALL_STATE
if (lclptr == NULL) {
! lclptr = (struct state *) malloc(sizeof *lclptr);
! if (lclptr == NULL) {
! settzname(); /* all we can do */
! return;
! }
}
- #endif /* defined ALL_STATE */
if (*name == '\0') {
/*
** User wants it fast rather than right.
--- 971,980 ----
if (lcl_is_set)
(void) strcpy(lcl_TZname, name);
if (lclptr == NULL) {
! settzname(NULL); /* all we can do */
! return;
}
if (*name == '\0') {
/*
** User wants it fast rather than right.
***************
*** 984,995 ****
} else if (tzload(name, lclptr) != 0)
if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
(void) gmtload(lclptr);
! settzname();
}
/*
** 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
** freely called. (And no, the PANS doesn't require the above behavior--
** but it *is* desirable.)
**
--- 989,1041 ----
} else if (tzload(name, lclptr) != 0)
if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
(void) gmtload(lclptr);
! settzname(lclptr);
! }
!
! void
! tzset P((void))
! {
! struct tzinfo * lclptr;
! int process_wide;
!
! LOCK_LCLPTR();
! lclptr = GET_LCLPTR(&process_wide);
! if (!process_wide) {
! UNLOCK_LCLPTR();
! return;
! }
!
! tzset_basic(lclptr);
! UNLOCK_LCLPTR();
! }
!
! const struct ttinfo *
! _tz_getttype(timep, sp)
! const time_t * const timep;
! const struct tzinfo * sp;
! {
! register int i;
! const time_t t = *timep;
!
! if (sp->timecnt == 0 || t < sp->ats[0]) {
! i = 0;
! while (sp->ttis[i].tt_isdst)
! if (++i >= sp->typecnt) {
! i = 0;
! break;
! }
! } else {
! for (i = 1; i < sp->timecnt; ++i)
! if (t < sp->ats[i])
! break;
! i = sp->types[i - 1];
! }
! return &sp->ttis[i];
}
/*
** The easy way to behave "as if no library function calls" localtime
! ** is to not call it--so we drop its guts into "tzsub", which can be
** freely called. (And no, the PANS doesn't require the above behavior--
** but it *is* desirable.)
**
***************
*** 998,1045 ****
/*ARGSUSED*/
static void
! localsub(timep, offset, tmp)
const time_t * const timep;
const long offset;
struct tm * const tmp;
{
- register struct state * sp;
register const struct ttinfo * ttisp;
- register int i;
const time_t t = *timep;
- sp = lclptr;
- #ifdef ALL_STATE
if (sp == NULL) {
! gmtsub(timep, offset, tmp);
return;
}
! #endif /* defined ALL_STATE */
! if (sp->timecnt == 0 || t < sp->ats[0]) {
! i = 0;
! while (sp->ttis[i].tt_isdst)
! if (++i >= sp->typecnt) {
! i = 0;
! break;
! }
! } else {
! for (i = 1; i < sp->timecnt; ++i)
! if (t < sp->ats[i])
! break;
! i = sp->types[i - 1];
! }
! ttisp = &sp->ttis[i];
/*
** To get (wrong) behavior that's compatible with System V Release 2.0
** you'd replace the statement below with
! ** t += ttisp->tt_gmtoff;
** timesub(&t, 0L, sp, tmp);
*/
! timesub(&t, ttisp->tt_gmtoff, sp, tmp);
tmp->tm_isdst = ttisp->tt_isdst;
! tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
#ifdef TM_ZONE
! tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
#endif /* defined TM_ZONE */
}
--- 1044,1089 ----
/*ARGSUSED*/
static void
! tzsub(timep, sp, offset, tmp, alter_tzname)
const time_t * const timep;
+ const struct tzinfo * const sp;
const long offset;
struct tm * const tmp;
+ int alter_tzname;
{
register const struct ttinfo * ttisp;
const time_t t = *timep;
if (sp == NULL) {
! nulltzsub(timep, offset, tmp);
return;
}
! ttisp = _tz_getttype(timep, sp);
/*
** To get (wrong) behavior that's compatible with System V Release 2.0
** you'd replace the statement below with
! ** t += ttisp->tt_gmtoff + offset;
** timesub(&t, 0L, sp, tmp);
+ **
+ ** ttisp->tt_gmtoff and offset should never both be non-zero
+ ** simultaneously.
*/
! timesub(&t, ttisp->tt_gmtoff + offset, sp, tmp);
tmp->tm_isdst = ttisp->tt_isdst;
! if (alter_tzname) {
! tzname[tmp->tm_isdst] = &sp->chars[ttisp->tt_abbrind];
! }
#ifdef TM_ZONE
! /*
! ** Could get fancy here and deliver something such as
! ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
! ** but this is no time for a treasure hunt.
! */
! if (offset != 0)
! tmp->TM_ZONE = wildabbr;
! else {
! tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
! }
#endif /* defined TM_ZONE */
}
***************
*** 1047,1055 ****
localtime(timep)
const time_t * const timep;
{
! tzset();
! localsub(timep, 0L, &tm);
! return &tm;
}
/*
--- 1091,1114 ----
localtime(timep)
const time_t * const timep;
{
! struct tm * tm;
! struct tzinfo * tz;
! int process_wide;
! tm = GET_TM_BUF();
! if (tm == NULL)
! return NULL;
!
! LOCK_LCLPTR();
! tz = GET_LCLPTR(&process_wide);
! if (tz == NULL) {
! UNLOCK_LCLPTR();
! return NULL;
! }
! if (process_wide)
! tzset_basic(tz);
! tzsub(timep, tz, 0L, tm, process_wide);
! UNLOCK_LCLPTR();
! return tm;
}
/*
***************
*** 1060,1088 ****
const time_t * const timep;
struct tm * tm;
{
! localsub(timep, 0L, tm);
return tm;
}
/*
! ** gmtsub is to gmtime as localsub is to localtime.
*/
static void
! gmtsub(timep, offset, tmp)
const time_t * const timep;
const long offset;
struct tm * const tmp;
{
! if (!gmt_is_set) {
! gmt_is_set = TRUE;
! #ifdef ALL_STATE
! gmtptr = (struct state *) malloc(sizeof *gmtptr);
! if (gmtptr != NULL)
! #endif /* defined ALL_STATE */
! gmtload(gmtptr);
! }
! timesub(timep, offset, gmtptr, tmp);
#ifdef TM_ZONE
/*
** Could get fancy here and deliver something such as
--- 1119,1150 ----
const time_t * const timep;
struct tm * tm;
{
! struct tzinfo * tz;
! int process_wide;
! LOCK_LCLPTR();
! tz = GET_LCLPTR(&process_wide);
! if (tz == NULL) {
! UNLOCK_LCLPTR();
! return NULL;
! }
! if (process_wide)
! tzset_basic(tz);
! tzsub(timep, tz, 0L, tm, process_wide);
! UNLOCK_LCLPTR();
return tm;
}
/*
! ** nulltzsub is a stripped-down version of tzsub used if tz is NULL.
*/
static void
! nulltzsub(timep, offset, tmp)
const time_t * const timep;
const long offset;
struct tm * const tmp;
{
! timesub(timep, offset, NULL, tmp);
#ifdef TM_ZONE
/*
** Could get fancy here and deliver something such as
***************
*** 1092,1105 ****
if (offset != 0)
tmp->TM_ZONE = wildabbr;
else {
! #ifdef ALL_STATE
! if (gmtptr == NULL)
! tmp->TM_ZONE = gmt;
! else tmp->TM_ZONE = gmtptr->chars;
! #endif /* defined ALL_STATE */
! #ifndef ALL_STATE
! tmp->TM_ZONE = gmtptr->chars;
! #endif /* State Farm */
}
#endif /* defined TM_ZONE */
}
--- 1154,1160 ----
if (offset != 0)
tmp->TM_ZONE = wildabbr;
else {
! tmp->TM_ZONE = gmt;
}
#endif /* defined TM_ZONE */
}
***************
*** 1108,1115 ****
gmtime(timep)
const time_t * const timep;
{
! gmtsub(timep, 0L, &tm);
! return &tm;
}
/*
--- 1163,1181 ----
gmtime(timep)
const time_t * const timep;
{
! struct tm * tm;
! struct tzinfo * tz;
! tm = GET_TM_BUF();
! if (tm == NULL)
! return NULL;
!
! tz = GET_GMTPTR();
! if (tz == NULL)
! return NULL;
! gmtset(tz);
! tzsub(timep, tz, 0L, tm, 0);
!
! return tm;
}
/*
***************
*** 1120,1126 ****
const time_t * const timep;
struct tm * tm;
{
! gmtsub(timep, 0L, tm);
return tm;
}
--- 1186,1199 ----
const time_t * const timep;
struct tm * tm;
{
! struct tzinfo * tz;
!
! tz = GET_GMTPTR();
! if (tz == NULL)
! return NULL;
! gmtset(tz);
! tzsub(timep, tz, 0L, tm, 0);
!
return tm;
}
***************
*** 1131,1138 ****
const time_t * const timep;
const long offset;
{
! gmtsub(timep, offset, &tm);
! return &tm;
}
#endif /* defined STD_INSPIRED */
--- 1204,1223 ----
const time_t * const timep;
const long offset;
{
! struct tm * tm;
! struct tzinfo * tz;
!
! tm = GET_TM_BUF();
! if (tm == NULL)
! return NULL;
!
! tz = GET_GMTPTR();
! if (tz == NULL)
! return NULL;
! gmtset(tz);
! tzsub(timep, tz, offset, tm, 0);
!
! return tm;
}
#endif /* defined STD_INSPIRED */
***************
*** 1141,1147 ****
timesub(timep, offset, sp, tmp)
const time_t * const timep;
const long offset;
! register const struct state * const sp;
register struct tm * const tmp;
{
register const struct lsinfo * lp;
--- 1226,1232 ----
timesub(timep, offset, sp, tmp)
const time_t * const timep;
const long offset;
! register const struct tzinfo * const sp;
register struct tm * const tmp;
{
register const struct lsinfo * lp;
***************
*** 1156,1167 ****
corr = 0;
hit = 0;
- #ifdef ALL_STATE
i = (sp == NULL) ? 0 : sp->leapcnt;
- #endif /* defined ALL_STATE */
- #ifndef ALL_STATE
- i = sp->leapcnt;
- #endif /* State Farm */
while (--i >= 0) {
lp = &sp->lsis[i];
if (*timep >= lp->ls_trans) {
--- 1241,1247 ----
***************
*** 1322,1336 ****
return result;
}
! static time_t
! time2sub(tmp, funcp, offset, okayp, do_norm_secs)
struct tm * const tmp;
! void (* const funcp) P((const time_t*, long, struct tm*));
const long offset;
- int * const okayp;
const int do_norm_secs;
{
- register const struct state * sp;
register int dir;
register int bits;
register int i, j ;
--- 1402,1415 ----
return result;
}
! static int
! time2sub(timep, tmp, sp, offset, do_norm_secs)
! time_t * timep;
struct tm * const tmp;
! const struct tzinfo * sp;
const long offset;
const int do_norm_secs;
{
register int dir;
register int bits;
register int i, j ;
***************
*** 1339,1366 ****
time_t t;
struct tm yourtm, mytm;
- *okayp = FALSE;
yourtm = *tmp;
if (do_norm_secs) {
if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
SECSPERMIN))
! return WRONG;
}
if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
! return WRONG;
if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
! return WRONG;
if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
! return WRONG;
/*
** Turn yourtm.tm_year into an actual year number for now.
** It is converted back to an offset from TM_YEAR_BASE later.
*/
if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
! return WRONG;
while (yourtm.tm_mday <= 0) {
if (increment_overflow(&yourtm.tm_year, -1))
! return WRONG;
i = yourtm.tm_year + (1 < yourtm.tm_mon);
yourtm.tm_mday += year_lengths[isleap(i)];
}
--- 1418,1444 ----
time_t t;
struct tm yourtm, mytm;
yourtm = *tmp;
if (do_norm_secs) {
if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
SECSPERMIN))
! return ERANGE;
}
if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
! return ERANGE;
if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
! return ERANGE;
if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
! return ERANGE;
/*
** Turn yourtm.tm_year into an actual year number for now.
** It is converted back to an offset from TM_YEAR_BASE later.
*/
if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
! return ERANGE;
while (yourtm.tm_mday <= 0) {
if (increment_overflow(&yourtm.tm_year, -1))
! return ERANGE;
i = yourtm.tm_year + (1 < yourtm.tm_mon);
yourtm.tm_mday += year_lengths[isleap(i)];
}
***************
*** 1368,1374 ****
i = yourtm.tm_year + (1 < yourtm.tm_mon);
yourtm.tm_mday -= year_lengths[isleap(i)];
if (increment_overflow(&yourtm.tm_year, 1))
! return WRONG;
}
for ( ; ; ) {
i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
--- 1446,1452 ----
i = yourtm.tm_year + (1 < yourtm.tm_mon);
yourtm.tm_mday -= year_lengths[isleap(i)];
if (increment_overflow(&yourtm.tm_year, 1))
! return ERANGE;
}
for ( ; ; ) {
i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
***************
*** 1378,1388 ****
if (++yourtm.tm_mon >= MONSPERYEAR) {
yourtm.tm_mon = 0;
if (increment_overflow(&yourtm.tm_year, 1))
! return WRONG;
}
}
if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
! return WRONG;
if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
/*
** We can't set tm_sec to 0, because that might push the
--- 1456,1466 ----
if (++yourtm.tm_mon >= MONSPERYEAR) {
yourtm.tm_mon = 0;
if (increment_overflow(&yourtm.tm_year, 1))
! return ERANGE;
}
}
if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
! return ERANGE;
if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
/*
** We can't set tm_sec to 0, because that might push the
***************
*** 1393,1399 ****
** which is a safer assumption than using 58 would be.
*/
if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
! return WRONG;
saved_seconds = yourtm.tm_sec;
yourtm.tm_sec = SECSPERMIN - 1;
} else {
--- 1471,1477 ----
** which is a safer assumption than using 58 would be.
*/
if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
! return ERANGE;
saved_seconds = yourtm.tm_sec;
yourtm.tm_sec = SECSPERMIN - 1;
} else {
***************
*** 1412,1424 ****
*/
t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
for ( ; ; ) {
! (*funcp)(&t, offset, &mytm);
dir = tmcomp(&mytm, &yourtm);
if (dir != 0) {
! if (bits-- < 0)
! return WRONG;
! if (bits < 0)
! --t; /* may be needed if new t is minimal */
else if (dir > 0)
t -= ((time_t) 1) << bits;
else t += ((time_t) 1) << bits;
--- 1490,1517 ----
*/
t = TYPE_SIGNED(time_t) ? 0 : (((time_t) 1) << bits);
for ( ; ; ) {
! tzsub(&t, sp, offset, &mytm, 0);
dir = tmcomp(&mytm, &yourtm);
if (dir != 0) {
! if (bits-- < 0) {
! /*
! ** Assume that MAX_TIME and MIN_TIME aren't
! ** near a leap-forward interval.
! ** With signed 32-bit time_t, they're in
! ** January and December respectively,
! ** so it's unlikely.
! */
! if (t == MAX_TIME || t == MIN_TIME)
! return ERANGE;
! return EINVAL;
! }
! if (bits < 0) {
! /* may be needed if new t is minimal,
! ** since we started above the median.
! */
! if (dir > 0)
! --t;
! }
else if (dir > 0)
t -= ((time_t) 1) << bits;
else t += ((time_t) 1) << bits;
***************
*** 1435,1447 ****
/*
** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
*/
- sp = (const struct state *)
- (((void *) funcp == (void *) localsub) ?
- lclptr : gmtptr);
- #ifdef ALL_STATE
if (sp == NULL)
! return WRONG;
! #endif /* defined ALL_STATE */
for (i = sp->typecnt - 1; i >= 0; --i) {
if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
continue;
--- 1528,1535 ----
/*
** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
*/
if (sp == NULL)
! return EINVAL;
for (i = sp->typecnt - 1; i >= 0; --i) {
if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
continue;
***************
*** 1450,1456 ****
continue;
newt = t + sp->ttis[j].tt_gmtoff -
sp->ttis[i].tt_gmtoff;
! (*funcp)(&newt, offset, &mytm);
if (tmcomp(&mytm, &yourtm) != 0)
continue;
if (mytm.tm_isdst != yourtm.tm_isdst)
--- 1538,1544 ----
continue;
newt = t + sp->ttis[j].tt_gmtoff -
sp->ttis[i].tt_gmtoff;
! tzsub(&newt, sp, offset, &mytm, 0);
if (tmcomp(&mytm, &yourtm) != 0)
continue;
if (mytm.tm_isdst != yourtm.tm_isdst)
***************
*** 1462,1524 ****
goto label;
}
}
! return WRONG;
}
label:
newt = t + saved_seconds;
if ((newt < t) != (saved_seconds < 0))
! return WRONG;
t = newt;
! (*funcp)(&t, offset, tmp);
! *okayp = TRUE;
! return t;
}
! static time_t
! time2(tmp, funcp, offset, okayp)
struct tm * const tmp;
! void (* const funcp) P((const time_t*, long, struct tm*));
const long offset;
- int * const okayp;
{
time_t t;
/*
** First try without normalization of seconds
** (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);
}
! static time_t
! time1(tmp, funcp, offset)
struct tm * const tmp;
! void (* const funcp) P((const time_t *, long, struct tm *));
const long offset;
{
! register time_t t;
! register const struct state * sp;
register int samei, otheri;
! int okay;
if (tmp->tm_isdst > 1)
tmp->tm_isdst = 1;
! t = time2(tmp, funcp, offset, &okay);
#ifdef PCTS
! /*
! ** PCTS code courtesy Grant Sullivan (grant(a)osf.org)
! */
! if (okay)
! return t;
! if (tmp->tm_isdst < 0)
! tmp->tm_isdst = 0; /* reset to std and try again */
! #endif /* defined PCTS */
! #ifndef PCTS
! if (okay || tmp->tm_isdst < 0)
! return t;
#endif /* !defined PCTS */
/*
** We're supposed to assume that somebody took a time of one type
** and did some math on it that yielded a "struct tm" that's bad.
--- 1550,1621 ----
goto label;
}
}
! return EINVAL;
}
label:
newt = t + saved_seconds;
if ((newt < t) != (saved_seconds < 0))
! return ERANGE;
t = newt;
! tzsub(&t, sp, offset, tmp, 0);
! *timep = t;
! return 0;
}
! static int
! time2(timep, tmp, tz, offset)
! time_t * timep;
struct tm * const tmp;
! const struct tzinfo * tz;
const long offset;
{
time_t t;
+ int err;
/*
** First try without normalization of seconds
** (in case tm_sec contains a value associated with a leap second).
** If that fails, try with normalization of seconds.
*/
! err = time2sub(&t, tmp, tz, offset, FALSE);
! if (err == 0) {
! *timep = t;
! return 0;
! }
! return time2sub(timep, tmp, tz, offset, TRUE);
}
! static int
! time1(timep, tmp, sp, offset, accomodate_gap)
! time_t * timep;
struct tm * const tmp;
! const struct tzinfo * sp;
const long offset;
+ int accomodate_gap;
{
! time_t t;
register int samei, otheri;
! int err;
if (tmp->tm_isdst > 1)
tmp->tm_isdst = 1;
! err = time2(&t, tmp, sp, offset);
! if (err == 0) {
! *timep = t;
! return 0;
! }
! if (tmp->tm_isdst < 0) {
#ifdef PCTS
! /*
! ** PCTS code courtesy Grant Sullivan (grant(a)osf.org)
! */
! if (accomodate_gap)
! /* reset to std and try again */
! tmp->tm_isdst = 0;
! else
#endif /* !defined PCTS */
+ return (err != 0 ? err : EINVAL);
+ }
/*
** We're supposed to assume that somebody took a time of one type
** and did some math on it that yielded a "struct tm" that's bad.
***************
*** 1528,1539 ****
/*
** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
*/
- sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
- lclptr : gmtptr);
- #ifdef ALL_STATE
if (sp == NULL)
! return WRONG;
! #endif /* defined ALL_STATE */
for (samei = sp->typecnt - 1; samei >= 0; --samei) {
if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
continue;
--- 1625,1632 ----
/*
** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
*/
if (sp == NULL)
! return (err != 0 ? err : EINVAL);
for (samei = sp->typecnt - 1; samei >= 0; --samei) {
if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
continue;
***************
*** 1543,1565 ****
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);
! if (okay)
! return t;
tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
sp->ttis[samei].tt_gmtoff;
tmp->tm_isdst = !tmp->tm_isdst;
}
}
! return WRONG;
}
time_t
mktime(tmp)
struct tm * const tmp;
{
! tzset();
! return time1(tmp, localsub, 0L);
}
#ifdef STD_INSPIRED
--- 1636,1670 ----
tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
sp->ttis[samei].tt_gmtoff;
tmp->tm_isdst = !tmp->tm_isdst;
! err = time2(&t, tmp, sp, offset);
! if (err == 0) {
! *timep = t;
! return 0;
! }
tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
sp->ttis[samei].tt_gmtoff;
tmp->tm_isdst = !tmp->tm_isdst;
}
}
! return (err != 0 ? err : EINVAL);
}
time_t
mktime(tmp)
struct tm * const tmp;
{
! time_t mktime_return_value;
! int err;
! struct tzinfo *tz;
! int process_wide;
!
! LOCK_LCLPTR();
! tz = GET_LCLPTR(&process_wide);
! if (process_wide)
! tzset_basic(tz);
! err = time1(&mktime_return_value, tmp, tz, 0L, 1);
! UNLOCK_LCLPTR();
! return (err == 0 ? mktime_return_value : WRONG);
}
#ifdef STD_INSPIRED
***************
*** 1576,1583 ****
timegm(tmp)
struct tm * const tmp;
{
tmp->tm_isdst = 0;
! return time1(tmp, gmtsub, 0L);
}
time_t
--- 1681,1692 ----
timegm(tmp)
struct tm * const tmp;
{
+ time_t timegm_return_value;
+ int err;
+
tmp->tm_isdst = 0;
! err = time1(&timegm_return_value, tmp, GET_GMTPTR(), 0L, 1);
! return (err == 0 ? timegm_return_value : WRONG);
}
time_t
***************
*** 1585,1594 ****
struct tm * const tmp;
const long offset;
{
tmp->tm_isdst = 0;
! return time1(tmp, gmtsub, offset);
}
#endif /* defined STD_INSPIRED */
#ifdef CMUCS
--- 1694,1811 ----
struct tm * const tmp;
const long offset;
{
+ time_t timeoff_return_value;
+ int err;
+
tmp->tm_isdst = 0;
! err = time1(&timeoff_return_value, tmp, GET_GMTPTR(), offset, 1);
! return (err == 0 ? timeoff_return_value : WRONG);
! }
!
!
! /*
! ** Thread-safety functions
! */
!
! int
! tz_prep(tz_p, name)
! struct tzinfo** tz_p;
! const char * name;
! {
! struct tzinfo * result;
!
! result = (struct tzinfo *) malloc(sizeof(struct tzinfo));
! if (result == NULL) {
! return errno;
! }
!
! if (name != NULL && *name == '\0') {
! /* Zero-length string. Indicates GMT. */
! #ifdef TZCODE_GMT_USAGE
! /*
! ** Traditional tzcode usage is that the zero-length string
! ** means GMT without leap seconds.
! */
! result->leapcnt = 0; /* so, we're off a little */
! result->timecnt = 0;
! result->typecnt = 0;
! result->ttis[0].tt_isdst = 0;
! result->ttis[0].tt_gmtoff = 0;
! result->ttis[0].tt_abbrind = 0;
! (void) strcpy(result->chars, gmt);
! #else
! gmtload(result);
! #endif
! }
! else {
! /*
! ** 1. Try to tzload.
! ** 2. If name is non-NULL and doesn't begin with :,
! ** try to tzparse.
! ** 3. Fail ENOENT.
! */
! if (tzload(name, result) != 0) {
! if (name != NULL && name[0] != ':') {
! if (tzparse(name, result, FALSE) != 0) {
! goto noentry;
! }
! }
! else {
! goto noentry;
! }
! }
! }
!
! *tz_p = result;
! return 0;
!
! noentry:
! free(result);
! return ENOENT;
}
+
+ void
+ tz_free(tz)
+ struct tzinfo* tz;
+ {
+ free(tz);
+ }
+
+
+ int
+ time_make(timep, tmp, tz)
+ time_t * timep;
+ struct tm *tmp;
+ const struct tzinfo *tz;
+ {
+ return time1(timep, tmp, tz, 0L, 0);
+ }
+
+
+ int time_breakup(tmp, timep, tz)
+ struct tm * tmp;
+ const time_t * timep;
+ const struct tzinfo * tz;
+ {
+ tzsub(timep, tz, 0L, tmp, 0);
+ return 0;
+ }
+
+
+ int
+ pthread_settz(tz)
+ const struct tzinfo * tz;
+ {
+ if (tz != NULL) {
+ return SET_THREAD_LCLPTR(tz);
+ }
+ else {
+ return CLEAR_THREAD_LCLPTR();
+ }
+ }
+
+
#endif /* defined STD_INSPIRED */
#ifdef CMUCS
***************
*** 1626,1640 ****
*/
static long
! leapcorr(timep)
! time_t * timep;
{
! register struct state * sp;
! register struct lsinfo * lp;
register int i;
! sp = lclptr;
! i = sp->leapcnt;
while (--i >= 0) {
lp = &sp->lsis[i];
if (*timep >= lp->ls_trans)
--- 1843,1856 ----
*/
static long
! leapcorr(timep, sp)
! time_t * timep;
! const struct tzinfo * const sp;
{
! register const struct lsinfo * lp;
register int i;
! i = (sp == NULL ? 0 : sp->leapcnt);
while (--i >= 0) {
lp = &sp->lsis[i];
if (*timep >= lp->ls_trans)
***************
*** 1647,1654 ****
time2posix(t)
time_t t;
{
! tzset();
! return t - leapcorr(&t);
}
time_t
--- 1863,1873 ----
time2posix(t)
time_t t;
{
! const struct tzinfo * sp;
!
! sp = GET_GMTPTR();
! gmtset(sp);
! return t - leapcorr(&t, sp);
}
time_t
***************
*** 1657,1683 ****
{
time_t x;
time_t y;
- tzset();
/*
** 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);
if (y < t) {
do {
x++;
! y = x - leapcorr(&x);
} while (y < t);
if (t != y)
return x - 1;
} else if (y > t) {
do {
--x;
! y = x - leapcorr(&x);
} while (y > t);
if (t != y)
return x + 1;
--- 1876,1905 ----
{
time_t x;
time_t y;
+ const struct tzinfo * sp;
+
+ sp = GET_GMTPTR();
+ gmtset(sp);
/*
** 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, sp);
! y = x - leapcorr(&x, sp);
if (y < t) {
do {
x++;
! y = x - leapcorr(&x, sp);
} while (y < t);
if (t != y)
return x - 1;
} else if (y > t) {
do {
--x;
! y = x - leapcorr(&x, sp);
} while (y > t);
if (t != y)
return x + 1;
diff -crN -x *~ ../tzcode2001c/private.h thread-safe-2/private.h
*** ../tzcode2001c/private.h Tue Jun 5 13:48:21 2001
--- thread-safe-2/private.h Mon Jun 18 15:59:54 2001
***************
*** 206,211 ****
--- 206,215 ----
void ifree P((char * pointer));
char * scheck P((const char *string, const char *format));
+ struct tzinfo;
+ const struct ttinfo * _tz_getttype P((const time_t * const timep,
+ const struct tzinfo * sp));
+
/*
** Finally, some convenience items.
***************
*** 237,242 ****
--- 241,256 ----
#define INT_STRLEN_MAXIMUM(type) \
((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
#endif /* !defined INT_STRLEN_MAXIMUM */
+
+ /*
+ ** Big enough for something such as
+ ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
+ ** (two three-character abbreviations, five strings denoting integers,
+ ** three explicit spaces, two explicit colons, a newline,
+ ** and a trailing ASCII nul).
+ */
+
+ #define ASCTIME_BUF_SIZE (3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + 3 + 2 + 1 + 1)
/*
** INITIALIZE(x)
diff -crN -x *~ ../tzcode2001c/pthread_settz.3 thread-safe-2/pthread_settz.3
*** ../tzcode2001c/pthread_settz.3 Wed Dec 31 19:00:00 1969
--- thread-safe-2/pthread_settz.3 Mon Jun 18 15:46:08 2001
***************
*** 0 ****
--- 1,100 ----
+ .TH PTHREAD_SETTZ 3
+ .SH NAME
+ pthread_settz \- locally set the time zone for the current POSIX thread
+ .SH SYNOPSIS
+ .nf
+ .B #include <time.h>
+ .B #include <pthread.h>
+ .PP
+ .B int pthread_settz(tz)
+ .B const struct tzinfo *tz;
+ .PP
+ .B cc ... -ltz -lpthread
+ .fi
+ .SH DESCRIPTION
+ .B Pthread_settz()
+ sets the time zone object to be used for time functions called from the
+ current POSIX thread to the time zone object specified by
+ .IR tz ,
+ if it is non-\fBNULL\fR. If
+ .IR tz
+ is
+ .BR NULL ,
+ the time zone for the current POSIX thread is reset to the global time zone
+ object. The time zone setting for all other threads remains the same.
+ .PP
+ Once
+ .B pthread_settz()
+ has been called with a non-\fBNULL\fR value, all calls to the functions
+ .BR localtime(3) ,
+ .BR localtime_r(3) ,
+ .BR ctime(3) ,
+ .BR ctime_r(3) ,
+ .BR asctime(3) ,
+ .BR asctime_r(3) ,
+ .BR mktime(3) ,
+ and
+ .BR strftime(3)
+ in the thread from which
+ .B pthread_settz()
+ was called will use the time zone specified by
+ .IR tz ,
+ rather than the process-wide time zone.
+ .PP
+ .B pthread_settz()
+ does not alter the global variable
+ .BR tzname .
+ No function which normally implicitly sets
+ .B tzname
+ will do so while a thread-local time zone is in effect.
+ .PP
+ Calls to
+ .B tzset(3)
+ or
+ .BR tzsetwall(3) ,
+ while a thread-local time zone is in effect, will alter the global time
+ zone, but will not affect the local time zone.
+ .PP
+ .B pthread_settz()
+ may allocate a thread-specific data key, which may count towards the number
+ of keys
+ .B PTHREAD_KEYS_MAX
+ that can be allocated by
+ .BR pthread_key_create() .
+ .SH RETURN VALUES
+ Upon successful completion,
+ .B pthread_settz()
+ function returns a value of
+ .BR 0 .
+ Otherwise, an error number is returned to indicate an error.
+ .SH ERRORS
+ .B pthread_settz()
+ may fail if:
+ .IP \fBENOMEM\fR
+ Insufficient memory exists to store the time zone information for the
+ thread.
+ .PP
+ .IP \fBEAGAIN\fR
+ The system lacked the necessary resources to associate the time zone
+ information with the thread.
+ .SH SEE ALSO
+ getenv(3),
+ newctime(3),
+ newstrftime(3),
+ newtzset(3),
+ pthread_key_create(3),
+ time(2),
+ time_make(3),
+ tz_prep(3),
+ tzfile(5)
+ .SH NOTES
+ This function allows existing code which uses the ISO C APIs to work
+ correctly, unmodified, in a threaded environment. However, code which uses
+ use the extended parts of the POSIX APIs (involving global variables, or
+ .BR tzset() )
+ may not work correctly unmodified.
+ .PP
+ No parallel function
+ .B pthread_gettz()
+ is defined.
+ .\" @(#)pthread_settz.3 1.0
diff -crN -x *~ ../tzcode2001c/strftime.c thread-safe-2/strftime.c
*** ../tzcode2001c/strftime.c Tue Jun 5 13:49:50 2001
--- thread-safe-2/strftime.c Mon Jun 18 16:11:40 2001
***************
*** 9,14 ****
--- 9,15 ----
#endif /* !defined lint */
#include "private.h"
+ #include "struct_tzinfo.h"
/*
** Copyright (c) 1989 The Regents of the University of California.
***************
*** 37,42 ****
--- 38,47 ----
#include "fcntl.h"
#include "locale.h"
+ #include "tztimeext.h"
+
+ #include TZTHREAD_HEADER_H
+
struct lc_time_T {
const char * mon[MONSPERYEAR];
const char * month[MONSPERYEAR];
***************
*** 108,114 ****
static char * _add P((const char *, char *, const char *));
static char * _conv P((int, const char *, char *, const char *));
! static char * _fmt P((const char *, const struct tm *, char *, const char *, int *));
size_t strftime P((char *, size_t, const char *, const struct tm *));
--- 113,119 ----
static char * _add P((const char *, char *, const char *));
static char * _conv P((int, const char *, char *, const char *));
! static char * _fmt P((const char *, const struct tm *, char *, const char *, int *, const struct tzinfo *));
size_t strftime P((char *, size_t, const char *, const struct tm *));
***************
*** 125,145 ****
#define IN_ALL 3
size_t
! strftime(s, maxsize, format, t)
char * const s;
const size_t maxsize;
const char * const format;
const struct tm * const t;
{
char * p;
int warn;
- tzset();
#ifdef LOCALE_HOME
localebuf.mon[0] = 0;
#endif /* defined LOCALE_HOME */
warn = IN_NONE;
! p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
(void) fprintf(stderr, "\n");
--- 130,151 ----
#define IN_ALL 3
size_t
! strftime_z(s, maxsize, format, t, tz)
char * const s;
const size_t maxsize;
const char * const format;
const struct tm * const t;
+ const struct tzinfo * tz;
{
char * p;
int warn;
#ifdef LOCALE_HOME
localebuf.mon[0] = 0;
#endif /* defined LOCALE_HOME */
warn = IN_NONE;
! p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn,
! tz);
#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
(void) fprintf(stderr, "\n");
***************
*** 156,174 ****
(void) fprintf(stderr, "\n");
}
#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
! if (p == s + maxsize)
return 0;
*p = '\0';
return p - s;
}
static char *
! _fmt(format, t, pt, ptlim, warnp)
const char * format;
const struct tm * const t;
char * pt;
const char * const ptlim;
int * warnp;
{
for ( ; *format; ++format) {
if (*format == '%') {
--- 162,206 ----
(void) fprintf(stderr, "\n");
}
#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
! if (p >= s + maxsize)
! /* XXX */
return 0;
*p = '\0';
return p - s;
}
+
+ size_t
+ strftime(s, maxsize, format, t)
+ char * const s;
+ const size_t maxsize;
+ const char * const format;
+ const struct tm * const t;
+ {
+ struct tzinfo * lclptr;
+ int dummy;
+ size_t ret;
+ tzset();
+
+ LOCK_LCLPTR();
+ lclptr = GET_LCLPTR(&dummy);
+
+ ret = strftime_z(s, maxsize, format, t, lclptr);
+ UNLOCK_LCLPTR();
+
+ if (ret > maxsize)
+ ret = 0;
+ return ret;
+ }
+
static char *
! _fmt(format, t, pt, ptlim, warnp, tz)
const char * format;
const struct tm * const t;
char * pt;
const char * const ptlim;
int * warnp;
+ const struct tzinfo * tz;
{
for ( ; *format; ++format) {
if (*format == '%') {
***************
*** 217,223 ****
{
int warn2 = IN_SOME;
! pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
--- 249,255 ----
{
int warn2 = IN_SOME;
! pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp, tz);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
***************
*** 225,231 ****
}
continue;
case 'D':
! pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
continue;
case 'd':
pt = _conv(t->tm_mday, "%02d", pt, ptlim);
--- 257,263 ----
}
continue;
case 'D':
! pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, tz);
continue;
case 'd':
pt = _conv(t->tm_mday, "%02d", pt, ptlim);
***************
*** 246,252 ****
pt = _conv(t->tm_mday, "%2d", pt, ptlim);
continue;
case 'F':
! pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
continue;
case 'H':
pt = _conv(t->tm_hour, "%02d", pt, ptlim);
--- 278,284 ----
pt = _conv(t->tm_mday, "%2d", pt, ptlim);
continue;
case 'F':
! pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, tz);
continue;
case 'H':
pt = _conv(t->tm_hour, "%02d", pt, ptlim);
***************
*** 310,319 ****
pt, ptlim);
continue;
case 'R':
! pt = _fmt("%H:%M", t, pt, ptlim, warnp);
continue;
case 'r':
! pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
continue;
case 'S':
pt = _conv(t->tm_sec, "%02d", pt, ptlim);
--- 342,351 ----
pt, ptlim);
continue;
case 'R':
! pt = _fmt("%H:%M", t, pt, ptlim, warnp, tz);
continue;
case 'r':
! pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp, tz);
continue;
case 'S':
pt = _conv(t->tm_sec, "%02d", pt, ptlim);
***************
*** 336,342 ****
}
continue;
case 'T':
! pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
continue;
case 't':
pt = _add("\t", pt, ptlim);
--- 368,374 ----
}
continue;
case 'T':
! pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, tz);
continue;
case 't':
pt = _add("\t", pt, ptlim);
***************
*** 449,455 ****
** "date as dd-bbb-YYYY"
** (ado, 1993-05-24)
*/
! pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
continue;
case 'W':
pt = _conv((t->tm_yday + DAYSPERWEEK -
--- 481,487 ----
** "date as dd-bbb-YYYY"
** (ado, 1993-05-24)
*/
! pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, tz);
continue;
case 'W':
pt = _conv((t->tm_yday + DAYSPERWEEK -
***************
*** 462,474 ****
pt = _conv(t->tm_wday, "%d", pt, ptlim);
continue;
case 'X':
! pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
continue;
case 'x':
{
int warn2 = IN_SOME;
! pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
--- 494,506 ----
pt = _conv(t->tm_wday, "%d", pt, ptlim);
continue;
case 'X':
! pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp, tz);
continue;
case 'x':
{
int warn2 = IN_SOME;
! pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2, tz);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
***************
*** 490,498 ****
pt = _add(t->TM_ZONE, pt, ptlim);
else
#endif /* defined TM_ZONE */
! if (t->tm_isdst >= 0)
! pt = _add(tzname[t->tm_isdst != 0],
pt, ptlim);
/*
** C99 says that %Z must be replaced by the
** empty string if the time zone is not
--- 522,539 ----
pt = _add(t->TM_ZONE, pt, ptlim);
else
#endif /* defined TM_ZONE */
! if (t->tm_isdst >= 0) {
! time_t tval;
! struct tm tcopy = *t;
! const struct ttinfo * ttisp;
!
! if (time_make(&tval, &tcopy, tz) != 0)
! continue;
! ttisp = _tz_getttype(&tval, tz);
!
! pt = _add(&tz->chars[ttisp->tt_abbrind],
pt, ptlim);
+ }
/*
** C99 says that %Z must be replaced by the
** empty string if the time zone is not
***************
*** 514,545 ****
** be computed by looking only at
** tm_isdst. This requirement is
** incorrect, since it means the code
! ** must rely on magic (in this case
! ** altzone and timezone), and the
** magic might not have the correct
! ** offset. Doing things correctly is
! ** tricky and requires disobeying C99;
! ** 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
! ** determinable, so output nothing if the
! ** appropriate variables are not available.
*/
! if (t->tm_isdst == 0)
! #ifdef USG_COMPAT
! diff = -timezone;
! #else /* defined USG_COMPAT */
! continue;
! #endif /* !defined USG_COMPAT */
! else
! #ifdef ALTZONE
! diff = -altzone;
! #else /* !defined ALTZONE */
! continue;
! #endif /* !defined ALTZONE */
#endif /* !defined TM_GMTOFF */
if (diff < 0) {
sign = "-";
--- 555,579 ----
** be computed by looking only at
** tm_isdst. This requirement is
** incorrect, since it means the code
! ** must rely on magic, and the
** magic might not have the correct
! ** offset.
! ** We perform hopefully-correct magic by
! ** peeking inside the struct tzinfo
! ** structure, in an encapsulation
! ** violation, to find the appropriate
! ** transition type.
*/
! {
! time_t tval;
! struct tm tcopy = *t;
! const struct ttinfo * ttisp;
!
! if (time_make(&tval, &tcopy, tz) != 0)
! continue;
! ttisp = _tz_getttype(&tval, tz);
! diff = ttisp->tt_gmtoff;
! }
#endif /* !defined TM_GMTOFF */
if (diff < 0) {
sign = "-";
***************
*** 553,559 ****
continue;
case '+':
pt = _fmt(Locale->date_fmt, t, pt, ptlim,
! warnp);
continue;
case '%':
/*
--- 587,593 ----
continue;
case '+':
pt = _fmt(Locale->date_fmt, t, pt, ptlim,
! warnp, tz);
continue;
case '%':
/*
***************
*** 603,609 ****
static const char locale_home[] = LOCALE_HOME;
static const char lc_time[] = "LC_TIME";
static char * locale_buf;
- static char locale_buf_C[] = "C";
int fd;
int oldsun; /* "...ain't got nothin' to do..." */
--- 637,642 ----
***************
*** 664,671 ****
goto bad_locale;
bufsize = namesize + st.st_size;
locale_buf = NULL;
! lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
! malloc(bufsize) : realloc(lbuf, bufsize);
if (lbuf == NULL)
goto bad_locale;
(void) strcpy(lbuf, name);
--- 697,703 ----
goto bad_locale;
bufsize = namesize + st.st_size;
locale_buf = NULL;
! lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
if (lbuf == NULL)
goto bad_locale;
(void) strcpy(lbuf, name);
***************
*** 714,720 ****
(void) close(fd);
no_locale:
localebuf = C_time_locale;
! locale_buf = locale_buf_C;
return &localebuf;
}
#endif /* defined LOCALE_HOME */
--- 746,752 ----
(void) close(fd);
no_locale:
localebuf = C_time_locale;
! locale_buf = NULL;
return &localebuf;
}
#endif /* defined LOCALE_HOME */
diff -crN -x *~ ../tzcode2001c/struct_tzinfo.h thread-safe-2/struct_tzinfo.h
*** ../tzcode2001c/struct_tzinfo.h Wed Dec 31 19:00:00 1969
--- thread-safe-2/struct_tzinfo.h Mon Jun 18 15:46:07 2001
***************
*** 0 ****
--- 1,58 ----
+ #ifndef STRUCT_TIMEZONE_H
+
+ #define STRUCT_TIMEZONE_H
+
+ #include "private.h"
+ #include "tzfile.h"
+
+ /*
+ ** This file is in the public domain, so clarified as of
+ ** 1996-06-05 by Arthur David Olson (arthur_david_olson(a)nih.gov)
+ */
+
+ /*
+ ** This header is for use ONLY with the time conversion code.
+ ** There is no guarantee that it will remain unchanged,
+ ** or that it will remain at all.
+ ** Do NOT copy it to any system include directory.
+ ** Thank you!
+ */
+
+ struct ttinfo { /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* TRUE if transition is std time */
+ int tt_ttisgmt; /* TRUE if transition is UTC */
+ };
+
+ struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+ };
+
+ #define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+ #ifdef TZNAME_MAX
+ #define MY_TZNAME_MAX TZNAME_MAX
+ #endif /* defined TZNAME_MAX */
+ #ifndef TZNAME_MAX
+ #define MY_TZNAME_MAX 255
+ #endif /* !defined TZNAME_MAX */
+
+ struct tzinfo {
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof "GMT"),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+ };
+
+
+
+ #endif /* !defined STRUCT_TIMEZONE_H */
diff -crN -x *~ ../tzcode2001c/time_make.3 thread-safe-2/time_make.3
*** ../tzcode2001c/time_make.3 Wed Dec 31 19:00:00 1969
--- thread-safe-2/time_make.3 Mon Jun 18 15:46:08 2001
***************
*** 0 ****
--- 1,277 ----
+ .TH TIME_MAKE 3
+ .SH NAME
+ time_make, time_breakup, strftime_z \- manipulate time-zone-dependent
+ time conversions in a thread-safe manner
+ .SH SYNOPSIS
+ .nf
+ .B #include <time.h>
+ .B #include <sys/types.h>
+ .PP
+ .B int time_make(clock, tm, tz)
+ .B time_t *clock;
+ .B struct tm *tm;
+ .B const struct tzinfo *tz;
+ .PP
+ .B int time_breakup(result, clock, tz)
+ .B struct tm *result;
+ .B const time_t *clock;
+ .B const struct tzinfo *tz;
+ .PP
+ .B size_t strftime_z(buf, maxsize, format, timeptr, tz)
+ .B char * restrict buf;
+ .B size_t maxsize;
+ .B const char * restrict format;
+ .B const struct tm * restrict timeptr;
+ .B const struct tzinfo * restrict tz;
+ .PP
+ .B cc ... -ltz
+ .fi
+ .SH DESCRIPTION
+ .B Time_make()
+ interprets the broken-down time in
+ .I *tm
+ as a local time in the timezone specified in
+ .IR *tz,
+ and writes the corresponding value into
+ .IR *clock,
+ using the same encoding as that of the values returned by the
+ .I time function.
+ .PP
+ The original values of the
+ .B tm_wday
+ and
+ .B tm_yday
+ components of
+ .I *tm
+ are ignored, and the original values of the other components are not
+ restricted to their normal ranges. (A positive or zero value for
+ .B tm_isdst
+ causes
+ .B time_make()
+ to presume initially that summer time (for example, Daylight Saving Time) is
+ or is not in effect for the specified time, respectively. A negative value
+ for
+ .B tm_isdst
+ causes the
+ .B time_make()
+ function to attempt to divine whether summer time is in effect for the
+ specified time.)
+ .PP
+ On successful completion, the values of the
+ .B tm_wday
+ and
+ .B tm_yday
+ components of
+ .I *tm
+ are set appropriately, and the other components are set to represent the
+ specified calendar time, but with their values forced to their normal
+ ranges; the final value of
+ .B tm_mday
+ is not set until
+ .B tm_mon
+ and
+ .B tm_year
+ are determined.
+ .PP
+ .B Time_breakup()
+ converts the time value in
+ .I *clock
+ to a broken-out time for the value, which it places in
+ .IR *result ,
+ after adjusting for the time zone specified in
+ .IR *tz .
+ .PP
+ .B Strftime_z()
+ formats the information from
+ .I timeptr
+ into the buffer
+ .I buf
+ according to the string pointed to by
+ .IR format .
+ The
+ .I format
+ string follows the same specification as for the function strftime(3),
+ except that the values for the conversion specifiers
+ .B %Z
+ and
+ .B %z
+ are obtained from the time zone
+ .IR tz .
+ If
+ .B strftime_z()
+ is passed a
+ .B struct tm
+ created by a call to
+ .B time_make()
+ or
+ .B time_breakup()
+ which was given a different time zone object than
+ .IR tz ,
+ or if it is passed a
+ .B struct tm
+ created by
+ .BR localtime() ,
+ .BR localtime_r() ,
+ .BR gmtime() ,
+ .BR gmtime_r() ,
+ .BR mktime() ,
+ or
+ .BR timegm() ,
+ the behavior of the
+ .B %Z
+ and
+ .B %z
+ conversion specifiers is undefined.
+ .SH RETURN VALUES
+ The
+ .B time_make()
+ function returns a value of
+ .B 0
+ on sucessful completion, sets
+ .IR *clock ,
+ and normalizes
+ .IR *tm .
+ On failure, it returns an error number, and leaves
+ .I *clock
+ and
+ .I *tm
+ in an indeterminate state.
+ .PP
+ The
+ .B time_breakup()
+ function returns
+ .BR 0 ,
+ and fills in
+ .IR *result ,
+ always.
+ .PP
+ On successful completion,
+ .B strftime_z()
+ fills in
+ .I buf
+ and returns the number of bytes converted. On failure,
+ .B strftime_z()
+ returns the number of bytes of buffer that would be required to fully
+ perform the conversion (including the terminating
+ .BR NUL ),
+ and leaves
+ .I buf
+ in an indeterminate state.
+ .SH ERRORS
+ .B time_make()
+ shall fail if:
+ .IP \fBERANGE\fR
+ .I *tm
+ does not represent a time representable by a time_t value.
+ .PP
+ .B time_make()
+ may fail if:
+ .IP \fBEINVAL\fR
+ .I *tm
+ does not represent a possible time in the timezone
+ .IR *tz .
+ (For instance, it indicates a time occuring during a leap-forward interval.)
+ .PP
+ No error status codes are defined for
+ .BR time_breakup() .
+ .PP
+ .B Strftime_z()
+ does not set or return error statuses.
+ .SH SEE ALSO
+ getenv(3),
+ newctime(3),
+ newstrftime(3),
+ newtzset(3),
+ pthread_settz(3),
+ time(2),
+ tz_prep(3),
+ tzfile(5)
+ .SH NOTES
+ .B time_make()
+ is a generalization of the ISO C function
+ .B mktime(3)
+ and the BSD/tzcode function
+ .BR timegm(3) .
+ .B time_breakup()
+ is a generalization of the ISO C functions
+ .B localtime(3)
+ and
+ .BR gmtime(3) ,
+ and the POSIX functions
+ .B localtime_r(3)
+ and
+ .BR gmtime_r(3) .
+ .B strftime_z()
+ is a generalization of the ISO C function
+ .BR strftime(3) .
+ .PP
+ Depending on how
+ .B tzcode
+ was compiled,
+ .B time_make()
+ may handle impossible time values (e.g., values that fall during a
+ leap-forward interval) by failing and returning
+ .BR EINVAL ,
+ or by normalizing
+ .I *tm
+ to a nearby valid time.
+ .PP
+ The return value of
+ .B strftime_z()
+ on error has changed from that of
+ .BR strftime() .
+ .B Strftime()
+ returns
+ .B 0
+ if
+ .I buf
+ is not large enough.
+ .BR Strftime_z() ,
+ instead, follows the example of ISO C 99's
+ .BR snprintf(3) ,
+ by returning the number of bytes that would be required to fully convert the
+ format string. This allows an appropriately-sized buffer to be allocated in
+ one step, rather than requiring a binary search. Error returns can be
+ easily detected by checking (\fIret\fR <= \fImaxbuf\fR).
+ .PP
+ The names and calling conventions of
+ .B time_make()
+ and
+ .B time_breakup()
+ are inspired by Markus Kuhn's proposed
+ .B xtime_make()
+ and
+ .B xtime_breakup()
+ functions. The
+ .B xtime_*
+ functions represent time using
+ .BR "struct xtime" ,
+ which is a much more sophisticated and better-defined representation of time
+ than
+ .BR time_t .
+ The
+ .B time_*
+ functions are designed to be less ambitious while still leaving room
+ for these future improvements.
+ .PP
+ Following Kuhn,
+ .B time_make()
+ corrects a flaw of
+ .B mktime()
+ and
+ .BR timegm() .
+ Those functions use the value
+ .B (time_t)-1
+ to represent an error return status. However, this value can also be a
+ correct translation of a
+ .B struct tm
+ representing the time "December 31, 1969, 23:59:59 GMT" (assuming POSIX
+ .B time_t
+ values),
+ and these cases are not distinguished. Instead,
+ .B time_make()
+ uses an out-of-band method to indicate error conditions, leaving
+ the entire
+ .B time_t
+ space free to represent valid values.
+ .\" @(#)time_make.3 1.0
diff -crN -x *~ ../tzcode2001c/tz_prep.3 thread-safe-2/tz_prep.3
*** ../tzcode2001c/tz_prep.3 Wed Dec 31 19:00:00 1969
--- thread-safe-2/tz_prep.3 Mon Jun 18 15:46:08 2001
***************
*** 0 ****
--- 1,120 ----
+ .TH TZ_PREP 3
+ .SH NAME
+ tz_prep, tz_free \- create and destroy time zone objects
+ .SH SYNOPSIS
+ .nf
+ .B #include <time.h>
+ .B #include <sys/types.h>
+ .PP
+ .B int tz_prep(tz, tzstring)
+ .B struct tzinfo** tz;
+ .B const char *tzstring;
+ .PP
+ .B void tz_free()
+ .B struct tzinfo* tzobj;
+ .PP
+ .B cc ... -ltz
+ .fi
+ .SH DESCRIPTION
+ The
+ .B tz_prep()
+ function creates a new time zone object, corresponding to the
+ given time zone name
+ .IR tzstring .
+ .I tzstring
+ is a pointer to a string, or
+ .BR NULL .
+ .PP
+ If
+ .I tzstring
+ is
+ .BR NULL ,
+ the returned time zone will represent the system's best approximation of its
+ current wall clock time. If
+ .I tzstring
+ is the zero-length string \fB""\fR, the returned time zone will represent the
+ system's best approximation of Coordinated Universal Time (UTC).
+ .PP
+ All other values of
+ .I tzstring
+ are interpreted in the same manner as the
+ .B TZ
+ environment variable, as specified in environ(7).
+ .PP
+ The
+ .B tz_free()
+ function frees a time zone object allocated by
+ .BR tz_prep() .
+ .SH RETURN VALUES
+ On successful completion,
+ .B tz_prep()
+ returns a value of
+ .BR 0
+ and fills in
+ .IR *tz .
+ On failure, an error number is returned and no resources are allocated.
+ .PP
+ The
+ .B tz_free()
+ function returns no value.
+ .SH ERRORS
+ The
+ .B tz_prep()
+ function will fail if:
+ .IP \fBENOMEM\fR
+ Not enough memory is available to create the time zone object.
+ .IP \fBENOENT\fR
+ No known time zone corresponds to tzname.
+ .PP
+ The
+ .B tz_prep()
+ function may fail if there is a problem with the system time
+ zone database which caused retrieval of the time zone information to fail
+ unexpectedly. In this case any appropriate error number may be returned.
+ .PP
+ The
+ .B tz_free()
+ function does not return any errors.
+ .SH FILES
+ .ta \w'/usr/local/etc/zoneinfo/posixrules\0\0'u
+ /usr/local/etc/zoneinfo time zone information directory
+ .br
+ /usr/local/etc/zoneinfo/localtime local time zone file
+ .br
+ /usr/local/etc/zoneinfo/posixrules used with POSIX-style TZ's
+ .br
+ /usr/local/etc/zoneinfo/GMT for UTC leap seconds
+ .sp
+ If
+ .B /usr/local/etc/zoneinfo/GMT
+ is absent,
+ UTC leap seconds are loaded from
+ .BR /usr/local/etc/zoneinfo/posixrules .
+ .SH SEE ALSO
+ environ(7),
+ getenv(3),
+ newlocaltime(3),
+ newstrftime(3),
+ newtzset(3),
+ pthread_settz(3),
+ time(2),
+ time_make(3),
+ tzfile(5)
+ .SH NOTES
+ This interface is designed so that calling
+ \fBtz_prep\fR(\fBgetenv\fR(\fB"TZ"\fR))
+ will return an object describing the default time zone object that
+ non-thread-aware versions of the time functions will use by default,
+ provided
+ .B TZ
+ (if set) is set to a valid time zone name.
+ .PP
+ .B tzset()
+ interprets a
+ .B TZ
+ value of the empty string \fB""\fR as meaning "UTC without leap-seconds".
+ .BR tz_prep() ,
+ however, defines it to mean the standard UTC value, with whatever
+ leap-second support the system uses by default. On POSIX-compliant systems,
+ which may not use leap second support by default, these will be equivalent.
+ .\" @(#)tz_prep.3 1.0
diff -crN -x *~ ../tzcode2001c/tzthread-dummy.c thread-safe-2/tzthread-dummy.c
*** ../tzcode2001c/tzthread-dummy.c Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-dummy.c Mon Jun 18 16:15:27 2001
***************
*** 0 ****
--- 1,42 ----
+ /*
+ ** This file is in the public domain. Contributed by Jonathan Lennox
+ ** <lennox(a)cs.columbia.edu>
+ */
+
+ #include "private.h"
+ #include "tzthread-dummy.h"
+ #include "struct_tzinfo.h"
+
+ /*
+ ** "Dummy" definitions of the TZ thread-safety operations. For use in
+ ** a non-threaded environment.
+ */
+
+ #ifdef ALL_STATE
+ struct tzinfo * _tz_lclptr;
+ struct tzinfo * _tz_gmtptr;
+
+ struct tzinfo * _tz_alloc_lclptr P((void))
+ {
+ _tz_lclptr = (struct tzinfo *) malloc(sizeof *_tz_lclptr);
+ return _tz_lclptr;
+ }
+
+
+ struct tzinfo * _tz_alloc_gmtptr P((void))
+ {
+ _tz_gmtptr = (struct tzinfo *) malloc(sizeof *_tz_gmtptr);
+ return _tz_gmtptr;
+ }
+
+ #else /* !defined ALL_STATE */
+
+ struct tzinfo _tz_lclmem;
+ struct tzinfo _tz_gmtmem;
+
+ #endif /* !defined ALL_STATE */
+
+ struct tm _tz_tm;
+
+ char _tz_asctime_buf[ASCTIME_BUF_SIZE];
+
diff -crN -x *~ ../tzcode2001c/tzthread-dummy.h thread-safe-2/tzthread-dummy.h
*** ../tzcode2001c/tzthread-dummy.h Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-dummy.h Mon Jun 18 16:15:17 2001
***************
*** 0 ****
--- 1,127 ----
+ #ifndef TZTHREAD_DUMMY_H
+ #define TZTHREAD_DUMMY_H
+
+ /*
+ ** This file is in the public domain. Contributed by Jonathan Lennox
+ ** <lennox(a)cs.columbia.edu>
+ */
+
+ #include "private.h"
+
+ /*
+ ** Thread primitives for tzcode. TZcode requires the following primitive
+ ** functions to be defined (probably as macros):
+ ** void LOCK_LCLPTR(void) - serialize references to the process-wide or
+ ** thread-specific local timezone object.
+ ** void LOCK_GMTPTR(void) - serialize references to write to the global
+ ** UTC timezone object.
+ ** void UNLOCK_LCLPTR(void) - Unlock the local timezone object.
+ ** void UNLOCK_GMTPTR(void) - Unlock the UTC timezone object.
+ **
+ ** struct tzinfo * GET_LCLPTR(int *process)
+ ** - Return the current local timezone object.
+ ** The argument 'process' is filled in with
+ ** a boolean value indicating whether the
+ ** returned object is process-wide (true)
+ ** or thread-specific (false).
+ ** This will only be called when the local
+ ** pointer is locked.
+ ** The thread primitive is not responsible for
+ ** filling in the value, only for assuring that
+ ** sizeof(struct tzinfo) bytes are available.
+ ** This function returns NULL on failure.
+ **
+ ** struct tzinfo * GET_GMTPTR(void)
+ ** - Return the UTC timezone object.
+ ** This *can* be called when the GMT pointer
+ ** is not locked. This function is responsible
+ ** for any resulting serialization necessary.
+ ** Any attempts to write to the object *will*
+ ** lock the object first, however.
+ ** The thread primitive is not responsible for
+ ** filling in the value, only for assuring that
+ ** sizeof(struct tzinfo) bytes are available.
+ ** This function returns NULL on failure.
+ **
+ ** struct tm *GET_TM_BUF(void) - Get a pointer to a struct tm, suitable for
+ ** use as a return value for the localtime()
+ ** family of functions. In a threaded
+ ** environment, this buffer should be thread-
+ ** local.
+ **
+ ** int SET_THREAD_LCLPTR(const struct tzinfo *)
+ ** - Set the thread-specific local timezone
+ ** object to (a copy of) the argument. Calls to
+ ** GET_LCLPTR() from the current thread should
+ ** from now on return this value.
+ ** If a previous local pointer was set, free
+ ** it first.
+ ** Return an errno error code on failure, or 0.
+ **
+ ** int CLEAR_THREAD_LCLPTR(void)
+ ** - Clear the thread-specific local timezone
+ ** object to the argument. Calls to
+ ** GET_LCLPTR() from the current thread should
+ ** from now on return the global pointer.
+ ** If a previous local pointer was set, free
+ ** it first.
+ ** Return an errno error code on failure, or 0.
+ */
+
+ /*
+ ** "Dummy" definitions of the TZ thread-safety operations. For use in
+ ** a non-threaded environment.
+ */
+
+ /*
+ ** There are actually two dummy TZ implementations -- one for ALL_STATE
+ ** (structures allocated as-needed) and one for static.
+ */
+
+ #ifdef ALL_STATE
+ extern struct tzinfo * _tz_lclptr;
+ extern struct tzinfo * _tz_gmtptr;
+
+ #define GET_LCLPTR(_process_wide) \
+ (*(_process_wide) = 1, \
+ (_tz_lclptr != NULL ? _tz_lclptr : _tz_alloc_lclptr()))
+ #define GET_GMTPTR() \
+ (_tz_gmtptr != NULL ? _tz_gmtptr : _tz_alloc_gmtptr())
+
+ extern struct tzinfo * _tz_alloc_lclptr P((void));
+ extern struct tzinfo * _tz_alloc_gmtptr P((void));
+
+ #else /* !defined ALL_STATE */
+
+ extern struct tzinfo _tz_lclmem;
+ extern struct tzinfo _tz_gmtmem;
+ #define GET_LCLPTR(_process_wide) (*(_process_wide) = 1, &_tz_lclmem)
+ #define GET_GMTPTR() (&_tz_gmtmem)
+
+ #endif /* !defined ALL_STATE */
+
+ /*
+ ** These dummy implementations apply regardless of ALL_STATE, since we're
+ ** non-threaded.
+ */
+
+ #define LOCK_LCLPTR() do { } while(0)
+ #define LOCK_GMTPTR() do { } while(0)
+
+ #define UNLOCK_LCLPTR() do { } while(0)
+ #define UNLOCK_GMTPTR() do { } while(0)
+
+ extern struct tm _tz_tm;
+
+ #define GET_TM_BUF() (&_tz_tm)
+
+ extern char _tz_asctime_buf[ASCTIME_BUF_SIZE];
+
+ #define GET_ASCTIME_BUF() (_tz_asctime_buf)
+
+ #define SET_THREAD_LCLPTR(x) (ENOSYS)
+ #define CLEAR_THREAD_LCLPTR() (ENOSYS)
+
+ #endif /* !defined TZTHREAD_DUMMY_H */
+
+
diff -crN -x *~ ../tzcode2001c/tzthread-posix.c thread-safe-2/tzthread-posix.c
*** ../tzcode2001c/tzthread-posix.c Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-posix.c Mon Jun 18 16:15:47 2001
***************
*** 0 ****
--- 1,282 ----
+ /*
+ ** This file is in the public domain. Contributed by Jonathan Lennox
+ ** <lennox(a)cs.columbia.edu>
+ */
+
+ #include "private.h"
+ #include "tzthread-posix.h"
+ #include "struct_tzinfo.h"
+
+ #include <pthread.h>
+
+ /*
+ ** POSIX Thread versions of the TZ thread-safety operations.
+ */
+
+ static pthread_mutex_t _tz_lcl_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_mutex_t _tz_gmt_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+ static int get_lclptr_key P((pthread_key_t *));
+
+ #ifdef ALL_STATE
+ static struct tzinfo * _tz_lclptr;
+ static struct tzinfo * _tz_gmtptr;
+
+ struct tzinfo*
+ _tz_get_lclptr(process_wide)
+ int * process_wide;
+ {
+ pthread_key_t lclptr_key;
+ struct tzinfo *ret;
+
+ if (get_lclptr_key(&lclptr_key) != 0) {
+ return NULL;
+ }
+ if ((ret = pthread_getspecific(lclptr_key)) != NULL) {
+ *process_wide = 0;
+ return ret;
+ }
+
+ *process_wide = 1;
+
+ if (_tz_lclptr == NULL) {
+ _tz_lclptr = (struct tzinfo *) malloc(sizeof *_tz_lclptr);
+ }
+ return _tz_lclptr;
+ }
+
+
+ struct tzinfo*
+ _tz_get_gmtptr P((void))
+ {
+ /*
+ ** Fast-path the common case. Nothing sets or alters gmtptr
+ ** once it's set.
+ */
+ if (_tz_gmtptr != NULL) {
+ return _tz_gmtptr;
+ }
+
+ _tz_lock_gmtptr();
+ /*
+ ** Check the pointer again, in case another thread just
+ ** allocated it.
+ */
+ if (_tz_gmtptr != NULL) {
+ _tz_unlock_gmtptr();
+ return _tz_gmtptr;
+ }
+
+ _tz_gmtptr = (struct tzinfo *) malloc(sizeof *_tz_gmtptr);
+ _tz_unlock_gmtptr();
+
+ return _tz_gmtptr;
+ }
+
+ #else /* !defined ALL_STATE */
+
+ static struct tzinfo _tz_lclmem;
+ static struct tzinfo _tz_gmtmem;
+
+ struct tzinfo *
+ _tz_get_lclptr(process_wide)
+ int * process_wide;
+ {
+ pthread_key_t lclptr_key;
+ struct tzinfo *ret;
+
+ if (get_lclptr_key(&lclptr_key) != 0) {
+ return NULL;
+ }
+ if ((ret = pthread_getspecific(lclptr_key)) != NULL) {
+ *process_wide = 0;
+ return ret;
+ }
+
+ *process_wide = 1;
+
+ return &_tz_lclmem;
+ }
+
+
+ struct tzinfo *
+ _tz_get_gmtptr P((void))
+ {
+ return &_tz_gmtmem;
+ }
+
+ #endif /* defined ALL_STATE */
+
+ void _tz_lock_lclptr P((void))
+ {
+ pthread_mutex_lock(&_tz_lcl_mutex);
+ }
+
+
+ void _tz_lock_gmtptr P((void))
+ {
+ pthread_mutex_lock(&_tz_gmt_mutex);
+ }
+
+
+ extern void _tz_unlock_lclptr P((void))
+ {
+ pthread_mutex_unlock(&_tz_lcl_mutex);
+ }
+
+
+ extern void _tz_unlock_gmtptr P((void))
+ {
+ pthread_mutex_unlock(&_tz_gmt_mutex);
+ }
+
+
+
+ struct tm*
+ _tz_get_tm_buf P((void))
+ {
+ static pthread_mutex_t tmbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_key_t tmbuf_key;
+ static int tmbuf_initialized = 0;
+ struct tm *p_tm;
+ int err;
+
+ if (!tmbuf_initialized) {
+ pthread_mutex_lock(&tmbuf_mutex);
+ if (!tmbuf_initialized) {
+ if ((err = pthread_key_create(&tmbuf_key, free))
+ != 0) {
+ errno = err;
+ pthread_mutex_unlock(&tmbuf_mutex);
+ return NULL;
+ }
+ tmbuf_initialized = 1;
+ }
+ pthread_mutex_unlock(&tmbuf_mutex);
+ }
+
+ p_tm = pthread_getspecific(tmbuf_key);
+ if (p_tm == NULL) {
+ if ((p_tm = (struct tm *) malloc(sizeof(struct tm))) == NULL) {
+ return NULL;
+ }
+ if ((err = pthread_setspecific(tmbuf_key, p_tm)) != 0) {
+ errno = err;
+ free(p_tm);
+ return NULL;
+ }
+ }
+ return p_tm;
+ }
+
+
+ char* _tz_get_asctime_buf P((void))
+ {
+ static pthread_mutex_t asctime_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_key_t asctime_buf_key;
+ static int asctime_buf_initialized = 0;
+ char *asctime_buf;
+ int err;
+
+ if (!asctime_buf_initialized) {
+ pthread_mutex_lock(&asctime_buf_mutex);
+ if (!asctime_buf_initialized) {
+ if ((err = pthread_key_create(&asctime_buf_key, free))
+ != 0) {
+ errno = err;
+ pthread_mutex_unlock(&asctime_buf_mutex);
+ return NULL;
+ }
+ asctime_buf_initialized = 1;
+ }
+ pthread_mutex_unlock(&asctime_buf_mutex);
+ }
+
+ asctime_buf = pthread_getspecific(asctime_buf_key);
+ if (asctime_buf == NULL) {
+ if ((asctime_buf = (char *) malloc(ASCTIME_BUF_SIZE))
+ == NULL) {
+ return NULL;
+ }
+ if ((err = pthread_setspecific(asctime_buf_key, asctime_buf))
+ != 0) {
+ errno = err;
+ free(asctime_buf);
+ return NULL;
+ }
+ }
+ return asctime_buf;
+ }
+
+
+ int _tz_set_thread_lclptr(tz)
+ const struct tzinfo * tz;
+ {
+ pthread_key_t lclptr_key;
+ int err;
+ struct tzinfo *old, *new;
+
+ if ((err = get_lclptr_key(&lclptr_key)) != 0) {
+ return err;
+ }
+ if ((old = pthread_getspecific(lclptr_key)) != NULL) {
+ free(old);
+ }
+
+ new = (struct tzinfo *) malloc(sizeof(struct tzinfo));
+ if (new == NULL) {
+ return ENOMEM;
+ }
+
+ *new = *tz;
+
+ if ((err = pthread_setspecific(lclptr_key, new)) != 0) {
+ free(new);
+ return err;
+ }
+ return 0;
+ }
+
+
+ int _tz_clear_thread_lclptr P((void))
+ {
+ pthread_key_t lclptr_key;
+ int err;
+ struct tzinfo *old;
+
+ if ((err = get_lclptr_key(&lclptr_key)) != 0) {
+ return err;
+ }
+ if ((old = pthread_getspecific(lclptr_key)) != NULL) {
+ free(old);
+ }
+
+ return pthread_setspecific(lclptr_key, NULL);
+ }
+
+
+ static int get_lclptr_key(lclptr_key_p)
+ pthread_key_t * lclptr_key_p;
+ {
+ static pthread_mutex_t lclptr_key_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static pthread_key_t lclptr_key;
+ static int lclptr_key_initialized = 0;
+
+ int err;
+
+ if (!lclptr_key_initialized) {
+ pthread_mutex_lock(&lclptr_key_mutex);
+ if (!lclptr_key_initialized) {
+ if ((err = pthread_key_create(&lclptr_key, free))
+ != 0) {
+ pthread_mutex_unlock(&lclptr_key_mutex);
+ return err;
+ }
+ lclptr_key_initialized = 1;
+ }
+ pthread_mutex_unlock(&lclptr_key_mutex);
+ }
+
+ *lclptr_key_p = lclptr_key;
+ return 0;
+ }
diff -crN -x *~ ../tzcode2001c/tzthread-posix.h thread-safe-2/tzthread-posix.h
*** ../tzcode2001c/tzthread-posix.h Wed Dec 31 19:00:00 1969
--- thread-safe-2/tzthread-posix.h Mon Jun 18 16:20:38 2001
***************
*** 0 ****
--- 1,109 ----
+ #ifndef TZTHREAD_POSIX_H
+ #define TZTHREAD_POSIX_H
+
+ /*
+ ** This file is in the public domain. Contributed by Jonathan Lennox
+ ** <lennox(a)cs.columbia.edu>
+ */
+
+ /*
+ ** Thread primitives for tzcode. TZcode requires the following primitive
+ ** functions to be defined (probably as macros):
+ ** void LOCK_LCLPTR(void) - serialize references to the process-wide or
+ ** thread-specific local timezone object.
+ ** void LOCK_GMTPTR(void) - serialize references to write to the global
+ ** UTC timezone object.
+ ** void UNLOCK_LCLPTR(void) - Unlock the local timezone object.
+ ** void UNLOCK_GMTPTR(void) - Unlock the UTC timezone object.
+ **
+ ** struct tzinfo * GET_LCLPTR(int *process)
+ ** - Return the current local timezone object.
+ ** The argument 'process' is filled in with
+ ** a boolean value indicating whether the
+ ** returned object is process-wide (true)
+ ** or thread-specific (false).
+ ** This will only be called when the local
+ ** pointer is locked.
+ ** The thread primitive is not responsible for
+ ** filling in the value, only for assuring that
+ ** sizeof(struct tzinfo) bytes are available.
+ ** This function returns NULL on failure.
+ **
+ ** struct tzinfo * GET_GMTPTR(void)
+ ** - Return the UTC timezone object.
+ ** This *can* be called when the GMT pointer
+ ** is not locked. This function is responsible
+ ** for any resulting serialization necessary.
+ ** Any attempts to write to the object *will*
+ ** lock the object first, however.
+ ** The thread primitive is not responsible for
+ ** filling in the value, only for assuring that
+ ** sizeof(struct tzinfo) bytes are available.
+ ** This function returns NULL on failure.
+ **
+ ** struct tm *GET_TM_BUF(void) - Get a pointer to a struct tm, suitable for
+ ** use as a return value for the localtime()
+ ** family of functions. In a threaded
+ ** environment, this buffer should be thread-
+ ** local.
+ **
+ ** int SET_THREAD_LCLPTR(struct tzinfo *)
+ ** - Set the thread-specific local timezone
+ ** object to the argument. Calls to
+ ** GET_LCLPTR() from the current thread should
+ ** from now on return this value.
+ ** If a previous local pointer was set, free
+ ** it first.
+ ** Return an errno error code on failure, or 0.
+ **
+ ** int CLEAR_THREAD_LCLPTR(void)
+ ** - Clear the thread-specific local timezone
+ ** object to the argument. Calls to
+ ** GET_LCLPTR() from the current thread should
+ ** from now on return the global pointer.
+ ** If a previous local pointer was set, free
+ ** it first.
+ ** Return an errno error code on failure, or 0.
+ */
+
+ #include "private.h"
+
+ /*
+ ** POSIX Thread versions of the TZ thread-safety operations.
+ */
+
+ extern struct tzinfo * _tz_get_lclptr P((int *));
+ extern struct tzinfo * _tz_get_gmtptr P((void));
+
+ #define GET_LCLPTR _tz_get_lclptr
+ #define GET_GMTPTR _tz_get_gmtptr
+
+ extern void _tz_lock_lclptr P((void));
+ extern void _tz_lock_gmtptr P((void));
+
+ extern void _tz_unlock_lclptr P((void));
+ extern void _tz_unlock_gmtptr P((void));
+
+ #define LOCK_LCLPTR _tz_lock_lclptr
+ #define LOCK_GMTPTR _tz_lock_gmtptr
+
+ #define UNLOCK_LCLPTR _tz_unlock_lclptr
+ #define UNLOCK_GMTPTR _tz_unlock_gmtptr
+
+ extern struct tm* _tz_get_tm_buf P((void));
+
+ #define GET_TM_BUF _tz_get_tm_buf
+
+ extern char* _tz_get_asctime_buf P((void));
+
+ #define GET_ASCTIME_BUF _tz_get_asctime_buf
+
+ extern int _tz_set_thread_lclptr P((const struct tzinfo *));
+ extern int _tz_clear_thread_lclptr P((void));
+
+ #define SET_THREAD_LCLPTR _tz_set_thread_lclptr
+ #define CLEAR_THREAD_LCLPTR _tz_clear_thread_lclptr
+
+ #endif /* !defined TZTHREAD_POSIX_H */
+
+
diff -crN -x *~ ../tzcode2001c/tztimeext.h thread-safe-2/tztimeext.h
*** ../tzcode2001c/tztimeext.h Wed Dec 31 19:00:00 1969
--- thread-safe-2/tztimeext.h Mon Jun 18 15:46:07 2001
***************
*** 0 ****
--- 1,75 ----
+ #ifndef TZTIMEEXT_H
+ #define TZTIMEEXT_H
+
+ /*
+ ** This file is in the public domain. Contributed by Jonathan Lennox
+ ** <lennox(a)cs.columbia.edu>
+ */
+
+ /*
+ ** This is an extended version of <time.h> which provides function definitions
+ ** for the public functions defined by tzcode.
+ */
+
+ #include <time.h>
+
+ #if __STDC_VERSION__ < 199901
+ #undef restrict
+ #define restrict
+ #endif
+
+ struct tzinfo;
+
+ #if defined(__STDC__)
+
+ extern void tzsetwall(void);
+ extern struct tm * offtime(const time_t *, const long);
+ extern time_t timelocal(struct tm * const);
+ extern time_t timegm(struct tm * const);
+ extern time_t timeoff(struct tm * const, const long);
+
+ extern time_t time2posix(time_t);
+ extern time_t posix2time(time_t);
+
+ extern int tz_prep(struct tzinfo**, const char *);
+ extern void tz_free(struct tzinfo*);
+
+ extern int time_make(time_t *, struct tm *,
+ const struct tzinfo *);
+ extern int time_breakup(struct tm *, const time_t *,
+ const struct tzinfo *);
+ extern size_t strftime_z(char * restrict, size_t,
+ const char * restrict,
+ const struct tm * restrict,
+ const struct tzinfo * restrict);
+
+ extern int pthread_settz(const struct tzinfo *);
+
+ #else /* !defined(__STDC__) */
+
+ extern void tzsetwall();
+ extern struct tm * offtime();
+ extern time_t timelocal();
+ extern time_t timegm();
+ extern time_t timeoff();
+
+ extern time_t time2posix();
+ extern time_t posix2time();
+
+ extern int tz_prep();
+ extern void tz_free();
+
+ extern int time_make();
+ extern int time_breakup();
+ extern size_t strftime_z();
+
+ extern int pthread_settz();
+
+ #endif /* defined(__STDC__) */
+
+
+ #if __STDC_VERSION__ < 199901
+ #undef restrict
+ #endif
+
+ #endif /* TZTIMEEXT_H */
--
Jonathan Lennox
lennox(a)cs.columbia.edu
1
0
June 18, 2001
I discovered, in the course of working on my thread-safe time zone changes,
an interesting side-effect of mktime().
The localsub() function sets tzname[tm->tm_isdst] to the correct zone
abbreviation, as of the time it calculates. This is probably correct for
localsub() as called by localtime().
However, since time2sub() calls localsub() (as the funcp passed it by
mktime()), mktime() *also* sets tzname, to the time zone in effect at the
time of mktime()'s argument. Additionally, because of time2sub()'s binary
search, the other element of tzname (tzname[!tm->tm_isdst]) is often also
set, to something random. (This will usually be the other time zone
abbreviation in effect at that time, but it'd probably be pretty
straightforward to construct some weird cases where it's a zone abbreviation
used long in the past or future.)
Is this:
* a bug, which should be fixed in tzcode?
* a feature, and thus I need to make sure this behavior is maintained by any
changes I make to the guts of tzcode when adding the thread-safe functions?
* undefined behavior, since mktime() behaves "as though it called tzset()",
but tzset() is poorly defined in the presence of changing time zone
abbreviations?
The attached C program illustrates the behavior in question. (Europe/Riga
uses EET/EEST now, but used MSK/MSD in 1981.)
#include "tztimeext.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct tm then = {
0, /* int tm_sec; */
0, /* int tm_min; */
0, /* int tm_hour; */
1, /* int tm_mday; */
8, /* int tm_mon; */
81, /* int tm_year; */
};
int main()
{
time_t now, then_t;
struct tm* a_tm;
putenv("TZ=Europe/Riga");
now = time(NULL);
a_tm = localtime(&now);
printf("now = %ld -- %s", (long) now, asctime(a_tm));
printf("tzname = {%s, %s}\n", tzname[0], tzname[1]);
then_t = mktime(&then);
printf("then = %ld -- %s", (long) then_t, asctime(&then));
printf("tzname = {%s, %s}\n", tzname[0], tzname[1]);
exit(0);
}
--
Jonathan Lennox
lennox(a)cs.columbia.edu
3
2
Hi!
Next week (24-jun-2001 0:00), Argentina is changin the time zone adopting the DST method. This time we are moving from the current standard time (GMT-3) to the geographical time zone (GMT-4 new and historical Standard Time). The new schema will be:
1st Sunday of March: GMT-4 (Standard Time - WinterTime) ART
1st Sunday of October: GMT-3 (DST - Summer Time) ARST
I'm attaching the patched southamerica files I took form glibc.
Hope you find it useful.
Regards.
Carlos Barcenilla
1
0
June 15, 2001
> The strftime locale code attempts to read the locale cache even if the
> cache wasn't set up correctly. This leads to bogus output on your
program's
> second (and subsequent) call to strftime(), if lookup failed.
> ...
> This patch fixes this.
An alternate fix appears below.
--ado
*** 7.62/strftime.c Fri Jun 15 13:00:07 2001
--- 7.63/strftime.c Fri Jun 15 13:00:07 2001
***************
*** 603,609 ****
static const char locale_home[] = LOCALE_HOME;
static const char lc_time[] = "LC_TIME";
static char * locale_buf;
- static char locale_buf_C[] = "C";
int fd;
int oldsun; /* "...ain't got nothin' to do..."
*/
--- 603,608 ----
***************
*** 664,671 ****
goto bad_locale;
bufsize = namesize + st.st_size;
locale_buf = NULL;
! lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
! malloc(bufsize) : realloc(lbuf, bufsize);
if (lbuf == NULL)
goto bad_locale;
(void) strcpy(lbuf, name);
--- 663,669 ----
goto bad_locale;
bufsize = namesize + st.st_size;
locale_buf = NULL;
! lbuf = (lbuf == NULL) ? malloc(bufsize) : realloc(lbuf, bufsize);
if (lbuf == NULL)
goto bad_locale;
(void) strcpy(lbuf, name);
***************
*** 714,720 ****
(void) close(fd);
no_locale:
localebuf = C_time_locale;
! locale_buf = locale_buf_C;
return &localebuf;
}
#endif /* defined LOCALE_HOME */
--- 712,718 ----
(void) close(fd);
no_locale:
localebuf = C_time_locale;
! locale_buf = NULL;
return &localebuf;
}
#endif /* defined LOCALE_HOME */
1
0
The strftime locale code attempts to read the locale cache even if the
cache wasn't set up correctly. This leads to bogus output on your program's
second (and subsequent) call to strftime(), if lookup failed.
I discovered this because lookup failed for me on Solaris and FreeBSD -- on
Solaris because locales are kept as .so's, and on FreeBSD because the "C"
locale is left implicit and not stored on disk.
This patch fixes this.
--- tzcode2001c/strftime.c Tue Jun 5 13:49:50 2001
+++ tzcode2001c-Sun5/strftime.c Thu Jun 14 14:27:35 2001
@@ -604,6 +604,7 @@
static const char lc_time[] = "LC_TIME";
static char * locale_buf;
static char locale_buf_C[] = "C";
+ static int cache_good;
int fd;
int oldsun; /* "...ain't got nothin' to do..." */
@@ -629,7 +630,7 @@
** If the locale name is the same as our cache, use the cache.
*/
lbuf = locale_buf;
- if (lbuf != NULL && strcmp(name, lbuf) == 0) {
+ if (cache_good && lbuf != NULL && strcmp(name, lbuf) == 0) {
p = lbuf;
for (ap = (const char **) &localebuf;
ap < (const char **) (&localebuf + 1);
@@ -705,6 +706,7 @@
** Record the successful parse in the cache.
*/
locale_buf = lbuf;
+ cache_good = 1;
return &localebuf;
@@ -715,6 +717,7 @@
no_locale:
localebuf = C_time_locale;
locale_buf = locale_buf_C;
+ cache_good = 0;
return &localebuf;
}
#endif /* defined LOCALE_HOME */
--
Jonathan Lennox
lennox(a)cs.columbia.edu
1
0
One of the major shortcomings of the current time zone API defined by ISO C
and POSIX is that they rely on global data to define the current time zone.
If you're writing a server program which needs to talk to people all over
the world in their local time zones, this can lead to remarkable
difficulties.
Therefore, I've written up a proposal (attached) for a thread-safe API for
time zone functions. These extend (I think) the current time API in a
natural way. There are two forms: one purely re-entrant, and another one
that specifies thread-local timezone data to be used by the existing
time functions.
I've drawn a lot of inspiration for this work from Ulrich Drepper's work on
thread-aware POSIX locales (http://www.cygnus.com/~drepper/tllocale.ps.bz2)
* Are people interested in this?
* Do people think that my proposal is a sensible API?
* Would people be interested in seeing the resulting code?
* Would people be interested in helping write the code?
* Would the resulting code (assuming it's written sensibly) be
acceptable/appropriate for incorporation into tzcode?
Comments very welcome!
A proposal for thread-safe time zone information.
Three functions manipulate a struct tz:
Name
newtz -- create a time zone object
Synopsis
#include <time.h>
struct tz* newtz(const char *tzname);
Description
The newtz() function creates a new time zone object, corresponding to
the given time zone name. tzname is a pointer to a string representing
the name of the allocated time zone. It has the same syntax as the "TZ"
environment variable, both the POSIX forms and locally-defined symbolic
names.
The allocated struct tz contains all necessary information to represent
times in the specified time zone. It has one externally-visible element:
tz_name, an array of two ASCII strings containing the time zone
abbreviations for standard and daylight time in the current time zone.
struct tz {
const char *tz_name[2];
(private data)
};
If tzname is NULL, the returned struct tz describes the local wall-clock
time, as best as it is known by the local system.
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".)
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.
Return Value
If the function call succeeds, the return value is a pointer to a time
zone object, which should be released by a call to freetz(). If it
fails, it returns NULL and sets errno.
Errors
The newtz() function shall fail if:
ENOMEM
Not enough memory is available to create the time zone object.
ENOENT
No known time zone corresponds to tzname.
newtz() may also fail with other errno values if there is a problem
with the system time zone database.
Name
freetz -- Free resources allocated for a time zone object
Synopsis
#include <time.h>
void freetz(struct tz* tzobj);
Description
The freetz() function frees the resources allocated for a time zone
object returned by a call to newtz() or duptz().
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.
Return Value
None.
Errors
None.
Name
duptz -- Duplicate a time zone object
Synopsis
#include <time.h>
struct tz* duptz(const struct tz* tzobj);
Description
The duptz() function can be used to duplicate an existing time zone
object. A time zone object can be used at any time in multiple places but
if the lifetime can possibly end before all uses are finished one has to
create a duplicate.
Return Value
If the function succeeds it returns a pointer to a time zone object
identical to the one represented by the pointer passed in tzobj. If it
fails it returns NULL.
Errors
The duptz() function shall fail if:
ENOMEM
Not enough memory is available to create the duplicated time zone object.
The duptz() function may fail if:
EINVAL
tzobj does not point to a valid time zone object.
Modified time-zone-aware time manipulation functions:
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. (The return value's tm_zone
value is set correctly, if the system has tm_zone.)
char *
ctime_z(const time_t *clock, char *buf, const struct tz *tz);
Equivalent to ctime_r(), in the time zone represented by tz.
char *
asctime_z(const struct tm *tm, char *buf, const struct tz *tz);
Equivalent to asctime_r(), in the time zone represented by tz.
time_t
mktime_z(struct tm* tm, const struct tz *tz);
Equivalent to mktime() or timegm(), in the time zone represented by tz.
size_t
strftime_z(char *buf, size_t maxsize, const char *format,
const struct tm* timeptr, const struct tz *tz);
Equivalent to strftime(), in the time zone represented by tz.
char *
strptime_z(const char *buf, const char *format, struct tm *timeptr,
const struct tz *tz);
Equivalent to strptime(), in the time zone represented by tz. (This
affects only the interpretation of the %Z format specifier.)
Thread-support functions
Name
tzuse -- use time zone object in current thread.
Synopsis
#include <time.h>
void tzuse(const struct tz *tz);
Description
The tzuze() function is similar to the tzset() function, but it does not
affect the global time zone. Instead it selects the new time zone only
for the current thread. The time zone setting for all other threads
remains the same.
Once tzuse() has been called, all calls to the functions localtime(),
localtime_r(), ctime(), ctime_r(), asctime(), asctime_r(), mktime(),
strftime(), and strptime() in the current thread will use the current
thread's define time zone.
If tzuse() is called with a NULL pointer as its argument, the current
thread will again use the global time zone object.
--
Jonathan Lennox
lennox(a)cs.columbia.edu
9
33
How to prove that Volgograd (Russia) is in the same timezone as Moscow?
Well now, I have tried some more search phrases and found at last a couple
of Russian pages with lists of all major Russian cities. It seems that there
are only let's say one or two originals and many copies of them at several
sites. I found the following URLs.
http://www.bereg.ru/sprav_info/inform/time.shtml
http://newhouse.ru/help/timezone.html
http://real.businesstime.ru/trip/sprav/timez.html
http://www.mark-itt.ru/Collection/Russia/tz_rus.html
and more with the same list.
The table heading appearing in most lists reads (literally) translated:
Table of corrections of time of cities of the Russian Federation and of
states of neighbouring foreign countries relative to the city of Moscow.
All lists tell us that Volgograd is in the same timezone as Moscow (UTC+3).
But also Saratov and very likely the whole Saratovskaya oblast' is in the
Moscow timezone!
Ul'yanovsk, Samara and Astrakhan' are in UTC+4 according to these lists.
Oscar van Vlijmen
13-Jun-2001
1
0
I have a question about how to interpret Zone/Rule data.
I'm unsure about this zone:
Zone America/Chihuahua -7:04:20 - LMT 1921 Dec 31 23:55:40
-7:00 - MST 1927 Jun 10 23:00
-6:00 - CST 1930 Nov 15
-7:00 - MST 1931 May 1 23:00
-6:00 - CST 1931 Oct
-7:00 - MST 1932 Apr 1
-6:00 - CST 1996
-6:00 Mexico C%sT 1998
-6:00 - CST 1998 Apr Sun>=1 3:00
-7:00 Mexico M%sT
The corresponding Rule lines are:
Rule Mexico 1996 2000 - Apr Sun>=1 2:00 1:00 D
Rule Mexico 1996 2000 - Oct lastSun 2:00 0 S
What is the wall-clock offset from UTC on April 10th 1998?
The last UNTIL value is "1998 Apr Sun>=1 3:00", which is just after the April Rule
came into effect ("Apr Sun>=1 2:00"). So I'm unsure if the Rule is supposed to
still be in effect or not. i.e. I don't know if it should be -7:00 (standard time)
or -6:00 (daylight time).
It makes more sense if it is daylight time, but then why didn't the UNTIL value
just use "Apr Sun>=1 2:00" and make it obvious.
My general question is: do Rules apply to a particular period within a Zone
(i.e. one line in the Zone description) even when they happened before the Zone
period started?
Damon
(I'm working on a program to convert the timezone database to the format
needed by the iCalendar specification, for use by calendaring apps etc.)
1
0
Hi,
one of the major newspapers here in Argentina said that the 1999 Timezone Law
(which never was effectively applied) will (would?) be in effect starting Sunday,
June 17th (yeah, in the middle of winter, down here).
The article is at http://ar.clarin.com/diario/2001-06-06/e-01701.htm for those that
can read Spanish.
The only verifiable Law applying is Murphy's Law applies since tzdata2001c was
published YESTERDAY.
The Law itself is "Ley N° 25155", sanctioned on 25-Aug-1999, enacted 17-Sep-1999
and published 21-Sep-1999.
The official publication is at:
http://www.boletin.jus.gov.ar/BON/Primera/1999/09-Septiembre/21/PDF/BO21-09
-99LEG.PDF
Regretfully, you have to subscribe (and pay) for the on-line version of the Boletin
Oficial at http://www.jus.gov.ar/servi/boletin/ to be able to access it.
There's another (paid) service where you can get all the Argentine Laws at
http://std.saij.jus.gov.ar/ which lawyers prefer.
Not paying, I only could see the Summary at:
http://www1.hcdn.gov.ar/folio-cgi-
bin/om_isapi.dll?clientID=1622813&advquery=Huso%20Horario&infobase=leyes.nfo&record=
{844}&softpage=Document42&x=22&y=19&zz=
Since I don't trust it's quite valid 'cause it has a very "dynamic" look, I got
there from http://www.diputados.gov.ar/ pressed the "BASES de DATOS" button,
followed the "Leyes Nacionales" link, entered "Huso Horario" and pressed "BUSCAR".
There you get to the summary (alas in Spanish).
I'll attempt a rough translation here:
====================================================================================
Title: Official Time.
Summary: Time zone four (4) hours to the west of the Greenwich Meridian is
established as the Official Time in all of the Country territory, according to the
correspondig place of the Argentine Republic in the International Time Zone System.
Starting at zero hour of the first Sunday of October every year, the time zone
three (3) [west] of the Greenwich Meridian will be adopted up until the zero hour
of the first Sunday of March in the following year.
Updates: Decree 186/2000, enacted 2-Mar-2000, published 3-Mar-2000: The change of
the Official Time enacted by Article 2 of Law # 25155 is prorrogated until the
first Sunday in March, 2001.
====================================================================================
Anyway, considering the problems that arised when the Law was published, the
general ignorance surrounding the subject in general (and especially in National
and Provincial Authorities), mixed with the will to confront anything the
government attempts (for good or for bad), I would wait a week before commiting
changes. Recent history is on my side :-)
In fact, I saw no Decree published in the last few days.
I attach a tentative 'diff -c' against the latest (tzdata2001c) southamerica file.
I'll keep the list informed of news regarding this.
--
Mariano Absatz - El Baby
mailto:baby@baby.com.ar
http://www.baby.com.ar/
PGP KEYS: http://www.baby.com.ar/datos/personales.html#claves_pgp
|\ _
_\\/'> Powered by Pegasus Mail
/|__) http://www.pmail.com
) )\
*** southamerica Wed Jun 6 09:58:19 2001
--- southamerica.new Wed Jun 6 10:11:23 2001
***************
*** 115,120 ****
--- 115,127 ----
Rule Arg 1999 only - Oct Sun>=1 0:00 1:00 S
Rule Arg 2000 only - Mar Sun>=1 0:00 0 -
#
+ # Mariano Absatz (2001-06-06):
+ # Though the Decree hasn't been published yet, apparently, Law #25155 from
+ # 1999 would be implemented starting Sunday, June 17th in 2001.
+ Rule Arg 2001 only - Jun Sun>=17 0:00 0 -
+ Rule Arg 2001 max - Oct Sun>=1 0:00 1:00 S
+ Rule Arg 2002 max - Mar Sun>=1 0:00 0 -
+ #
# From Peter Gradelski via Steffen Thorsen (2000-03-01):
# We just checked with our Sao Paulo office and they say the government of
# Argentina decided not to become one of the countries that go on or off DST.
***************
*** 135,141 ****
-4:00 Arg AR%sT 1969 Oct 5
-3:00 Arg AR%sT 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART
#
# Santa Fe (SF), Entre Rios (ER), Corrientes (CN), Misiones (MN), Chaco (CC),
# Formosa (FM), La Pampa (LP), Chubut (CH)
--- 142,149 ----
-4:00 Arg AR%sT 1969 Oct 5
-3:00 Arg AR%sT 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART 2001 Jun 17 0:00
! -4:00 Arg AR%sT
#
# Santa Fe (SF), Entre Rios (ER), Corrientes (CN), Misiones (MN), Chaco (CC),
# Formosa (FM), La Pampa (LP), Chubut (CH)
***************
*** 146,152 ****
-3:00 Arg AR%sT 1991 Jul
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART
#
# Cordoba (CB), Santiago del Estero (SE), Salta (SA), Tucuman (TM), La Rioja (LR), San Juan (SJ), San Luis (SL),
# Neuquen (NQ), Rio Negro (RN)
--- 154,161 ----
-3:00 Arg AR%sT 1991 Jul
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART 2001 Jun 17 0:00
! -4:00 Arg AR%sT
#
# Cordoba (CB), Santiago del Estero (SE), Salta (SA), Tucuman (TM), La Rioja (LR), San Juan (SJ), San Luis (SL),
# Neuquen (NQ), Rio Negro (RN)
***************
*** 157,163 ****
-3:00 Arg AR%sT 1990 Jul
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART
#
# Jujuy (JY)
Zone America/Jujuy -4:21:12 - LMT 1894 Nov
--- 166,173 ----
-3:00 Arg AR%sT 1990 Jul
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART 2001 Jun 17 0:00
! -4:00 Arg AR%sT
#
# Jujuy (JY)
Zone America/Jujuy -4:21:12 - LMT 1894 Nov
***************
*** 170,176 ****
-4:00 - WART 1992 Oct 18
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART
#
# Catamarca (CT)
Zone America/Catamarca -4:23:08 - LMT 1894 Nov
--- 180,187 ----
-4:00 - WART 1992 Oct 18
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART 2001 Jun 17 0:00
! -4:00 Arg AR%sT
#
# Catamarca (CT)
Zone America/Catamarca -4:23:08 - LMT 1894 Nov
***************
*** 182,188 ****
-3:00 Arg AR%sT 1992 Jul
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART
#
# Mendoza (MZ)
Zone America/Mendoza -4:35:16 - LMT 1894 Nov
--- 193,200 ----
-3:00 Arg AR%sT 1992 Jul
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART 2001 Jun 17 0:00
! -4:00 Arg AR%sT
#
# Mendoza (MZ)
Zone America/Mendoza -4:35:16 - LMT 1894 Nov
***************
*** 195,201 ****
-4:00 - WART 1992 Oct 18
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART
# Aruba
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
--- 207,214 ----
-4:00 - WART 1992 Oct 18
-3:00 - ART 1999 Oct 3 0:00
-4:00 Arg AR%sT 2000 Mar 3 0:00
! -3:00 - ART 2001 Jun 17 0:00
! -4:00 Arg AR%sT
# Aruba
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
1
1