diff options
Diffstat (limited to 'libntp/clocktime.c')
-rw-r--r-- | libntp/clocktime.c | 211 |
1 files changed, 114 insertions, 97 deletions
diff --git a/libntp/clocktime.c b/libntp/clocktime.c index 371859cda21c..c1a3ba09c0af 100644 --- a/libntp/clocktime.c +++ b/libntp/clocktime.c @@ -2,131 +2,148 @@ * clocktime - compute the NTP date from a day of year, hour, minute * and second. */ +#include <config.h> #include "ntp_fp.h" #include "ntp_unixtime.h" #include "ntp_stdlib.h" +#include "ntp_calendar.h" /* - * Hacks to avoid excercising the multiplier. I have no pride. + * We check that the time be within CLOSETIME seconds of the receive + * time stamp. This is about 4 hours, which hopefully should be wide + * enough to collect most data, while close enough to keep things from + * getting confused. */ -#define MULBY10(x) (((x)<<3) + ((x)<<1)) -#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */ -#define MULBY24(x) (((x)<<4) + ((x)<<3)) +#define CLOSETIME (4u*60u*60u) /* - * Two days, in seconds. + * Since we try to match years, the result of a full search will not + * change when we are already less than a half year from the receive + * time stamp. Since the length of a year is variable we use a + * slightly narrower limit; this might require a full evaluation near + * the edge, but will make sure we always get the correct result. */ -#define TWODAYS (2*24*60*60) +#define NEARTIME (182u * SECSPERDAY) /* - * We demand that the time be within CLOSETIME seconds of the receive - * time stamp. This is about 4 hours, which hopefully should be - * wide enough to collect most data, while close enough to keep things - * from getting confused. + * local calendar helpers */ -#define CLOSETIME (4*60*60) - +static int32 ntp_to_year(u_int32); +static u_int32 year_to_ntp(int32); +/* + * Take a time spec given as day-of-year, hour, minute and second as + * well as a GMT offset in hours and convert it to a NTP time stamp in + * '*ts_ui'. The value will be in the range (rec_ui-0.5yrs) to + * (rec_ui+0.5yrs). A hint for the current start-of-year will be + * read from '*yearstart'. + * + * On return '*ts_ui' will always the best matching solution, and + * '*yearstart' will receive the associated start-of-year. + * + * The function will tell if the result in 'ts_ui' is in CLOSETIME + * (+/-4hrs) around the receive time by returning a non-zero value. + * + * Note: The function puts no constraints on the value ranges for the + * time specification, but evaluates the effective seconds in + * 32-bit arithmetic. + */ int clocktime( - int yday, - int hour, - int minute, - int second, - int tzoff, - u_long rec_ui, - u_long *yearstart, - u_int32 *ts_ui - ) + int yday , /* day-of-year */ + int hour , /* hour of day */ + int minute , /* minute of hour */ + int second , /* second of minute */ + int tzoff , /* hours west of GMT */ + u_int32 rec_ui , /* pivot value */ + u_long *yearstart, /* cached start-of-year, should be fixed to u_int32 */ + u_int32 *ts_ui ) /* effective time stamp */ { - register long tmp; - register u_long date; - register u_long yst; - + u_int32 ystt[3]; /* year start */ + u_int32 test[3]; /* result time stamp */ + u_int32 diff[3]; /* abs difference to receive */ + int32 y, tmp, idx, min; + /* - * Compute the offset into the year in seconds. Note that + * Compute the offset into the year in seconds. Note that * this could come out to be a negative number. */ - tmp = (long)(MULBY24((yday-1)) + hour + tzoff); - tmp = MULBY60(tmp) + (long)minute; - tmp = MULBY60(tmp) + (long)second; - + tmp = ((int32)second + + SECSPERMIN * ((int32)minute + + MINSPERHR * ((int32)hour + (int32)tzoff + + HRSPERDAY * ((int32)yday - 1)))); /* - * Initialize yearstart, if necessary. + * Based on the cached year start, do a first attempt. Be + * happy and return if this gets us better than NEARTIME to + * the receive time stamp. Do this only if the cached year + * start is not zero, which will not happen after 1900 for the + * next few thousand years. */ - yst = *yearstart; - if (yst == 0) { - yst = calyearstart(rec_ui); - *yearstart = yst; + if (*yearstart) { + /* -- get time stamp of potential solution */ + test[0] = (u_int32)(*yearstart) + tmp; + /* -- calc absolute difference to receive time */ + diff[0] = test[0] - rec_ui; + if (diff[0] >= 0x80000000u) + diff[0] = ~diff[0] + 1; + /* -- can't get closer if diff < NEARTIME */ + if (diff[0] < NEARTIME) { + *ts_ui = test[0]; + return diff[0] < CLOSETIME; + } } /* - * Now the fun begins. We demand that the received clock time - * be within CLOSETIME of the receive timestamp, but - * there is uncertainty about the year the timestamp is in. - * Use the current year start for the first check, this should - * work most of the time. + * Now the dance begins. Based on the receive time stamp and + * the seconds offset in 'tmp', we make an educated guess + * about the year to start with. This takes us on the spot + * with a fuzz of +/-1 year. + * + * We calculate the effective timestamps for the three years + * around the guess and select the entry with the minimum + * absolute difference to the receive time stamp. */ - date = (u_long)(tmp + (long)yst); - if (date < (rec_ui + CLOSETIME) && - date > (rec_ui - CLOSETIME)) { - *ts_ui = date; - return 1; + y = ntp_to_year(rec_ui - tmp); + for (idx = 0; idx < 3; idx++) { + /* -- get year start of potential solution */ + ystt[idx] = year_to_ntp(y + idx - 1); + /* -- get time stamp of potential solution */ + test[idx] = ystt[idx] + tmp; + /* -- calc absolute difference to receive time */ + diff[idx] = test[idx] - rec_ui; + if (diff[idx] >= 0x80000000u) + diff[idx] = ~diff[idx] + 1; } + /* -*- assume current year fits best, then search best fit */ + for (min = 1, idx = 0; idx < 3; idx++) + if (diff[idx] < diff[min]) + min = idx; + /* -*- store results and update year start */ + *ts_ui = test[min]; + *yearstart = ystt[min]; - /* - * Trouble. Next check is to see if the year rolled over and, if - * so, try again with the new year's start. - */ - yst = calyearstart(rec_ui); - if (yst != *yearstart) { - date = (u_long)((long)yst + tmp); - *ts_ui = date; - if (date < (rec_ui + CLOSETIME) && - date > (rec_ui - CLOSETIME)) { - *yearstart = yst; - return 1; - } - } + /* -*- tell if we could get into CLOSETIME*/ + return diff[min] < CLOSETIME; +} - /* - * Here we know the year start matches the current system - * time. One remaining possibility is that the time code - * is in the year previous to that of the system time. This - * is only worth checking if the receive timestamp is less - * than a couple of days into the new year. - */ - if ((rec_ui - yst) < TWODAYS) { - yst = calyearstart(yst - TWODAYS); - if (yst != *yearstart) { - date = (u_long)(tmp + (long)yst); - if (date < (rec_ui + CLOSETIME) && - date > (rec_ui - CLOSETIME)) { - *yearstart = yst; - *ts_ui = date; - return 1; - } - } - } +static int32 +ntp_to_year( + u_int32 ntp) +{ + vint64 t; + ntpcal_split s; - /* - * One last possibility is that the time stamp is in the year - * following the year the system is in. Try this one before - * giving up. - */ - yst = calyearstart(rec_ui + TWODAYS); - if (yst != *yearstart) { - date = (u_long)((long)yst + tmp); - if (date < (rec_ui + CLOSETIME) && - date > (rec_ui - CLOSETIME)) { - *yearstart = yst; - *ts_ui = date; - return 1; - } - } + t = ntpcal_ntp_to_ntp(ntp, NULL); + s = ntpcal_daysplit(&t); + s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL); + return s.hi + 1; +} - /* - * Give it up. - */ - return 0; +static u_int32 +year_to_ntp( + int32 year) +{ + u_int32 days; + days = ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1; + return days * SECSPERDAY; } |