I want a fully-functional date and time library for my Arduino applications. The goal behind my GPS clock is to have a clock with no buttons. Yes, if you’re wondering, I have been reading about Steve Jobs. No buttons!
Why no buttons? Because fundamentally any clock with buttons on it requires a human operator to set the time. Since every house has many clocks and every clock has buttons and every clock keeps slightly different time and it’s physically impossible to synchronise all your clocks you inevitably have different time displayed in each room of the house. Sorry for the run-on sentence. I’m sure there are some people for whom this is not a problem. But if you are a little OCD (and I say that admiringly, not mockingly) you’re regularly annoyed by the need to fix your clocks.
A clock with a GPS receiver sets it own time. Better yet, the time it keeps is based on the atomic-clock precision kept by GPS satellites. If this clock is inaccurate, it’s purely down to the programming I have done in the microcontroller. At least now I’ll know when any clock disagrees with my GPS clock, which one is right and which one is wrong. A further goal here is to automatically synchronise clocks … but later.
A GPS clock is not worth spit if it can’t correctly convert UTC (from satellites) to local time every second of the day. The ability to jump from UTC to local time brings on an interesting set of issues. Depending on the time of day, day of the month, the year and so on, a “minor” adjustment in time-of-day might carry with it a change of day, week, month, year, decade, century, even changes to daylight savings time.
The date and time data types must take care of:
There are dates. These are shown on calendars. Forgive my ignorance of other cultures, but I’m assuming the Gregorian Calendar. There are 12 months. Every year starts on the 1st of January and is 365 or 366 days long. Since an earth-year is not a nice neat number, the calendar incorporates a set of rules for calculating leap years so that the mean length of the calendar is 365.2425 days. That’s pretty close to the duration of Earth’s actual orbit around the Sun.
There are times. These are shown on clocks. As much as I’m fascinated by decimal time it, disappointingly, hasn’t really taken off. So we’re stuck with the very un-metric 60 seconds to the minute, 60 minutes to the hour, 24 hours to the day clock that we all grew up with.
If you’re going to keep track of date and time, these two units must be bound together. As many systems do, I call this DateTime. As the seconds overflow into minutes, so do hours (time) overflow into days (date).
Now, back to the two kinds of time. If I want to adjust for daylight savings, I have to account for both kinds. The GPS satellite will tell me the DateTime, an instant in universal time. The moment you were born was an instant in time, regardless of which calendar you use. It happened. It happened exactly then. Likewise for future events; they will happen at an instant.
The other kind of time is an interval. 100 hours or 10,000 seconds won’t appear on a clock or calendar, but are both valid intervals of time.
When I want to correct for daylight savings I need to add an interval to an instant. When I need the difference between this time (instant) and that time (instant), I need the interval. Interestingly, I probably need the interval in some kind of units that are useful for the purpose. So I might need to say “how many seconds between this instant and that instant?”.
Ordinarily when I think about microcontroller programming, I think about simplicity. I want to use only a byte data type if that will do. An int wastes a byte. If I can get by on addition and subtraction and not have to use multiplication and division, all the better. Floating point numbers and arithmetic are just pure evil. But here, in the realm of time there is no choice but to embrace the complexity. Without a decimal clock and calendar (binary?) there’s just no simple way to deal with dates and times.
As far as I understand, computational solutions fall somewhere on a continuum. At one end a solution may require almost no space (memory) but use a lot of computation (processing), at the other end there’s a lot of space used and comparatively little computation. A microcontroller, such as those used for Arduino, have precious little of both memory and computational power. Designing an implementation of date and time data types for Arduino requires balance and some careful choices.
UNIX time is a simple number, the number of seconds elapsed since midnight at the start of 01-01-1970. This traditionally uses a 4-byte signed integer and can therefore cover the time range from somewhere in the year 1901 to 03:14:07 on 19th January 2038 (UTC) when, of course, the time value overflows and you have the “year 2038 problem” somewhat along the lines of the Y2K problem.
I found, searching the forum, mentions of using Time.h and Date.h. I searched the Arduino folder on my machine for these files and didn’t find them. I likewise can’t find reference to the date_t and time_t data types these provide access to.
The first reference I found to DateTime for Arduino is the playground page at http://playground.arduino.cc/Code/DateTime. This is superseded and now points here: http://playground.arduino.cc/Code/Time. This page likewise points to an update here: http://www.pjrc.com/teensy/td_libs_Time.html. Gee, they’re not making life easy are they?
Hmm. I’ve had a play with the files from http://www.pjrc.com/teensy/td_libs_Time.html and, while I can see how they work (kind of) I am put off by what appears to me a ‘pure C’ approach. Where’s the class definition? There isn’t one. There are global variables and a stack of functions. This looks weird to me although I know it’s only because I haven’t written ‘proper C’ much. I’m not interested to either. I’m trying to learn the Object Oriented Programming (OOP) paradigm and I’m not stopping now to go back to a procedural style. I also don’t want a mix of OOP and procedural code in my projects – although it can be perfectly functional, it’s just too much to learn and know at this stage. So as good and correct as this implementation may be I’m abandoning it just because it doesn’t suit my coding philosophy. It’s interesting to note, though, that the DateTime data is being stored as both a set of discrete values for Second, Minute, Hour … and as a UNIX Time. The conversion between the two being handled by the breakTime() and makeTime() functions. The same code is hosted on Google Code here : https://code.google.com/p/arduino-time/source/browse/trunk/Time.h
I was ready to give up and move on but then I found this: https://code.google.com/p/larduino/source/browse/trunk/lib/DateTime/?r=6. It seems Mauricio started with an earlier version of Michael’s code (above, from prjc.com) and somewhat modified it to include a class definition. This is more like what I’m aiming for. But, disturbingly, Mauricio’s code calculates a leap year using #define LEAP_YEAR(_year) ((_year%4)==0) which is plain wrong. So he’s going to have some fun with that. What other gremlins lie in this code I don’t know and I don’t want to find out. I see also that Mauricio is using the millis() function to keep track of time without a real clock source – I’m not interested in doing that either. Oops, I just noticed the last commit is four years old, I should have looked at that first!
OK. Having spent a few hours searching, downloading and trialling code I consider my research done. I’m going to assume there isn’t an up-to-date DateTime class defined for Arduino and go back to the previous one I made and start from there. The goal is to create a DateTime class that:
The repo is up: https://github.com/prawnhead/ArduinoDateTime. Development to continue until complete.
That is all.
Why no buttons? Because fundamentally any clock with buttons on it requires a human operator to set the time. Since every house has many clocks and every clock has buttons and every clock keeps slightly different time and it’s physically impossible to synchronise all your clocks you inevitably have different time displayed in each room of the house. Sorry for the run-on sentence. I’m sure there are some people for whom this is not a problem. But if you are a little OCD (and I say that admiringly, not mockingly) you’re regularly annoyed by the need to fix your clocks.
A clock with a GPS receiver sets it own time. Better yet, the time it keeps is based on the atomic-clock precision kept by GPS satellites. If this clock is inaccurate, it’s purely down to the programming I have done in the microcontroller. At least now I’ll know when any clock disagrees with my GPS clock, which one is right and which one is wrong. A further goal here is to automatically synchronise clocks … but later.
A GPS clock is not worth spit if it can’t correctly convert UTC (from satellites) to local time every second of the day. The ability to jump from UTC to local time brings on an interesting set of issues. Depending on the time of day, day of the month, the year and so on, a “minor” adjustment in time-of-day might carry with it a change of day, week, month, year, decade, century, even changes to daylight savings time.
The date and time data types must take care of:
- when days begin and end, rolling the date forward or back each time midnight is passed
- when daylight savings begins and ends, if it’s required
- which years are leap years so that the number of days in each month can be known
- ability to add and subtract any unit of time from existing date/times
- compare any pair of dates and times and determine which is earliest
- optionally, calculate the difference between any dates and times
There are dates. These are shown on calendars. Forgive my ignorance of other cultures, but I’m assuming the Gregorian Calendar. There are 12 months. Every year starts on the 1st of January and is 365 or 366 days long. Since an earth-year is not a nice neat number, the calendar incorporates a set of rules for calculating leap years so that the mean length of the calendar is 365.2425 days. That’s pretty close to the duration of Earth’s actual orbit around the Sun.
There are times. These are shown on clocks. As much as I’m fascinated by decimal time it, disappointingly, hasn’t really taken off. So we’re stuck with the very un-metric 60 seconds to the minute, 60 minutes to the hour, 24 hours to the day clock that we all grew up with.
If you’re going to keep track of date and time, these two units must be bound together. As many systems do, I call this DateTime. As the seconds overflow into minutes, so do hours (time) overflow into days (date).
Now, back to the two kinds of time. If I want to adjust for daylight savings, I have to account for both kinds. The GPS satellite will tell me the DateTime, an instant in universal time. The moment you were born was an instant in time, regardless of which calendar you use. It happened. It happened exactly then. Likewise for future events; they will happen at an instant.
The other kind of time is an interval. 100 hours or 10,000 seconds won’t appear on a clock or calendar, but are both valid intervals of time.
When I want to correct for daylight savings I need to add an interval to an instant. When I need the difference between this time (instant) and that time (instant), I need the interval. Interestingly, I probably need the interval in some kind of units that are useful for the purpose. So I might need to say “how many seconds between this instant and that instant?”.
Ordinarily when I think about microcontroller programming, I think about simplicity. I want to use only a byte data type if that will do. An int wastes a byte. If I can get by on addition and subtraction and not have to use multiplication and division, all the better. Floating point numbers and arithmetic are just pure evil. But here, in the realm of time there is no choice but to embrace the complexity. Without a decimal clock and calendar (binary?) there’s just no simple way to deal with dates and times.
As far as I understand, computational solutions fall somewhere on a continuum. At one end a solution may require almost no space (memory) but use a lot of computation (processing), at the other end there’s a lot of space used and comparatively little computation. A microcontroller, such as those used for Arduino, have precious little of both memory and computational power. Designing an implementation of date and time data types for Arduino requires balance and some careful choices.
UNIX time is a simple number, the number of seconds elapsed since midnight at the start of 01-01-1970. This traditionally uses a 4-byte signed integer and can therefore cover the time range from somewhere in the year 1901 to 03:14:07 on 19th January 2038 (UTC) when, of course, the time value overflows and you have the “year 2038 problem” somewhat along the lines of the Y2K problem.
I found, searching the forum, mentions of using Time.h and Date.h. I searched the Arduino folder on my machine for these files and didn’t find them. I likewise can’t find reference to the date_t and time_t data types these provide access to.
The first reference I found to DateTime for Arduino is the playground page at http://playground.arduino.cc/Code/DateTime. This is superseded and now points here: http://playground.arduino.cc/Code/Time. This page likewise points to an update here: http://www.pjrc.com/teensy/td_libs_Time.html. Gee, they’re not making life easy are they?
Hmm. I’ve had a play with the files from http://www.pjrc.com/teensy/td_libs_Time.html and, while I can see how they work (kind of) I am put off by what appears to me a ‘pure C’ approach. Where’s the class definition? There isn’t one. There are global variables and a stack of functions. This looks weird to me although I know it’s only because I haven’t written ‘proper C’ much. I’m not interested to either. I’m trying to learn the Object Oriented Programming (OOP) paradigm and I’m not stopping now to go back to a procedural style. I also don’t want a mix of OOP and procedural code in my projects – although it can be perfectly functional, it’s just too much to learn and know at this stage. So as good and correct as this implementation may be I’m abandoning it just because it doesn’t suit my coding philosophy. It’s interesting to note, though, that the DateTime data is being stored as both a set of discrete values for Second, Minute, Hour … and as a UNIX Time. The conversion between the two being handled by the breakTime() and makeTime() functions. The same code is hosted on Google Code here : https://code.google.com/p/arduino-time/source/browse/trunk/Time.h
I was ready to give up and move on but then I found this: https://code.google.com/p/larduino/source/browse/trunk/lib/DateTime/?r=6. It seems Mauricio started with an earlier version of Michael’s code (above, from prjc.com) and somewhat modified it to include a class definition. This is more like what I’m aiming for. But, disturbingly, Mauricio’s code calculates a leap year using #define LEAP_YEAR(_year) ((_year%4)==0) which is plain wrong. So he’s going to have some fun with that. What other gremlins lie in this code I don’t know and I don’t want to find out. I see also that Mauricio is using the millis() function to keep track of time without a real clock source – I’m not interested in doing that either. Oops, I just noticed the last commit is four years old, I should have looked at that first!
OK. Having spent a few hours searching, downloading and trialling code I consider my research done. I’m going to assume there isn’t an up-to-date DateTime class defined for Arduino and go back to the previous one I made and start from there. The goal is to create a DateTime class that:
- correctly stores and manipulates dates and times at least in the range of years from 1900 to 2100
- strikes a good balance between storage and computational costs
- has millisecond resolution (because GPS does)
- can localise UTC time including correctly applying daylight savings rules
- follows the OOP paradigm (“proper” C++)
The repo is up: https://github.com/prawnhead/ArduinoDateTime. Development to continue until complete.
That is all.
I've been searching for a library that can handle years past 2038 and likely well past 2100.. have you found anything on your journey? . Really need a 64 bit time_t ...
ReplyDelete