todbot / circuitpython-tricks

Some CircuitPython tricks, mostly reminders to myself
MIT License
577 stars 65 forks source link

Bug in calculating timezone offset in time data received from worldtimeapi.org #14

Closed jkvato closed 1 year ago

jkvato commented 1 year ago

In the section "Set RTC time from time service," the field raw_offset from the json received from the time service does not take daylight savings into consideration. However, utc_offset does. I was getting the wrong local time here in Southern California during daylight savings until I made the following modification:

print("Getting current time:")
response = requests.get("http://worldtimeapi.org/api/ip")
time_data = response.json()
tz_hour_offset = int(time_data['utc_offset'][0:3])
tz_min_offset = int(time_data['utc_offset'][4:6])
if (tz_hour_offset < 0):
    tz_min_offset *= -1
#unixtime = int(time_data['unixtime']) + int(time_data['raw_offset']) # << Incorrect offset during DST
unixtime = int(time_data['unixtime'] + (tz_hour_offset * 60 * 60)) + (tz_min_offset * 60)

print(time_data)
print("URL time: ", response.headers['date'])

rtc.RTC().datetime = time.localtime( unixtime ) # create time struct and set RTC with it

Here is the json data I received from worldtimeapi.org:

{
   'timezone': 'America/Los_Angeles',
   'utc_datetime': '2023-03-18T19:20:44.368793+00:00',
   'raw_offset': -28800,
   'client_ip': 'redacted',
   'dst_from': '2023-03-12T10:00:00+00:00',
   'unixtime': 1679167244,
   'utc_offset': '-07:00',
   'datetime': '2023-03-18T12:20:44.368793-07:00',
   'week_number': 11,
   'abbreviation': 'PDT',
   'day_of_year': 77,
   'day_of_week': 6,
   'dst': True,
   'dst_offset': 3600,
   'dst_until': '2023-11-05T09:00:00+00:00'
}

Here we can verify that raw_offset of -28800 = -8 hours, but utc_offset is -7 hours, which is correct during DST.

deilers78 commented 1 year ago

Hi jkvato,

I had the same problem, but then found another solution. In the requested json data, what is written at datetime is correct. That's why I used this.

# Raspberry Pico W
import board
import busio

import wifi
import socketpool
import adafruit_requests as requests
from secrets import secrets

import time
from adafruit_datetime import datetime
import adafruit_ds3231

i2c = busio.I2C(board.GP21, board.GP20)
rtc = adafruit_ds3231.DS3231(i2c)

print(f'Connecting to {secrets["ssid"]}')
wifi.radio.connect(secrets["ssid"], secrets["password"])
print(f'Connected to {secrets["ssid"]}')

pool = socketpool.SocketPool(wifi.radio)

def get_internet_time(socketpool: socketpool.SocketPool) -> time.struct_time:
    '''Return a struct_time object with current time from internet request.'''
    request = requests.Session(socketpool)
    response = request.get('http://worldtimeapi.org/api/ip').json()['datetime']
    _datetime = datetime.fromisoformat(response)
    return _datetime.timetuple()

rtc.datetime = get_internet_time(pool)
print(rtc.datetime)

Hope this helps you.

jkvato commented 1 year ago

Hi @deilers78,

That's a simple approach, for sure. Thanks for sharing!