251 lines
8.8 KiB
C
251 lines
8.8 KiB
C
|
/****************************************************************************
|
||
|
*
|
||
|
* Open Watcom Project
|
||
|
*
|
||
|
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* This file contains Original Code and/or Modifications of Original
|
||
|
* Code as defined in and that are subject to the Sybase Open Watcom
|
||
|
* Public License version 1.0 (the 'License'). You may not use this file
|
||
|
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
|
||
|
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
|
||
|
* provided with the Original Code and Modifications, and is also
|
||
|
* available at www.sybase.com/developer/opensource.
|
||
|
*
|
||
|
* The Original Code and all software distributed under the License are
|
||
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||
|
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
|
||
|
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
||
|
* NON-INFRINGEMENT. Please see the License for the specific language
|
||
|
* governing rights and limitations under the License.
|
||
|
*
|
||
|
* ========================================================================
|
||
|
*
|
||
|
* Description: time utility functions
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
|
||
|
#include "variety.h"
|
||
|
#include <time.h>
|
||
|
#include "rtdata.h"
|
||
|
#include "timedata.h"
|
||
|
|
||
|
static int time_less( const struct tm *t1, const struct tm *t2 );
|
||
|
|
||
|
static int calc_yday( const struct tm *timetm, int year )
|
||
|
{
|
||
|
struct tm tmptm;
|
||
|
int month_days;
|
||
|
int first_wday;
|
||
|
int nth_week;
|
||
|
short const *diyr;
|
||
|
|
||
|
if( timetm->tm_isdst == 0 ) { // M.m.n.d form
|
||
|
diyr = ( __leapyear( ( unsigned ) year + 1900 ) ) ? __dilyr : __diyr;
|
||
|
month_days = diyr[timetm->tm_mon + 1] - diyr[timetm->tm_mon];
|
||
|
tmptm.tm_sec = 0;
|
||
|
tmptm.tm_min = 0;
|
||
|
tmptm.tm_hour = 0;
|
||
|
tmptm.tm_mday = 1;
|
||
|
tmptm.tm_mon = timetm->tm_mon;
|
||
|
tmptm.tm_year = year;
|
||
|
tmptm.tm_isdst = 0;
|
||
|
( void ) mktime( &tmptm );
|
||
|
first_wday = ( timetm->tm_wday - tmptm.tm_wday + 7 ) % 7;
|
||
|
if( timetm->tm_mday == 5 ) {
|
||
|
if( ( 1 + first_wday + ( timetm->tm_mday - 1 ) * 7 ) > month_days )
|
||
|
nth_week = timetm->tm_mday - 2; // fifth req. weekday does not exist
|
||
|
else
|
||
|
nth_week = timetm->tm_mday - 1;
|
||
|
} else
|
||
|
nth_week = timetm->tm_mday - 1;
|
||
|
return( tmptm.tm_yday + first_wday + nth_week * 7 );
|
||
|
}
|
||
|
if( timetm->tm_isdst == 1 ) /* if Jn form */
|
||
|
return( timetm->tm_yday - 1 );
|
||
|
return( timetm->tm_yday );
|
||
|
}
|
||
|
|
||
|
/* determine if in souther hemisphere -> start is after end */
|
||
|
static int check_order( const struct tm *start, const struct tm *end, int year )
|
||
|
{
|
||
|
int start_day;
|
||
|
int end_day;
|
||
|
|
||
|
/* these quick checks should always be enough */
|
||
|
if( ( start->tm_isdst == 0 ) && ( end->tm_isdst == 0 ) ) { // M.m.n.d form
|
||
|
if( start->tm_mon > end->tm_mon )
|
||
|
return( 1 );
|
||
|
if( start->tm_mon < end->tm_mon )
|
||
|
return( 0 );
|
||
|
}
|
||
|
/* start/end of daylight savings time is in the same month (rare case) */
|
||
|
/* these are *expensive* calculations under NT since 2 TZ checks must be done */
|
||
|
start_day = calc_yday( start, year );
|
||
|
end_day = calc_yday( end, year );
|
||
|
if( start_day > end_day )
|
||
|
return( 1 );
|
||
|
return( 0 );
|
||
|
}
|
||
|
|
||
|
/* determine if daylight savings time */
|
||
|
int __isindst( struct tm *t )
|
||
|
{
|
||
|
int month;
|
||
|
int dst;
|
||
|
int n1;
|
||
|
int n2;
|
||
|
int month_days;
|
||
|
int time_check;
|
||
|
int south;
|
||
|
struct tm const *start;
|
||
|
struct tm const *end;
|
||
|
short const *diyr;
|
||
|
|
||
|
// already determined -- if we are sure
|
||
|
if( t->tm_isdst >= 0 )
|
||
|
return( t->tm_isdst );
|
||
|
dst = 0;
|
||
|
// if zone doesn't have a daylight savings period
|
||
|
if( _RWD_daylight == 0 )
|
||
|
return( t->tm_isdst = dst );
|
||
|
// // check for no daylight savings time rule
|
||
|
// if( tzname[1][0] == '\0' ) { // doesn't work since Win32 says
|
||
|
// return( t->tm_isdst = dst );// daylight zone name = standard zone name
|
||
|
// }
|
||
|
|
||
|
south = check_order( &_RWD_start_dst, &_RWD_end_dst, t->tm_year );
|
||
|
if( south ) {
|
||
|
// if southern hemisphere
|
||
|
// invert start and end dates and then invert return value
|
||
|
start = &_RWD_end_dst;
|
||
|
end = &_RWD_start_dst;
|
||
|
} else {
|
||
|
start = &_RWD_start_dst;
|
||
|
end = &_RWD_end_dst;
|
||
|
}
|
||
|
month = t->tm_mon;
|
||
|
diyr = ( __leapyear( ( unsigned ) t->tm_year + 1900 ) ) ? __dilyr : __diyr;
|
||
|
month_days = diyr[month + 1] - diyr[month];
|
||
|
time_check = 0;
|
||
|
/*
|
||
|
* M.m.n.d form
|
||
|
* m = start->tm_mon (month 0-11)
|
||
|
* n = start->tm_mday (n'th week day 1-5)
|
||
|
* d = start->tm_wday (week day 0-6)
|
||
|
*/
|
||
|
if( start->tm_isdst == 0 ) { /* if Mm.n.d form */
|
||
|
if( month > start->tm_mon )
|
||
|
dst = 1; /* assume dst for now */
|
||
|
else if( month == start->tm_mon ) {
|
||
|
/* calculate for current day */
|
||
|
n1 = t->tm_mday - ( t->tm_wday + 7 - start->tm_wday ) % 7;
|
||
|
/* calculate for previous day */
|
||
|
n2 = t->tm_mday - 1 - ( t->tm_wday - 1 + 7 - start->tm_wday ) % 7;
|
||
|
// n_ stands for the day of the month that is past &&
|
||
|
// is closest to today && is the required weekday
|
||
|
if( start->tm_mday == 5 ) {
|
||
|
if( n1 > month_days - 7 ) {
|
||
|
dst = 1; /* assume dst for now */
|
||
|
if( n2 <= month_days - 7 )
|
||
|
time_check = 1;
|
||
|
}
|
||
|
} else {
|
||
|
if( n1 >= 7 * ( start->tm_mday - 1 ) + 1 ) {
|
||
|
dst = 1; /* assume dst for now */
|
||
|
if( n2 < 7 * ( start->tm_mday - 1 ) + 1 )
|
||
|
time_check = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
n1 = start->tm_yday;
|
||
|
if( start->tm_isdst == 1 ) { /* if Jn form */
|
||
|
if( __leapyear( ( unsigned ) t->tm_year + 1900 ) ) {
|
||
|
if( n1 > __diyr[2] )
|
||
|
n1++; /* past Feb 28 */
|
||
|
}
|
||
|
n1--;
|
||
|
}
|
||
|
if( t->tm_yday >= n1 ) {
|
||
|
dst = 1; /* assume dst for now */
|
||
|
if( t->tm_yday == n1 )
|
||
|
time_check = 1;
|
||
|
}
|
||
|
}
|
||
|
/* if it is the day for a switch-over then check the time too */
|
||
|
if( time_check )
|
||
|
dst = !time_less( t, start );
|
||
|
|
||
|
/* if we are certain that it is before daylight saving then return */
|
||
|
if( dst == 0 ) {
|
||
|
if( south )
|
||
|
dst = south - dst; /* invert value of dst */
|
||
|
return( t->tm_isdst = dst );
|
||
|
}
|
||
|
|
||
|
/* now see if it is after daylight saving */
|
||
|
time_check = 0;
|
||
|
if( end->tm_isdst == 0 ) { /* if Mm.n.d form */
|
||
|
if( month > end->tm_mon )
|
||
|
dst = 0; /* not dst */
|
||
|
else if( month == end->tm_mon ) {
|
||
|
dst = 0;
|
||
|
/* calculate for current day */
|
||
|
n1 = t->tm_mday - ( t->tm_wday + 7 - end->tm_wday ) % 7;
|
||
|
/* calculate for previous day */
|
||
|
n2 = t->tm_mday - 1 -
|
||
|
( t->tm_wday - 1 + 7 - end->tm_wday ) % 7;
|
||
|
if( end->tm_mday == 5 ) {
|
||
|
if( n1 <= month_days - 7 )
|
||
|
dst = 1;
|
||
|
else if( n2 <= month_days - 7 )
|
||
|
time_check = 1;
|
||
|
} else {
|
||
|
if( n1 < 7 * ( end->tm_mday - 1 ) + 1 )
|
||
|
dst = 1;
|
||
|
else if( n2 < 7 * ( end->tm_mday - 1 ) + 1 )
|
||
|
time_check = 1;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
n1 = end->tm_yday;
|
||
|
if( end->tm_isdst == 1 ) { /* if Jn form */
|
||
|
if( __leapyear( ( unsigned ) t->tm_year + 1900 ) ) {
|
||
|
if( n1 > __diyr[2] )
|
||
|
n1++; /* past Feb 28 */
|
||
|
}
|
||
|
n1--;
|
||
|
}
|
||
|
if( t->tm_yday >= n1 ) {
|
||
|
dst = 0;
|
||
|
if( t->tm_yday == n1 )
|
||
|
time_check = 1;
|
||
|
}
|
||
|
}
|
||
|
/* if it is the day for a switch-over then check the time too */
|
||
|
if( time_check )
|
||
|
dst = time_less( t, end );
|
||
|
if( south )
|
||
|
dst = south - dst; /* invert value of dst */
|
||
|
return( t->tm_isdst = dst );
|
||
|
}
|
||
|
|
||
|
static int time_less( const struct tm *t1, const struct tm *t2 )
|
||
|
{
|
||
|
int before;
|
||
|
|
||
|
before = 0;
|
||
|
if( t1->tm_hour < t2->tm_hour )
|
||
|
before = 1;
|
||
|
else if( t1->tm_hour == t2->tm_hour ) {
|
||
|
if( t1->tm_min < t2->tm_min
|
||
|
|| t1->tm_min == t2->tm_min && t1->tm_sec < t2->tm_sec )
|
||
|
before = 1;
|
||
|
}
|
||
|
return( before );
|
||
|
}
|