I started work on a potential solution for automating DST on my Photon’s last week, and I’ve gotten far enough to know that all of the puzzle pieces exist. Before I go any further I want to do a sanity check to see how this solution sounds to others …
Basic Concepts.
-
Store the IANA defined time zone ID (aka Olson Name) in the Photon’s eprom. (Examples of these IDs include:“America/New_York”, “Asia/Katmandu”, “Austrailia/Sydney”, “Europe/Helsinki”, … etc.) IANA periodically publishes the “authoritative” time zone database used by OS and Compiler vendors, and the IANA defined zone ID unlocks the DST rules in that database.
-
Maintain a ".JSON file (for each time zone) on a server that the Photon can download. This file would contain the standard offset, current zone offset, DST offset, DST (true/false), and DST transition information for all time zones. The Transition information would include the transition date in epoch(seconds) and ISO8601 format along with the new time zone offset, DST offset, and DST(true/false) settings.
-
On boot-up, Photon firmware would execute a tzQuery() function get the JSON file for the time-zone recorded in the eprom (or UTC by default). The function would then:
a) parse the JSON file
b) update the Photon’s current offsets and DST settings
c) store (or update) the settings and transition information in eprom -
In the firmware “loop”, a function would be executed to perform the DST transition when current-time >= the DST transition-time. In normal operation, this would add very little overhead to the loop. The function would merely exit after determining that the transition time is still in the future. When the transition time arrives, the function would update the Photon’s offset and DST settings, and it would move the transition settings to current settings in eprom. To avoid all Photons hitting the server at the same time, the function would also select a random time to retrieve fresh JSON data.
To Date
The biggest question I had going into this was: “How do we generate the JSON data?”. It turns out that Java 8 introduced a lot of new functionality related to zone rules. Armed with this functionality, I was able to generate the JSON data in 60 lines of loosely packed code. At present all of the data resides in a single file, but it will be easy to generate separate files after I/we determine the best download method … HTTP?.
Here is a subset of the JSON data I was able to generate:
America/Kentucky/Louisville = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Kentucky/Louisville],"Offset":-5.0,"DST":false"}}
America/Kentucky/Monticello = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Kentucky/Monticello],"Offset":-5.0,"DST":false"}}
America/Knox_IN = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/Knox_IN],"Offset":-6.0,"DST":false"}}
America/Kralendijk = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/La_Paz = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Lima = {"StdOffset":-5.0,"CurrentOffset":-5.0,"DST":false}}
America/Los_Angeles = {"StdOffset":-8.0,"CurrentOffset":-7.0,"DST":true,"NextTransition":{,"EpochSeconds":1509872400,"ISO8601":"2017-11-05T01:00-08:00[America/Los_Angeles],"Offset":-8.0,"DST":false"}}
America/Louisville = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Louisville],"Offset":-5.0,"DST":false"}}
America/Lower_Princes = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Maceio = {"StdOffset":-3.0,"CurrentOffset":-3.0,"DST":false}}
America/Managua = {"StdOffset":-6.0,"CurrentOffset":-6.0,"DST":false}}
America/Manaus = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Marigot = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Martinique = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Matamoros = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/Matamoros],"Offset":-6.0,"DST":false"}}
America/Mazatlan = {"StdOffset":-7.0,"CurrentOffset":-6.0,"DST":true,"NextTransition":{,"EpochSeconds":1509264000,"ISO8601":"2017-10-29T01:00-07:00[America/Mazatlan],"Offset":-7.0,"DST":false"}}
America/Mendoza = {"StdOffset":-3.0,"CurrentOffset":-3.0,"DST":false}}
America/Menominee = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/Menominee],"Offset":-6.0,"DST":false"}}
America/Merida = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509260400,"ISO8601":"2017-10-29T01:00-06:00[America/Merida],"Offset":-6.0,"DST":false"}}
America/Metlakatla = {"StdOffset":-9.0,"CurrentOffset":-8.0,"DST":true,"NextTransition":{,"EpochSeconds":1509876000,"ISO8601":"2017-11-05T01:00-09:00[America/Metlakatla],"Offset":-9.0,"DST":false"}}
America/Mexico_City = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509260400,"ISO8601":"2017-10-29T01:00-06:00[America/Mexico_City],"Offset":-6.0,"DST":false"}}
America/Miquelon = {"StdOffset":-3.0,"CurrentOffset":-2.0,"DST":true,"NextTransition":{,"EpochSeconds":1509854400,"ISO8601":"2017-11-05T01:00-03:00[America/Miquelon],"Offset":-3.0,"DST":false"}}
America/Moncton = {"StdOffset":-4.0,"CurrentOffset":-3.0,"DST":true,"NextTransition":{,"EpochSeconds":1509858000,"ISO8601":"2017-11-05T01:00-04:00[America/Moncton],"Offset":-4.0,"DST":false"}}
America/Monterrey = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509260400,"ISO8601":"2017-10-29T01:00-06:00[America/Monterrey],"Offset":-6.0,"DST":false"}}
America/Montevideo = {"StdOffset":-3.0,"CurrentOffset":-3.0,"DST":false}}
America/Montreal = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Montreal],"Offset":-5.0,"DST":false"}}
America/Montserrat = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Nassau = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Nassau],"Offset":-5.0,"DST":false"}}
America/New_York = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/New_York],"Offset":-5.0,"DST":false"}}
America/Nipigon = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Nipigon],"Offset":-5.0,"DST":false"}}
America/Nome = {"StdOffset":-9.0,"CurrentOffset":-8.0,"DST":true,"NextTransition":{,"EpochSeconds":1509876000,"ISO8601":"2017-11-05T01:00-09:00[America/Nome],"Offset":-9.0,"DST":false"}}
America/Noronha = {"StdOffset":-2.0,"CurrentOffset":-2.0,"DST":false}}
America/North_Dakota/Beulah = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/North_Dakota/Beulah],"Offset":-6.0,"DST":false"}}
America/North_Dakota/Center = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/North_Dakota/Center],"Offset":-6.0,"DST":false"}}
America/North_Dakota/New_Salem = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/North_Dakota/New_Salem],"Offset":-6.0,"DST":false"}}
America/Ojinaga = {"StdOffset":-7.0,"CurrentOffset":-6.0,"DST":true,"NextTransition":{,"EpochSeconds":1509868800,"ISO8601":"2017-11-05T01:00-07:00[America/Ojinaga],"Offset":-7.0,"DST":false"}}
America/Panama = {"StdOffset":-5.0,"CurrentOffset":-5.0,"DST":false}}
America/Pangnirtung = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Pangnirtung],"Offset":-5.0,"DST":false"}}
America/Paramaribo = {"StdOffset":-3.0,"CurrentOffset":-3.0,"DST":false}}
America/Phoenix = {"StdOffset":-7.0,"CurrentOffset":-7.0,"DST":false}}
America/Port-au-Prince = {"StdOffset":-5.0,"CurrentOffset":-4.0,"DST":true,"NextTransition":{,"EpochSeconds":1509861600,"ISO8601":"2017-11-05T01:00-05:00[America/Port-au-Prince],"Offset":-5.0,"DST":false"}}
America/Port_of_Spain = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Porto_Acre = {"StdOffset":-5.0,"CurrentOffset":-5.0,"DST":false}}
America/Porto_Velho = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Puerto_Rico = {"StdOffset":-4.0,"CurrentOffset":-4.0,"DST":false}}
America/Punta_Arenas = {"StdOffset":-3.0,"CurrentOffset":-3.0,"DST":false}}
America/Rainy_River = {"StdOffset":-6.0,"CurrentOffset":-5.0,"DST":true,"NextTransition":{,"EpochSeconds":1509865200,"ISO8601":"2017-11-05T01:00-06:00[America/Rainy_River],"Offset":-6.0,"DST":false"}}
Europe/Isle_of_Man = {"StdOffset":0.0,"CurrentOffset":1.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T01:00Z[Europe/Isle_of_Man],"Offset":0.0,"DST":false"}}
Europe/Istanbul = {"StdOffset":3.0,"CurrentOffset":3.0,"DST":false}}
Europe/Jersey = {"StdOffset":0.0,"CurrentOffset":1.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T01:00Z[Europe/Jersey],"Offset":0.0,"DST":false"}}
Europe/Kaliningrad = {"StdOffset":2.0,"CurrentOffset":2.0,"DST":false}}
Europe/Kiev = {"StdOffset":2.0,"CurrentOffset":3.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T03:00+02:00[Europe/Kiev],"Offset":2.0,"DST":false"}}
Europe/Kirov = {"StdOffset":3.0,"CurrentOffset":3.0,"DST":false}}
Europe/Lisbon = {"StdOffset":0.0,"CurrentOffset":1.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T01:00Z[Europe/Lisbon],"Offset":0.0,"DST":false"}}
Europe/Ljubljana = {"StdOffset":1.0,"CurrentOffset":2.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T02:00+01:00[Europe/Ljubljana],"Offset":1.0,"DST":false"}}
Europe/London = {"StdOffset":0.0,"CurrentOffset":1.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T01:00Z[Europe/London],"Offset":0.0,"DST":false"}}
Europe/Luxembourg = {"StdOffset":1.0,"CurrentOffset":2.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T02:00+01:00[Europe/Luxembourg],"Offset":1.0,"DST":false"}}
Europe/Madrid = {"StdOffset":1.0,"CurrentOffset":2.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T02:00+01:00[Europe/Madrid],"Offset":1.0,"DST":false"}}
Europe/Malta = {"StdOffset":1.0,"CurrentOffset":2.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T02:00+01:00[Europe/Malta],"Offset":1.0,"DST":false"}}
Europe/Mariehamn = {"StdOffset":2.0,"CurrentOffset":3.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T03:00+02:00[Europe/Mariehamn],"Offset":2.0,"DST":false"}}
Europe/Minsk = {"StdOffset":3.0,"CurrentOffset":3.0,"DST":false}}
Europe/Monaco = {"StdOffset":1.0,"CurrentOffset":2.0,"DST":true,"NextTransition":{,"EpochSeconds":1509238800,"ISO8601":"2017-10-29T02:00+01:00[Europe/Monaco],"Offset":1.0,"DST":false"}}
Europe/Moscow = {"StdOffset":3.0,"CurrentOffset":3.0,"DST":false}}