This code quickly determines if a date is within daylight savings time (DST). A companion function adjusts the date and time if necessary.
There are many ways to determine if a date is within DST. This code begins by checking large time spans (months) and works down to shorter time spans (hours) as necessary. So for most of the year the decision is made very quickly. The worst cases occur on the days of the transition.
BCD representation is used for compatibility with most hardware RTC chips. The day of week (T_RTC.dow) must be set properly for this code to work. Day of week is a value of 1 (Sunday) to 8 (Saturday).
There are many ways to determine if a date is within DST. This code begins by checking large time spans (months) and works down to shorter time spans (hours) as necessary. So for most of the year the decision is made very quickly. The worst cases occur on the days of the transition.
BCD representation is used for compatibility with most hardware RTC chips. The day of week (T_RTC.dow) must be set properly for this code to work. Day of week is a value of 1 (Sunday) to 8 (Saturday).
typedef struct { // RTC data structure
uint8_t sec; //
uint8_t min; //
uint8_t hour; //
uint8_t dow; //
uint8_t day; //
uint8_t month; //
uint8_t year; //
} T_RTC; //
// Starting in 2007, most of the United States and Canada observe DST from the second Sunday in March to the first Sunday in November
int rtc_is_dst(T_RTC const * const rtc) // --- Check if time & date are within DST
{ //
if((rtc->month > 0x03) && (rtc->month < 0x11)) return 1; // After March and before November is DST
if((rtc->month < 0x03) || (rtc->month > 0x11)) return 0; // Before March or after November is not DST
if(rtc->month == 0x03) { // March
if(rtc->day > 0x15) return 1; // After second week, is DST
if(rtc->day < 0x08) return 0; // Before second week, is not DST
const int d = ((rtc->day >> 4) * 10) + (rtc->day & 0x0F); // Integer day of month
int s = d - rtc->dow + 1; // Current or previous Sunday as day of month
if(s < 8) s += 7; // Make sure Sunday is in second week
if(d < s) return 0; // Before Sunday, is not DST
if(d > s) return 1; // After Sunday, is DST
if((rtc->hour & 0x3F) < 0x02) return 0; // Before 2:00, is not DST
return 1; // 2:00 or after, is DST
} else { // rtc->month == 0x11 // November
if(rtc->day > 0x07) return 0; // After first week, not DST
const int d = ((rtc->day >> 4) * 10) + (rtc->day & 0x0F); // Integer day of month
int s = d - rtc->dow + 1; // Current or previous Sunday as day of month
if(s < 0) s += 7; // Make sure Sunday is in first week
if(d < s) return 1; // Before Sunday, is DST
if(d > s) return 0; // After Sunday, is not DST
if((rtc->hour & 0x3F) < 0x02) return 1; // Before 2:00, is DST
return 0; // 2:00 or after, is not DST
} //
} //
//
void rtc_adjust_dst(T_RTC * const rtc) // --- Correct RTC structure for DST if necessary
{ //
static const uint8_t dm[19] = { 0, 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0, 0, 0, 0, 0, 0, 0x31, 0x30, 0x31 };
if(rtc_is_dst(rtc)) { // If DST
++rtc->hour; // Increment hour
if((rtc->hour & 0x0F) > 9) rtc->hour += 6; // Adjust for BCD
if(rtc->hour > 0x23) { // If next day
rtc->hour = 0; // Set hour to 0
++rtc->dow; // Increment day of week
if(rtc->dow > 7) rtc->dow = 1; // Adjust for wrap around
++rtc->day; // Increment day of month
if((rtc->day & 0x0F) > 9) rtc->day += 6; // Adjust for BCD
if(rtc->day > dm[rtc->month]) { // Check if next month
rtc->day = 0x01; // Wrap to first day of next month
++rtc->month; // Increment month
if((rtc->month & 0x0F) > 9) rtc->month += 6; // Adjust for BCD
} //
} //
} //
} //