I am working with an Arduino and a real time clock chip. The chip compensates for leap years and such, so it will always have the correct date, but it does not handle daylight s
Code for Central Europe (tested for every day in range 2014-3000 year)
public static bool IsDst(int day, int month, int dow)
{
if (month < 3 || month > 10) return false;
if (month > 3 && month < 10) return true;
int previousSunday = day - dow;
if (month == 3) return previousSunday >= 25;
if (month == 10) return previousSunday < 25;
return false; // this line never gonna happend
}
Test function
static void Main(string[] args)
{
TimeZoneInfo tzf2 = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time");
var date = new DateTime(2014, 01, 1, 5, 0,0);
bool wasSummer = false;
while (date <= new DateTime(3000,1,1))
{
var dow = (int) date.DayOfWeek;
var isDst = IsDst(date.Day, date.Month, dow);
DateTime f2 = TimeZoneInfo.ConvertTime(date, tzf2);
var isSummer = f2.IsDaylightSavingTime();
if (isSummer != isDst)
{
Console.WriteLine("ERROR");
Console.WriteLine(date);
}
if (isSummer != wasSummer)
{
Console.WriteLine(date.AddDays(-1).ToShortDateString());
}
date = date.AddDays(1);
wasSummer = isSummer;
}
Console.ReadKey();
}
Here is my answer, and I welcome any corrections. It is assumed that the years are between 2000 and 2099 inclusive. Further details available via the reference link.
int timezone = 0; // Set to correct initial value depending on where you are (or via GPS if you like).
// Calculate day of week for Daylight savings time.
int day_of_week = (day_of_month + int(2.6 * (((month + 12 - 3) % 12) + 1) - 0.2) - 40 +
(month < 3 ? year-1 : year) + int((month < 3 ? year-1 : year)/4) + 5) % 7;
// Adjust timezone based on Daylight savings time for northern hemisphere, USA
if ((month > 3 && month < 11 ) ||
(month == 3 && day_of_month >= 8 && day_of_week == 0 && hour >= 2) || // DST starts 2nd Sunday of March; 2am
(month == 11 && day_of_month < 8 && day_of_week > 0) ||
(month == 11 && day_of_month < 8 && day_of_week == 0 && hour < 2)) { // DST ends 1st Sunday of November; 2am
timezone++;
}
Day of Week calculation reference: How to determine the day of the week, given the month, day and year
DST test reference is via this article as answered by captncraig and my own reasoning and interpretation of his answer.
If anyone is looking for this in python, there is a great article here: http://code.activestate.com/recipes/425607-findng-the-xth-day-in-a-month/
It contains the following code:
from calendar import monthrange
def dow_date_finder(which_weekday_in_month=FIRST,day=MONDAY,month=JANUARY,year=2000):
bom, days = monthrange(year, month)
firstmatch = (day - bom) % 7 + 1
return xrange(firstmatch, days+1, 7)[which_weekday_in_month]
Keep in mind that you need to have all of the variables present from the top of the page at the link above to make it more user-friendly.
The below is good for any year 2007 or later:
def find_dt_of_daylight_savings_time(yr=date.today().year):
dst = {}
spring = dow_date_finder(SECOND, SUNDAY, MARCH, yr)
spring_dt = datetime(int(yr), 3, spring, 3, 0)
dst['spring'] = spring_dt
fall = dow_date_finder(FIRST, SUNDAY, NOVEMBER, yr)
fall_dt = datetime(int(yr), 11, fall, 3, 0)
dst['fall'] = fall_dt
return dst
I am trying with this approach and I think it simple and accurate:
// for first Sunday of March as if DoW = 1 for Sunday if (month==3 && day>=8 && day <=14 && DoW=1) return True
// for second Sunday of Nov as if DoW = 1 for Sunday if (month==11 && day>=1 && day <=7 && DoW=1) return True
March 14th and November 7 are always part of the week that the day light savings occur in the united states.. they may be the Sunday or the Saturday or and day of the week in-between but they are always part of that week. the code below will find the Sunday of that those two dates are part of in the year that the date in question occurs in. it then adds 2 hours to this date to get the time in which the daylight savings actually occur. you then compare the date in question against the daylight savings beginning and end dates and adjust the time by the gmt offset. This process can work for other countries begin and end dates. you could setup a table that has every country code and postal code in it with daylight savings end and begin dates and the gmt offset for both periods. the dates would be the 7th, 14th, 21th, and 28th, for the first through forth Sundays of a month. you would put in the max day in for the last Sunday of the month ex Sep 30th or Oct 31st.
2nd Sunday in March:
cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24
1st Sunday November:
cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24
Ex.
If(now() < cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-5,now()), if(now() < cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-6,now()), dateadd("H",-5,now())))
t_SQL example
CASE WHEN [date2check] < DATEADD(hh, 2, CAST('3/14/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4)) AS datetime) + 1 - DATEPART(w, CAST('3/14/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4)) AS datetime))) THEN dateadd(hh, - DST_GMT_TM_ZN_DIFF, [date2check]) ELSE CASE WHEN [date2check] < DATEADD(hh, 2, CAST('11/7/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4)) AS datetime) + 1 - DATEPART(w, CAST('11/7/' + CAST(DATEPART(yyyy, [date2check]) AS nvarchar(4)) AS datetime))) THEN dateadd(hh, - STD_GMT_TM_ZN_DIFF, [date2check]) ELSE dateadd(hh, - DST_GMT_TM_ZN_DIFF, [date2check]) END END
While it is easy to calculate whether a particular date is in DST for a particular location under the current rules, do note that DST is at the whim of politicians and could change at any point. I have a clock manufactured pre-2007 that automatically adjusts for daylight savings time, and now I have to change it four times a year: twice when the actual change occurs, and twice when it now-incorrectly changes itself at the old dates.
In this case, you might be able to ignore DST completely by the simple expedient of having the user enter the timezone along with the date and time. Or you could do like most consumer devices and let the user adjust the time to the local time zone twice a year.
But if you really need to handle DST and really want to do things right, use the zoneinfo database and make sure it can be updated somehow. If you can't do that for some reason, at least allow the user to override the rules. And if even that is too difficult, at least give the user the option to turn off automatic adjustments (unlike my stupid alarm clock).