Jump to content
oPossum

Daylight Saving Time detection and adjustment

Recommended Posts

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).

 

 

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
      }                                                 //
    }                                                   //
  }                                                     //
}                                                       //

Share this post


Link to post
Share on other sites

Formulas for US and EU beginnings and endings of DST:

 

For the United States:
Begin DST: Sunday April (2+6*y-y/4) mod 7+1
End DST: Sunday October (31-(y*5/4+1) mod 7)
Valid for years 1900 to 2006, though DST wasn't adopted until the 1950s-1960s. 2007 and after:
Begin DST: Sunday March 14 - (1 + y*5/4) mod 7
End DST: Sunday November 7 - (1 + y*5/4) mod 7;

European Economic Community:
Begin DST: Sunday March (31 - (5*y/4 + 4) mod 7) at 1h U.T.
End DST: Sunday October (31 - (5*y/4 + 1) mod 7) at 1h U.T.
Since 1996, valid through 2099

(Equations by Wei-Hwa Huang (US), and Robert H. van Gent (EC))

 

taken from http://www.webexhibits.org/daylightsaving/i.html

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×