-
-
Save Beormund/f0f39c72e066da497f6308d1964c9627 to your computer and use it in GitHub Desktop.
import utime | |
# hemisphere [0 = Northern, 1 = Southern] | |
# week [0 = last week of month, 1..4 = first..fourth] | |
# month [1 = January; 12 = December] | |
# weekday [0 = Monday; 6 = Sunday] (day of week) | |
# hour (hour at which dst/std changes) | |
# timezone [-780..780] (offset from UTC in MINUTES - 780min / 60min=13hrs) | |
class Policy: | |
def __init__(self, hemisphere, week, month, weekday, hour, timezone): | |
if hemisphere not in [0,1]: raise ValueError('hemisphere must be 0..1') | |
if week not in range (0,5): raise ValueError('week must be 0 or 1..4') | |
if month not in range (1,13): raise ValueError('month must be 1..12') | |
if weekday not in range(0,7): raise ValueError('weekday must be 0..6') | |
if hour not in range(0,24): raise ValueError('hour must be 0..23') | |
if timezone not in range(-780,781): raise ValueError('timezone must be -780..780') | |
self.hemisphere = hemisphere | |
self.week = week | |
self.month = month | |
self.weekday = weekday | |
self.hour = hour | |
self.timezone = timezone | |
def __str__(self): | |
self.days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sum'] | |
self.abbr = ['last', 'first', 'second', 'third', 'fourth'] | |
self.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] | |
return ('Daylight Saving Policy: daylight saving {} on the {} {} of {} at {:02}:00 hrs (UTC{})').format( | |
self.prefix, | |
self.abbr[self.week], | |
self.days[self.weekday], | |
self.months[self.month-1], | |
self.hour, | |
'' if not self.timezone else '{:+1}'.format(self.timezone/60) | |
) | |
class StandardTimePolicy(Policy): | |
def __init__(self, hemisphere, week, month, weekday, hour, timezone): | |
self.prefix = 'ends' | |
super(StandardTimePolicy, self).__init__(hemisphere, week, month, weekday, hour, timezone) | |
class DaylightSavingPolicy(Policy): | |
def __init__(self, hemisphere, week, month, weekday, hour, timezone): | |
self.prefix = 'starts' | |
super(DaylightSavingPolicy, self).__init__(hemisphere, week, month, weekday, hour, timezone) | |
class DaylightSaving: | |
def __init__(self, dstp, stdp): | |
self.dstp = dstp | |
self.stdp = stdp | |
print(self.dstp) | |
print(self.stdp) | |
def isleapyear(self, year): | |
return ( year % 4 == 0 and year % 100 != 0) or year % 400 == 0 | |
def dayofmonth(self, week, month, weekday, day, year): | |
# Get the first or last day of the month | |
t = utime.mktime((year, month, day, 0, 0, 0, 0, 0)) | |
# Get the weekday of the first or last day of the month | |
d = utime.localtime(t)[6] | |
increment = lambda d: d + 1 if d < 6 else 0 | |
decrement = lambda d: d - 1 if d > 0 else 6 | |
while d != weekday: | |
# Increment if start of month else decrement | |
day = day + 1 if week else day - 1 | |
d = increment(d) if week else decrement(d) | |
# Increment day of month by number of weeks | |
return day + (week - 1) * 7 if week else day | |
def nthweekday(self, week, month, weekday, hour, year): | |
monthlength = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] | |
if self.isleapyear(year): | |
monthlength[1] = 29 | |
day = 1 if week else monthlength[month-1] | |
day = self.dayofmonth(week, month, weekday, day, year) | |
return utime.mktime((year, month, day, hour, 0, 0, 0, 0)) | |
def gettfromp(self, p, year): | |
return self.nthweekday(p.week, p.month, p.weekday, p.hour, year) | |
def localtime(self, utc): | |
print(f'UTC: {self.ftime(utc)}') | |
year = utime.gmtime(utc)[0] | |
dst = self.gettfromp(self.dstp, year) | |
print(f'Daylight Saving Starts: {self.ftime(dst)}') | |
std = self.gettfromp(self.stdp, year) | |
print(f'Daylight Saving Ends: {self.ftime(std)}') | |
saving = False | |
if self.stdp.hemisphere: | |
# Sourthern hemisphere | |
saving = utc > dst or utc < std | |
else: | |
# Northern hemisphere | |
saving = utc > dst and utc < std | |
offset = self.dstp.timezone if saving else self.stdp.timezone | |
local = utc + (offset * 60) | |
print(f'Local: {self.ftime(local)}') | |
return utc + (offset * 60) | |
def ftime(self, t): | |
year, month, day, hour, minute, second, ms, dayinyear = utime.localtime(t) | |
return "{:4}-{:02}-{:02}T{:02}:{:02}:{:02}".format(year, month, day, hour, minute, second) |
This can then be used in ntptime.py like so:
import socket
import struct
from utime import gmtime
import daylightsaving as dls
# (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60
# (date(1970, 1, 1) - date(1900, 1, 1)).days * 24*60*60
NTP_DELTA = 3155673600 if gmtime(0)[0] == 2000 else 2208988800
DS = dls.DaylightSaving(
dls.DaylightSavingPolicy(0, 0, 3, 6, 1, 60),
dls.StandardTimePolicy(0, 0, 10, 6, 2, 0)
)
# The NTP host can be configured at runtime by doing: ntptime.host = 'myhost.org'
host = "pool.ntp.org"
def time(hrs_offset=0): # Local time offset in hrs relative to UTC
NTP_QUERY = bytearray(48)
NTP_QUERY[0] = 0x1B
addr = socket.getaddrinfo(host, 123)[0][-1]
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.settimeout(1)
res = s.sendto(NTP_QUERY, addr)
msg = s.recv(48)
finally:
s.close()
val = struct.unpack("!I", msg[40:44])[0]
return val - NTP_DELTA + hrs_offset * 3600
# There's currently no timezone support in MicroPython, and the RTC is normally set in UTC time.
# Use daylightsaving module to calculate local time.
def settime():
import machine
t = time()
tm = gmtime(DS.localtime(t))
machine.RTC().datetime((tm[0], tm[1], tm[2], tm[6] + 1, tm[3], tm[4], tm[5], 0))
Thank you for providing this code. Is not hrs_offset
redundant now?
Would suggest submitting this to https://awesome-micropython.com/#ntp as I am sure many people would find this useful.
Thanks for this great gist! The text of error should be corrected in line: https://gist.github.com/Beormund/f0f39c72e066da497f6308d1964c9627#file-daylightsaving-py-L16
Thanks for this great gist! The text of error should be corrected in line: https://gist.github.com/Beormund/f0f39c72e066da497f6308d1964c9627#file-daylightsaving-py-L16
Done - thanks for spotting.
Hi @Beormund
I found your post about the UDP issue on the micropython forum and it looks like you were also trying to get the fauxmo / echo integration to work on the Pico W, this is exactly what I am trying to accomplish in a hobby project now, did you get it to work on the Pico W ? if yes, do you mind in sharing how you did it? I am stuck at this right now :(
Would you mind sharing your code?? Thank you!
GB Example
Output: