home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Windows Gam…ming Gurus (2nd Edition)
/
Disc2.iso
/
vc98
/
crt
/
src
/
tzset.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-06-17
|
30KB
|
934 lines
/***
*tzset.c - set timezone information and see if we're in daylight time
*
* Copyright (c) 1985-1998, Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines _tzset() - set timezone and daylight saving time vars
*
*******************************************************************************/
#ifdef _WIN32
#include <cruntime.h>
#include <ctype.h>
#include <ctime.h>
#include <time.h>
#include <stdlib.h>
#include <internal.h>
#include <mtdll.h>
#include <windows.h>
#include <setlocal.h>
#include <string.h>
#include <dbgint.h>
/*
* Number of milliseconds in one day
*/
#define DAY_MILLISEC (24L * 60L * 60L * 1000L)
/*
* The macro below is valid for years between 1901 and 2099, which easily
* includes all years representable by the current implementation of time_t.
*/
#define IS_LEAP_YEAR(year) ( (year & 3) == 0 )
/*
* Pointer to a saved copy of the TZ value obtained in the previous call
* to tzset() set (if any).
*/
static char * lastTZ = NULL;
/*
* Flag indicating that time zone information came from GetTimeZoneInformation
* API call.
*/
static int tzapiused;
static TIME_ZONE_INFORMATION tzinfo;
/*
* Structure used to represent DST transition date/times.
*/
typedef struct {
int yr; /* year of interest */
int yd; /* day of year */
long ms; /* milli-seconds in the day */
} transitiondate;
/*
* DST start and end structs.
*/
static transitiondate dststart = { -1, 0, 0L };
static transitiondate dstend = { -1, 0, 0L };
static int __cdecl _isindst_lk(struct tm *);
/***
*void tzset() - sets timezone information and calc if in daylight time
*
*Purpose:
* Sets the timezone information from the TZ environment variable
* and then sets _timezone, _daylight, and _tzname. If we're in daylight
* time is automatically calculated.
*
*Entry:
* None, reads TZ environment variable.
*
*Exit:
* sets _daylight, _timezone, and _tzname global vars, no return value
*
*Exceptions:
*
*******************************************************************************/
#ifdef _MT
static void __cdecl _tzset_lk(void);
#else /* _MT */
#define _tzset_lk _tzset
#endif /* _MT */
void __cdecl __tzset(void)
{
static int first_time = 0;
if ( !first_time ) {
_mlock( _TIME_LOCK );
if ( !first_time ) {
_tzset_lk();
first_time++;
}
_munlock(_TIME_LOCK );
}
}
#ifdef _MT
void __cdecl _tzset (
void
)
{
_mlock( _TIME_LOCK );
_tzset_lk();
_munlock( _TIME_LOCK );
}
static void __cdecl _tzset_lk (
#else /* _MT */
void __cdecl _tzset (
#endif /* _MT */
void
)
{
char *TZ;
int defused;
int negdiff = 0;
_mlock(_ENV_LOCK);
/*
* Clear the flag indicated whether GetTimeZoneInformation was used.
*/
tzapiused = 0;
/*
* Set year fields of dststart and dstend structures to -1 to ensure
* they are recomputed as after this
*/
dststart.yr = dstend.yr = -1;
/*
* Fetch the value of the TZ environment variable.
*/
if ( (TZ = _getenv_lk("TZ")) == NULL ) {
/*
* There is no TZ environment variable, try to use the time zone
* information from the system.
*/
_munlock(_ENV_LOCK);
if ( GetTimeZoneInformation( &tzinfo ) != 0xFFFFFFFF ) {
/*
* Note that the API was used.
*/
tzapiused = 1;
/*
* Derive _timezone value from Bias and StandardBias fields.
*/
_timezone = tzinfo.Bias * 60L;
if ( tzinfo.StandardDate.wMonth != 0 )
_timezone += (tzinfo.StandardBias * 60L);
/*
* Check to see if there is a daylight time bias. Since the
* StandardBias has been added into _timezone, it must be
* compensated for in the value computed for _dstbias.
*/
if ( (tzinfo.DaylightDate.wMonth != 0) &&
(tzinfo.DaylightBias != 0) )
{
_daylight = 1;
_dstbias = (tzinfo.DaylightBias - tzinfo.StandardBias) *
60L;
}
else {
_daylight = 0;
/*
* Set daylight bias to 0 because GetTimeZoneInformation
* may return TIME_ZONE_ID_DAYLIGHT even though there is
* no DST (in NT 3.51, just turn off the automatic DST
* adjust in the control panel)!
*/
_dstbias = 0;
}
/*
* Try to grab the name strings for both the time zone and the
* daylight zone. Note the wide character strings in tzinfo
* must be converted to multibyte characters strings. The
* locale codepage, __lc_codepage, is used for this. Note that
* if setlocale() with LC_ALL or LC_CTYPE has not been called,
* then __lc_codepage will be 0 (_CLOCALECP), which is CP_ACP
* (which means use the host's default ANSI codepage).
*/
if ( (WideCharToMultiByte( __lc_codepage,
WC_COMPOSITECHECK |
WC_SEPCHARS,
tzinfo.StandardName,
-1,
_tzname[0],
63,
NULL,
&defused ) != 0) &&
(!defused) )
_tzname[0][63] = '\0';
else
_tzname[0][0] = '\0';
if ( (WideCharToMultiByte( __lc_codepage,
WC_COMPOSITECHECK |
WC_SEPCHARS,
tzinfo.DaylightName,
-1,
_tzname[1],
63,
NULL,
&defused ) != 0) &&
(!defused) )
_tzname[1][63] = '\0';
else
_tzname[1][0] = '\0';
}
/*
* Time zone information is unavailable, just return.
*/
return;
}
if ( (*TZ == '\0') || ((lastTZ != NULL) && (strcmp(TZ, lastTZ) == 0)) )
{
/*
* Either TZ is NULL, pointing to '\0', or is the unchanged
* from a earlier call (to this function). In any case, there
* is no work to do, so just return
*/
_munlock(_ENV_LOCK);
return;
}
/*
* Update lastTZ
*/
_free_crt(lastTZ);
if ((lastTZ = _malloc_crt(strlen(TZ)+1)) == NULL)
{
_munlock(_ENV_LOCK);
return;
}
strcpy(lastTZ, TZ);
_munlock(_ENV_LOCK);
/*
* Process TZ value and update _tzname, _timezone and _daylight.
*/
strncpy(_tzname[0], TZ, 3);
_tzname[0][3] = '\0';
/*
* time difference is of the form:
*
* [+|-]hh[:mm[:ss]]
*
* check minus sign first.
*/
if ( *(TZ += 3) == '-' ) {
negdiff++;
TZ++;
}
/*
* process, then skip over, the hours
*/
_timezone = atol(TZ) * 3600L;
while ( (*TZ == '+') || ((*TZ >= '0') && (*TZ <= '9')) ) TZ++;
/*
* check if minutes were specified
*/
if ( *TZ == ':' ) {
/*
* process, then skip over, the minutes
*/
_timezone += atol(++TZ) * 60L;
while ( (*TZ >= '0') && (*TZ <= '9') ) TZ++;
/*
* check if seconds were specified
*/
if ( *TZ == ':' ) {
/*
* process, then skip over, the seconds
*/
_timezone += atol(++TZ);
while ( (*TZ >= '0') && (*TZ <= '9') ) TZ++;
}
}
if ( negdiff )
_timezone = -_timezone;
/*
* finally, check for a DST zone suffix
*/
if ( _daylight = *TZ ) {
strncpy(_tzname[1], TZ, 3);
_tzname[1][3] = '\0';
}
else
*_tzname[1] = '\0';
}
/***
*static void cvtdate( trantype, datetype, year, month, week, dayofweek,
* date, hour, min, second, millisec ) - convert
* transition date format
*
*Purpose:
* Convert the format of a transition date specification to a value of
* a transitiondate structure.
*
*Entry:
* int trantype - 1, if it is the start of DST
* 0, if is the end of DST (in which case the date is
* is a DST date)
* int datetype - 1, if a day-in-month format is specified.
* 0, if an absolute date is specified.
* int year - year for which the date is being converted (70 ==
* 1970)
* int month - month (0 == January)
* int week - week of month, if datetype == 1 (note that 5== last
* week of month),
* 0, otherwise.
* int dayofweek - day of week (0 == Sunday), if datetype == 1.
* 0, otherwise.
* int date - date of month (1 - 31)
* int hour - hours (0 - 23)
* int min - minutes (0 - 59)
* int sec - seconds (0 - 59)
* int msec - milliseconds (0 - 999)
*
*Exit:
* dststart or dstend is filled in with the converted date.
*
*******************************************************************************/
static void __cdecl cvtdate (
int trantype,
int datetype,
int year,
int month,
int week,
int dayofweek,
int date,
int hour,
int min,
int sec,
int msec
)
{
int yearday;
int monthdow;
if ( datetype == 1 ) {
/*
* Transition day specified in day-in-month format.
*/
/*
* Figure the year-day of the start of the month.
*/
yearday = 1 + (IS_LEAP_YEAR(year) ? _lpdays[month - 1] :
_days[month - 1]);
/*
* Figure the day of the week of the start of the month.
*/
monthdow = (yearday + ((year - 70) * 365) + ((year - 1) >> 2) -
_LEAP_YEAR_ADJUST + _BASE_DOW) % 7;
/*
* Figure the year-day of the transition date
*/
if ( monthdow < dayofweek )
yearday += (dayofweek - monthdow) + (week - 1) * 7;
else
yearday += (dayofweek - monthdow) + week * 7;
/*
* May have to adjust the calculation above if week == 5 (meaning
* the last instance of the day in the month). Check if year falls
* beyond after month and adjust accordingly.
*/
if ( (week == 5) &&
(yearday > (IS_LEAP_YEAR(year) ? _lpdays[month] :
_days[month])) )
{
yearday -= 7;
}
}
else {
/*
* Transition day specified as an absolute day
*/
yearday = IS_LEAP_YEAR(year) ? _lpdays[month - 1] :
_days[month - 1];
yearday += date;
}
if ( trantype == 1 ) {
/*
* Converted date was for the start of DST
*/
dststart.yd = yearday;
dststart.ms = (long)msec +
(1000L * (sec + 60L * (min + 60L * hour)));
/*
* Set year field of dststart so that unnecessary calls to
* cvtdate() may be avoided.
*/
dststart.yr = year;
}
else {
/*
* Converted date was for the end of DST
*/
dstend.yd = yearday;
dstend.ms = (long)msec +
(1000L * (sec + 60L * (min + 60L * hour)));
/*
* The converted date is still a DST date. Must convert to a
* standard (local) date while being careful the millisecond field
* does not overflow or underflow.
*/
if ( (dstend.ms += (_dstbias * 1000L)) < 0 ) {
dstend.ms += DAY_MILLISEC;
dstend.yd--;
}
else if ( dstend.ms >= DAY_MILLISEC ) {
dstend.ms -= DAY_MILLISEC;
dstend.yd++;
}
/*
* Set year field of dstend so that unnecessary calls to cvtdate()
* may be avoided.
*/
dstend.yr = year;
}
return;
}
/***
*int _isindst(tb) - determine if broken-down time falls in DST
*
*Purpose:
* Determine if the given broken-down time falls within daylight saving
* time (DST). The DST rules are either obtained from Win32 (tzapiused !=
* TRUE) or assumed to be USA rules, post 1986.
*
* If the DST rules are obtained from Win32's GetTimeZoneInformation API,
* the transition dates to/from DST can be specified in either of two
* formats. First, a day-in-month format, similar to the way USA rules
* are specified, can be used. The transition date is given as the n-th
* occurence of a specified day of the week in a specified month. Second,
* an absolute date can be specified. The two cases are distinguished by
* the value of wYear field in the SYSTEMTIME structure (0 denotes a
* day-in-month format).
*
* USA rules for DST are that a time is in DST iff it is on or after
* 02:00 on the first Sunday in April, and before 01:00 on the last
* Sunday in October.
*
*Entry:
* struct tm *tb - structure holding broken-down time value
*
*Exit:
* 1, if time represented is in DST
* 0, otherwise
*
*******************************************************************************/
int __cdecl _isindst (
struct tm *tb
)
#ifdef _MT
{
int retval;
_mlock( _TIME_LOCK );
retval = _isindst_lk( tb );
_munlock( _TIME_LOCK );
return retval;
}
static int __cdecl _isindst_lk (
struct tm *tb
)
#endif /* _MT */
{
long ms;
if ( _daylight == 0 )
return 0;
/*
* Compute (recompute) the transition dates for daylight saving time
* if necessary.The yr (year) fields of dststart and dstend is
* compared to the year of interest to determine necessity.
*/
if ( (tb->tm_year != dststart.yr) || (tb->tm_year != dstend.yr) ) {
if ( tzapiused ) {
/*
* Convert the start of daylight saving time to dststart.
*/
if ( tzinfo.DaylightDate.wYear == 0 )
cvtdate( 1,
1, /* day-in-month format */
tb->tm_year,
tzinfo.DaylightDate.wMonth,
tzinfo.DaylightDate.wDay,
tzinfo.DaylightDate.wDayOfWeek,
0,
tzinfo.DaylightDate.wHour,
tzinfo.DaylightDate.wMinute,
tzinfo.DaylightDate.wSecond,
tzinfo.DaylightDate.wMilliseconds );
else
cvtdate( 1,
0, /* absolute date */
tb->tm_year,
tzinfo.DaylightDate.wMonth,
0,
0,
tzinfo.DaylightDate.wDay,
tzinfo.DaylightDate.wHour,
tzinfo.DaylightDate.wMinute,
tzinfo.DaylightDate.wSecond,
tzinfo.DaylightDate.wMilliseconds );
/*
* Convert start of standard time to dstend.
*/
if ( tzinfo.StandardDate.wYear == 0 )
cvtdate( 0,
1, /* day-in-month format */
tb->tm_year,
tzinfo.StandardDate.wMonth,
tzinfo.StandardDate.wDay,
tzinfo.StandardDate.wDayOfWeek,
0,
tzinfo.StandardDate.wHour,
tzinfo.StandardDate.wMinute,
tzinfo.StandardDate.wSecond,
tzinfo.StandardDate.wMilliseconds );
else
cvtdate( 0,
0, /* absolute date */
tb->tm_year,
tzinfo.StandardDate.wMonth,
0,
0,
tzinfo.StandardDate.wDay,
tzinfo.StandardDate.wHour,
tzinfo.StandardDate.wMinute,
tzinfo.StandardDate.wSecond,
tzinfo.StandardDate.wMilliseconds );
}
else {
/*
* GetTimeZoneInformation API was NOT used, or failed. USA
* daylight saving time rules are assumed.
*/
cvtdate( 1,
1,
tb->tm_year,
4, /* April */
1, /* first... */
0, /* ...Sunday */
0,
2, /* 02:00 (2 AM) */
0,
0,
0 );
cvtdate( 0,
1,
tb->tm_year,
10, /* October */
5, /* last... */
0, /* ...Sunday */
0,
2, /* 02:00 (2 AM) */
0,
0,
0 );
}
}
/*
* Handle simple cases first.
*/
if ( dststart.yd < dstend.yd ) {
/*
* Northern hemisphere ordering
*/
if ( (tb->tm_yday < dststart.yd) || (tb->tm_yday > dstend.yd) )
return 0;
if ( (tb->tm_yday > dststart.yd) && (tb->tm_yday < dstend.yd) )
return 1;
}
else {
/*
* Southern hemisphere ordering
*/
if ( (tb->tm_yday < dstend.yd) || (tb->tm_yday > dststart.yd) )
return 1;
if ( (tb->tm_yday > dstend.yd) && (tb->tm_yday < dststart.yd) )
return 0;
}
ms = 1000L * (tb->tm_sec + 60L * tb->tm_min + 3600L * tb->tm_hour);
if ( tb->tm_yday == dststart.yd ) {
if ( ms >= dststart.ms )
return 1;
else
return 0;
}
else {
/*
* tb->tm_yday == dstend.yd
*/
if ( ms < dstend.ms )
return 1;
else
return 0;
}
}
#else /* _WIN32 */
#if defined (_M_MPPC) || defined (_M_M68K)
#include <cruntime.h>
#include <ctype.h>
#include <ctime.h>
#include <time.h>
#include <stdlib.h>
#include <internal.h>
#include <string.h>
#include <macos\script.h>
#include <macos\osutils.h>
/***
*void tzset() - sets timezone information and calc if in daylight time
*
*Purpose:
* Sets the timezone information from the TZ environment variable
* and then sets _timezone, _daylight, and _tzname. If we're in daylight
* time is automatically calculated.
*
*Entry:
* None, reads TZ environment variable.
*
*Exit:
* sets _daylight, _timezone, and _tzname global vars, no return value
*
*Exceptions:
*
*******************************************************************************/
void __cdecl _tzset (
void
)
{
REG1 char *TZ;
char *lastTZ=NULL;
MachineLocation ml;
long gmtDelta;
REG2 int negdiff = 0;
/*
* Fetch the value of the TZ environment variable. If there is no TZ
* environment variable, or if it is trivial, then the timezone
* information will be taken from the OS.
*/
if ( (TZ = getenv("TZ")) && (*TZ) ) {
/*
* TZ environment variable exists and is non-trivial. See if
* it is unchanged from a previous _tzset call.
*/
if ( (lastTZ == NULL) || (strcmp(TZ, lastTZ) != 0) ) {
/*
* TZ has changed, or there has been no prior _tzset call.
* Update lastTZ value.
*/
free(lastTZ);
lastTZ = _strdup(TZ);
}
else {
/*
* Timezone environment variable hasn't changed since the
* last _tzset call, just return.
*/
return;
}
}
else {
/*
* The TZ environment variable either does not exist, or is
* trivial. Therefore, timezone information will be obtained
* from the OS.
*/
if ( lastTZ != NULL ) {
free(lastTZ);
lastTZ = NULL;
}
ReadLocation(&ml);
//get gmtDelta from machinelocation in RAM
gmtDelta = ml.u.gmtDelta & 0x00ffffff;
if ((gmtDelta >> 23) & 1) //need to sign extend
gmtDelta = gmtDelta | 0xff000000;
//set timezone and daylight
_timezone = - gmtDelta;
_daylight = (ml.u.dlsDelta ? 1 : 0);
*_tzname[0] = '\0';
*_tzname[1] = '\0';
return;
}
strncpy(_tzname[0], TZ, 3);
/*
* time difference is of the form:
*
* [+|-]hh[:mm[:ss]]
*
* check minus sign first.
*/
if ( *(TZ += 3) == '-' ) {
negdiff++;
TZ++;
}
/*
* process, then skip over, the hours
*/
_timezone = atol(TZ) * 3600L;
while ( (*TZ == '+') || ((*TZ >= '0') && (*TZ <= '9')) ) TZ++;
/*
* check if minutes were specified
*/
if ( *TZ == ':' ) {
/*
* process, then skip over, the minutes
*/
_timezone += atol(++TZ) * 60L;
while ( (*TZ >= '0') && (*TZ <= '9') ) TZ++;
/*
* check if seconds were specified
*/
if ( *TZ == ':' ) {
/*
* process, then skip over, the seconds
*/
_timezone += atol(++TZ);
while ( (*TZ >= '0') && (*TZ <= '9') ) TZ++;
}
}
if ( negdiff )
_timezone = -_timezone;
/*
* finally, check for a DST zone suffix
*/
if (*TZ)
strncpy(_tzname[1], TZ, 3);
else
*_tzname[1] = '\0';
_daylight = *_tzname[1] != '\0';
}
/*
* _isindst - Tells whether Xenix-type time value falls under DST
*
* This is the rule for years before 1987:
* a time is in DST iff it is on or after 02:00:00 on the last Sunday
* in April and before 01:00:00 on the last Sunday in October.
* This is the rule for years starting with 1987:
* a time is in DST iff it is on or after 02:00:00 on the first Sunday
* in April and before 01:00:00 on the last Sunday in October.
*
* ENTRY tb - 'time' structure holding broken-down time value
*
* RETURN 1 if time represented is in DST, else 0
*/
int __cdecl _isindst (
REG1 struct tm *tb
)
{
int mdays;
REG2 int yr;
int lastsun;
/* If the month is before April or after October, then we know
* immediately it can't be DST. */
if (tb->tm_mon < 3 || tb->tm_mon > 9)
return(0);
/* If the month is after April and before October then we know
* immediately it must be DST. */
if (tb->tm_mon > 3 && tb->tm_mon < 9)
return(1);
/*
* Now for the hard part. Month is April or October; see if date
* falls between appropriate Sundays.
*/
/*
* The objective for years before 1987 (after 1986) is to determine
* if the day is on or after 2:00 am on the last (first) Sunday in
* April, or before 1:00 am on the last Sunday in October.
*
* We know the year-day (0..365) of the current time structure. We must
* determine the year-day of the last (first) Sunday in this month,
* April or October, and then do the comparison.
*
* To determine the year-day of the last Sunday, we do the following:
* 1. Get the year-day of the last day of the current month (Apr
* or Oct)
* 2. Determine the week-day number of #1,
* which is defined as 0 = Sun, 1 = Mon, ... 6 = Sat
* 3. Subtract #2 from #1
*
* To determine the year-day of the first Sunday, we do the following:
* 1. Get the year-day of the 7th day of the current month (April)
* 2. Determine the week-day number of #1,
* which is defined as 0 = Sun, 1 = Mon, ... 6 = Sat
* 3. Subtract #2 from #1
*/
yr = tb->tm_year + 1900; /* To see if this is a leap-year */
/* First we get #1. The year-days for each month are stored in _days[]
* they're all off by -1 */
if (yr > 1986 && tb->tm_mon == 3)
mdays = 7 + _days[tb->tm_mon];
else
mdays = _days[tb->tm_mon+1];
/* if this is a leap-year, add an extra day */
if (!(yr & 3))
mdays++;
/* mdays now has #1 */
yr = tb->tm_year - 70;
/* Now get #2. We know the week-day number of the beginning of the
* epoch, Jan. 1, 1970, which is defined as the constant _BASE_DOW. We
* then add the number of days that have passed from _BASE_DOW to the day
* of #2
* mdays + 365 * yr
* correct for the leap years which intervened
* + (yr + 1)/ 4
* and take the result mod 7, except that 0 must be mapped to 7.
* This is #2, which we then subtract from #1, mdays
*/
lastsun = mdays - ((mdays + 365*yr + ((yr+1)/4) + _BASE_DOW) % 7);
/* Now we know 1 and 3; we're golden: */
return (tb->tm_mon==3
? (tb->tm_yday > lastsun ||
(tb->tm_yday == lastsun && tb->tm_hour >= 2))
: (tb->tm_yday < lastsun ||
(tb->tm_yday == lastsun && tb->tm_hour < 1)));
}
#endif /* defined (_M_MPPC) || defined (_M_M68K) */
#endif /* _WIN32 */