aboutsummaryrefslogtreecommitdiff
path: root/libntp/clocktime.c
diff options
context:
space:
mode:
Diffstat (limited to 'libntp/clocktime.c')
-rw-r--r--libntp/clocktime.c211
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;
}