#ifndef _xbDATETIME_
#define _xbDATETIME_
/*<<<***************************************************************************
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.
***************************************************************************>>>*/

#ifndef _xbSTRING_
#	include "xbString.h"
#endif

#ifndef _xbEXCEPTION_
#	include "xbException.h"
#endif

/*class xbEXPORT __declspec(uuid("{83CC9F04-9B3D-4204-9A76-DCBF99217A65}"))  
	xbEdate:public xbEadv
{
public:
	virtual LPCSTR GetFullEname() const {return "xbEdate";}
	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(xbEdate);
		return xbEdate::GetInterface(riid,ppInterf);
	}

	xbEdate(HRESULT hr,const xbString &msg)
		:xbEadv(hr,msg)
	{
		traceExcept();
	}
	xbEdate(const xbString &msg)
		:xbEadv(msg)
	{
		traceExcept();
	}
	xbEdate(HRESULT hr,const xbString &msg,const xbString &ctx)
		:xbEadv(hr,msg,ctx)
	{
		traceExcept();
	}
	xbEdate(const xbString &msg,const xbString &ctx)
		:xbEadv(msg,ctx)
	{
		traceExcept();
	}

	virtual xbMBiconType GetMBoxIcon() const {return xbMBicon_Stop;}
	virtual const char *GetMBoxTitle() const {return "Date error";} 
private:
	xbDclNoCopyForObject(xbEdate);
public:
	xbDefaultHeapDnewImpl(xbEdate);
};
*/
enum xbDateStatus
{
	xbDate_empty = 0,
	xbDate_auto = 1,
	xbDate_datetime = 2,
	xbDate_date = 3,
	xbDate_time = 4,
	xbDate_timelife = 5
};

#define xbDateTime_REF		693899 // day 693899 (number of days since 1/3/0000 to 30/12/1899, year 0 based) = 30/12/1899, day 693901 = 1/1/1900
// -> day 0 (ole date based) = 30/12/1899(saturday)
// -> day 1 (ole date based) = 31/12/1899(sunday), day 2 = 1/1/1900(monday)
//To check: 16/2/1900 = friday, 1/1/1970 was Thursday
#define xbDateTime_YEARMIN	100		//Year min
#define xbDateTime_YEARMAX	9999	//Year max

class xbEXPORT xbDateTime
{
// Constructors
public:
/*	static xbDateTime GetCurrentTime()
	{
		SYSTEMTIME systi;
		::GetLocalTime(&systi);
		return xbDateTime(systi);
	}*/
	static xbDateTime /*GetLocalTime*/ Now()
	{
		SYSTEMTIME systi;
		::GetLocalTime(&systi);
		return xbDateTime(systi);
	}
	static xbDateTime /*GetUtcTime*/ UtcNow()
	{
		SYSTEMTIME systi;
		::GetSystemTime(&systi);
		return xbDateTime(systi);
	}
	static bool IsLeapYear(int year); //must be between 100 and 9999, années bissextiles (1984, ...)
	static int GetDaysOfMonth(int year,int month); //year must be between 100 and 9999, month : 1 and 12
	bool ToSystemTime(SYSTEMTIME *pSysTime,xbOpMode opm =xbOp_Required) const;
	bool ToFileTime(FILETIME *pFileTime,xbOpMode opm =xbOp_Required) const;
	xbDateTime ToLocalTime() const
	{
		FILETIME ti,tiLoc;
		ToFileTime(&ti);
		if (!::FileTimeToLocalFileTime(&ti,&tiLoc))
			xbThrow xbEsys(xbHR_LAST,"xbDateTime::ToLocalTime() : Can't convert date to local time zone");
		return xbDateTime(tiLoc);
	}
	xbDateTime ToUtcTime() const
	{
		FILETIME ti,tiLoc;
		ToFileTime(&ti);
		if (!::LocalFileTimeToFileTime(&ti,&tiLoc))
			xbThrow xbEsys(xbHR_LAST,"xbDateTime::ToUtcTime() : Can't convert date to UTC time zone");
		return xbDateTime(tiLoc);
	}
	xbDateTime() 
	{
		m_status = xbDate_empty;
	}

	xbDateTime(double dtSrc,xbDateStatus status = xbDate_auto);
//	xbDateTime(time_t timeSrc);
	xbDateTime(const SYSTEMTIME& systimeSrc);
	xbDateTime(const FILETIME& filetimeSrc);

	xbDateTime(int nYear, int nMonth, int nDay,
		int nHour, int nMin, int nSec, int nMsec = 0);
	xbDateTime(int nYear, int nMonth, int nDay);
//	xbDateTime(WORD wDosDate, WORD wDosTime);
	xbDateTime(const xbDateTime &dt);

	void ReadYMD(const xbString &str); //Format : YYYYMMDD
	xbString ToYMD() const;
	void ReadYMDHMS(const xbString &str); //Format : YYYYMMDDHHMMSS
	xbString ToYMDHMS() const;

	operator double() const {return m_dt;}

	bool	IsValid() const
	{
		return m_status != xbDate_empty && m_dt != 0;
	}
	bool	IsEmpty() const
	{
		return m_status == xbDate_empty || m_dt == 0;
	}
	void Empty()
	{
		m_dt = 0;
		m_status = xbDate_empty;
	}

	xbDateStatus GetStatus() const
	{
		return m_status;
	}
	void SetStatus(xbDateStatus status)
	{
		m_status = status;
	}
	xbPropertyRW(xbDateStatus,Status);

	xbDateStatus GetCalcStatus() const;
	xbPropertyR(xbDateStatus,CalcStatus);

	bool HasDate() const;
	bool HasTime() const;
	bool HasDateAndTime() const;
	bool IsTimeLife() const;

	xbDateTime Date() const;
	xbDateTime Time() const;

	const xbDateTime& operator=(const xbDateTime& dt);
	const xbDateTime& operator=(const FILETIME& filetimeSrc);
	const xbDateTime& operator=(const SYSTEMTIME& systimeSrc);

	int Compare(const xbDateTime& dt) const;

		//PPAMDIS
/*	xbString Format(DWORD dwFlags = 0, LCID lcid = LANG_USER_DEFAULT) const;
	xbString Format(LPCSTR lpszFormat) const;
*/
	xbString ToString(LPCSTR pTZ = NULL) const; //us/http format

	static xbString tzInfoShort(const TIME_ZONE_INFORMATION *pInfo,const xbDateTime *pDt = NULL);
	static bool tzInfoIsUtc(const TIME_ZONE_INFORMATION *pInfo);
	static bool tzInfoHasDayLight(const TIME_ZONE_INFORMATION *pInfo);

	static bool tzInfoMakeDate(ulong year,const SYSTEMTIME &tiIn, SYSTEMTIME &tiOut);
	xbDateTime tzInfoDaylightDate(const TIME_ZONE_INFORMATION *pInfo) const;
	xbDateTime tzInfoStandardDate(const TIME_ZONE_INFORMATION *pInfo) const;
	int tzInfoLocalBias(const TIME_ZONE_INFORMATION *pInfo) const;
	bool tzInfoLocalInDayLight(const TIME_ZONE_INFORMATION *pInfo) const;
	static xbString tzBiasToStr(int bias);
	xbDateTime ToLocalTime(const TIME_ZONE_INFORMATION *pInfo) const;
	xbDateTime ToUtcTime(const TIME_ZONE_INFORMATION *pInfo) const;

	static xbDateTime GetLocalTime(const TIME_ZONE_INFORMATION *pInfo);
	static int systemTimeDiff(const SYSTEMTIME &ti1,const SYSTEMTIME &ti2,bool bYearCmp);

	int GetDayOfWeek() const;	// 0 -> Monday, 6 -> Sunday
	int GetDayOfWeekMon() const;			// 0 -> Monday, 6 -> Sunday
	int GetDayOfWeekSun() const;			// 0 -> Sunday, 6 -> Saturday
	int GetYear() const;					// Full year, ex: 2013
	int GetMonth() const;					// 1 -> January, 12 -> December
	int GetDay() const;						// 1 -> 31
	int GetHour() const;					// 0 -> 23
	int GetMinute() const;					// 0 -> 59
	int GetSecond() const;					// 0 -> 59
	int GetMilliSecond() const;				// 0 -> 999

	bool SetDate(int y,int m,int d,xbOpMode opm =xbOp_Required);
	bool SetDateTime(int y,int mon,int d,int h,int min,int s,int ms=0,xbOpMode opm =xbOp_Required);
	bool SetTime(int h,int min,int s,int ms=0,xbOpMode opm =xbOp_Required);
	bool SetTimeLife(int d,int h,int min,int s,int ms=0,xbOpMode opm =xbOp_Required);

	xbDateTime &operator-=(double dt);
	xbDateTime &operator+=(double dt);

	static bool dateIsValid(int y, int m, int d);
	static double dateToDouble(int y, int m, int d);
	static bool dateFromDouble(double val,int *py,int *pm,int *pd);
	
	static bool timeIsValid(int h, int m, int s,int ms);
	static double timeToDouble(int h, int m, int s,int ms);
	static bool timeFromDouble(double val,int *pd,int *ph,int *pm,int *ps,int *pms);
protected:
	xbDateStatus	m_status;
	double			m_dt;
private:
	xbDclNoNewForObject(xbDateTime);
};

inline  bool  operator==(const xbDateTime& d1, const xbDateTime& d2)
	{ return d1.Compare(d2) == 0; }
inline  bool  operator!=(const xbDateTime& d1, const xbDateTime& d2)
	{ return d1.Compare(d2) != 0; }
inline  bool  operator>(const xbDateTime& d1, const xbDateTime& d2)
	{ return d1.Compare(d2) > 0; }
inline  bool  operator<(const xbDateTime& d1, const xbDateTime& d2)
	{ return d1.Compare(d2) < 0; }
inline  bool  operator>=(const xbDateTime& d1, const xbDateTime& d2)
	{ return d1.Compare(d2) >= 0; }
inline  bool  operator<=(const xbDateTime& d1, const xbDateTime& d2)
	{ return d1.Compare(d2) <= 0; }

xbEXPORT xbDateTime operator+(const xbDateTime& d1, const xbDateTime& d2);
xbEXPORT xbDateTime operator-(const xbDateTime& d1, const xbDateTime& d2);

//MS: 0 -> Sunday, 6 -> Saturday <-> Lib: 0 -> Monday, 6 -> Sunday
#define xbDayOfWeekToMS(d)		((d+1)%7)
#define xbDayOfWeekFromMS(d)	((d+6)%7)
#endif
