2

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;
5

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! – RDragonrydr May 30 '19 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 an N 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 May 31 '19 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. – RDragonrydr May 31 '19 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.