/*** *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 #include #include #include #include #include #include #include #include #include #include /* * 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 #include #include #include #include #include #include #include #include /*** *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 */