micropython / micropython-lib

Core Python libraries ported to MicroPython
Other
2.3k stars 981 forks source link

ntptime wrong NTP_DELTA #786

Closed DracoTomes closed 5 months ago

DracoTomes commented 6 months ago

Hi,

the NTP_DELTA values for the different epochs seem the wrong way around.

The following happens on my ESP32:

import ntptime, utime

print(utime.gmtime(0)[0])
print(ntptime.time())
2000
757785416

This timestamp is sometime in 1994.

If I switch the deltas around I get the correct timestamp from ntptime.

2000
1704471142
if EPOCH_YEAR == 2000:
    # (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60
    #old NTP_DELTA = 3155673600
    NTP_DELTA = 2208988800
elif EPOCH_YEAR == 1970:
    # (date(1970, 1, 1) - date(1900, 1, 1)).days * 24*60*60
    #old NTP_DELTA = 2208988800
    NTP_DELTA = 3155673600

But this doesn't make sense intuitively... Somehow 1970-1900 needs to be bigger than 2000-1900.

Maybe something about the port is off? I'm running

MicroPython infos: (sysname='esp32', nodename='esp32', release='1.22.0', version='v1.22.0 on 2023-12-27', machine='Generic ESP32 module with ESP32')

on a ESP-32-Wroom-32 from AZ-Delivery.

Any tips are appreciated.

DracoTomes commented 6 months ago

Actually it seems even weirder. The only way I get it to work is by copying the time() function out of the ntptime.py file. This is what I initially tried as a workaround but I've now installed the raw ntptime.py instead of the ntptime.mpy and switched the values there but it still won't work - switching the values in the external file does nothing at all.

So if I run this script:

import ntptime, utime, machine

import utime

try:
    import usocket as socket
except:
    import socket
try:
    import ustruct as struct
except:
    import struct

# The NTP host can be configured at runtime by doing: ntptime.host = 'myhost.org'
host = "pool.ntp.org"
# The NTP socket timeout can be configured at runtime by doing: ntptime.timeout = 2
timeout = 3

def time():
    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(timeout)
        res = s.sendto(NTP_QUERY, addr)
        msg = s.recv(48)
    finally:
        s.close()
    val = struct.unpack("!I", msg[40:44])[0]

    EPOCH_YEAR = utime.gmtime(0)[0]
    if EPOCH_YEAR == 2000:
        # (date(2000, 1, 1) - date(1900, 1, 1)).days * 24*60*60
        #NTP_DELTA = 3155673600
        NTP_DELTA = 2208988800
    elif EPOCH_YEAR == 1970:
        # (date(1970, 1, 1) - date(1900, 1, 1)).days * 24*60*60
        #NTP_DELTA = 2208988800
        NTP_DELTA = 3155673600
    else:
        raise Exception("Unsupported epoch: {}".format(EPOCH_YEAR))

    return val - NTP_DELTA

print(utime.gmtime(0)[0])
print(machine.RTC().datetime())
print(ntptime.time())
print(time())

I get

2000
(2024, 1, 6, 5, 0, 21, 9, 678042)
757812070
1704496870

Neither ntptime.time() nor ntptime.settime() uses the right epoch.

DracoTomes commented 5 months ago

Ok I let this sit a bit and then had another go at it. It seems the machine.RTC() clock is set correctly using the default, and reads correctly using ntptime.

Weirdly, utime.time() seems about 30 years behind. Actually it's exactly 3155673600 - 2208988800 seconds behind, which is exactly the difference between the 1970 and the 2000 epoch.

So it seems this line in the documentation is off:

Returns the number of seconds, as an integer, since the Epoch, assuming that underlying RTC is set and maintained as described above.

since the RTC is set, but the returned epoch is wrong. Well... actually it's not really wrong, since it assumes the epoch 2000.

I've since found quite a few posts also struggling with the time, this one even solving my issue, but me somehow missing the solution late at night.

At this point I don't know whether this is an genuine issue with the documentation, that quite a few people seem to run into troubles with it or simply my incompetence and not RTFM (even though I did try my best).