Arduino RTC: Calculate if daylight saving time adjustment applies
When I revisited some code making use of the real time clock (RTC) on an Arduino MKR 1010 board I found that I had left a "kludge" to adjust for time zone and daylight savings. If you are using an Internet time server to set your real time clock then you are probably going to get a response of GMT (well since 1972, more officially, UTC). Your base line adjustment for your time zone (if any) is going to be fixed but you might want to allow for any seasonal daylight savings adjustment.
It would be nice if we could add some code to make a daylight savings adjustment automatically. Your local rules may vary from mine but the UK rules, applied here, would serve for EU countries. Other locations might need a tweak or two.
Daylight savings adjustments occur in the early hours of a Sunday. So, we need to know what the current day of the week it is when setting our RTC if we want to decide if we should apply any adjustment to UTC/GMT plus or minus our time zone difference.
Code to get the day of the week for a given date can be found in a previous post here although your RTC library may already make a similar function available. Which just leaves the code to decide if a seasonal adjustment should be made. Here is a function that makes that decision and returns a Boolean value. This function assumes that adjustments are made on the last Sunday of March and the last Sunday of October.
You will notice that any final time based decision on those crucial Sundays can be added where indicated although omitted here so that the function can be tested on a basic Arduino Uno or similar.
The following is a simple test rig that allows dates to be entered via the Arduino IDE Serial Monitor with feedback given on any date entered.
Feel free to copy, paste and test - plus adapt to your needs.
char dateBuffer[11];
char sChar;
int buffCount = 0;
template<class T> inline Print &operator<<(Print &obj, T arg) {
obj.print(arg); return obj;}
void setup() {
Serial.begin(9600); // slow so this works with IDE 2 Serial monitor
while (!Serial) {}
clearBuffer();
Serial << "Please set Serial Monitor line ending to NL & CR\n";
Serial << "Then enter dates in dd/mm/yyyy format and press <return>\n";
}
void loop() {
if ( Serial.available()){
sChar = Serial.read();
switch (sChar){
case 10:
case 13:
if(buffCount >= 8){
String dt = dateBuffer;
int daySlash = dt.indexOf(('/'));
int monthSlash = dt.indexOf('/', daySlash + 1);
bool res = isBST(dt.substring(0, daySlash).toInt(), dt.substring(daySlash + 1, monthSlash).toInt(), dt.substring(monthSlash + 1).toInt());
Serial << '\n' << dateBuffer;
if(res){
Serial << " Daylight saving applies\n";
} else {
Serial << " Time is GMT\n";
}
Serial << "Try another one\n";
}
clearBuffer();
break;
default:
if(buffCount <= 10){
dateBuffer[buffCount++] = sChar;
}
}
}
}
// function will check if date is in BST (currently good for EU member states)
// some other time zones change on different days
bool isBST(int d, int m, int y){
if(m > 3 && m < 10){ return true;}
int weekDay = dayOfWeek(d, m, y);
int priorSunday = d - (weekDay - 1);
if(m == 3){
if(weekDay == 1) {
if(priorSunday >= 25){
// can add a check on the hour here as BST if hour >=2 TODO when RTC active
// if before 2 then not BST yet
return true;
}
} else if (priorSunday >= 25){
return true;
}
} else if (m == 10){
if(weekDay == 1) {
if(priorSunday < 25){
return true;
} else {
// time check can be added here
// BST true if hour <= 1
}
} else {
if(priorSunday < 25){
return true;
}
}
}
return false;
}
int dayOfWeek(int d, int m, int y){
// note arguments are not validated
bool leapYear = false;
if((y % 4 == 0 && y % 100 != 0) || y % 400 == 0){
leapYear = true;
}
while (y > 2299){ y -=400;} // extend function to before 1900 and beyond 2299
while (y < 1900) {y += 400;}
int century = y / 100;
int yearInC = y % 100;
int cValue = 0; // default for 21st C
switch(century){
case 19:
cValue = 1;
break;
case 21:
cValue = 5;
break;
case 22:
cValue = 3;
break;
}
int mValue = 0; // good for month 10
switch(m) {
case 1:
if(leapYear) { mValue = 6;}
break;
case 2:
mValue = leapYear ? 2 : 3;
break;
case 3:
case 11:
mValue = 3;
break;
case 4:
case 7:
mValue = 6;
break;
case 5:
mValue = 1;
break;
case 6:
mValue = 4;
break;
case 8:
mValue = 2;
break;
case 9:
case 12:
mValue = 5;
break;
}
int aValue = d + mValue + yearInC + cValue + yearInC / 4;
int dValue = aValue % 7;
return dValue > 0 ? dValue : 7; // Sunday 1, Saturday 7
}
void clearBuffer(){
memset(dateBuffer, 0, 11);
buffCount = 0;
}
If you are using the Arduino IDE version 2 you should ensure that the Serial speed is kept at 9600 (as above) but you can set a higher value if you are using the earlier version of the IDE.
If you had an RTC running continuously once set and would like to have it adjust its time automatically when clocks are adjusted I would suppose that an alarm set to run every day (RTCZero.setAlarmTime() maybe) to run a check on the current date and time - 2AM if daylight savings has been applied and 1AM otherwise. Just a thought. Please let me know if you try it.
Comments
Post a Comment