#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <common/sdate.hpp>
#include <common/string.hpp>

SDate::SDate(void)
{
  initArray();
	getTodaysDate();
}

SDate::SDate(const CompatibleDate &someDate)
{
  initArray();
	CompatibleDate::Month=someDate.Month;
	CompatibleDate::Day=someDate.Day;
	CompatibleDate::Year=someDate.Year;
}

SDate::SDate(long someLongDate)
{
  initArray();
	*this=someLongDate;
}

SDate::SDate(unsigned char month,unsigned char day,unsigned year)
{
  initArray();
	CompatibleDate::Month=month;
	CompatibleDate::Day=day;
	CompatibleDate::Year=year;
}

SDate::SDate(const SDate &someDate)
{
  initArray();
	CompatibleDate::Month=someDate.CompatibleDate::Month;
	CompatibleDate::Day=someDate.CompatibleDate::Day;
	CompatibleDate::Year=someDate.CompatibleDate::Year;
}

SDate::SDate(Options dateOptions)
{
  initArray();
	switch(dateOptions)
	{
		case TodaysDate :
			getTodaysDate();
			break;
		default :
			break;
	}
}

SDate::~SDate()
{
}

void SDate::getTodaysDate(void)
{
	struct tm *lpTime;
	time_t currTime;

	::time(&currTime);
	lpTime=::localtime(&currTime);
	CompatibleDate::Month=lpTime->tm_mon+1;
	CompatibleDate::Day=lpTime->tm_mday;
	CompatibleDate::Year=lpTime->tm_year+1900;
}

SDate &SDate::operator=(const CompatibleDate &someDate)
{
	CompatibleDate::Month=(unsigned)someDate.Month;
	CompatibleDate::Day=(unsigned)someDate.Day;
	CompatibleDate::Year=(unsigned)someDate.Year;
	return *this;
}

SDate &SDate::operator=(long someLongDate)
{
	CompatibleDate::Year=(unsigned)(someLongDate/10000L);
	CompatibleDate::Month=(unsigned)((someLongDate/100L)-((long)CompatibleDate::Year*100L));
	CompatibleDate::Day=(unsigned)(someLongDate-(((long)CompatibleDate::Year*10000L)+((long)CompatibleDate::Month*100L)));
	return *this;
}

int SDate::operator<(const CompatibleDate &someDate)const
{
	if (CompatibleDate::Year<someDate.Year) return TRUE;
	if (CompatibleDate::Year>someDate.Year) return FALSE;
	 if (CompatibleDate::Month<someDate.Month) return TRUE;
	 if (CompatibleDate::Month>someDate.Month) return FALSE;
	return(CompatibleDate::Day<someDate.Day);
}

int SDate::operator>(const CompatibleDate &someDate)const
{
	if (CompatibleDate::Year>someDate.Year) return TRUE;
	if (CompatibleDate::Year<someDate.Year) return FALSE;
	 if (CompatibleDate::Month>someDate.Month) return TRUE;
   if (CompatibleDate::Month<someDate.Month) return FALSE;
	return(CompatibleDate::Day>someDate.Day);
}

int SDate::operator==(const CompatibleDate &someDate)const
{
	return (CompatibleDate::Month==someDate.Month &&
					CompatibleDate::Day==someDate.Day &&
					CompatibleDate::Year==someDate.Year
					);
}

int SDate::operator<=(const CompatibleDate &someDate)const
{
	if(!(operator<(someDate)))return operator==(someDate);
	return TRUE;
}

int SDate::month(void)const
{
  return CompatibleDate::Month;
}

void SDate::month(int newMonth)
{
	CompatibleDate::Month=newMonth;
}

int SDate::day(void)const
{
  return CompatibleDate::Day;
}

void SDate::day(int newDay)
{
	CompatibleDate::Day=newDay;
}

int SDate::year(void)const
{
	return CompatibleDate::Year;
}

void SDate::year(int newYear)
{
	CompatibleDate::Year=newYear;
}

long SDate::makeLong(void)const
{
  return 10000L*CompatibleDate::Year + 100L*CompatibleDate::Month + CompatibleDate::Day;
}

String SDate::makeString(void)const
{
	String tempString;

	tempString.reserve(String::MaxString);
        ::sprintf((LPSTR)tempString,"%02d/%02d/%04d",
		CompatibleDate::Month,
		CompatibleDate::Day,
                CompatibleDate::Year);
	return tempString;
}

SDate SDate::monthYearStringToDate(char *str,short default_day)
{
	SDate null_date;
	SDate return_date;
	SDate cur_date;
	short n_items;
	short first_num;
	short second_num;
	short third_num;
	short first_two_digits;
	short second_two_digits;
	short last_two_digits;
	short first_four_digits;
	short last_four_digits;
	char s[256];
	char s2[80];
	char first[32];
	char second[32];
	char third[32];
	char *ps;
	char *ps2;
	unsigned short ndays[sizeof(mDayArray)/sizeof(WORD)];

	::memcpy(ndays,mDayArray,sizeof ndays);
	cur_date=SDate(SDate::TodaysDate);
	::memset(&null_date,0,sizeof(null_date));
	::memset(first,0,sizeof(first));
	::memset(second,0,sizeof(second));
	::memset(third,0,sizeof(third));
	if(!str)return null_date;

	if(::strlen(str)>255)::strncpy(s,str,255);
	else ::strcpy(s,str);
	::strupr(s);
	for(ps=s,ps2=s2;*ps!='\0';++ps)
	{
		if(!isalnum(*ps))
		{
			*ps=' ';
			if(ps2==s2)continue;
		}
		if(ps2>s2)
		{
			if(isdigit(*(ps2-1))&&isalpha(*ps)||isalpha(*(ps2-1)) && isdigit(*ps))	
			{
				*ps2=' ';
				++ps2;
			}
			else
			if(*(ps2-1)==' '&&*ps==' ')continue;
		}
		*ps2=*ps;
		++ps2;
	}
	*ps2='\0';	/* Terminating null */
	strcpy(s,s2);
	n_items=::sscanf(s," %s %s %s",first,second,third);
	if(n_items<=0)return null_date;
	if(1==n_items)
	{
		long single_num;
		if(isalpha(first[0]))		/* Not a numeric value */
		{
			return_date.Month=stringToMonth(first);
			if(0==return_date.Month)return null_date;
			return_date.Year=cur_date.Year;
			if(0==default_day)
			{
				if(2==return_date.Month&&((short)return_date.Year%4))ndays[2]=28;
				default_day=ndays[return_date.Month];
			}
			else
			if(default_day<1||default_day>ndays[return_date.Month])return null_date;
			return_date.Day=default_day;
			return return_date;
		}
		single_num=::atol(first);
		if(single_num>1000||(first[0]=='0'&&single_num>=100))
		{
			if (single_num>100000)
			{
				first_two_digits=(first[0]-'0')*10+(first[1]-'0');
				second_two_digits=(first[2]-'0')*10+(first[3]-'0');
				last_two_digits=single_num%100;
			}
			else
			if(single_num>10000)
			{
				first_four_digits=single_num/10;
				last_four_digits=single_num%10000;
				if(last_four_digits>=1960&&last_four_digits<=2060)
				{									
					return_date.Year=last_four_digits;
					return_date.Month=single_num/10000;
					if(0==default_day)
					{
						if(2==return_date.Month&&((short)return_date.Year%4))ndays[2]=28;
						default_day=ndays[return_date.Month];
					}
					return_date.Day=default_day;
					n_items=0;
				}
				else
				if(first_four_digits>=1960&&first_four_digits<=2060&&(single_num%10)!=0)
				{			
					return_date.Year=first_four_digits;
					return_date.Month=single_num%10;
					if(0==default_day)
					{
						if(return_date.Month==2&&((short)return_date.Year%4))ndays[2]=28;
						default_day=ndays[return_date.Month];
					}
					return_date.Day=default_day;
					n_items=0;
				}
				else	
				{
					first_two_digits=(first[0]-'0')*10+(first[1]-'0');
					last_two_digits=single_num%100;
					second_num=single_num/100;
					if((second_num % 100) <= 31 && (second_num / 100) != 0)
					{
						sprintf(second, "%d", second_num / 100);	/* Month or day string */
						sprintf(third, "%d", second_num % 100);	/* Day or month string */
						return_date=stringToMonthDay(second,third);
						return_date.Year = last_two_digits;
						n_items = 0;
					}
					else
					if ((second_num % 10) > 0 || (second_num / 10) <= 31)
					{												/* Split it differently */
						sprintf(second, "%d", second_num / 10);
						sprintf(third, "%d", second_num % 10);
						return_date = stringToMonthDay(second, third);
						return_date.Year = last_two_digits;
						n_items = 0;
					}
					else		/* Assume first 2 digits are the year */
					{
						second_num = single_num % 1000;	/* Assume first 2 digits are yr */
						if ((second_num % 100) <= 31 && (second_num / 100) != 0)
						{
							sprintf(second, "%d", second_num / 100);	/* Month or day string */
							sprintf(third, "%d", second_num % 100);	/* Day or month string */
							return_date = stringToMonthDay(second, third);
							return_date.Year = first_two_digits;
							n_items = 0;
						}
						else
						if ((second_num % 10) > 0 || (second_num / 10) <= 31)
						{												/* Split it differently */
							sprintf(second, "%d", second_num / 10);
							sprintf(third, "%d", second_num % 10);
							return_date = stringToMonthDay(second, third);
							return_date.Year = first_two_digits;
							n_items = 0;
						}
						else	/* Nothing works */
						{
							sprintf(second, "%d", second_num / 100);	/* Month or day string */
							sprintf(third, "%d", second_num % 100);	/* Day or month string */
							return_date = stringToMonthDay(second, third);
							return_date.Year = last_two_digits;
							n_items = 0;
						}
					}
				}
			}
			else		/* Only 4 digits */
			{
				first_two_digits = (first[0] - '0') * 10 + (first[1] - '0');
				last_two_digits = single_num % 100;
				
				if ((last_two_digits < 1 || last_two_digits > 12) &&
					first_two_digits >= 1 && first_two_digits <= 12)
				{										/* Definitely MMYY */
					return_date.Year = last_two_digits;
					return_date.Month = first_two_digits;
					if (default_day == 0)
					{
						if (return_date.Month == 2 && ((short)return_date.Year % 4))
							ndays[2] = 28;		/* Not a leap year */
						default_day = ndays[return_date.Month];
					}
					return_date.Day = default_day;
					n_items = 0;
				}
				else
				if ((first_two_digits < 1 || first_two_digits > 12) &&
					last_two_digits >= 1 && last_two_digits <= 12)
				{										/* Definitely YYMM */
					return_date.Year = last_two_digits;
					return_date.Month = last_two_digits;
					if (default_day == 0)
					{
						if (return_date.Month == 2 && ((short)return_date.Year % 4))
							ndays[2] = 28;		/* Not a leap year */
						default_day = ndays[return_date.Month];
					}
					return_date.Day = default_day;
					n_items = 0;
				}
				else
				if (first_two_digits >= 1 && first_two_digits <= 12)
				{										/* Qualifies as MMYY */
					return_date.Year = last_two_digits;
					return_date.Month = first_two_digits;
					if (default_day == 0)
					{
						if (return_date.Month == 2 && ((short)return_date.Year % 4))
							ndays[2] = 28;		/* Not a leap year */
						default_day = ndays[return_date.Month];
					}
					return_date.Day = default_day;
					n_items = 0;
				}
				else		/* Might be month/day/year combination */
				{
					if (first[0] == '0' || first[1] == '0')
					{				/* First two digits are most likely the year */
						second_two_digits = first[2] - '0';
						last_two_digits = first[3] - '0';
					}
					else
					if (first[2] == '0' || first[3] == '0')
					{				/* Last two digits are most likely the year */
						first_two_digits = first[0] - '0';
						second_two_digits = first[1] - '0';
					}
					else
					if ((last_two_digits < 30 || last_two_digits > 80) &&
						first_two_digits >= 30 && first_two_digits <= 80)
					{				/* Last two digits are most likely the year */
						first_two_digits = first[0] - '0';
						second_two_digits = first[1] - '0';
					}
					else
					if ((first_two_digits < 30 || first_two_digits > 80) &&
						last_two_digits >= 30 && last_two_digits <= 80)
					{				/* First two digits are most likely the year */
						second_two_digits = first[2] - '0';
						last_two_digits = first[3] - '0';
					}
					else		/* Really don't know; assume last two digits are year */
					{				/* Last two digits are most likely the year */
						first_two_digits = first[0] - '0';
						second_two_digits = first[1] - '0';
					}
				}
			}

			if (n_items == 1)
			if (last_two_digits == 0 || last_two_digits > 31)
			{							/* Last two digits are definitely the year */
				if (single_num > 1000000)		/* It is a 4-digit year */
					second_num = single_num / 10000;
				else		/* It is a 2-digit year */
					second_num = single_num / 100;
				
				sprintf(second, "%d", second_num / 100);	/* Month or day string */
				sprintf(third, "%d", second_num % 100);	/* Day or month string */
				if ((second_num % 100) > 31 || (second_num / 100) == 0)
				{												/* Split it differently */
					sprintf(second, "%d", second_num / 10);
					sprintf(third, "%d", second_num % 10);
				}
				return_date = stringToMonthDay(second, third);
				return_date.Year = last_two_digits;
				n_items = 0;
			}
			else
			if (first_two_digits == 0 || first_two_digits > 31)
			{											/* First 2 digits are the year */
				second_num = atoi(&first[2]);
				sprintf(second, "%d", second_num / 100);
				sprintf(third, "%d", second_num % 100);
				if ((second_num % 100) > 31 || (second_num / 100) == 0)
				{												/* Split it differently */
					sprintf(second, "%d", second_num / 10);
					sprintf(third, "%d", second_num % 10);
				}
				return_date = stringToMonthDay(second, third);
				return_date.Year = first_two_digits;
				n_items = 0;
			}
			else
			if (second_two_digits == 0 || second_two_digits > 31)
			{		/* Second 2 digits are the year, assume 4-digit year was given */
				second_num = atoi(&first[4]);
				sprintf(second, "%d", second_num / 100);
				sprintf(third, "%d", second_num % 100);
				if ((second_num % 100) > 31 || (second_num / 100) == 0)
				{												/* Split it differently */
					sprintf(second, "%d", second_num / 10);
					sprintf(third, "%d", second_num % 10);
				}
				return_date = stringToMonthDay(second, third);
				return_date.Year = first_two_digits * 100 + second_two_digits;
				n_items = 0;
			}
			else	/* Can't tell from looking for 2-digit years; try 4-digit yrs */
			{
				short first_four_digits, last_four_digits;
				
				strncpy(second, first, 4);
				second[4] = '\0';
				first_four_digits = atoi(second);
				last_four_digits = single_num % 10000;
				
				if (last_four_digits >= 1960 && last_four_digits <= 2060)
				{		/* Last 4 digits are definitely the year */
					second_num = single_num / 10000;
					sprintf(second, "%d", second_num / 100);
					sprintf(third, "%d", second_num % 100);
					if ((second_num % 100) > 31 || (second_num / 100) == 0)
					{												/* Split it differently */
						sprintf(second, "%d", second_num / 10);
						sprintf(third, "%d", second_num % 10);
					}
					return_date = stringToMonthDay(second, third);
					return_date.Year = last_four_digits;
					n_items = 0;
				}
				else		/* Give up; assume YYMMDD format */
				if (first_four_digits >= 1960 && first_four_digits <= 2060)
				{		/* First 4 digits are definitely the year */
					second_num = atoi(&first[4]);
					sprintf(second, "%d", second_num / 100);
					sprintf(third, "%d", second_num % 100);
					if ((second_num % 100) > 31 || (second_num / 100) == 0)
					{												/* Split it differently */
						sprintf(second, "%d", second_num / 10);
						sprintf(third, "%d", second_num % 10);
					}
					return_date = stringToMonthDay(second, third);
					return_date.Year = first_four_digits;
					n_items = 0;
				}
				else
				{
					sprintf(first, "%d", first_two_digits);
					sprintf(second, "%d", second_two_digits);
					sprintf(third, "%d", last_two_digits);
					n_items = 3;
				}
			}
		}
		else		/* 3 or fewer digits */
		{
			return null_date;		/* Does not qualify as month/year */
		}
	}
	
	switch (n_items)
	{
		case 0:		/* This means we already parsed the date */
			break;
		case 1:	/* Only 1 item scanned, assume it is a month */
			return_date.Month = atoi(first);
			if (return_date.Month >= 1 && return_date.Month <= 12)
			{
				return_date.Year = cur_date.Year;
				if (default_day == 0)
				{
					if (return_date.Month == 2 && ((short)return_date.Year % 4))
						ndays[2] = 28;		/* Not a leap year */
					default_day = ndays[return_date.Month];
				}
				return_date.Day = default_day;
			}
			else		/* Not a valid month number; an error */
				return null_date;
			break;
			
		case 2:	/* 2 items scanned, assume month and year */
		{
			short n_days;
			
			return_date=stringToMonthYear(first, second);
			
			if (*(long *)&return_date)		/* It was month/year */
			{
				if (default_day == 0)
				{
					if (return_date.Month == 2 && ((short)return_date.Year % 4))
						ndays[2] = 28;		/* Not a leap year */
					if (return_date.Month >= 1 && return_date.Month <= 12)
						default_day = ndays[return_date.Month];
				}
				return_date.Day = default_day;
			}
			else		/* Not month/year */
			{
				return_date = stringToMonthDay(first, second);
				return_date.Year = cur_date.Year;
				n_days = daysBetweenActual(cur_date, return_date);
				if (n_days < -182)			/* More than 6 months back */
					++return_date.Year;		/* Assume it's next year */
				else
				if (n_days > 182)				/* More than 6 months in the future */
					--return_date.Year;		/* Assume it's last year */
			}
			break;
		}
		case 3:	/* 3 items scanned, assume month, day, and year */
			first_num = atoi(first);
			second_num = atoi(second);
			third_num = atoi(third);
			if (first_num > 31)		/* First item is a year */
			{
				return_date = stringToMonthDay(second, third);
				return_date.Year = first_num;
			}
			else
			if (third_num > 31)		/* Third item is a year */
			{
				return_date = stringToMonthDay(first, second);
				return_date.Year = third_num;
			}
			else
			if (second_num > 31)		/* Second item might be a year? */
			{
				return_date = stringToMonthDay(first, third);
				return_date.Year = second_num;
			}
			else
			if (isalpha(first[0]))	/* First item is a month */
			{								/* Assume second is day, third is year */
				return_date = stringToMonthDay(first, second);
				return_date.Year = third_num;
			}
			else
			if (isalpha(second[0]))	/* Second item is a month */
			{								/* Assume first is day, third is year */
				return_date = stringToMonthDay(first, second);
				return_date.Year = third_num;
			}
			else
			if (isalpha(third[0]))	/* Third item might be a month? */
			{								/* Assume first is year, second is day */
				return_date = stringToMonthDay(second, third);
				return_date.Year = first_num;
			}
			else		/* Assume month, day, year */
			{
				return_date = stringToMonthDay(first, second);
				return_date.Year = third_num;
			}
			break;
	}	/* End of switch */
	
	if (return_date.Month < 1 || return_date.Month > 12)
		return null_date;			/* Month is out of range */
	
	if (return_date.Day < 1 || return_date.Day > ndays[return_date.Month])
	{							/* Day of month is out of range */
		if (default_day == 0)
		{
			if (return_date.Month == 2 && ((short)return_date.Year % 4))
				ndays[2] = 28;		/* Not a leap year */
			default_day = ndays[return_date.Month];
		}
		return_date.Day = default_day;
	}
	
	if (((short)return_date.Year % 4) != 0)		/* Not a leap year */
		ndays[2] = 28;
	
	if (return_date.Year > 1900)
		return return_date;		/* A valid date */
	
	if (return_date.Year >= 100)		/* Lotus format date for 21st century */
	{
		return_date.Year = 2000 + ((short)return_date.Year % 100);
		return return_date;
	}

	if (return_date.Year > 60)
		return_date.Year += 1900;
	else
		return_date.Year += 2000;
	
	return return_date;
	
}

short SDate::stringToMonth(char *szMonth)const /* Converts string to month number */
{
  String strMonth(szMonth);
	char *str;

	if(strMonth.isNull())return FALSE;
	strMonth.upper();
  str=(char*)strMonth;
	switch (str[0])		/* Look at first character to determine month */
	{
		case 'F':		/* February */
			return 2;
		case 'S':		/* September */
			return 9;
		case 'O':		/* October */
			return 10;
		case 'N':		/* November */
			return 11;
		case 'D':		/* December */
			return 12;
		case 'J':		/* January, June, or July */
			switch (str[1])		/* Look at second character */
			{
				case 'A':	/* January */
					return 1;
				case 'U':	/* June or July */
					switch (str[2])	/* Look at third character */
					{
						case 'N':	/* June */
							return 6;
						case 'L':	/* July */
							return 7;
					}	/* End of switch (str[2]) */
					break;
				case 'E':	/* June */
					return 6;
				case 'L':	/* July */
					return 7;
			}	/* End of switch (str[1]) */
			
			return 0;	/* Cannot determine what month this is */
			
		case 'M':		/* March or May */
			switch (str[1])		/* Look at second character */
			{
				case 'A':	/* March or May */
					switch (str[2])	/* Look at third character */
					{
						case 'R':	/* March */
							return 3;
						case 'Y':	/* May */
							return 5;
					}	/* End of switch (str[2]) */
					return 0;		/* Cannot determine the month */
				case 'R':	/* March */
					return 3;
				case 'Y':
					return 5;
			}	/* End of switch (str[1]) */
			
			return 0;	/* Cannot determine what month this is */
			
		case 'A':		/* April or August */
			switch (str[1])		/* Look at second character */
			{
				case 'P':	/* April */
				case 'R':
					return 4;
				case 'U':	/* August */
				case 'G':
					return 8;
			}	/* End of switch (str[1]) */
			
			return 0;	/* Cannot determine what month this is */
			
		default:
			return 0;	/* Cannot determine what month this is */
	}
}

SDate SDate::stringToMonthDay(char *first, char *second)
{
	SDate return_date;
	short first_num;
	short second_num;
	
	::memset(&return_date,0,sizeof(return_date));
	if(isalpha(first[0]))
	{
		return_date.month(stringToMonth(first));
		return_date.day(::atoi(second));
		return return_date;
	}
	if (isalpha(second[0]))		/* Second item is a month */
	{
		return_date.month(stringToMonth(second));
		return_date.day(::atoi(first));
		return return_date;
	}
	first_num=::atoi(first);
	second_num=::atoi(second);
	if(first_num>12)
	{
		return_date.day(first_num);
		return_date.month(second_num);
		return return_date;
	}
	if(second_num>12)
	{
		return_date.day(second_num);
		return_date.month(first_num);
		return return_date;
	}
	return_date.month(first_num);
	return_date.day(second_num);
	return return_date;
}

SDate SDate::stringToMonthYear(char *first, char *second)
{
	SDate return_date;
	short first_num, second_num;

	::memset(&return_date, 0, sizeof return_date);
	if(isalpha(first[0]))		/* First item is a month */
	{
		return_date.month(stringToMonth(first));
		return_date.year(::atoi(second));
		return return_date;
	}
	if(isalpha(second[0]))		/* Second item is a month */
	{
		return_date.month(stringToMonth(second));
		return_date.year(::atoi(first));
		return return_date;
	}
	first_num=::atoi(first);
	second_num=::atoi(second);
	if((second_num<1||second_num>12)&&first_num>=1&&first_num<=12)
	{
		return_date.year(second_num);
		return_date.month(first_num);
		return return_date;
	}
	else
	if((first_num<1||first_num>12)&&second_num>=1&&second_num<=12)
	{
		return_date.year(first_num);
		return_date.month(second_num);
		return return_date;
	}
	else
	if(first_num>=1&&first_num<=12)
	{
		return_date.year(second_num);
		return_date.month(first_num);
		return return_date;
	}
	return_date.year(first_num);
	return_date.month(second_num);
	return return_date;
}

short SDate::daysBetweenActual(SDate toDate)const
{
  return daysBetweenActual(*this,toDate);
}

short SDate::daysBetweenActual(SDate fromDate,SDate toDate)const
{
	short fromCount;
	short fromDay;
	short fromMonth;
	short fromYear;
	short fromQuad;
	short toCount;
	short toDay;
	short toMonth;
	short toYear;
	short toQuad;
	short daysTo[13];

	daysTo[0]=0;
	daysTo[1]=0;
	daysTo[2]=31;
	daysTo[3]=59;
	daysTo[4]=90;
	daysTo[5]=120;
	daysTo[6]=151;
	daysTo[7]=181;
	daysTo[8]=212;
	daysTo[9]=243;
	daysTo[10]=273;
	daysTo[11]=304;
	daysTo[12]=334;

	if(fromDate.makeLong()<19010101||
		 fromDate.makeLong()>20991231||
		 toDate.makeLong()<19010101||
		 toDate.makeLong()>20991231)return FALSE;
	fromDay=fromDate.day();
	fromMonth=fromDate.month();
	fromYear=((short)fromDate.year()-1)%4;
	fromQuad=((short)fromDate.year()-1)/4;
	fromCount=fromDay+daysTo[fromMonth]+fromYear*365+fromQuad*((4*365)+1);
	if(3==fromYear&&fromMonth>2)++fromCount;
	toDay=toDate.day();
	toMonth=toDate.month();
	toYear=((short)toDate.year()-1)%4;
	toQuad=((short)toDate.year()-1)/4;
	toCount=toDay+daysTo[toMonth]+toYear*365+toQuad*((4*365)+1);
	if(3==toYear&&toMonth>2)++toCount;
  return toCount-fromCount;
}

SDate SDate::daysAdd360(short dayCount)
{
	long days;
	WORD thirtyOne;
	SDate toDate;

	if(!year()&&!day()&&!month())return *this;
	if(!dayCount)return *this;
	thirtyOne=(31==day());
  if(thirtyOne)day(30);
	if(2==month()&&day()>27)
	{
	  int daysInMonth=!(year%4)&&(year%100)||!(year%400)?28:29;
		if(day()>=daysInMonth)day(30);
	}
	days=360*(long)year()+30*(long)month()+(long)day()+dayCount-391;
	toDate.day((short)(days%30)+1);
	toDate.month((short)(days/30)%12+1);
	toDate.year((short)(days/360)+1);
	if(!(dayCount%30)&&thirtyOne)
	{
		switch(toDate.month())
		{
			case 1 :
			case 3 :
			case 5 :
			case 7 :
			case 8 :
			case 10 :
			case 12 :
				toDate.day(toDate.day()+1);
				break;
		}
	}
	return toDate;
}

SDate SDate::daysAddActual(short actualDays)const
{
  return daysAddActual(*this,actualDays);
}

SDate SDate::daysAddActual(SDate fromDate,short actualDays)const
{
	short day;
	short month;
  short year;
	short daysInMonth;
	short days360;
	short dayCount;
	SDate date360;
	SDate returnDate;

	if(!fromDate.day()&&!fromDate.month()&&!fromDate.year())return fromDate;
	days360=((float)actualDays*360.00/365.24)+.5;

//  date360=fromDate.daysAdd360(days360);
  date360=SDate(fromDate).daysAdd360(days360);

	dayCount=actualDays-daysBetweenActual(fromDate,date360);
	day=date360.day();
	month=date360.month();
	year=date360.year();
	if(dayCount>=0)
	{
		while(dayCount>0)
		{
			switch(month)
			{
				case 2 :
					if(!(year%4)&&(year%100)||!(year%400))daysInMonth=29;
					else daysInMonth=28;
					break;
				case 4 :
				case 6 :
				case 9 :
				case 11 :
					daysInMonth=30;
					break;
				case 1:
				case 3:
				case 5:
				case 7:
				case 8:
				case 10:
				case 12:
					daysInMonth=31;
					break;
			}
			if(dayCount<=(daysInMonth-day)){day+=dayCount;dayCount=0;}
			else
			{
				dayCount-=daysInMonth-day+1;
				month=(month%12)+1;
				day=1;
				if(1==month)++year;
			}
		}
	}
	else
	{
		while(dayCount<0)
		{
			if(abs(dayCount)<day)
			{
				day+=dayCount;
				dayCount=0;
			}
			else
			{
				dayCount+=day;
				--month;
				if(0==month)
				{
					month=12;
					--year;
				}
				switch(month)
				{
					case 2 :
						if(!(year%4)&&(year%100)||!(year%400))day=29;
						else day=28;
						break;
					case 4:
					case 6:
					case 9:
					case 11:
						day=30;
						break;
					case 1:
					case 3:
					case 5:
					case 7:
					case 8:
					case 10:
					case 12:
						day=31;
						break;
				}
			}
		}
	}
	returnDate.year(year);
	returnDate.month(month);
	returnDate.day(day);
	return returnDate;
}

short SDate::daysBetween360(SDate fromDate,SDate toDate)const
{
	if(fromDate.day()>=30)
	{
		if(toDate.day()>=30)return (short)(360*((long)toDate.year()-(long)fromDate.year())+30*((long)toDate.month()-(long)fromDate.month()));
    fromDate.day(30);
	}
  return (short)(360*((long)toDate.year()-(long)fromDate.year())+30*((long)toDate.month()-(long)fromDate.month())+(long)toDate.day()-(long)fromDate.day());
}


