/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbDateTime
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbDateTime.h"
#include "xbTypesAdv.h"

void xbDateTime::ReadYMD(const xbString &str)
{
	int	y = xbStrToInt(str.Mid(0,4));
	int	m = xbStrToInt(str.Mid(4,2));
	int	d = xbStrToInt(str.Mid(6,2));

	SetDate(y,m,d);
}

xbString xbDateTime::ToYMD() const
{
	return xbFormat("%04d%02d%02d",GetYear(),GetMonth(),GetDay());
}

void xbDateTime::ReadYMDHMS(const xbString &str)
{
	if (str.GetLength() == 8)
	{
		ReadYMD(str);
		return;
	}
	int	y = xbStrToInt(str.Mid(0,4));
	int	mon = xbStrToInt(str.Mid(4,2));
	int	d = xbStrToInt(str.Mid(6,2));

	int	h = xbStrToInt(str.Mid(8,2));
	int	min = xbStrToInt(str.Mid(10,2));
	int	s = xbStrToInt(str.Mid(12,2));

	SetDateTime(y,mon,d,h,min,s);
}

xbString xbDateTime::ToYMDHMS() const
{
	return xbFormat("%04d%02d%02d%02d%02d%02d",GetYear(),GetMonth(),GetDay(),
		GetHour(),GetMinute(),GetSecond());
}

bool xbDateTime::IsLeapYear(int year) //must be between 100 and 9999, années bissextiles (1984, ...)
{
	if ((year % 4) != 0)
		return false;
	if ((year % 100) == 0
		&& (year % 400) != 0)
		return false;
	return true;
}

int xbDateTime::GetDaysOfMonth(int year,int month) //year must be between 100 and 9999, month : 1 and 12
{
	if (month < 1 || month > 12)
		xbThrowEsysFmt("xbDateTime::GetDaysOfMonth(%d,%d), bad month (=%d)",year,month,month);
	if ((month % 2) == 1)
		return month <= 7?31:30;

	if (month == 2)
		return IsLeapYear(year)?29:28;

	return month > 7?31:30;
}

xbString xbDateTime::tzInfoShort(const TIME_ZONE_INFORMATION *pInfo,const xbDateTime *pDt)
{
	if (tzInfoIsUtc(pInfo))
		return "UTC";

	if (!pDt || !tzInfoHasDayLight(pInfo))
		return tzBiasToStr(pInfo->Bias);

	if (pDt->tzInfoLocalInDayLight(pInfo))
		return tzBiasToStr(pInfo->Bias+pInfo->DaylightBias);

	return tzBiasToStr(pInfo->Bias+pInfo->StandardBias);
}

xbString xbDateTime::tzBiasToStr(int bias)
{
	if (bias > 0)
		return xbFormat("+%02ld:%02ld",bias/60,bias%60);
	bias = -bias;
	return xbFormat("-%02ld:%02ld",bias/60,bias%60);
}

bool xbDateTime::tzInfoIsUtc(const TIME_ZONE_INFORMATION *pInfo)
{
	return !pInfo->Bias && !pInfo->StandardDate.wMonth && !pInfo->DaylightDate.wMonth;
}

bool xbDateTime::tzInfoHasDayLight(const TIME_ZONE_INFORMATION *pInfo)
{
	return pInfo->StandardDate.wMonth && pInfo->DaylightDate.wMonth;
}

int xbDateTime::tzInfoLocalBias(const TIME_ZONE_INFORMATION *pInfo) const
{
	if (!tzInfoHasDayLight(pInfo))
		return -(pInfo->Bias);

	if (tzInfoLocalInDayLight(pInfo))
		return -(pInfo->Bias+pInfo->DaylightBias);
	return -(pInfo->Bias+pInfo->StandardBias);
}

bool xbDateTime::tzInfoMakeDate(ulong year,const SYSTEMTIME &tiIn, SYSTEMTIME &tiOut)
{
	tiOut = tiIn;
	tiOut.wYear = (WORD)year;
	if (tiOut.wDayOfWeek>6 || tiOut.wDay<=0 || tiOut.wDay>5)
	{
		xbRdbgOutput("xbDateTime::tzInfoMakeDate: day of week not in range");
		return false;
	}
	tiOut.wDay = 1;
	xbDateTime dt(tiOut);
	if (dt.IsEmpty())
	{
		tiOut = tiIn;
		tiOut.wYear = (WORD)year;
		xbRdbgOutput("xbDateTime::tzInfoMakeDate: date invalid");
		return false;
	}
	int dayOfWeek = dt.GetDayOfWeekSun();
/*	if (dayOfWeek < 0 || dayOfWeek > 6)
	{
		tiOut = tiIn;
		tiOut.wYear = (WORD)year;
		xbRdbgOutput("xbDateTime::tzInfoMakeDate: day invalid");
		return false;
	}*/
	if (tiIn.wDayOfWeek >= dayOfWeek)
		tiOut.wDay += tiIn.wDayOfWeek - dayOfWeek;
	else
		tiOut.wDay += 7 - (dayOfWeek - tiIn.wDayOfWeek);
	int noWeek = tiIn.wDay-1;
	tiOut.wDay += noWeek*7;
retryDt:
	if (tiOut.wDay>31)
		dt.Empty();
	else
		dt = tiOut;
	if (dt.IsEmpty())
	{
		if (tiOut.wDay > 14)
		{	
#ifdef _RELDBG
			if (noWeek < 4)
				xbRdbgOutput("xbDateTime::tzInfoMakeDate: date invalid, retry ("+xbString(tiOut.wDay)+"/"+xbString(tiOut.wMonth)+"/"+xbString(tiOut.wYear)+")");
#endif
			noWeek--;
			tiOut.wDay -= 7;
			goto retryDt;
		}
		tiOut = tiIn;
		tiOut.wYear = (WORD)year;
		xbRdbgOutput("xbDateTime::tzInfoMakeDate: date invalid, all retries failed");
		return false;
	}
	tiOut.wDayOfWeek = dt.GetDayOfWeekSun();
	xbRdbgAssert(tiOut.wDayOfWeek == tiIn.wDayOfWeek);
	return true;
}

xbDateTime xbDateTime::tzInfoDaylightDate(const TIME_ZONE_INFORMATION *pInfo) const
{
	SYSTEMTIME ti,tiOut;
	ToSystemTime(&ti);

	tzInfoMakeDate(ti.wYear,pInfo->DaylightDate,tiOut);

	return tiOut;
}

xbDateTime xbDateTime::tzInfoStandardDate(const TIME_ZONE_INFORMATION *pInfo) const
{
	SYSTEMTIME ti,tiOut;
	ToSystemTime(&ti);

	tzInfoMakeDate(ti.wYear,pInfo->StandardDate,tiOut);

	return tiOut;
}

bool xbDateTime::tzInfoLocalInDayLight(const TIME_ZONE_INFORMATION *pInfo) const
{
	if (!tzInfoHasDayLight(pInfo))
		return false;

	SYSTEMTIME ti,tiDaylight,tiStandard;
	ToSystemTime(&ti);

	if (systemTimeDiff(pInfo->DaylightDate,pInfo->StandardDate,false) > 0)
	{
		tzInfoMakeDate(ti.wYear,pInfo->DaylightDate,tiDaylight);
		if (systemTimeDiff(ti,tiDaylight,false) >= 0)
			return true;
		tzInfoMakeDate(ti.wYear,pInfo->StandardDate,tiStandard);
		if (systemTimeDiff(ti,tiStandard,false) < 0)
			return true;
		return false;
	}
	tzInfoMakeDate(ti.wYear,pInfo->StandardDate,tiStandard);
	if (systemTimeDiff(ti,tiStandard,false) >= 0)
		return false;
	tzInfoMakeDate(ti.wYear,pInfo->DaylightDate,tiDaylight);
	if (systemTimeDiff(ti,tiDaylight,false) >= 0)
		return true;
	return false;
}

xbDateTime xbDateTime::ToLocalTime(const TIME_ZONE_INFORMATION *pInfo) const
{
	if (tzInfoIsUtc(pInfo))
		return *this;

	xbDateTime dt(*this);
	dt -= double(pInfo->Bias) / (24.0 * 60.0);
	if (!tzInfoHasDayLight(pInfo))
		return dt;

	SYSTEMTIME ti,tiDaylight,tiStandard;

	if (systemTimeDiff(pInfo->DaylightDate,pInfo->StandardDate,false) > 0)
	{
		dt -= double(pInfo->DaylightBias) / (24.0 * 60.0);
		dt.ToSystemTime(&ti);

		tzInfoMakeDate(ti.wYear,pInfo->StandardDate,tiStandard);
		if (systemTimeDiff(ti,tiStandard,false) < 0)
			return dt;

		dt -= double(pInfo->StandardBias - pInfo->DaylightBias) / (24.0 * 60.0);
		dt.ToSystemTime(&ti);
		tzInfoMakeDate(ti.wYear,pInfo->DaylightDate,tiDaylight);
		if (systemTimeDiff(ti,tiDaylight,false) < 0)
			return dt;

		dt -= double(pInfo->DaylightBias - pInfo->StandardBias) / (24.0 * 60.0);
		return dt;
	}
	dt -= double(pInfo->StandardBias) / (24.0 * 60.0);
	dt.ToSystemTime(&ti);
	tzInfoMakeDate(ti.wYear,pInfo->DaylightDate,tiDaylight);
	if (systemTimeDiff(ti,tiDaylight,false) < 0)
		return dt;

	dt -= double(pInfo->DaylightBias - pInfo->StandardBias) / (24.0 * 60.0);
	dt.ToSystemTime(&ti);
	tzInfoMakeDate(ti.wYear,pInfo->StandardDate,tiStandard);
	if (systemTimeDiff(ti,tiStandard,false) < 0)
		return dt;

	dt -= double(pInfo->StandardBias - pInfo->DaylightBias) / (24.0 * 60.0);
	return dt;
}

xbDateTime xbDateTime::ToUtcTime(const TIME_ZONE_INFORMATION *pInfo) const
{
	if (tzInfoIsUtc(pInfo))
		return *this;

	if (!tzInfoHasDayLight(pInfo))
	{
		xbDateTime dt(*this);
		dt += double(pInfo->Bias) / (24.0 * 60.0);
		return dt;
	}

	SYSTEMTIME ti,tiDaylight,tiStandard;
	ToSystemTime(&ti);
	tzInfoMakeDate(ti.wYear,pInfo->StandardDate,tiStandard);
	tzInfoMakeDate(ti.wYear,pInfo->DaylightDate,tiDaylight);

	int bias = pInfo->Bias;
	for(;;)
	{
		if (systemTimeDiff(pInfo->DaylightDate,pInfo->StandardDate,false) > 0)
		{
			if (systemTimeDiff(ti,tiDaylight,false) >= 0 || systemTimeDiff(ti,tiStandard,false) < 0)
			{
				bias += pInfo->DaylightBias;
				break;
			}
			bias += pInfo->StandardBias;
			break;
		}
		if (systemTimeDiff(ti,tiStandard,false) >= 0 || systemTimeDiff(ti,tiDaylight,false) < 0)
		{
			bias += pInfo->StandardBias;
			break;
		}
		bias += pInfo->DaylightBias;
		break;
	}
	xbDateTime dt(*this);
	dt += double(bias) / (24.0 * 60.0);
	return dt;
}

xbDateTime xbDateTime::GetLocalTime(const TIME_ZONE_INFORMATION *pInfo)
{
	SYSTEMTIME systi;
	::GetSystemTime(&systi);
	return xbDateTime(systi).ToLocalTime(pInfo);
}

int xbDateTime::systemTimeDiff(const SYSTEMTIME &ti1,const SYSTEMTIME &ti2,bool bYearCmp)
{
	if (bYearCmp && ti1.wYear != ti2.wYear)
		return int(ti1.wYear) - int(ti2.wYear);
	if (ti1.wMonth != ti2.wMonth)
		return int(ti1.wMonth) - int(ti2.wMonth);
	if (ti1.wDay != ti2.wDay)
		return int(ti1.wDay) - int(ti2.wDay);
	if (ti1.wHour != ti2.wHour)
		return int(ti1.wHour) - int(ti2.wHour);
	if (ti1.wMinute != ti2.wMinute)
		return int(ti1.wMinute) - int(ti2.wMinute);
	if (ti1.wSecond != ti2.wSecond)
		return int(ti1.wSecond) - int(ti2.wSecond);
	if (ti1.wMilliseconds != ti2.wMilliseconds)
		return int(ti1.wMilliseconds) - int(ti2.wMilliseconds);
	return 0;
}

bool xbDateTime::dateIsValid(int y, int m, int d)
{
	if (y < xbDateTime_YEARMIN || y > xbDateTime_YEARMAX
		|| m < 1 || m > 12 
		|| d < 1 || d > GetDaysOfMonth(y,m))
		return false;
	return true;
}

double xbDateTime::dateToDouble(int y, int m, int d)
{
//http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
	m = (m + 9) % 12;
	y = y - m/10;
	int64 cday = (int64)365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + ( d - 1 )
		- xbDateTime_REF;
	return (double)cday;
}

bool xbDateTime::dateFromDouble(double val,int *py,int *pm,int *pd)
{
//http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
	int64 ival = ((int64)val) + xbDateTime_REF;
	if (ival <= 0)
		return false;

	int y = (int)((10000*ival + 14780)/3652425);
	int64 ddd = ival - (365*y + y/4 - y/100 + y/400);
	if (ddd < 0)
	{
		y = y - 1;
		ddd = ival - (365*y + y/4 - y/100 + y/400);
	}
	int mi = (int)((100*ddd + 52)/3060);
	int mm = (mi + 2)%12 + 1;
	y = y + (mi + 2)/12;
	int dd = (int)(ddd - (mi*306 + 5)/10 + 1);
	if (py)
		*py = y;
	if (pm)
		*pm = mm;
	if (pd)
		*pd = dd;
	return y >= xbDateTime_YEARMIN && y <= xbDateTime_YEARMAX;
}

bool xbDateTime::timeIsValid(int h, int m, int s,int ms)
{
	if (h < 0 || h > 23
		|| m < 0 || m > 59
		|| s < 0 || s > 59
		|| ms < 0 || ms > 999)
		return false;
	return true;
}

double xbDateTime::timeToDouble(int h, int m, int s,int ms)
{
	int val = h*60*60*1000
		+m*60*1000+s*1000+ms;
	return (double)val / (double)(24*60*60*1000);
}

bool xbDateTime::timeFromDouble(double ti,int *pd,int *ph,int *pm,int *ps,int *pms)
{
	if (ti < 0)
		return false;

	int64 nDay = int64(ti);
	ti -= double(nDay);
	ti *= double(24.0);

	uint nHour = uint(ti);
	ti -= double(nHour);
	ti *= double(60.0);

	uint nMin = uint(ti);
	ti -= double(nMin);
	ti *= double(60.0);

	uint nSec = uint(ti);
	ti -= double(nSec);
	ti *= double(1000.0);

	uint nMilliSec = uint(ti+0.5);
	if (nMilliSec>=1000)
	{
		nMilliSec=0;
		nSec++;
		if (nSec>=60)
		{
			nSec=0;
			nMin++;
			if (nMin>=60)
			{
				nMin=0;
				nHour++;
				if (nHour>=24)
				{
					nDay++;
					nHour=0;
				}
			}
		}
	}

	if (pd)
		*pd = (int)nDay;
	if (ph)
		*ph = nHour;
	if (pm)
		*pm = nMin;
	if (ps)
		*ps = nSec;
	if (pms)
		*pms = nMilliSec;
	return true;
}

xbDateTime::xbDateTime(double dtSrc,xbDateStatus status)
{
	m_dt = dtSrc;
	m_status = status;
}

//	xbDateTime(time_t timeSrc);
xbDateTime::xbDateTime(const SYSTEMTIME& systimeSrc)
{
	if (!dateIsValid(systimeSrc.wYear,systimeSrc.wMonth,systimeSrc.wDay)
		|| !timeIsValid(systimeSrc.wHour,systimeSrc.wMinute,systimeSrc.wSecond,systimeSrc.wMilliseconds))
		xbThrow xbEarg("Invalid date or time","xbDateTime::xbDateTime(const SYSTEMTIME& systimeSrc)");

	m_dt = dateToDouble(systimeSrc.wYear,systimeSrc.wMonth,systimeSrc.wDay)
		+timeToDouble(systimeSrc.wHour,systimeSrc.wMinute,systimeSrc.wSecond,systimeSrc.wMilliseconds);
	m_status = xbDate_datetime;
}

xbDateTime::xbDateTime(const FILETIME& filetimeSrc)
{
	SYSTEMTIME systimeSrc;
	if (!::FileTimeToSystemTime(&filetimeSrc,&systimeSrc))
		xbThrow xbEsys(xbHR_LAST,"xbDateTime::xbDateTime(const FILETIME& filetimeSrc) : Can't convert file time to system time object");

	m_dt = dateToDouble(systimeSrc.wYear,systimeSrc.wMonth,systimeSrc.wDay)
		+timeToDouble(systimeSrc.wHour,systimeSrc.wMinute,systimeSrc.wSecond,systimeSrc.wMilliseconds);
	m_status = xbDate_datetime;
}

xbDateTime::xbDateTime(int nYear, int nMonth, int nDay,
		int nHour, int nMin, int nSec, int nMsec)
{
	if (!dateIsValid(nYear,nMonth,nDay)
		|| !timeIsValid(nHour,nMin,nSec,nMsec))
		xbThrow xbEarg("Invalid date or time","xbDateTime::xbDateTime(int nYear, int nMonth, int nDay,...)");

	m_dt = dateToDouble(nYear,nMonth,nDay)
		+timeToDouble(nHour,nMin,nSec,nMsec);
	m_status = xbDate_datetime;
}

xbDateTime::xbDateTime(int nYear, int nMonth, int nDay)
{
	if (!dateIsValid(nYear,nMonth,nDay))
		xbThrow xbEarg("Invalid date or time","xbDateTime::xbDateTime(int nYear, int nMonth, int nDay)");

	m_dt = dateToDouble(nYear,nMonth,nDay);
	m_status = xbDate_date;
}

xbDateTime::xbDateTime(const xbDateTime &dt)
{
	m_dt = dt.m_dt;
	m_status = dt.m_status;
}

const xbDateTime& xbDateTime::operator=(const xbDateTime& dt)
{
	m_dt = dt.m_dt;
	m_status = dt.m_status;
	return *this;
}

const xbDateTime& xbDateTime::operator=(const FILETIME& filetimeSrc)
{
	SYSTEMTIME systimeSrc;
	if (!::FileTimeToSystemTime(&filetimeSrc,&systimeSrc))
		xbThrow xbEsys(xbHR_LAST,"xbDateTime::operator=(const FILETIME& filetimeSrc) : Can't convert file time to system time object");

	m_dt = dateToDouble(systimeSrc.wYear,systimeSrc.wMonth,systimeSrc.wDay)
		+timeToDouble(systimeSrc.wHour,systimeSrc.wMinute,systimeSrc.wSecond,systimeSrc.wMilliseconds);
	m_status = xbDate_datetime;
	return *this;
}

const xbDateTime& xbDateTime::operator=(const SYSTEMTIME& systimeSrc)
{
	if (!dateIsValid(systimeSrc.wYear,systimeSrc.wMonth,systimeSrc.wDay)
		|| !timeIsValid(systimeSrc.wHour,systimeSrc.wMinute,systimeSrc.wSecond,systimeSrc.wMilliseconds))
		xbThrow xbEarg("Invalid date or time","xbDateTime::operator=(const SYSTEMTIME& systimeSrc)");

	m_dt = dateToDouble(systimeSrc.wYear,systimeSrc.wMonth,systimeSrc.wDay)
		+timeToDouble(systimeSrc.wHour,systimeSrc.wMinute,systimeSrc.wSecond,systimeSrc.wMilliseconds);
	m_status = xbDate_datetime;
	return *this;
}

int xbDateTime::Compare(const xbDateTime& dt) const
{
	if (m_dt > dt.m_dt)
		return 1;
	if (m_dt < dt.m_dt)
		return -1;
	return 0;
}

int xbDateTime::GetDayOfWeek() const	// 0 -> Monday, 6 -> Sunday
{
	return (((int)m_dt) + 5) % 7;
}

int xbDateTime::GetDayOfWeekMon() const				// 0 -> Monday, 6 -> Sunday
{
	return (((int)m_dt) + 5) % 7;
}

int xbDateTime::GetDayOfWeekSun() const				// 0 -> Sunday, 6 -> Saturday
{
	return (((int)m_dt) + 6) % 7;
}

int xbDateTime::GetYear() const // Full year, ex: 2013
{
	int y;
	if (!dateFromDouble(m_dt,&y,NULL,NULL))
		xbThrow xbEarg("Invalid date","xbDateTime::GetYear()");
	return y;
}

int xbDateTime::GetMonth() const					// 1 -> January, 12 -> December
{
	int m;
	if (!dateFromDouble(m_dt,NULL,&m,NULL))
		xbThrow xbEarg("Invalid date","xbDateTime::GetMonth()");
	return m;
}

int xbDateTime::GetDay() const						// 1 -> 31
{
	int d;
	if (!dateFromDouble(m_dt,NULL,NULL,&d))
		xbThrow xbEarg("Invalid date","xbDateTime::GetDay()");
	return d;
}

int xbDateTime::GetHour() const						// 0 -> 23
{
	int h;
	if (!timeFromDouble(m_dt,NULL,&h,NULL,NULL,NULL))
		xbThrow xbEarg("Invalid date","xbDateTime::GetHour()");
	return h;
}

int xbDateTime::GetMinute() const					// 0 -> 59
{
	int m;
	if (!timeFromDouble(m_dt,NULL,NULL,&m,NULL,NULL))
		xbThrow xbEarg("Invalid date","xbDateTime::GetMinute()");
	return m;
}

int xbDateTime::GetSecond() const					// 0 -> 59
{
	int s;
	if (!timeFromDouble(m_dt,NULL,NULL,NULL,&s,NULL))
		xbThrow xbEarg("Invalid date","xbDateTime::GetSecond()");
	return s;
}

int xbDateTime::GetMilliSecond() const				// 0 -> 999
{
	int ms;
	if (!timeFromDouble(m_dt,NULL,NULL,NULL,NULL,&ms))
		xbThrow xbEarg("Invalid date","xbDateTime::GetMilliSecond()");
	return ms;
}


bool xbDateTime::SetDate(int y,int m,int d,xbOpMode opm)
{
	if (!dateIsValid(y,m,d))
	{
		if (opm == xbOp_Required)
			xbThrow xbEarg("Invalid date","xbDateTime::SetDate(...)");
		return false;
	}

	m_dt = dateToDouble(y,m,d);
	m_status = xbDate_date;
	return true;
}

bool xbDateTime::SetDateTime(int y,int mon,int d,int h,int min,int s,int ms,xbOpMode opm)
{
	if (!dateIsValid(y,mon,d)
		|| !timeIsValid(h,min,s,ms))
	{
		if (opm == xbOp_Required)
			xbThrow xbEarg("Invalid date or time","xbDateTime::SetDateTime(...)");
		return false;
	}
	m_dt = dateToDouble(y,mon,d)
		+timeToDouble(h,min,s,ms);
	m_status = xbDate_datetime;
	return true;
}

bool xbDateTime::SetTime(int h,int min,int s,int ms,xbOpMode opm)
{
	if (!timeIsValid(h,min,s,ms))
	{
		if (opm == xbOp_Required)
			xbThrow xbEarg("Invalid time","xbDateTime::SetTime(...)");
		return false;
	}
	m_dt = timeToDouble(h,min,s,ms);
	m_status = xbDate_datetime;
	return true;
}

bool xbDateTime::SetTimeLife(int d,int h,int min,int s,int ms,xbOpMode opm)
{
	m_dt = timeToDouble(h,min,s,ms)+(double)d;
	m_status = xbDate_timelife;
	return true;
}

xbDateTime &xbDateTime::operator-=(double dt)
{
	m_dt -= dt;
	return *this;
}

xbDateTime &xbDateTime::operator+=(double dt)
{
	m_dt += dt;
	return *this;
}

bool xbDateTime::ToSystemTime(SYSTEMTIME *pSysTime,xbOpMode opm) const
{
	int y,mon,d;
	if (!dateFromDouble(m_dt,&y,&mon,&d))
	{
		if (opm == xbOp_Required)
			xbThrow xbEarg("Invalid date","xbDateTime::ToSystemTime()");
		return false;
	}
	int h,min,s,ms;
	if (!timeFromDouble(m_dt,NULL,&h,&min,&s,&ms))
	{
		if (opm == xbOp_Required)
			xbThrow xbEarg("Invalid date","xbDateTime::ToSystemTime()");
		return false;
	}
	if (pSysTime)
	{
		pSysTime->wDay = d;
		pSysTime->wMonth = mon;
		pSysTime->wYear = y;
		pSysTime->wHour = h;
		pSysTime->wMinute = min;
		pSysTime->wSecond = s;
		pSysTime->wMilliseconds = ms;
	}

	return true;
}
bool xbDateTime::ToFileTime(FILETIME *pFileTime,xbOpMode opm) const
{
	SYSTEMTIME ti;
	if (!ToSystemTime(&ti,opm))
		return false;

	if (pFileTime
		&& !::SystemTimeToFileTime(&ti,pFileTime))
	{
		if (opm == xbOp_Required)
			xbThrow xbEsys(xbHR_LAST,"xbDateTime::GetAsFileTime() : Can't convert date and time to file time object");
		return false;
	}

	return true;
}

xbDateStatus xbDateTime::GetCalcStatus() const
{
	if (m_status != xbDate_auto)
		return m_status;
	
	if (m_dt == 0)
		return xbDate_empty;

	if (GetYear() > 1909)
	{
		if (((int64)m_dt) == m_dt)
			return xbDate_date;
		return xbDate_datetime;
	}
	
	if (m_dt < 1.0)
		return xbDate_time;
	return xbDate_timelife;
}

bool xbDateTime::HasDate() const
{
	xbDateStatus dt = GetCalcStatus();
	return dt == xbDate_datetime || dt == xbDate_date;
}

bool xbDateTime::HasTime() const
{
	xbDateStatus dt = GetCalcStatus();
	return dt == xbDate_datetime || dt == xbDate_time;
}

bool xbDateTime::HasDateAndTime() const
{
	xbDateStatus dt = GetCalcStatus();
	return dt == xbDate_datetime;
}

bool xbDateTime::IsTimeLife() const
{
	xbDateStatus dt = GetCalcStatus();
	return dt == xbDate_timelife;
}

xbDateTime xbDateTime::Date() const
{
	if (!HasDate())
		xbThrow xbEarg("Invalid date or time, no date available","xbDateTime::Date");
	return xbDateTime((double)((int64)m_dt),xbDate_date);
}

xbDateTime xbDateTime::Time() const
{
	if (!HasTime())
		xbThrow xbEarg("Invalid date or time, no time available","xbDateTime::Time");

	double dtInt = (double)((int64)m_dt);
	return xbDateTime(m_dt - dtInt,xbDate_time);
}

xbDateTime operator+(const xbDateTime& dt1, const xbDateTime& dt2)
{
	xbDateStatus st1 = dt1.CalcStatus;
	xbDateStatus st2 = dt2.CalcStatus;
	xbDateStatus stRet;
	for(;;)
	{
		if (st1 == xbDate_empty)
		{
			stRet = st2;
			break;
		}
		if (st2 == xbDate_empty)
		{
			stRet = st1;
			break;
		}
		if (st1 == xbDate_timelife || st2 == xbDate_timelife)
		{
			stRet = xbDate_timelife;
			break;
		}
		if (st1 == xbDate_time && (st2 == xbDate_date || st2 == xbDate_datetime))
		{
			stRet = xbDate_datetime;
			break;
		}
		if ((st1 == xbDate_date || st1 == xbDate_datetime) && st2 == xbDate_time)
		{
			stRet = xbDate_datetime;
			break;
		}

		xbThrow xbEarg("DateTime addition not supported for operands types","xbDateTime operator+(const xbDateTime& d1, const xbDateTime& d2)");
	}

	return xbDateTime(((double)dt1) +  ((double)dt2),stRet);
}

xbDateTime operator-(const xbDateTime& dt1, const xbDateTime& dt2)
{
	xbDateStatus st1 = dt1.CalcStatus;
	xbDateStatus st2 = dt2.CalcStatus;
	for(;;)
	{
		if (st1 == xbDate_empty || st2 == xbDate_empty)
			break;

		if (st1 == st2)
			break;
		if (st1 == xbDate_datetime)
			break;

		xbThrow xbEarg("DateTime addition not supported for operands types","xbDateTime operator+(const xbDateTime& d1, const xbDateTime& d2)");
	}
	return xbDateTime(((double)dt1) -  ((double)dt2), xbDate_timelife);
}

xbString xbDateTime::ToString(LPCSTR pTZ) const
{
	if (IsTimeLife())
	{
		int d,h,m,s,ms;
		timeFromDouble(m_dt,&d,&h,&m,&s,&ms);

		xbString sret;
		if (d>0)
			sret += " "+xbString(d)+"d";
		if (h>0)
			sret += " "+xbString(h)+"h";
		if (m>0)
			sret += " "+xbString(m)+"min";
		if (s>0 || ms > 0)
		{
			sret += " "+xbString(s);
			if (ms>0)
				sret += xbFormat(".%03ds",ms);
			else
				sret += "s";
		}
		return sret.RetTrim();
	}
	comcfgTime ti(*this);

	return ti.Get(comcfgTimeDflt,pTZ);
}
/*
	xbString Format(DWORD dwFlags = 0, LCID lcid = LANG_USER_DEFAULT) const;
	xbString Format(LPCSTR lpszFormat) const;
*/
