gambas-source-code/main/lib/vb/vbdate.c
Benoît Minisini f65184802d [CONFIGURATION]
* NEW: Update copyright and license string in all source files.


git-svn-id: svn://localhost/gambas/trunk@2241 867c0c6c-44f3-4631-809d-bfa615b0a4ec
2009-08-17 10:41:51 +00:00

206 lines
6.1 KiB
C

/***************************************************************************
vbdate.c
(c) 2000-2003 Nigel Gerrard <nigel@gerrard1123.freeserve.co.uk>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***************************************************************************/
#define __VBDATE_C
#include "vbdate.h"
#include <stdio.h>
#include <string.h>
#define DATE_YEAR_MIN -4801
#define DATE_YEAR_MAX 9999
/* Helper functions */
const char days_in_months[2][13] =
{ /* error, jan feb mar apr may jun jul aug sep oct nov dec */
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
};
const short days_in_year[2][14] =
{ /* 0, jan feb mar apr may jun jul aug sep oct nov dec */
{ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
{ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } /* leap year */
};
/* Returns 1 for a leap year, 0 else */
int date_is_leap_year(short year)
{
if (year < 0)
year += 8001;
if ((((year % 4) == 0) && ((year % 100) != 0)) || (year % 400) == 0)
return 1;
else
return 0;
}
int date_is_valid(GB_DATE_SERIAL *date)
{
return ((date->month >= 1) && (date->month <= 12) &&
(date->year >= DATE_YEAR_MIN) && (date->year <= DATE_YEAR_MAX) && (date->year != 0) &&
(date->day >= 1) && (date->day <= days_in_months[date_is_leap_year(date->year)][(short)date->month]) &&
(date->hour >= 0) && (date->hour <= 23) && (date->min >= 0) && (date->min <= 59) &&
(date->sec >= 0) && (date->sec <= 59));
}
/** Modulo function that works correctly for negative divisors */
#define modulo(x,y) ((x)%(y)<0?(x)%(y)+(y):(x)%(y))
void DATE_adjust( GB_DATE *vdate, int period, int interval) /* Adjust the date by the interval period */
{
GB_DATE_SERIAL *date;
int year, month, day;
date = GB.SplitDate(vdate);
switch(period){
case 0: /* Calendar Month */
year = ((date->year * 12) + (date->month - 1) + interval)/12;
month = modulo((date->month - 1)+interval, 12) + 1;
day = date->day > days_in_months[date_is_leap_year(year)][month] ? days_in_months[date_is_leap_year(year)][month] : date->day;
date->day = day;
date->month = month;
date->year = year;
GB.MakeDate(date, vdate);
break;
case 1: /* days */
vdate->value.date += interval;
break;
case 2: /* Time */
vdate->value.time += interval;
break;
case 3: /* weekdays - in this case weekdays are Mon - Fri */
vdate->value.date += ( 7 * ( interval / 5));
date->weekday += ( interval % 5 );
if (date->weekday > 5){
date->weekday -= 5;
vdate->value.date += 2;
}
if (date->weekday < 1){
date->weekday += 5;
vdate->value.date -= 2;
}
vdate->value.date += ( interval % 5);
break;
case 4: /* Add year */
while ( interval != 0){
if ( interval < 0 ){
vdate->value.date -= days_in_year[date_is_leap_year(date->year)][13];
date->year--;
interval++;
}
else {
vdate->value.date += days_in_year[date_is_leap_year(date->year)][13];
date->year++;
interval--;
}
}
break;
}
/* Now if time takes it into another day */
while (vdate->value.time >= 86400000){
vdate->value.date++;
vdate->value.time -= 86400000;
}
/* Or time is negative so we need to take off a day */
while (vdate->value.time < 0){
vdate->value.date--;
vdate->value.time += 86400000;
}
CLEAR(&date);
date = GB.SplitDate(vdate);
if (!date_is_valid(date)){
/*printf("Invalid date : year [%i] month [%i] day [%i] hour [%i] min [%i] sec [%i] weekday [%i] msec [%i]\n",
date->year, date->month, date->day, date->hour, date->min, date->sec, date->weekday, date->msec);*/
GB.Error("Invalid Date Returned");
}
CLEAR(&date);
}
int DATE_diff( GB_DATE *vdate1, GB_DATE *vdate2, int period)
{
GB_DATE_SERIAL *date1, *date2;
int diff = 0;
int year1, year2, month1, month2, weekday1, weekday2;
date1 = GB.SplitDate(vdate1);
weekday1 = date1->weekday;
year1 = date1->year;
month1 = date1->month;
date2 = GB.SplitDate(vdate2);
weekday2 = date2->weekday;
year2 = date2->year;
month2 = date2->month;
switch(period){
case 0: /* Calendar Month */
diff = ((year1 * 12) + month1) - ((year2 * 12) + month2);
break;
case 1: /* days */
diff = vdate1->value.date - vdate2->value.date;
break;
case 2: /* Time */
diff = ((vdate1->value.date - vdate2->value.date) * 86400000) + (vdate1->value.time - vdate2->value.time);
break;
case 3: /* weekdays */
/* If invalid weekday then find closest valid day:
* eg. 7 -1, 6 -5*/
weekday1 == 7 ? weekday1 = 1 : weekday1 == 6 ? weekday1 = 5 : weekday1;
weekday2 == 7 ? weekday2 = 1 : weekday2 == 6 ? weekday2 = 5 : weekday2;
diff = (((vdate1->value.date - vdate2->value.date) / 7) * 5)
+ (weekday1 - weekday2);
/* Needs validating */
break;
case 4: /* year */
diff = year1 - year2;
break;
case 5: /* Weeks */
diff = (vdate1->value.date - vdate2->value.date)/7;
break;
}
CLEAR(&date1);
CLEAR(&date2);
return diff;
}
/* End Helper functions */
/*
GB_DESC CVbDateDesc[] =
{
GB_DECLARE("Vb", 0), GB_NOT_CREATABLE(),
GB_STATIC_METHOD("DateAdd", "d", CVB_DateAdd, "(period)s(interval)i(Date)v"),
GB_STATIC_METHOD("DateDiff", "i", CVB_DateDiff, "(period)s(Date1)v(Date2)v"),
GB_END_DECLARE
};*/