1*cdfa2a7eSchristos /* $NetBSD: clocktime.c,v 1.5 2020/05/25 20:47:24 christos Exp $ */
2abb0f93cSkardel
3abb0f93cSkardel /*
4abb0f93cSkardel * clocktime - compute the NTP date from a day of year, hour, minute
5abb0f93cSkardel * and second.
6abb0f93cSkardel */
78585484eSchristos #include <config.h>
8abb0f93cSkardel #include "ntp_fp.h"
9abb0f93cSkardel #include "ntp_unixtime.h"
10abb0f93cSkardel #include "ntp_stdlib.h"
118585484eSchristos #include "ntp_calendar.h"
12abb0f93cSkardel
13abb0f93cSkardel /*
148585484eSchristos * We check that the time be within CLOSETIME seconds of the receive
158585484eSchristos * time stamp. This is about 4 hours, which hopefully should be wide
168585484eSchristos * enough to collect most data, while close enough to keep things from
178585484eSchristos * getting confused.
18abb0f93cSkardel */
198585484eSchristos #define CLOSETIME (4u*60u*60u)
20abb0f93cSkardel
21abb0f93cSkardel /*
228585484eSchristos * Since we try to match years, the result of a full search will not
238585484eSchristos * change when we are already less than a half year from the receive
248585484eSchristos * time stamp. Since the length of a year is variable we use a
258585484eSchristos * slightly narrower limit; this might require a full evaluation near
268585484eSchristos * the edge, but will make sure we always get the correct result.
27abb0f93cSkardel */
288585484eSchristos #define NEARTIME (182u * SECSPERDAY)
29abb0f93cSkardel
30abb0f93cSkardel /*
318585484eSchristos * local calendar helpers
32abb0f93cSkardel */
338585484eSchristos static int32 ntp_to_year(u_int32);
348585484eSchristos static u_int32 year_to_ntp(int32);
35abb0f93cSkardel
368585484eSchristos /*
378585484eSchristos * Take a time spec given as day-of-year, hour, minute and second as
388585484eSchristos * well as a GMT offset in hours and convert it to a NTP time stamp in
398585484eSchristos * '*ts_ui'. The value will be in the range (rec_ui-0.5yrs) to
408585484eSchristos * (rec_ui+0.5yrs). A hint for the current start-of-year will be
418585484eSchristos * read from '*yearstart'.
428585484eSchristos *
438585484eSchristos * On return '*ts_ui' will always the best matching solution, and
448585484eSchristos * '*yearstart' will receive the associated start-of-year.
458585484eSchristos *
468585484eSchristos * The function will tell if the result in 'ts_ui' is in CLOSETIME
478585484eSchristos * (+/-4hrs) around the receive time by returning a non-zero value.
488585484eSchristos *
498585484eSchristos * Note: The function puts no constraints on the value ranges for the
508585484eSchristos * time specification, but evaluates the effective seconds in
518585484eSchristos * 32-bit arithmetic.
528585484eSchristos */
53abb0f93cSkardel int
clocktime(int yday,int hour,int minute,int second,int tzoff,u_int32 rec_ui,u_long * yearstart,u_int32 * ts_ui)54abb0f93cSkardel clocktime(
558585484eSchristos int yday , /* day-of-year */
568585484eSchristos int hour , /* hour of day */
578585484eSchristos int minute , /* minute of hour */
588585484eSchristos int second , /* second of minute */
598585484eSchristos int tzoff , /* hours west of GMT */
608585484eSchristos u_int32 rec_ui , /* pivot value */
618585484eSchristos u_long *yearstart, /* cached start-of-year, should be fixed to u_int32 */
628585484eSchristos u_int32 *ts_ui ) /* effective time stamp */
63abb0f93cSkardel {
648585484eSchristos u_int32 ystt[3]; /* year start */
658585484eSchristos u_int32 test[3]; /* result time stamp */
668585484eSchristos u_int32 diff[3]; /* abs difference to receive */
678585484eSchristos int32 y, tmp, idx, min;
68abb0f93cSkardel
69abb0f93cSkardel /*
70abb0f93cSkardel * Compute the offset into the year in seconds. Note that
71abb0f93cSkardel * this could come out to be a negative number.
72abb0f93cSkardel */
738585484eSchristos tmp = ((int32)second +
748585484eSchristos SECSPERMIN * ((int32)minute +
758585484eSchristos MINSPERHR * ((int32)hour + (int32)tzoff +
768585484eSchristos HRSPERDAY * ((int32)yday - 1))));
77abb0f93cSkardel /*
788585484eSchristos * Based on the cached year start, do a first attempt. Be
798585484eSchristos * happy and return if this gets us better than NEARTIME to
808585484eSchristos * the receive time stamp. Do this only if the cached year
818585484eSchristos * start is not zero, which will not happen after 1900 for the
828585484eSchristos * next few thousand years.
83abb0f93cSkardel */
848585484eSchristos if (*yearstart) {
858585484eSchristos /* -- get time stamp of potential solution */
868585484eSchristos test[0] = (u_int32)(*yearstart) + tmp;
878585484eSchristos /* -- calc absolute difference to receive time */
888585484eSchristos diff[0] = test[0] - rec_ui;
898585484eSchristos if (diff[0] >= 0x80000000u)
908585484eSchristos diff[0] = ~diff[0] + 1;
918585484eSchristos /* -- can't get closer if diff < NEARTIME */
928585484eSchristos if (diff[0] < NEARTIME) {
938585484eSchristos *ts_ui = test[0];
948585484eSchristos return diff[0] < CLOSETIME;
95abb0f93cSkardel }
96abb0f93cSkardel }
97abb0f93cSkardel
98abb0f93cSkardel /*
998585484eSchristos * Now the dance begins. Based on the receive time stamp and
1008585484eSchristos * the seconds offset in 'tmp', we make an educated guess
1018585484eSchristos * about the year to start with. This takes us on the spot
1028585484eSchristos * with a fuzz of +/-1 year.
1038585484eSchristos *
1048585484eSchristos * We calculate the effective timestamps for the three years
1058585484eSchristos * around the guess and select the entry with the minimum
1068585484eSchristos * absolute difference to the receive time stamp.
107abb0f93cSkardel */
1088585484eSchristos y = ntp_to_year(rec_ui - tmp);
1098585484eSchristos for (idx = 0; idx < 3; idx++) {
1108585484eSchristos /* -- get year start of potential solution */
1118585484eSchristos ystt[idx] = year_to_ntp(y + idx - 1);
1128585484eSchristos /* -- get time stamp of potential solution */
1138585484eSchristos test[idx] = ystt[idx] + tmp;
1148585484eSchristos /* -- calc absolute difference to receive time */
1158585484eSchristos diff[idx] = test[idx] - rec_ui;
1168585484eSchristos if (diff[idx] >= 0x80000000u)
1178585484eSchristos diff[idx] = ~diff[idx] + 1;
118abb0f93cSkardel }
1198585484eSchristos /* -*- assume current year fits best, then search best fit */
1208585484eSchristos for (min = 1, idx = 0; idx < 3; idx++)
1218585484eSchristos if (diff[idx] < diff[min])
1228585484eSchristos min = idx;
1238585484eSchristos /* -*- store results and update year start */
1248585484eSchristos *ts_ui = test[min];
1258585484eSchristos *yearstart = ystt[min];
1268585484eSchristos
1278585484eSchristos /* -*- tell if we could get into CLOSETIME*/
1288585484eSchristos return diff[min] < CLOSETIME;
129abb0f93cSkardel }
130abb0f93cSkardel
1318585484eSchristos static int32
ntp_to_year(u_int32 ntp)1328585484eSchristos ntp_to_year(
1338585484eSchristos u_int32 ntp)
1348585484eSchristos {
1358585484eSchristos vint64 t;
1368585484eSchristos ntpcal_split s;
1378585484eSchristos
1388585484eSchristos t = ntpcal_ntp_to_ntp(ntp, NULL);
1398585484eSchristos s = ntpcal_daysplit(&t);
1408585484eSchristos s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL);
1418585484eSchristos return s.hi + 1;
142abb0f93cSkardel }
143abb0f93cSkardel
1448585484eSchristos static u_int32
year_to_ntp(int32 year)1458585484eSchristos year_to_ntp(
1468585484eSchristos int32 year)
1478585484eSchristos {
1488585484eSchristos u_int32 days;
1498585484eSchristos days = ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1;
1508585484eSchristos return days * SECSPERDAY;
151abb0f93cSkardel }
152