diff options
Diffstat (limited to 'private/crt32/time/tzset.c')
-rw-r--r-- | private/crt32/time/tzset.c | 927 |
1 files changed, 927 insertions, 0 deletions
diff --git a/private/crt32/time/tzset.c b/private/crt32/time/tzset.c new file mode 100644 index 000000000..d5b5c711e --- /dev/null +++ b/private/crt32/time/tzset.c @@ -0,0 +1,927 @@ +/*** +*tzset.c - set timezone information and see if we're in daylight time +* +* Copyright (c) 1985-1993, Microsoft Corporation. All rights reserved. +* +*Purpose: +* defines _tzset() - set timezone and daylight saving time vars +* +*Revision History: +* 03-??-84 RLB initial version +* 03-26-86 TC added minus capability to time difference w.r.t GMT +* 03-27-86 TC fixed daylight davings time calculation, off by a day +* error +* 12-03-86 SKS daylight savings time is different starting april 1987 +* Fixed off-by-1 errors when either Apr 30 or Oct 31 is +* Sat. Simplified leap year check: this works for +* 1970-2099 only! +* 11-19-87 SKS Add __tzset() which calls tzset only the first time +* Made _isindst() a near procedure +* 11-25-87 WAJ Added calls to _lock and _unlock +* 12-11-87 JCR Added "_LOAD_DS" to declaration +* 01-27-88 SKS Made _isindst() and _dtoxtime() are no longer near (for +* QC) +* 05-24-88 PHG Merged DLL and normal versions +* 03-20-90 GJF Made calling type _CALLTYPE1, added #include +* <cruntime.h>, removed #include <register.h>, removed +* some leftover 16-bit support and fixed the copyright. +* Also, cleaned up the formatting a bit. +* 03-23-90 GJF Made static functions _CALLTYPE4. +* 07-30-90 SBM Added void to empty function arg lists to create +* prototypes +* 10-04-90 GJF New-style function declarators. +* 01-21-91 GJF ANSI naming. +* 08-10-92 PBS Posix Support (TZ stuff). +* 03-30-93 GJF Ported C8-16 version to Win32. +* 06-28-93 GJF Limited support for system's notion of time zone +* in Windows NT. +* 07-15-93 GJF Resurrected __tzset(). +* +*******************************************************************************/ + +#include <cruntime.h> +#include <ctype.h> +#include <ctime.h> +#include <time.h> +#include <stdlib.h> +#include <internal.h> +#ifdef _POSIX_ +#include <limits.h> +#else +#include <os2dll.h> +#include <windows.h> +#endif +#include <string.h> + +#ifndef _POSIX_ +/* + * Pointer to a saved copy of the TZ value obtained in the previous call + * to tzset() set (if any). + */ +static char * lastTZ = NULL; +#endif + + +/*** +*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: +* +*******************************************************************************/ + +#ifndef _POSIX_ + +#ifdef MTHREAD +static void _CRTAPI3 _tzset_lk(void); +#else +#define _tzset_lk _tzset +#endif + +void _CRTAPI1 __tzset(void) +{ + static int first_time; + + if ( !first_time ) { + + _mlock( _TIME_LOCK ); + + if ( !first_time ) { + _tzset_lk(); + first_time++; + } + + _munlock(_TIME_LOCK ); + + } +} + + +#ifdef MTHREAD /* multi-thread; define both tzset and _tzset_lk */ +void _CRTAPI1 _tzset ( + void + ) +{ + _mlock( _TIME_LOCK ); + + _tzset_lk(); + + _munlock( _TIME_LOCK ); +} + + +static void _CRTAPI3 _tzset_lk ( + +#else /* non multi-thread; only define tzset */ + +void _CRTAPI1 _tzset ( + +#endif /* rejoin common code */ + + void + ) +{ + REG1 char *TZ; + REG2 int negdiff = 0; + + _mlock(_ENV_LOCK); + + /* + * Fetch the value of the TZ environment variable. + */ + if ( (TZ = _getenv_lk("TZ")) == NULL ) { + /* + * If there is no TZ environment variable, try to use the + * time zone information from the system. + */ + TIME_ZONE_INFORMATION tzinfo; + + if ( GetTimeZoneInformation( &tzinfo ) != 0xffffffff ) { + /* + * 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. If so, we + * ASSUME it is USA daylight saving time (that is all we + * support for rev. one). + */ + if ( (tzinfo.DaylightDate.wMonth != 0) && + (tzinfo.DaylightBias != 0) ) + _daylight = 1; + else + _daylight = 0; + + /* + * Remove the default names, but don't try to determine + * suitable replacements (NT does not enforce any standard + * or convention for timezone naming). + */ + *_tzname[0] = '\0'; + *_tzname[1] = '\0'; + } + + /* + * Time zone information is unavailable, just return. + */ + _munlock(_ENV_LOCK); + 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 + */ + if (lastTZ != NULL) { + free(lastTZ); + } + lastTZ = strdup(TZ); + + _munlock(_ENV_LOCK); + + /* + * Process TZ value and update _tzname, _timezone and _daylight. + */ + + 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 ( _daylight = *TZ ) + strncpy(_tzname[1], TZ, 3); + else + *_tzname[1] = '\0'; + +} + + +/*** +*int _isindst(tb) - determine if broken-down time falls in DST +* +*Purpose: +* Determine if the given broken-down time falls within DST. Only +* modern, summer-time DST is handled (USA, post 1967). +* +* 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: +* struct tm *tb - structure holding broken-down time value +* +*Exit: +* 1, if time represented is in DST +* 0, otherwise +* +*******************************************************************************/ + +int _CRTAPI1 _isindst ( + REG1 struct tm *tb + ) +{ + int mdays; + REG2 int yr; + int critsun; + + /* + * Handle easy cases. + * + * Modern DST was put into effect by Congress in 1967. Also, if the + * month is before April or after October, it cannot be DST. + */ + if ( (tb->tm_year < 67) || (tb->tm_mon < 3) || (tb->tm_mon > 9) ) + return(0); + + /* + * If the month is after April and before October, 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 + */ + + /* + * First we get #1. The year-days for each month are stored in _days[] + * they're all off by -1 + */ + if ( ((yr = tb->tm_year) > 86) && (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 + */ + + /* Now get #2. We know the week-day number of January 1, 1970, which is + * defined as the constant _BASE_DOW. Add to this the number of elapsed + * days since then, take the result mod 7, and we have our day number. + * + * To obtain #3, we just subtract this from mdays. + */ + + critsun = mdays - ((mdays + 365 * (yr - 70) + ((yr - 1) >> 2) - + _LEAP_YEAR_ADJUST + _BASE_DOW) % 7); + + /* Now we know 1 and 3; we're golden: */ + + return ( (tb->tm_mon == 3) + ? ((tb->tm_yday > critsun) || + ((tb->tm_yday == critsun) && (tb->tm_hour >= 2))) + : ((tb->tm_yday < critsun) || + ((tb->tm_yday == critsun) && (tb->tm_hour < 1))) ); +} + + +#else /* _POSIX_ */ + +/* + * The following is an implementation of the TZ grammar specified in the + * document: + * + * 8.1.1 Extension to Time Functions + * IEEE Std 1003.1 - 1990 + * Page 152 - 153 + * + * The TZ grammar looks like: + * + * stdoffset[dst[offset][,start[/time],end[/time]]] + * + * Variables used in code: + * + * tzname[0] ==> std + * _timezone ==> offset(the one after 'std') + * tzname[1] ==> dst + * _dstoffset ==> offset(the one after 'dst') + * _startdate ==> start + * _starttime ==> time(the one after 'start') + * _enddate ==> end + * _endtime ==> time(the one after 'end') + * + */ + +/* + * Refer to the document for the detailed description of fields of _DSTDATE. + * Two of Jn, n, and Mm are -1, indicating the one(not -1) is a vaild value. + */ + +typedef struct _DSTDATE { + int Jn; /* -1 or [1, 365](year day and leap day shall not be counted) */ + int n; /* -1 or [0, 365](year day and leap day shall be counted) */ + int Mm; /* -1 or [1, 12](month) */ + int Mn; /* [1, 5] if Mm != -1 (week) */ + int Md; /* [0, 6] if Mm != -1 (weekday, Sunday == 0) */ +} DSTDATE, *PDSTDATE; + +#define SEC_PER_HOUR (60 * 60) +#define SEC_PER_DAY (SEC_PER_HOUR * 24) + + +/* + * The default TZ in tzset() should look like: + * + * TZ = "PST8PDT,M4.1.0/2:00,M10.5.0/2:00"; + */ + +/* Day light saving start/end date and default vaules */ +static DSTDATE _startdate = { -1, -1, 4, 1, 0}; +static DSTDATE _enddate = {-1, -1, 10, 5, 0}; + + +/* Seconds since midnight on _startdate/_enddate with default values. + * _endtime is 1am instead of 2am because the DST end time is 2am + * local time, which by default is 1am standard time. + */ +long _starttime = 7200L, _endtime = 7200L; + +/* + * If we are only interested in years between 1901 and 2099, we could use this: + * + * #define IS_LEAP_YEAR(y) (y % 4 == 0) + */ + +#define IS_LEAP_YEAR(y) ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) + + +/* + * ParsePosixStdOrDst - parse the std or dst element in TZ. + * + * ENTRY pch - beginning of the substring in TZ. + * + * RETURN pointer to one position after the std or dst element parsed, + * or NULL if failed. + */ + + +static char * _CRTAPI3 +ParsePosixStdOrDst( + REG1 char *pch + ) +{ +#define UNWANTED(x) (isdigit(x) || x=='\0' || x==',' || x=='-' || x=='+') + int i; + + /* + * Check against the rule. + */ + + if(*pch == ':' || UNWANTED(*pch)) { + return NULL; + } + + /* + * Get a valid std or dst(i.e. 3 <= lenth_of(std | dst) <= TZNAME_MAX). + */ + + for(i=1, ++pch; (i < TZNAME_MAX) && !UNWANTED(*pch); i++, pch++) { + ; + } + + /* + * pch now point to 1 position after the valid std or dst. + */ + + return (i >= 3) ? pch : NULL; +} + + +/* + * ParsePosixOffset - parse the offset element in TZ. The format of time is: + * + * [- | +]hh[:mm[:ss]] + * + * ENTRY pch - beginning of the substring in TZ. + * + * ptime - pointer to a variable(_timezone or _dstoffset) storing the + * time(in seconds) parsed. + * + * RETURN pointer to one position after the end of the offset element parsed. + */ + + +static char * _CRTAPI3 +ParsePosixOffset( + REG1 char *pch, + REG2 long *poffset + ) +{ + int fNegative; + long offset; + + if((fNegative = (*pch == '-')) || *pch == '+') { + pch++; + } + + offset = atol(pch)*3600L; /* hh */ + + while(*pch && isdigit(*pch)) { + pch++; + } + + if(*pch == ':') { + offset += atol(++pch)*60L; /* mm */ + while(*pch && isdigit(*pch)) { + pch++; + } + + if(*pch == ':') { + offset += atol(++pch); /* ss */ + while(*pch && isdigit(*pch)) { + pch++; + } + } + } + + *poffset = fNegative ? -offset : offset; + + return pch; +} + + + +/* + * ParsePosixDate - parse the date element in TZ. The format of date is one + * of following: + * + * Jn, n, and Mm.n.d + * + * ENTRY pch - beginning of the substring in TZ. + * + * pDstDate - pointer to _startdate or _enddate storing the result. + * + * RETURN pointer to one position after the end of the date element parsed, + * or NULL if failed. + */ + +static char * _CRTAPI3 +ParsePosixDate( + REG1 char *pch, + REG2 PDSTDATE pDstDate + ) +{ + pDstDate->Jn = -1; + pDstDate->n = -1; + pDstDate->Mn = -1; + + /* + * Two out of the three -1's will remain. + */ + + if(*pch == 'J') { /* Jn */ + pDstDate->Jn = atoi(++pch); + } else if(*pch != 'M') { /* n */ + pDstDate->n = atoi(pch); + } else { /* Mm.n.d */ + + pDstDate->Mm = atoi(++pch); + + if(*++pch != '.') { + pch++; + } + pDstDate->Mn = atoi(++pch); + + if(*++pch != '.') { + pch++; + } + pDstDate->Md = atoi(++pch); + } + + while(*pch && isdigit(*pch)) { + pch++; + } + +#define IN_RANGE(x, a, b) (x >= a && x <= b) + + return ((pDstDate->Jn != -1 && IN_RANGE(pDstDate->Jn, 1, 365)) || + (pDstDate->n != -1 && IN_RANGE(pDstDate->n, 0, 365)) || + (pDstDate->Mm != -1 && IN_RANGE(pDstDate->Mm, 1, 12) && + IN_RANGE(pDstDate->Mn, 1, 5) && IN_RANGE(pDstDate->Md, 0, 6))) + ? pch : NULL; +} + +/* + * ParsePosixTime - parse the time element in TZ. The format of time is: + * + * hh[:mm[:ss]] + * + * ENTRY pch - beginning of the substring in TZ. + * + * ptime - pointer to a variable(_starttime or _endtime) storing the + * time(in seconds) parsed. + * + * RETURN pointer to one position after the end of the time element parsed. + */ + +static char * _CRTAPI3 +ParsePosixTime( + REG1 char *pch, + REG2 long *ptime + ) +{ + long time; + + time = atol(pch)*SEC_PER_HOUR; /* hh */ + + while(*pch && isdigit(*pch)) { + pch++; + } + + if(*pch == ':') { + + time += atol(++pch)*60L; /* mm */ + while(*pch && isdigit(*pch)) { + pch++; + } + + if(*pch == ':') { + + time += atol(++pch); /* ss */ + while(*pch && isdigit(*pch)) { + pch++; + } + } + } + + *ptime = time; + + return pch; +} + +/* + * tzset - sets the timezone information from the TZ environment variable. + * Global tzname[], _timezone, _daylight, and _dstoffset will be + * set. Static _startdate, _enddate, _starttime, and _endtime will + * also be set. TZ string looks like: + * + * stdoffset[dst[offset][,start[/time],end[/time]]] + * + * In form of variables: tzname[0]_timezone[tzname[1][_dstoffset] + * [,_startdate[/_starttime],_enddate[/_endtime]]] + * + * ENTRY none. + * + * RETURN none. + */ + +void _CRTAPI1 tzset( + void + ) +{ + /* pch points to the beginning of an element to be parsed. */ + REG1 char *pch; + + /* pchCurr points to one position after the end of last element parsed. */ + REG2 char *pchCurr; + + char *TZ; + + _endtime = 7200L; + _starttime = 7200L; + + if (!(TZ = getenv("TZ")) || !*TZ) { + TZ = "PST8PDT7,M4.1.0/2:00,M10.5.0/2:00"; /* use default */ + } + + if((pchCurr = ParsePosixStdOrDst(pch=TZ)) == NULL) { + return; + } + + memcpy(tzname[0], pch, (int)(pchCurr-pch)); + tzname[0][(int)(pchCurr-pch)] = '\0'; + + if((pchCurr = ParsePosixOffset(pch=pchCurr, &_timezone)) == NULL) { + return; + } + + _daylight = (*pchCurr != '\0'); + + if(!_daylight) { + return; + } + + if((pchCurr = ParsePosixStdOrDst(pch=pchCurr)) == NULL) { + return; + } + + memcpy(tzname[1], pch, (int)(pchCurr-pch)); + tzname[1][(int)(pchCurr-pch)] = '\0'; + + if(isdigit(*pchCurr) || *pchCurr == '-' || *pchCurr == '+') { + if((pchCurr = ParsePosixOffset(pch=pchCurr, &_dstoffset)) == NULL) { + return; + } + } else { + /* default: 1 hour ahead of standard time */ + _dstoffset = _timezone - SEC_PER_HOUR; + } + + if(*pchCurr == ',') { /* ,start[/time],end[/time] */ + + if((pchCurr = ParsePosixDate(pchCurr+1, &_startdate)) == NULL) { + goto out; + } + + if(*pchCurr == '/') { + if(!(pchCurr = ParsePosixTime(pchCurr+1, &_starttime))) { + goto out; + } + } + + if(*pchCurr != ',') { + goto out; + } + + if ((pchCurr = ParsePosixDate(pchCurr+1, &_enddate)) == NULL) { + goto out; + } + + if (*pchCurr == '/') { + if(!(pchCurr = ParsePosixTime(pchCurr+1, &_endtime))) { + goto out; + } + } + } +out: + /* + * Adjust the _endtime to account for the fact that + * dst ends at _endtime local time, rather than + * standard time. + */ + + _endtime -= (_timezone - _dstoffset); +} + + +#define DAY1 (4) /* Jan 1 1970 was a Thursday */ + +/* + * GetDstStartOrEndYearDay - Converts day info from DSTDATE into 0-based + * year-day. + * + * ENTRY tm_year - the year concerned(tb->tm_year). + * + * pDstDate - pointer to either _startdate or _enddate. + * + * RETURN the year-day calculated. + */ + +static int _CRTAPI3 +GetDstStartOrEndYearDay( + REG1 int tm_year, + REG2 PDSTDATE pDstDate + ) +{ + REG1 int yday; /* year-day */ + REG2 int theyear; + + theyear = tm_year + 1900; + + if(pDstDate->Jn != -1) { + + /* + * Jn is in [1, 365] and leap day is not counted. + * Convert Jn to 0-based yday; Note: 60 is March 1. + */ + + + yday = (IS_LEAP_YEAR(theyear) && (pDstDate->Jn >= 60)) + ? pDstDate->Jn : pDstDate->Jn - 1; + + } else if(pDstDate->n != -1) { + + /* + * n is in [0, 365] and leap day is counted. + */ + + yday = pDstDate->n; + + } else { /* Mm.n.d */ + + int *ptrday; + int years; + int wday; + + /* + * We first need to calculate year-day(yday) and week-day + * (wday) of 1st day of month pDstDate->Mm. We then figure + * out year-day(yday) of Md day of week Mn of month Mm. + */ + + ptrday = IS_LEAP_YEAR(theyear) ? _lpdays : _days; + + yday = ptrday[pDstDate->Mm-1] + 1; /* ptrday[i] are all by -1 off */ + + years = tm_year - 70; + + /* + * Here constant Day1 is the week-day of Jan 1, 1970. + * (years+1)/4 is for correcting the leap years. + */ + + wday = (yday + 365*years + (years+1)/4 + DAY1) % 7; + + /* + * Calculate yday of Md day of week 1 of month Mm. + */ + + yday += pDstDate->Md - wday; + if(pDstDate->Md < wday) { + yday += 7; + } + + /* + * Calculate yday of Md day of week Mn of month Mm. + */ + + yday += (pDstDate->Mn-1)*7; + + /* + * Adjust if yday goes beyond the end of the month. + */ + + if(pDstDate->Md == 5 && yday >= ptrday[pDstDate->Mm] + 1) { + yday -= 7; + } + + } + + return yday; +} + +/* + * _isindst - Tells whether Xenix-type time value falls under DST. + * + * ENTRY tb - 'time' structure holding broken-down time value. + * + * RETURN 1 if time represented is in DST, else 0. + */ + +int _CRTAPI1 _isindst ( + REG1 struct tm *tb + ) +{ + int st_yday, end_yday; + int st_sec, end_sec; + + int check_time; + + /* + * We need start/end year-days of DST in syday/eyday which are converted + * from one of the format Jn, n, and Mm.n.d. We already have start/end + * time (in seconds) of DST in _starttime/_endtime. + */ + + st_yday = GetDstStartOrEndYearDay(tb->tm_year, &_startdate); + end_yday = GetDstStartOrEndYearDay(tb->tm_year, &_enddate); + + st_sec = st_yday * SEC_PER_DAY + _starttime; + end_sec = end_yday * SEC_PER_DAY + _endtime; + + check_time = tb->tm_yday * SEC_PER_DAY + tb->tm_hour * SEC_PER_HOUR + + tb->tm_min * 60 + tb->tm_sec; + + if (check_time >= st_sec && check_time < end_sec) + return 1; + + return 0; +} + +/* + * _isskiptime - Tells whether the given time is skipped at the + * dst change. For instance, we set our clocks forward one + * hour at 2am to 3am... This function returns true for + * the times between 1:59:59 and 3:00:00. + * + * ENTRY tb - 'time' structure holding broken-down time value. + * + * RETURN 1 if time represented is in the skipped period, 0 + * otherwise. + */ + +int _CRTAPI1 _isskiptime ( + REG1 struct tm *tb + ) +{ + int st_yday; + int st_sec; + int check_time; + + st_yday = GetDstStartOrEndYearDay(tb->tm_year, &_startdate); + st_sec = st_yday * SEC_PER_DAY + _starttime; + + check_time = tb->tm_yday * SEC_PER_DAY + tb->tm_hour * SEC_PER_HOUR + + tb->tm_min * 60 + tb->tm_sec; + + if (check_time >= st_sec && check_time < st_sec - _dstoffset) { + return 1; + } + return 0; +} + +#endif /* _POSIX_ */ |