Quantcast
Channel: clock – Hackaday
Viewing all articles
Browse latest Browse all 494

Automatic Daylight Saving Time compensation for your clock projects

$
0
0

Pretty early in development of my Ping Pong Clock I came up with the idea of automatic Daylight Saving Time compensation. It’s an interesting feature, but it’s a luxury and so I figured I could add it as a future improvement. Now’s the time and I’m reporting back on what I’ve learned and how you can add this to your own projects.

There’s two things to think about before adding this feature. Is it worth the effort and does it make the clock more confusing rather than easier to use?

As to the latter, if you are responsible for setting the time initially but you are not responsible for resetting the clock when we fall back or spring forward will it cause confusion? Perhaps initially, but the battery-backed RTC that I used in my project should mean that you set it once and never have to reset it again. The one exception is DST and that’s what I’m compensating for.

Whether it is worth it or not is difficult to answer until after the fact. You should take into consideration that the DST rules are not set in stone, they change from time to time. Add to that the fact that not all parts of the world observe the practice. This means that not only do you need to implement the compensation, but you should add a method of switching it on and off as well as changing the rules for when it is observed.

Join me after the break to learn the method and code I use to make time adjustments automatically twice a year.

Implementation

There are two chunks that go into implementing DST compensation. The first part is to figure out if we are currently observing DST or if standard time is in force. The second portion of the problem is to develop a method to do the compensating without upsetting how the clock runs or how it is set.

This post will tackle the first problem only; how to decided when DST is in effect. I am planning a second post to detail the mechanics necessary to use that information.

The problem

Image source: Wikimedia Commons

Daylight Saving Time does not start on a set day, for instance April 15th of each year. Instead, it starts on a specific day of the week. The United States currently observes the start of DST at 2:00am on the second Sunday of April. The issue is further compounded by the fact that this legislated rule changes from time to time, most recently the US rules were changed in 2007. In order to perform compensation we must be able to answer the question: what is the exact date that DST starts?

We’ll need an algorithm that takes the day-of-week (DOW) based rules and translates them into an exact date answer. What is the minimum amount of input information necessary for the algorithm to still work? Let’s find out.

Working it out

If you were going to find the date when DST starts how would you do it? Given that DST begins on the second Sunday in April, what was the date that it started last year (2011)? Naturally, you would look at a calendar for April 2011, and count the Tuesdays until you get to the second one. That’s exactly what our algorithm will do, except it will not need to have a calendar in front of it. To solve our problem, let’s state explicitly the method we want to use:

  • Find the day-of-week for the first day in the given month.
  • Increment the date until you arrive at the target day-of-week.
  • increment weeks until you reach the target day.

The one useful piece of information that we get from looking at a calendar is what day of the week the month started on. Luckily, this is easily calculable and it’s just a quick Google search away.

Calculating Day-of-Week for any date

Wikipedia has a very nice article about calculating the day of the week. Much of the time spent in this calculation is used to establish if the year is a leap year or not. The rest of the calculation involves lookup tables to get to the final answer. Fortunately, DOW calculations are a common problem so there are several streamlined algorithms available for the task. I chose to use Sakamoto’s Algorithm because it’s already written in C and it’s quite simple.

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

Explaining exactly how this works is an exercise you can save for a rainy day. When trying to work it out make sure you look up the rules of precedence. Note that “m < 3″ will be evaluated first, returning 0 if false and 1 if true. We’d also like to point out that the three division operations in the third line of code are used to adjust for leap years.

Finding the correct starting day

Now that we know the day of week for the first of the month, we need to find the date of the first occurrence of our target day. If we assign our target day to the variable “DOW”, day of week on the first day of the month to “firstDOW”, it’s simple to use a loop to increment until we get to our target:

char targetDate = 1;
  while (firstDOW != DOW){
    firstDOW = (firstDOW+1)%7;
    targetDate++;
  }

This code loop will exit when the variable “targetDate” matches the first occurrence of our target day. But this only solves a portion of the problem. We also need to identify the date for the second, third, or other occurrence of that day in the month.

Adjusting the target date to account for weeks

If we’re looking for the second Sunday in a given month, we can assign the number 2 to the variable “NthWeek”:

  targetDate += (NthWeek-1)*7;

This quick snippet will add seven to the date for each week after the first occurrence of our target day. Since I am subtracting one before multiplying by 7 (the number of days in a week) nothing will be added if we are looking for the first Sunday in the month.

Putting it all together

If we wrap all of our code into a nice little package we’ll end up with a function that returns the date based on input information. To keep focus on the problem, I first defined the information that will be passed into the function, and what I plan to get back from it:

  • Inputs: year, month, day-of-week (eg: Sunday = 0),  nth week of month (eg: the 2nd Sunday of the month = 2)
  • Output: an integer that represents the date (eg: 15 would be the 15th day of the month)

Because our first step relies on another algorithm it needs to be included in our package. I changed it just a little bit by replacing as many of the int data types with char as possible. Depending on the compiler you use this may end up saving on ram.

#include <stdio.h>
char buff[50];

int myYear = 2011;
char myMonth = 04;
char myDOW = 0;
char myNthWeek = 2;

/*--------------------------------------------------------------------------
  FUNC: 6/11/11 - Returns day of week for any given date
  PARAMS: year, month, date
  RETURNS: day of week (0-7 is Sun-Sat)
  NOTES: Sakamoto's Algorithm
    http://en.wikipedia.org/wiki/Calculating_the_day_of_the_week#Sakamoto.27s_algorithm
    Altered to use char when possible to save microcontroller ram
--------------------------------------------------------------------------*/
char dow(int y, char m, char d)
   {
       static char 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;
   }

/*--------------------------------------------------------------------------
  FUNC: 6/11/11 - Returns the date for Nth day of month. For instance,
    it will return the numeric date for the 2nd Sunday of April
  PARAMS: year, month, day of week, Nth occurence of that day in that month
  RETURNS: date
  NOTES: There is no error checking for invalid inputs.
--------------------------------------------------------------------------*/
char NthDate(int year, char month, char DOW, char NthWeek){
  char targetDate = 1;
  char firstDOW = dow(year,month,targetDate);
  while (firstDOW != DOW){
    firstDOW = (firstDOW+1)%7;
    targetDate++;
  }
  //Adjust for weeks
  targetDate += (NthWeek-1)*7;
  return targetDate;
}

int main(void){
  //Used to test on a computer
  sprintf(buff,"%i",NthDate(myYear,myMonth,myDOW,myNthWeek));
  printf("%s\n",buff);
}

I wrote this with the intent of using it on a microcontroller, but just for testing, I’ve included some I/O for feedback on  a computer. You can remove the first two lines and the entire main function and plop it into your project. On a Linux box compile it with GCC using: “gcc -o dst dst.c” and then run it using “./dst”.

Conclusion

This is a rather small amount of code which doesn’t require a lot of processing power to used. If you can add the option to set the current date to your clock project this is a snap to roll into the code. The one thing you need to work out is how to handle setting and storing the time. This will be different depending on whether or not DST is in effect.

As a side note. I ended up reusing this code for one of the Project Euler questions. I guess the work is already paying dividends.

Resources

Example code


Filed under: clock hacks, how-to

Viewing all articles
Browse latest Browse all 494

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>