The Arduino NTP implementation is rather naive in some respects. It basically just grabs the time in seconds from the raw packet, and then converts it to Unix time via subtraction. How would it be possible to create an implementation that is safe from at least this next NTP rollover (due in 2036)? I'd rather not design something that might suddenly fail in strange ways in a decade or two when I have forgotten why it might do so (but it's not horribly important if it's not easily fixed).
If it can make it to 2100, I'd have larger problems (the RTC would crap itself), so just this rollover is fine.
The existing implementation (https://github.com/arduino-libraries/NTPClient/blob/master/NTPClient.cpp):
unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]);
unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
this->_currentEpoc = secsSince1900 - SEVENZYYEARS;
1 Answer 1
This implementation is perfectly fine. The calculations are done with
unsigned numbers, so they are naturally rollover safe. The day NTP time
rolls over, secsSince1900
will become a very small number, and
subtracting SEVENZYYEARS
from it will cause an extra roll over. Owing
to the rules of modular arithmetics, these two rollovers compensate and
you are guaranteed to get the correct result modulo
232 s.
In the end, you get a Unix time as an unsigned 32-bit integer. Unix time is traditionally signed, which makes the 32-bit representation roll over in January 2038. The authors of this library have instead chosen to represent it as an unsigned number, which means it will roll over in February 2106.
-
Huh. That's actually rather cool. I knew of a similar method for millis(), but didn't know this worked for NTP as well. Thanks!user47164– user471642019年05月30日 23:09:01 +00:00Commented May 30, 2019 at 23:09
-
@RDragonrydr: carry propagates from low to high in addition/subtraction. The low bits of the result do not depend on any higher bit positions, so if you want a 32-bit result it's always safe to truncate your inputs instead of doing a 64-bit subtract and truncating the result. Same for left shift, or the low
N
bits of anN x N
multiply. See also Which 2's complement integer operations can be used without zeroing high bits in the inputs, if only the low part of the result is wanted?Peter Cordes– Peter Cordes2019年05月31日 06:21:47 +00:00Commented May 31, 2019 at 6:21 -
I knew that, but I didn't know that the Arduino's method would manage to handle the rollover in NTP, not quite accidentally, but without needing to detect it per se. I figured that, since the time is an absolute delta past a certain start date, that any rollover would HAVE to give the wrong time once it occurred.user47164– user471642019年05月31日 18:31:04 +00:00Commented May 31, 2019 at 18:31