// aplDateTime.cpp
#include "stdafx.h"
#include <math.h>
#include <time.h>
#include "aplAggr.h"


#ifndef _MFC_VER
	const int maxTimeBufferSize = 128; // Размер буфера при отобр. даты в строку
	const long maxDaysInSpan  =	3615897L;
#endif

#ifndef VTDATEGRE_MAX
	#define VTDATEGRE_MAX 2958465   /* Dec 31, 9999, 0:0:0 in Gregorain Calendar */
	#define VTDATEGRE_MIN -657434   /* Jan  1,  100, 0:0:0 in Gregorain Calendar */
#endif

#define IsLeapYear(y) (((y % 4) == 0) && (((y % 100) != 0) || ((y % 400) == 0)))

bool aplConvertSystemTimeToVariantTime(const SYSTEMTIME& systimeSrc, double &VarDtTm);
bool aplConvertVariantTimeToSystemTime(double dDtTm,	SYSTEMTIME &sysTime);

//*********************************************************************************************
/* Roll a date forwards or backwards to correct it */
bool RollDate(SYSTEMTIME &sysTime)
{
	static const BYTE days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	short iYear, iMonth, iDay, iHour, iMinute, iSecond;

	/* interpret values signed */
	iYear   = sysTime.wYear;
	iMonth  = sysTime.wMonth;
	iDay    = sysTime.wDay;
	iHour   = sysTime.wHour;
	iMinute = sysTime.wMinute;
	iSecond = sysTime.wSecond;

	if (iYear > 9999 || iYear < -9999)	return false; /* Invalid value */
	/* Year 0 to 29 are treated as 2000 + year */
	//if (iYear >= 0 && iYear < 30) iYear += 2000;
	/* Remaining years < 100 are treated as 1900 + year */
	//else if (iYear >= 30 && iYear < 100) iYear += 1900;

	iMinute += iSecond / 60;
	iSecond  = iSecond % 60;
	iHour   += iMinute / 60;
	iMinute  = iMinute % 60;
	iDay    += iHour / 24;
	iHour    = iHour % 24;
	iYear   += iMonth / 12;
	iMonth   = iMonth % 12;
	if (iMonth<=0) {iMonth+=12; iYear--;}
	while (iDay > days[iMonth])
	{
		if (iMonth == 2 && IsLeapYear(iYear))
			iDay -= 29;
		else
			iDay -= days[iMonth];
		iMonth++;
		iYear += iMonth / 12;
		iMonth = iMonth % 12;
	}
	while (iDay <= 0)
	{
		iMonth--;
		if (iMonth<=0) {iMonth+=12; iYear--;}
		if (iMonth == 2 && IsLeapYear(iYear))
			iDay += 29;
		else
			iDay += days[iMonth];
	}

	if (iSecond<0){iSecond+=60; iMinute--;}
	if (iMinute<0){iMinute+=60; iHour--;}
	if (iHour<0)  {iHour+=24; iDay--;}
	if (iYear<=0)  iYear+=2000;

	sysTime.wYear   = iYear;
	sysTime.wMonth  = iMonth;
	sysTime.wDay    = iDay;
	sysTime.wHour   = iHour;
	sysTime.wMinute = iMinute;
	sysTime.wSecond = iSecond;

	return true;
}
//*********************************************************************************************
bool aplConvertVariantTimeToSystemTime(double dDtTm,	SYSTEMTIME &sysTime)
{
	if(dDtTm<VTDATEGRE_MIN || dDtTm>VTDATEGRE_MAX) {memset(&sysTime,0,sizeof(SYSTEMTIME)); return false;}

	sysTime.wMilliseconds=0;
	sysTime.wDayOfWeek=0;

	int jd= (int) dDtTm;

	// - VTDATEGRE_MIN : Convert to + days from 1 Jan 100 AD 
	// + 1757585 : Convert to + days from 23 Nov 4713 BC (Julian) 
	jd+=-(VTDATEGRE_MIN) +1757585;

	int j, i, l, n;

	l = jd + 68569;
	n = l * 4 / 146097;
	l -= (n * 146097 + 3) / 4;
	i = (4000 * (l + 1)) / 1461001;
	l += 31 - (i * 1461) / 4;
	j = (l * 80) / 2447;
	sysTime.wDay = l - (j * 2447) / 80;
	l = j / 11;
	sysTime.wMonth = (j + 2) - (12 * l);
	sysTime.wYear = 100 * (n - 49) + i + l;

	//Определение дня недели
	int nw= (int)(((int)dDtTm)/7); //кол-во недель с нулеой даты
	int idw= ((int)dDtTm)-(nw*7); // 
	idw+=6; //0-й день - это субота 
	if(idw>6) idw-=7;
	sysTime.wDayOfWeek=idw;
	


	// Time 
	double timePart=dDtTm-((int)dDtTm); // отбрасываем целое

	if(dDtTm<0) timePart*=-1.0;

	timePart *= 24.0;
	sysTime.wHour = (WORD) timePart;
	timePart -= sysTime.wHour;
	timePart *= 60.0;
	sysTime.wMinute =  (WORD) timePart;
	timePart -= sysTime.wMinute;
	timePart *= 60.0;
	sysTime.wSecond =  (WORD) timePart;
	timePart -= sysTime.wSecond;

	// Округляем милисекунды
	if (timePart > 0.5)
	{
		if (sysTime.wSecond < 59) (sysTime.wSecond)++;
		else
		{
			sysTime.wSecond = 0;
			if (sysTime.wMinute < 59) sysTime.wMinute++;
			else
			{
				sysTime.wMinute = 0;
				if (sysTime.wHour < 23)	sysTime.wHour++;
				else
				{
					sysTime.wHour = 0;
					// Проматываем день
					if (sysTime.wDay > 27)
						RollDate(sysTime);
				}
			}
		}
	}

#ifdef _DEBUG
	static bool bRecurse=false;  // Эта такая извращенческая конструкция чтобы проверки не зациклились
	if(!bRecurse)
	{
		bRecurse=true;
		double d4Test;
		if(!aplConvertSystemTimeToVariantTime(sysTime,d4Test)){APL_STOP_IF_DEBUG};
		if(d4Test!=dDtTm){APL_STOP_IF_DEBUG};
		bRecurse=false;
	}
#endif

	return true;
}
//*********************************************************************************************
bool CaplDateTime::CheckSystemTime(const SYSTEMTIME& systime)
{
	static const BYTE days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	if(systime.wYear<100 || systime.wYear>9999 ) return false;
	if(systime.wMonth<1 || systime.wMonth>12 ) return false;
	if(systime.wDay<1) return false;
	if(systime.wDay>days[systime.wMonth]) return false;
	return true;
}
//*********************************************************************************************
bool aplConvertSystemTimeToVariantTime(const SYSTEMTIME& systimeSrc, double &VarDtTm)
{
	VarDtTm=0.0;
	if(!CaplDateTime::CheckSystemTime(systimeSrc)) return false;
	
	int m12 = (systimeSrc.wMonth - 14) / 12;

	double julianDays= ((1461 * (systimeSrc.wYear + 4800 + m12)) / 4 + (367 * (systimeSrc.wMonth - 2 - 12 * m12)) / 12 -
		(3 * ((systimeSrc.wYear + 4900 + m12) / 100)) / 4 + systimeSrc.wDay - 32075);

	// -1757585 : Convert to + days from 1 Jan 100 AD 
	// +VTDATEGRE_MIN : Convert to +/- days from 1 Jan 1899 AD 

	double dateVal = julianDays + (VTDATEGRE_MIN -1757585) ;

	double dateSign = (dateVal < 0.0) ? -1.0 : 1.0;

	dateVal += dateSign * ((systimeSrc.wHour / 24.0)  + (systimeSrc.wMinute / (24.0*60)) + (systimeSrc.wSecond / (24.0*60*60)));

	VarDtTm=dateVal;

#ifdef _DEBUG

	SYSTEMTIME sysTime4Check;
	::ZeroMemory(&sysTime4Check, sizeof(SYSTEMTIME));

	if(!aplConvertVariantTimeToSystemTime(VarDtTm, sysTime4Check)){APL_STOP_IF_DEBUG};
	if(!(systimeSrc.wYear == sysTime4Check.wYear &&
		systimeSrc.wMonth == sysTime4Check.wMonth &&
		systimeSrc.wDay == sysTime4Check.wDay &&
		systimeSrc.wHour == sysTime4Check.wHour &&
		systimeSrc.wMinute == sysTime4Check.wMinute && 
		systimeSrc.wSecond == sysTime4Check.wSecond)) {APL_STOP_IF_DEBUG};
#endif
	return true;
}


CaplDateTime CaplDateTime::GetCurrentTime()
{
 	//return CaplDateTime(::_time64(NULL));
	return CaplDateTime(time(0));
}

CaplDateTime::~CaplDateTime()
{
	 Free_m_pSysTime();
}

CaplDateTime::CaplDateTime():
	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
{
}


CaplDateTime::CaplDateTime(const time_t timeSrc):
	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
{
	*this=timeSrc;
}

//  CaplDateTime::CaplDateTime( const VARIANT& varSrc ):
// 	m_pSysTime(0), m_dt( 0.0 ), m_status(valid)
// {
// 	*this = varSrc;
// }

CaplDateTime::CaplDateTime( DATE dtSrc ):
	m_dt( dtSrc ), m_status(valid), m_pSysTime(0)
{
	CheckRange();
}

// CaplDateTime::CaplDateTime( __time32_t timeSrc ):
// 	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
// {
// 	*this = timeSrc;
// }
// 
// CaplDateTime::CaplDateTime( __time64_t timeSrc ):
// 	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
// {
// 	//!!!	*this = timeSrc;
// }

CaplDateTime::CaplDateTime( const SYSTEMTIME& systimeSrc ):
	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
{
	*this = systimeSrc;
}
// 
//  CaplDateTime::CaplDateTime( const FILETIME& filetimeSrc ):
// m_dt( 0.0 ), m_status(valid)
// {
// 	*this = filetimeSrc;
// }

CaplDateTime::CaplDateTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec):
	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
{
	SetDateTime(nYear, nMonth, nDay, nHour, nMin, nSec);
}

//  CaplDateTime::CaplDateTime(WORD wDosDate, WORD wDosTime)
//	m_dt( 0.0 ), m_status(valid), m_pSysTime(0)
// {
// 	m_status = ::DosDateTimeToVariantTime(wDosDate, wDosTime, &m_dt) ?
// valid : invalid;
// }

 void CaplDateTime::SetStatus(DateTimeStatus status)
{
	m_status = status;
}

 CaplDateTime::DateTimeStatus CaplDateTime::GetStatus() const
{
	return m_status;
}


bool CaplDateTime::GetAsSystemTime(SYSTEMTIME& sysTime) const
{
	return GetStatus() == valid && ::aplConvertVariantTimeToSystemTime(m_dt, sysTime);
}

bool CaplDateTime::GetAsUDATE(UDATE &udate) const
{
	//return SUCCEEDED(::VarUdateFromDate(m_dt, 0, &udate));

	udate.wDayOfYear=0;
	if(!GetAsSystemTime(udate.st)) return false;

	int iDayOfYear=0;

	if (udate.st.wMonth > 2 && IsLeapYear(udate.st.wYear)) iDayOfYear = 1; /* After February, in a leap year */

	/* Cumulative totals of days per month */
	static const USHORT cumulativeDays[] =  { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

	iDayOfYear += cumulativeDays[udate.st.wMonth];
	iDayOfYear += udate.st.wDay;

	udate.wDayOfYear=(USHORT)iDayOfYear;

	return true;
}

void CaplDateTime::Init_m_pSysTime()
{ 
	if(valid!=m_status) {Free_m_pSysTime(); return;}

	if(0!=m_pSysTime) return;  // В этом смысл кэширования

	m_pSysTime =new SYSTEMTIME;
	if(!GetAsSystemTime(*m_pSysTime)) Free_m_pSysTime();
}

void CaplDateTime::Free_m_pSysTime()
{
	if(0!=m_pSysTime) {delete m_pSysTime; m_pSysTime=0;}
}

int CaplDateTime::GetYear()
{
	//SYSTEMTIME st;
	//return GetAsSystemTime(st) ? st.wYear : error;

	Init_m_pSysTime();
	return 0!=m_pSysTime ? m_pSysTime->wYear : (int)error;
}

int CaplDateTime::GetMonth()
{
	 //SYSTEMTIME st;
	 //return GetAsSystemTime(st) ? st.wMonth : error;

	 Init_m_pSysTime();
	 return 0!=m_pSysTime ? m_pSysTime->wMonth : (int)error;
}

int CaplDateTime::GetDay()
{
	//SYSTEMTIME st;
	// return GetAsSystemTime(st) ? st.wDay : error;

	 Init_m_pSysTime();
	 return 0!=m_pSysTime ? m_pSysTime->wDay : (int)error;
}

int CaplDateTime::GetHour()
{
	 //SYSTEMTIME st;
	 //return GetAsSystemTime(st) ? st.wHour : error;

	 Init_m_pSysTime();
	 return 0!=m_pSysTime ? m_pSysTime->wHour : (int)error;
}

int CaplDateTime::GetMinute()
{
	 //SYSTEMTIME st;
	 //return GetAsSystemTime(st) ? st.wMinute : error;

	 Init_m_pSysTime();
	 return 0!=m_pSysTime ? m_pSysTime->wMinute : (int)error;
}

int CaplDateTime::GetSecond()
{ 
	 //SYSTEMTIME st;
	 //return GetAsSystemTime(st) ? st.wSecond : error;

	 Init_m_pSysTime();
	 return 0!=m_pSysTime ? m_pSysTime->wSecond : (int)error;
}

int CaplDateTime::GetDayOfWeek()
{
	 //SYSTEMTIME st;
	 //return GetAsSystemTime(st) ? st.wDayOfWeek + 1 : error;

	 Init_m_pSysTime();
	 return 0!=m_pSysTime ? m_pSysTime->wDayOfWeek : (int)error;
}

  int CaplDateTime::GetDayOfYear() const
 {
	 UDATE udate;
	 return GetAsUDATE(udate) ? udate.wDayOfYear : (int)error;
 }


//   CaplDateTime& CaplDateTime::operator=(const VARIANT& varSrc)
//  {
//	 Free_m_pSysTime();
// 	 if (varSrc.vt != VT_DATE)
// 	 {
// 		 VARIANT varDest;
// 		 varDest.vt = VT_EMPTY;
// 		 if(SUCCEEDED(::VariantChangeType(&varDest, const_cast<VARIANT *>(&varSrc), 0, VT_DATE)))
// 		 {
// 			 m_dt = varDest.date;
// 			 m_status = valid;
// 		 }
// 		 else
// 			 m_status = invalid;
// 	 }
// 	 else
// 	 {
// 		 m_dt = varSrc.date;
// 		 m_status = valid;
// 	 }
// 
// 	 return *this;
//  }


CaplDateTime& CaplDateTime::operator=(DATE dtSrc)
 {
	 Free_m_pSysTime();
	 m_dt = dtSrc;
	 m_status = valid;
	 CheckRange();
	 return *this;
 }

//   CaplDateTime& CaplDateTime::operator=(const __time32_t& timeSrc)
//  {
//   Free_m_pSysTime();
// 	 return operator=(static_cast<__time64_t>(timeSrc));
//  }

//   CaplDateTime& CaplDateTime::operator=(const __time64_t& timeSrc)
//  {
//   Free_m_pSysTime();
// 	 SYSTEMTIME st;
// 	 CTime tmp(timeSrc);
// 
// 	 m_status = tmp.GetAsSystemTime(st) &&
// 		 ConvertSystemTimeToVariantTime(st) ? valid : invalid;	
// 	 return *this;
//  }
 
CaplDateTime& CaplDateTime::operator=(time_t timeSrc)
{
	Free_m_pSysTime();

	 struct tm  tms1;
#ifdef __linux__
	 // в линуксе возвращается указатель  при успехе и ноль при ошибке
	 if(0==localtime_r(&timeSrc,&tms1))
#else
	 // в винде возвращается 0 при успехе или код ошибки при ошибке
	 if(0!=localtime_s(&tms1,&timeSrc))
#endif
	 {
		 m_dt=0.0;
		 m_status = invalid;
	 }
	 else
	 {
		 SYSTEMTIME systime;
		 systime.wMilliseconds=0;
		 systime.wDayOfWeek=0;
		 systime.wYear=tms1.tm_year+1900;
		 systime.wMonth=tms1.tm_mon+1;
		 systime.wDay=tms1.tm_mday;
		 systime.wHour=tms1.tm_hour;
		 systime.wMinute=tms1.tm_min;
		 systime.wSecond=tms1.tm_sec;
		 if(ConvertSystemTimeToVariantTime(systime)) m_status = valid; else  m_status = invalid;
	}
	return *this;
}


  CaplDateTime &CaplDateTime::operator=(const SYSTEMTIME &systimeSrc)
 {
	 if(0!=m_pSysTime){delete m_pSysTime; m_pSysTime=0;}

	 m_status = ConvertSystemTimeToVariantTime(systimeSrc) ?	valid : invalid;
	 return *this;
 }

//   CaplDateTime &CaplDateTime::operator=(const FILETIME &filetimeSrc)
//  {
//   Free_m_pSysTime();
// 	 FILETIME ftl;
// 	 SYSTEMTIME st;
// 
// 	 m_status =  ::FileTimeToLocalFileTime(&filetimeSrc, &ftl) && 
// 		 ::FileTimeToSystemTime(&ftl, &st) &&
// 		 ConvertSystemTimeToVariantTime(st) ? valid : invalid;
// 
// 	 return *this;
//  }



bool CaplDateTime::ConvertSystemTimeToVariantTime(const SYSTEMTIME& systimeSrc)
{
	Free_m_pSysTime();
	return  aplConvertSystemTimeToVariantTime(systimeSrc,m_dt);	
}

CaplDateTime &CaplDateTime::operator=(const UDATE &udate)
{
	 //m_status = (S_OK == VarDateFromUdate((UDATE*)&udate, 0, &m_dt)) ? valid : invalid;

	 Free_m_pSysTime();
	 m_status =   aplConvertSystemTimeToVariantTime(udate.st,m_dt) ?  valid : invalid;
	 return *this;
}

 bool CaplDateTime::operator==( const CaplDateTime& date ) const
 {
	 if(GetStatus() != date.GetStatus()) return false;
	 
	 if(GetStatus() == valid)
		 return( m_dt == date.m_dt );

	 return (GetStatus() == null);
 }

  bool CaplDateTime::operator!=( const CaplDateTime& date ) const
 {
	 return !operator==(date);
 }

  bool CaplDateTime::operator<( const CaplDateTime& date ) const
 {
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(date.GetStatus() == CaplDateTime::valid);
	 
	 if( (GetStatus() == valid) && (GetStatus() == date.GetStatus()) )
		 return( DoubleFromDate( m_dt ) < DoubleFromDate( date.m_dt ) );

	 return false;
 }

bool CaplDateTime::operator>( const CaplDateTime& date ) const
{
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(date.GetStatus() == CaplDateTime::valid);

	 if( (GetStatus() == valid) && (GetStatus() == date.GetStatus()) )
		 return( DoubleFromDate( m_dt ) > DoubleFromDate( date.m_dt ) );

	 if( (GetStatus() == valid) && (GetStatus() == date.GetStatus()) )
		 return (m_dt  > date.m_dt );

	 return false;		
}

bool CaplDateTime::operator<=( const CaplDateTime& date ) const
{
	return operator<(date) || operator==(date);
}

  bool CaplDateTime::operator>=( const CaplDateTime& date ) const
 {
	 return operator>(date) || operator==(date);
 }


  CaplDateTime CaplDateTime::operator+( CaplDateTimeSpan dateSpan ) const
 {
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	 return( CaplDateTime( DateFromDouble( DoubleFromDate( m_dt )+(double)dateSpan ) ) );
 }

  CaplDateTime CaplDateTime::operator-( CaplDateTimeSpan dateSpan ) const
 {
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	 return( CaplDateTime( DateFromDouble( DoubleFromDate( m_dt )-(double)dateSpan ) ) );
 }

  CaplDateTime& CaplDateTime::operator+=( CaplDateTimeSpan dateSpan )
 {
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	 m_dt = DateFromDouble( DoubleFromDate( m_dt )+(double)dateSpan );
	 return( *this );
 }

  CaplDateTime& CaplDateTime::operator-=( CaplDateTimeSpan dateSpan )
 {
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	 m_dt = DateFromDouble( DoubleFromDate( m_dt )-(double)dateSpan );
	 return( *this );
 }

CaplDateTimeSpan CaplDateTime::operator-(const CaplDateTime& date) const
 {
	 ASSERT(GetStatus() == CaplDateTime::valid);
	 ASSERT(date.GetStatus() == CaplDateTime::valid);
	 return DoubleFromDate(m_dt) - DoubleFromDate(date.m_dt);
 }


CaplDateTime::operator DATE() const
{
	ASSERT(GetStatus() == CaplDateTime::valid);
	return( m_dt );
}


int CaplDateTime::SetDateTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec)
 {
	 SYSTEMTIME st;
	 ::ZeroMemory(&st, sizeof(SYSTEMTIME));

	 st.wMilliseconds=0;
	 st.wDayOfWeek=0;
	 st.wYear = WORD(nYear);
	 st.wMonth = WORD(nMonth);
	 st.wDay = WORD(nDay);
	 st.wHour = WORD(nHour);
	 st.wMinute = WORD(nMin);
	 st.wSecond = WORD(nSec);

	 m_status = ConvertSystemTimeToVariantTime(st) ? valid : invalid;

	 return m_status;
 }

int CaplDateTime::SetDate(int nYear, int nMonth, int nDay)
{
	return SetDateTime(nYear, nMonth, nDay, 0, 0, 0);
}

int CaplDateTime::SetTime(int nHour, int nMin, int nSec)
{
	// Set date to zero date - 12/30/1899
	return SetDateTime(1899, 12, 30, nHour, nMin, nSec);
}

double CaplDateTime::DoubleFromDate( DATE date )
{
	 double fTemp;

	 // No problem if positive
	 if( date >= 0 )
	 {
		 return( date );
	 }

	 // If negative, must convert since negative dates not continuous
	 // (examples: -1.25 to -.75, -1.50 to -.50, -1.75 to -.25)
	 fTemp = ceil( date );

	 return( fTemp-(date-fTemp) );
}


DATE CaplDateTime::DateFromDouble( double f )
{
	 double fTemp;

	 // No problem if positive
	 if( f >= 0 )
	 {
		 return( f );
	 }

	 // If negative, must convert since negative dates not continuous
	 // (examples: -.75 to -1.25, -.50 to -1.50, -.25 to -1.75)
	 fTemp = floor( f ); // fTemp is now whole part

	 return( fTemp+(fTemp-f) );
}

inline void CaplDateTime::CheckRange()
{
	// About year 100 to about 9999
	if(m_dt > VTDATEGRE_MAX || m_dt < VTDATEGRE_MIN)
	{
		SetStatus(invalid);    
	}
}

CString CaplDateTime::Format(LPCTSTR pFormat) const
{
	ASSERT(pFormat != NULL);

	if(0==pFormat)  return _T("");
	if(_T('\0')==pFormat[0])  return _T("");

	// If null, return empty string
	if(GetStatus() == null) 	return _T("");

	const LPCTSTR sDATETIME_INVALID=_T("Invalid DateTime");

	if(GetStatus() != valid) return sDATETIME_INVALID;

	UDATE ud;
	if (!GetAsUDATE(ud)) return sDATETIME_INVALID;

	struct tm tmTemp;
	tmTemp.tm_sec	= ud.st.wSecond;
	tmTemp.tm_min	= ud.st.wMinute;
	tmTemp.tm_hour	= ud.st.wHour;
	tmTemp.tm_mday	= ud.st.wDay;
	tmTemp.tm_mon	= ud.st.wMonth - 1;
	tmTemp.tm_year	= ud.st.wYear - 1900;
	tmTemp.tm_wday	= ud.st.wDayOfWeek;
	tmTemp.tm_yday	= ud.wDayOfYear - 1;
	tmTemp.tm_isdst	= 0;

	CString strDate;
	LPTSTR lpszTemp = strDate.GetBufferSetLength(256);
#ifdef _MFC_VER
	_tcsftime(lpszTemp, strDate.GetLength(), pFormat, &tmTemp);
#else
	wcsftime(lpszTemp, strDate.GetLength(), pFormat, &tmTemp);
#endif
	strDate.ReleaseBuffer();

	return strDate;
}


//***************************************************************************************************************
//***************************************************************************************************************
// CaplDateTimeSpan
//***************************************************************************************************************
//***************************************************************************************************************

CaplDateTimeSpan::CaplDateTimeSpan() : m_span(0), m_status(valid)
{
}

CaplDateTimeSpan::CaplDateTimeSpan(double dblSpanSrc) : m_span(dblSpanSrc), m_status(valid)
{
	CheckRange();
}

CaplDateTimeSpan::CaplDateTimeSpan(LONG lDays, int nHours, int nMins, int nSecs)
{
	SetDateTimeSpan(lDays, nHours, nMins, nSecs);
}

void CaplDateTimeSpan::SetStatus(DateTimeSpanStatus status)
{
	m_status = status;
}

CaplDateTimeSpan::DateTimeSpanStatus CaplDateTimeSpan::GetStatus() const
{
	return m_status;
}

const double CaplDateTimeSpan::OLE_DATETIME_HALFSECOND = 1.0 / (2.0 * (60.0 * 60.0 * 24.0));

double CaplDateTimeSpan::GetTotalDays() const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	return (double)LONGLONG(m_span + (m_span < 0 ?
		-OLE_DATETIME_HALFSECOND : OLE_DATETIME_HALFSECOND));
}

double CaplDateTimeSpan::GetTotalHours() const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	return (double)LONGLONG((m_span + (m_span < 0 ? 
		-OLE_DATETIME_HALFSECOND : OLE_DATETIME_HALFSECOND)) * 24);
}

double CaplDateTimeSpan::GetTotalMinutes() const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	return (double)LONGLONG((m_span + (m_span < 0 ?
		-OLE_DATETIME_HALFSECOND : OLE_DATETIME_HALFSECOND)) * (24 * 60));
}

double CaplDateTimeSpan::GetTotalSeconds() const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	return (double)LONGLONG((m_span + (m_span < 0 ?
		-OLE_DATETIME_HALFSECOND : OLE_DATETIME_HALFSECOND)) * (24 * 60 * 60));
}

LONG CaplDateTimeSpan::GetDays() const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	return LONG(GetTotalDays());
}

LONG CaplDateTimeSpan::GetHours() const
{
	return LONG(GetTotalHours()) % 24;
}

LONG CaplDateTimeSpan::GetMinutes() const
{
	return LONG(GetTotalMinutes()) % 60;
}

LONG CaplDateTimeSpan::GetSeconds() const
{
	return LONG(GetTotalSeconds()) % 60;
}

CaplDateTimeSpan& CaplDateTimeSpan::operator=(double dblSpanSrc)
{
	m_span = dblSpanSrc;
	m_status = valid;
	CheckRange();
	return *this;
}

bool CaplDateTimeSpan::operator==(const CaplDateTimeSpan& dateSpan) const
{
	if(GetStatus() == dateSpan.GetStatus())
	{
		if(GetStatus() == valid)
			return (m_span == dateSpan.m_span);			

		return (GetStatus() == null);
	}

	return false;
}

bool CaplDateTimeSpan::operator!=(const CaplDateTimeSpan& dateSpan) const
{
	return !operator==(dateSpan);
}

bool CaplDateTimeSpan::operator<(const CaplDateTimeSpan& dateSpan) const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	if( (GetStatus() == valid) && (GetStatus() == dateSpan.GetStatus()) )
		return m_span < dateSpan.m_span;

	return false;
}

bool CaplDateTimeSpan::operator>(const CaplDateTimeSpan& dateSpan) const
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	if( (GetStatus() == valid) && (GetStatus() == dateSpan.GetStatus()) )
		return m_span > dateSpan.m_span ;

	return false;
}

bool CaplDateTimeSpan::operator<=(const CaplDateTimeSpan& dateSpan) const
{
	return operator<(dateSpan) || operator==(dateSpan);
}

bool CaplDateTimeSpan::operator>=(const CaplDateTimeSpan& dateSpan) const
{
	return operator>(dateSpan) || operator==(dateSpan);
}

CaplDateTimeSpan CaplDateTimeSpan::operator+(const CaplDateTimeSpan& dateSpan) const
{
	CaplDateTimeSpan dateSpanTemp;

	// If either operand Null, result Null
	if (GetStatus() == null || dateSpan.GetStatus() == null)
	{
		dateSpanTemp.SetStatus(null);
		return dateSpanTemp;
	}

	// If either operand Invalid, result Invalid
	if (GetStatus() == invalid || dateSpan.GetStatus() == invalid)
	{
		dateSpanTemp.SetStatus(invalid);
		return dateSpanTemp;
	}

	// Add spans and validate within legal range
	dateSpanTemp.m_span = m_span + dateSpan.m_span;
	dateSpanTemp.CheckRange();

	return dateSpanTemp;
}

CaplDateTimeSpan CaplDateTimeSpan::operator-(const CaplDateTimeSpan& dateSpan) const
{
	CaplDateTimeSpan dateSpanTemp;

	// If either operand Null, result Null
	if (GetStatus() == null || dateSpan.GetStatus() == null)
	{
		dateSpanTemp.SetStatus(null);
		return dateSpanTemp;
	}

	// If either operand Invalid, result Invalid
	if (GetStatus() == invalid || dateSpan.GetStatus() == invalid)
	{
		dateSpanTemp.SetStatus(invalid);
		return dateSpanTemp;
	}

	// Subtract spans and validate within legal range
	dateSpanTemp.m_span = m_span - dateSpan.m_span;
	dateSpanTemp.CheckRange();

	return dateSpanTemp;
}

CaplDateTimeSpan& CaplDateTimeSpan::operator+=(const CaplDateTimeSpan dateSpan)
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	*this = *this + dateSpan;
	CheckRange();
	return *this;
}

CaplDateTimeSpan& CaplDateTimeSpan::operator-=(const CaplDateTimeSpan dateSpan)
{
	ASSERT(GetStatus() == CaplDateTimeSpan::valid);
	ASSERT(dateSpan.GetStatus() == CaplDateTimeSpan::valid);
	*this = *this - dateSpan;
	CheckRange();
	return *this;
}

CaplDateTimeSpan CaplDateTimeSpan::operator-() const
{
	return -this->m_span;
}

CaplDateTimeSpan::operator double() const
{
	return m_span;
}

void CaplDateTimeSpan::SetDateTimeSpan(LONG lDays, int nHours, int nMins, int nSecs)
{
	// Set date span by breaking into fractional days (all input ranges valid)
	m_span = lDays + ((double)nHours)/24 + ((double)nMins)/(24*60) +
		((double)nSecs)/(24*60*60);
	m_status = valid;
	CheckRange();
}

void CaplDateTimeSpan::CheckRange()
{
	if(m_span < -maxDaysInSpan || m_span > maxDaysInSpan)
		m_status = invalid;
}



#define _CAPLDATESPANFORMATS 3


CString CaplDateTimeSpan::Format(LPCTSTR pFormat) const
// formatting timespans is a little trickier than formatting CTimes
//  * we are only interested in relative time formats, ie. it is illegal
//      to format anything dealing with absolute time (i.e. years, months,
//         day of week, day of year, timezones, ...)
//  * the only valid formats:
//      %D - # of days
//      %H - hour in 24 hour format
//      %M - minute (0-59)
//      %S - seconds (0-59)
//      %% - percent sign
//	%#<any_of_mods> - skip leading zeros
{
	ASSERT(pFormat != NULL);

	if(0==pFormat)  return _T("");
	if(_T('\0')==pFormat[0])  return _T("");

	// If null, return empty string
	if(GetStatus() == null) 	return _T("");

	// If invalid, return DateTime global string
	if(GetStatus() != valid) return _T("Invalid DateTimeSpan");

	enum _CAPLDATESPANFORMATSTEP
	{
		_CTFS_NONE   = 0,	
		_CTFS_FORMAT = 1,
		_CTFS_NZ     = 2
	};

	CString hmsFormats [_CAPLDATESPANFORMATS] = {_T("%c"),_T("%02ld"),_T("%d")};
	CString dayFormats [_CAPLDATESPANFORMATS] = {_T("%c"),_T("%i"),_T("%i")};
	CString strBuffer,strTmp;
	strBuffer.GetBuffer(maxTimeBufferSize);
	strBuffer.ReleaseBuffer();
	TCHAR ch;

	while ((ch = *pFormat++) != _T('\0'))
	{
		enum _CAPLDATESPANFORMATSTEP formatstep = _CTFS_NONE;
		if(ch == _T('%'))
		{
			formatstep = _CTFS_FORMAT;
			ch = *pFormat++;
			if(ch == _T('#'))
			{
				formatstep = _CTFS_NZ;
				ch = *pFormat++;
			}
		}
		switch (ch)
		{
		case '%':
			strBuffer += ch;
			break;
		case 'D':
			strTmp.Format(dayFormats[formatstep], formatstep ? GetDays()    : ch);
			strBuffer+=strTmp;
			break;
		case 'H':
			strTmp.Format(hmsFormats[formatstep], formatstep ? GetHours()   : ch);
			strBuffer+=strTmp;
			break;
		case 'M':
			strTmp.Format(hmsFormats[formatstep], formatstep ? GetMinutes() : ch);
			strBuffer+=strTmp;
			break;
		case 'S':
			strTmp.Format(hmsFormats[formatstep], formatstep ? GetSeconds() : ch);
			strBuffer+=strTmp;
			break;
		default:
			if(formatstep)
			{
				ASSERT(FALSE);      // probably a bad format character
			}
			else
			{
				strBuffer += ch;
#ifdef _MBCS
				if (_istlead(ch))
				{
					strBuffer += *pFormat++;
				}
#endif
			}
			break;
		}
	}

	return strBuffer;
}
