[ntp:questions] Re: NTP, Unix and the 34-year itch (better read this)

Terje Mathisen terje.mathisen at hda.hydro.com
Fri Jan 23 08:53:13 UTC 2004

Thomas A. Horsley wrote:

>>Almost all computers of today have some means, such as a time-of-year
>>(TOY) chip, to set the system clock at power up or reboot surely within
>>within 34 years, but some embedded systems do not.
> Speaking of 34 years, my little NTPTime client has the following
> magic number built into it:
> #define JAN_1_1900 2415021L
> Which is the julian day number of the base date for NTP timestamps.
> Obviously once we roll into a new 64 bit chunk of time, I'll need a
> new base date.
> Anyone know what the actual constant is for the next date coming up?

Easily calculated:

   2^32 / 86400 = 49710
   2^32 % 86400 = 23296

I.e. the second NTP epoch starts at julian day (241502+49710), at 06:28:16.

There are two easy ways to handle this conversion yourself:

a) Using 64-bit (or at least more than 32) math, define a window 
starting about now, and check that a given NTP timestamp is not less 
than this. If it is, add 2^32 seconds to the count until the time is 
indeed at least in year 2004.

b) Split the ntp timestamp into two parts: julian day and seconds into 
this day. This does not require any extended precision integer math.

Check that the resulting day number is in range, if not go into a loop 
similar to (a), but this time add 49710 to the day number and 23296 to 
the seconds count. Each time the seconds count is >= 86400, subtract 
that and increment the day number.

However, the easiest method (by far!), and the one I have submitted as a 
patch to ntp, is to simply say:

My OS has to support dates beyond the unix epoch end before it makes any 
sense for me to do so, right?

In which case I can simply cast the ntp_seconds count to a unix time_t 
value, and adjust for the base offset of (1970-1900).

The next step is to use the library calls (localtime/gmtime) to convert 
this to printable format, and check that the year field is believable:

If time_t is 32-bit, and the OS supports epoch wraparound, then it must 
employ windowing internally, and everything will 'simply work'.

If it does not, then the only alternative for a working OS is to use an 
extended-precision time_t, which means that you can simply add 2^32 
seconds to it and verify that you do get into range:

I.e. like this, (typed from memory):

   time_t unix_seconds = (time_t) ntp_seconds - (
          (mjd1970 - mjd1900) * 86400);

   struct tm *mytm = gmtime(unix_seconds);

   if (mytm->tm_year < 104) {  // 2004 - 1900 baseline
     time_t temp = unix_seconds;
     do {
       temp += ((time_t) 65536) << 16;
       if (temp == unix_second) return ERROR_TIME_T_IS_32_BIT;
       mytm = gmtime(temp);
     } while (mytm->tm_year < 104);


- <Terje.Mathisen at hda.hydro.com>
"almost all programming can be viewed as an exercise in caching"

More information about the questions mailing list