kolibrios/programs/develop/open watcom/trunk/clib/time/timeutil.c
Sergey Semyonov (Serge) 3be3768ce0 update CLib
git-svn-id: svn://kolibrios.org@614 a494cfbc-eb01-0410-851d-a64ba20cac60
2007-08-28 10:31:46 +00:00

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 );
}