
#define SECONDS_IN_MINUTE	60
#define SECONDS_IN_HOUR		3600
#define SECONDS_IN_DAY		86400

#define TIME_GMT			0
#define TIME_LONDON		1
#define TIME_NEWYORK		2
#define TIME_TOKYO		3
#define TIME_CYPRUS		4
#define TIME_CHICAGO		5
#define TIME_FRANKFURT	6

//	Offline Settings (Backtesting)
#define TIME_SERVER		TIME_FRANKFURT
#define TIME_LOCAL		TIME_FRANKFURT
/*
//	Online Settings
#define TIME_SERVER		7
#define TIME_LOCAL		8
*/

#import "kernel32.dll"
void		GetSystemTime(int& TimeArray[]);
int		GetTimeZoneInformation(int& TZInfoArray[]);
#import
int		TimeArray[4],
			TZInfoArray[43];

//+------------------------------------------------------------------+
datetime ConvertDateTime(datetime timestamp, int source, int target) {

	// if source = target, no calcs required
	if (source == target) return(timestamp);
	
	// remove DST from source time if required
	if (OnDaylightSavingsTime(source,timestamp))
		timestamp -= SECONDS_IN_HOUR;
	
	// convert to GMT
	timestamp -= GmtOffset(source)*SECONDS_IN_HOUR;
	
	// convert to target time
	timestamp += GmtOffset(target)*SECONDS_IN_HOUR;

	// add DST to target time if required
	if (OnDaylightSavingsTime(target,timestamp))
		timestamp += SECONDS_IN_HOUR;

	// return the adjusted timestamp
	return(timestamp);	
}


//+------------------------------------------------------------------+
int GmtOffset(int place) {
	switch(place) {
		case TIME_GMT:			return( 0);
		case TIME_LONDON:		return( 0);
		case TIME_NEWYORK:	return(-5);
		case TIME_CHICAGO:	return(-6);
		case TIME_FRANKFURT:	return( 1);
		case TIME_CYPRUS:		return( 2);
		case TIME_TOKYO:		return( 9);
		case TIME_SERVER:		return(ServerGmtOffset());
		case TIME_LOCAL:		return(LocalGmtOffset());
	}
}

//+------------------------------------------------------------------+
double ServerGmtOffset()
{
	int shift = (TimeCurrent()-TimeGMT()) / 60;	
	return(RoundClosest(shift,15) / 60.0);		// Round towards closest 15 min. and convert to hours
}

//+------------------------------------------------------------------+
double LocalServerOffset()
{
	int shift = (TimeLocal()-TimeCurrent()) / 60;
	return(RoundClosest(shift,15) / 60.0);		// Round towards closest 15 min. and convert to hours
}


//+------------------------------------------------------------------+
double LocalGmtOffset()
{
	int gmt_shift=0;
	int ret=GetTimeZoneInformation(TZInfoArray);
	if(ret!=0) gmt_shift=TZInfoArray[0];	//	Difference between your local time and GMT in minutes (winter time)
	if(ret==2) gmt_shift+=TZInfoArray[42];	//	Current difference between your local time and GMT in minutes

	return(-gmt_shift / 60.0);					// Convert to hours
}

//+------------------------------------------------------------------+
int RoundClosest(int n, int step)
{
	if(n > 0)	n += step/2;
	else			n -= step/2;
	return(n - n%step);
}

//+------------------------------------------------------------------+
datetime TimeGMT()
{
	return(StrToTime(sTimeGMT()));
}

//+------------------------------------------------------------------+
string sTimeGMT()
{
	GetSystemTime(TimeArray);
	string YY = TimeArray[0]&0x0000FFFF;
	string MM = TimeArray[0]>>16;
	string DD = TimeArray[1]>>16;
	string hh = TimeArray[2]&0x0000FFFF;
	string mm = TimeArray[2]>>16;
	string ss = TimeArray[3]&0x0000FFFF;
	string ms = TimeArray[3]>>16;

	while(StringLen(YY)<4) YY = "0"+YY;
	while(StringLen(MM)<2) MM = "0"+MM;
	while(StringLen(DD)<2) DD = "0"+DD;
	while(StringLen(hh)<2) hh = "0"+hh;
	while(StringLen(mm)<2) mm = "0"+mm;
	while(StringLen(ss)<2) ss = "0"+ss;
	while(StringLen(ms)<3) ms = "0"+ms;

	return(StringConcatenate(YY,".",MM,".",DD," ",hh,":",mm,":",ss,".",ms));
}

//+------------------------------------------------------------------+
bool OnDaylightSavingsTime(int place, datetime timestamp) {

	int year = TimeYear(timestamp);
	datetime daylightstart;
	datetime daylightend;
	
	switch(place) {
		
		// DST calc not required in some instances
		case TIME_GMT:
		case TIME_TOKYO:
		return(false);
		
		// Since 1996 European Summer Time has been observed from the 
		// last Sunday in March to the last Sunday in October, with clocks 
		// shifted at 01:00 UTC
		case TIME_LONDON:
		case TIME_CYPRUS:
		case TIME_FRANKFURT:
		daylightstart	= getLastSunday(year,3,31);
		daylightend		= getLastSunday(year,10,31);
		if (timestamp >= daylightstart && timestamp < daylightend) 
			return(true);
		else
			return(false);
		
		// Starting in 2007, most of the United States and Canada observe 
		// DST from the second Sunday in March to the first Sunday in November, 
		// shifting clocks typically at 02:00 local time.
		// From 1987 through 2006, the start and end dates were the first Sunday 
		// in April and the last Sunday in October,
		case TIME_NEWYORK:
		case TIME_CHICAGO:
		if (year >= 2007) {
			daylightstart	= getLastSunday(year,3,14);
			daylightend		= getLastSunday(year,11,7);
		}
		else {
			daylightstart	= getLastSunday(year,4,7);
			daylightend		= getLastSunday(year,10,31);
		}
		if (timestamp >= daylightstart && timestamp < daylightend) 
			return(true);
		else
			return(false);
	}
}


// ==================================================================
// getLastSunday
//
// Returns datetime identifying last Sunday prior to specified date
// ==================================================================
datetime getLastSunday(int year, int month, int day) {
	
	datetime retval;
	
	// set a timestamp to first day of month
	datetime date = StrToTime(year + "." + month + ".1");
	
	// while it's same month and less than maxdays, add days and capture sundays
	while (true) {
		// if it's sunday, capture it into retval
		if (TimeDayOfWeek(date) == 0) retval = date;
		// add a day
		date += SECONDS_IN_DAY;
		// if it's now the next month or past maxday, return
		if (TimeMonth(date) != month || TimeDay(date) > day) return(retval);
	}
}