- 
      
- 
        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: