Introduction: The Arduino and Daylight Saving Time (Europe)
Just this past weekend Europe has started the Daylight Saving Time. You all had to reset your clocks, but what about your microcontroller? Many programs on the arduino and other microcontrollers keep time and do that with one of the popular RTC chips such as the DS1307 or the DS3231. They keep time very well (but I prefer the DS3231) and there are a hoist of libraries available for them. One thing they do not do is to automatically switch between standard time and daylight saving time (DST). Obviously that would be quite hard as there are different DST conditions all over the world
Though one could of course reset the RTC chip manually twice a year but that is tedious, especially while it can easily be solved with a few lines of code.
Different parts of the world have different rules for when the daylight saving time takes place and many countries of course have no daylight saving: It only makes sense (if at all) to do it between the tropics and the Arctic circles, so in fact between the tropic of Cancer and the Arctic circle and the tropic of Capricorn and the Antarctic circle.
In Australia daylight saving time is a matter of the states and territories but in general daylight saving time is between the first sunday in oktober and the first Sunday in April (remember, it is down under)
In Canada it largely depends on where you live, some states follow the United states. Newfoundland en Nunavut have their own rule and many of the northern territories do not have daylight saving time
The USA has daylight saving time from the 2nd Sunday of March till the first Sunday of November.
The EU of course has a guideline on it that determines the daylight saving time to start on the last Sunday of March and to end on the last Sunday of October. In the EU the hour to switch is at 01:00 UTC. In practice UTC is equal to GMT (Greenwich Mean Time). UTC itself is not an abbreviation but a compromise between the French abbreviation "TUC" and the English "CUT" (Temps Universel Coordonné resp Coordinated Universal Time).
Catering for your local daylight savings program is usually quite easy: Let your program check for the proper month and then count the number of Sundays (the change is usually on a Sunday). When you arrived at the required Sunday check the time and if it is say 2 am, advance the clock an hour (=set it to 3) if DST has to start, or wait till 3 am and reset the clock hours to '2'. Obviously you also need to set a flag to do the clock correction only once.
Simple enough... Unless you live in the EU (or Nunavut). In the EU the switch isn’t made at the first, second, third or fourth Sunday in March resp. October, but at the LAST Sunday of the month.
Well, a month has four Sundays right? Wrong. If for instance October starts on a Friday, Saturday or Sunday, it has 5 Sundays. So what to do? Well you could of course determine if the first day starts on Friday, Saturday or Sunday and if so, count to 5 and otherwise count to 4. But there is a simpler way, in which you don’t even have to count.
Lets put that in a program for Western Europe, which means that the switch times are 2 and 3 am (that is 1:00 UTC)
Let's agree that you have an RTC that will keep the "day of week" (as the DS1307 and the DS3231 do). As you set these yourself lets agree that you have set the day of week such that Monday is day 1 and Sunday is day 7. Some people like to start the week on Sunday, but that is just a matter of taste. Anyway, we have the following variables read from our RTC
dow= day of week (as in 1-7)
mo=month
d=day (as in 1-31)
h=hour
DST is a flag we set when DST starts and that we clear at the end of DST to avoid that the program starts correcting the time for a full hour and even getting in an endless loop when we set the clock back.
As October has 31 days, we know that the last Sunday will always fall from the 25th to the 31st. Therefore our check at the end of daylight saving will be as follows:
if (dow == 7 && mo == 10 && d >= 25 && h == 3 && DST==1)
{
setclockto 2 am;
DST=0;//store this in your NVRAM
}
As you may be using different libraries the command 'setclockto 2 am' is just a place holder for your specific routine to set your RTC to 2 am. I would advise to store the DST flag somewhere in non volatile memory, in case you need to unplug your arduino. The DS1307 has some on chip Non Volatile RAM where you can store it, the DS3231 does not has the Alarm registers 0x07 - 0x0D that can be used as NVRAM if you are not using alarms. Both however usually come as a module with an EEPROM on it as well, that can be used. Also the EEPROM of the Arduino can be used.
Anyway lets do a check if we have the right routine: Suppose October 1 is a Sunday. That means the 25th is a Wednesday and the 29th a Sunday (the last and 5th Sunday), so the routine will indeed be activated on the 29th as all conditions are met. Suppose October starts on a Monday. That means there are only 4 Sundays and the last Sunday will fall on the 28th. Again, that is between 25 and 31 so the condition is met. Suppose October first falls on a Thursday, then the 25th will be a Sunday (the fourth Sunday) and the 31st will be a Saturday, so again the condition will be met for the last (and in this case 4th Sunday).
To start summertime/daylightsaving time on the last Sunday in March is mutatis mutandis the same:
if (dow == 7 && mo == 3 && d >= 25 && h ==2 && DST==0)
{
setclockto 3 am;
DST=1; //store this in your NVRAM
}
March also has 31 days so the calculations are the same. This time though we check at 2 am and a reset DST flag and then set the clock to 3 am and set the DST flag to indicate it is done already.
Now if you do NOT use an RTC that keeps the day of week or if you keep track of time and date in some other way than with an RTC, then there is an easy formula to determine the day of the week from the date with Sakamoto's Algorithm
//gives day of week for a given date Sunday=0, Saturday=6
int dow(int y, int m, int d)
{ static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3; return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; }
mind you though that this routine counts from 0 (Sunday) to 6 (Saturday)
I have been informed that the Avr-libc has time.h with DST support but not only do you have to tell the library you want to use DST but you also have to include a quite extensive utility file, depending on whether you are in Europe, or the USA. I dare say my code is substantially shorter. There is also a TimeZone library, that can set DST