Notifications
Clear all

UTC to standard or daylight times conversion

10 Posts
2 Users
1 Likes
1,258 Views
jfabernathy
(@jfabernathy)
Estimable Member
Joined: 2 years ago
Posts: 148
Topic starter  

I have a CircuitPython program running on an Adafruit Magtag board which has an eInk display. It displays some data it pulls from a database where the data is timestamped in UTC.  When I wrote the program it was winter time and to get the timezone right I subtracted 5 from my UTC time to get EST. Now it's spring and we are in EDT and I need to change the time to -4. I hate to have to do this twice a year.  I can't use any Linux functions since this is CP without an O/S.

Any ideas?

 

If your code won't compile, have another glass of bourbon. Eventual the problem will be solved.


   
Quote
MadMisha
(@madmisha)
Reputable Member
Joined: 3 years ago
Posts: 346
 

I am assuming you are using the built in RTC of the ESP32-S2. I was looking at ways to use gmtime and local time but then I ran across this library and realized it looks so much easier. Timezone Conversion Library  

 

I hope this helps. But I do have to ask, how fast is the updating on the e-ink display on that unit?


   
ReplyQuote
jfabernathy
(@jfabernathy)
Estimable Member
Joined: 2 years ago
Posts: 148
Topic starter  

@madmisha Not using the RTC as I don't care what time it is. I care about the time in the database for the data I'm displaying. The only thing I have running an an ESP32-S2 is CircuitPython. Arduino and ESP-IDF are really flaky to me on the ESP32-S2.

I really haven't looked at the update time on the e-ink display of the Magtag.  My application wakes from deep sleep every 5 minutes and retrieves the latest temp and humidity from the influxDB where it's being recording and displays the latest readings. 

If your code won't compile, have another glass of bourbon. Eventual the problem will be solved.


   
ReplyQuote
MadMisha
(@madmisha)
Reputable Member
Joined: 3 years ago
Posts: 346
 

I wrote that and forgot in the middle of looking things up that you said you were using CircuitPython. Since I have never used it, I can only suggest things in Python that I hope will work. In Python you can use tzinfo and timezone to account for offsets from UTC and tzinfo can account for DST. Both are a part of the datetime library. Python Datetime reference link  If you actually can't use that one then it looks like Adafruit created their own library for circuit python that is based off of the full Python library. adafruit-circuitpython-datetime It does list the ESP32-S2 as being supported.

 

Edit:

Also, this guy made his own were he set the rules about when DST begins and ends. You could do the same with your UTC timestamp and if the date is between 2 dates then make this correction instead. https://emergent.unpythonic.net/01595021837

Edit 2:

The real pain is that fact that the make it something like the second Sunday of a certain month for the time change. That seems like the hardest bit. But, lets say you make a start list with tuples and a stop list of tuples. Each contains the year and date it starts or stops. Then a basic IF statement could be used based off the parsed data you get from the timestamp. Go at least 10 years in the future.


   
ReplyQuote
MadMisha
(@madmisha)
Reputable Member
Joined: 3 years ago
Posts: 346
 

@jfabernathy

I came up with this

 

dstStart = {2021 : 142022 : 132023 : 122024 : 102025 : 92026 : 82027 : 142028 : 122029 : 112030 : 102031 : 9}

dstStop = {2021 : 72022 : 62023 : 52024 : 32025 : 22026 : 12027 : 72028 : 52029 : 42030 : 32031 : 2}

parsedUTC = [20211171320#Year, month, day, hours(24 hr format)

def noDst():
time = (parsedUTC[3] -500) #or just 5 depending on how you parse it
print('No DST')

def dst():
time = (parsedUTC[3] -400) #or just 4 depending on how you parse it
print('DST Used')

if int(parsedUTC[1]) in range(3,12):

    if int(parsedUTC[1]) == 3 and int(parsedUTC[2]) >= int(dstStart[parsedUTC[0]])+1 or int(parsedUTC[1]) == 3 and int(parsedUTC[2]) == int(dstStart[parsedUTC[0]]) and int(parsedUTC[3]) >= 200 :

        dst()

    elif int(parsedUTC[1]) == 11 and int(parsedUTC[2]) <= int(dstStop[parsedUTC[0]]) :

        if int(parsedUTC[2]) <= int(dstStop[parsedUTC[0]])-1 or int(parsedUTC[2]) == int(dstStop[parsedUTC[0]]) and int(parsedUTC[3]) <= 199 :

            dst()

        else:

            noDst()

    elif int(parsedUTC[1]) in range(4,11):

        dst()

    else:

        noDst()

else:

    noDst()

 

It does not need a library but the thing that bugs me is the fact that it does not build the dates itself, but stores hard values. I mean maybe it will outlive the IOT device and you can always include more years but to rebuild it every time it wakes from sleep also sounds terrible and EEPROM reads doesn't sound good either. Maybe this dictionary approach isn't that bad.

But at least that code should keep the correct time up until the exact time change at 2am. I also have no clue as to how you are parsing the UTC and I'm sure converting to int could be done less. I will be using something similar in a few months so if you do use this route, or any other, please let me know if this will work.


   
ReplyQuote
jfabernathy
(@jfabernathy)
Estimable Member
Joined: 2 years ago
Posts: 148
Topic starter  

@madmisha Thanks, I thought about hard coding it just like your example. Great minds think alike 😀 😀 

 

If your code won't compile, have another glass of bourbon. Eventual the problem will be solved.


   
ReplyQuote
MadMisha
(@madmisha)
Reputable Member
Joined: 3 years ago
Posts: 346
 

@jfabernathy

 

Although, now that switch case is coming to Python soon, that might change how I would do this a little.

I don't know how exactly that will work on circuit python/when it will be implemented there.


   
ReplyQuote
jfabernathy
(@jfabernathy)
Estimable Member
Joined: 2 years ago
Posts: 148
Topic starter  

I came up with an algorithm that seems to work for me for EST to EDT using your dictionary approach.  What I have attached is a test program that I used to check out the corner cases.  It's fine for the spring forward, not sure if on the fallback if you repeat the 1-2 or at 3pm you go back to 2 which is what this code does.  Either way I'm asleep.

I wish I knew how to attach code so you could read it without losing the indents. Everytime I paste in the code it loses it's indention which is 4 spaces for each tab.

Here's the file renames to .txt.

If your code won't compile, have another glass of bourbon. Eventual the problem will be solved.


   
MadMisha reacted
ReplyQuote
MadMisha
(@madmisha)
Reputable Member
Joined: 3 years ago
Posts: 346
 

@jfabernathy

I believe both time changes happen at 2am, so technically your hours could say

        if month == 3 and day >= int(dstStart[year])+1 or month == 3 and day == int(dstStart[year]) and hour >= 2:
            timezoneOffset = 4
            
        elif month == 11 and day <= int(dstStop[year]):
            if day <= int(dstStop[year])-1 or day == int(dstStop[year]) and hour <= 2:

 

and it would be correct although the stop day would happen at 3:00am. But since you're asleep, I guess it won't matter. 6 and 7 am seems to me like it might give you a little confusion if you woke up early.

 

As for keeping the tabbing, I hit enter a bunch of time the to make sure there is plenty of space after what I paste and then paste. Sometimes it automatically detects it as code(as in it just did above, I don't remember seeing that happen before).


   
ReplyQuote
jfabernathy
(@jfabernathy)
Estimable Member
Joined: 2 years ago
Posts: 148
Topic starter  
Posted by: @madmisha

@jfabernathy

I believe both time changes happen at 2am, so technically your hours could say

        if month == 3 and day >= int(dstStart[year])+1 or month == 3 and day == int(dstStart[year]) and hour >= 2:
            timezoneOffset = 4
            
        elif month == 11 and day <= int(dstStop[year]):
            if day <= int(dstStop[year])-1 or day == int(dstStop[year]) and hour <= 2:

 

and it would be correct although the stop day would happen at 3:00am. But since you're asleep, I guess it won't matter. 6 and 7 am seems to me like it might give you a little confusion if you woke up early.

 

As for keeping the tabbing, I hit enter a bunch of time the to make sure there is plenty of space after what I paste and then paste. Sometimes it automatically detects it as code(as in it just did above, I don't remember seeing that happen before).

I found that I needed the time condition to check against 6 or 7 because the time I'm comparing against is UTC so when I see 2 in the hour, it's not EST or EDT 2.  

Basically, the times I'm reading from the database are in UTC but they represent times of events recorded locally in the EST/EDT.  So if I was up at 2AM on the day when daylight saving time started, I see the raw time in UTC as 7am. i.e. the -5 delta that becomes a -4 delta at that precise hour.

This is really an overkill project.  All I need to see the current data is to open a browser on any device in my house and look at the grafana chart I created for all sites temp and humidity data that is being recorded every minute.  grafana is part of my IOTstack and is running on Rasberry Pi OS so it takes care of the time for me. 

However, I had an Adafruit Magtag ESP32-S2 with e-ink display that needed something to do to earn it's keep. So it wakes from deep sleep every 5 minutes and uses http to submit a JSON get to my Nodered hub that causes it to send a query to my influxdb to retrieve the latest reading for one of my remote locations.

The only way I can think of to make it more complicated is to build a robot to ride around and collect the data. 😀  

the project is on github so I can keep from losing all the work:

https://github.com/jfabernathy/MQTT-humidity-and-temperature

If your code won't compile, have another glass of bourbon. Eventual the problem will be solved.


   
ReplyQuote