1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert * clocktime - compute the NTP date from a day of year, hour, minute
3c0b746e5SOllivier Robert * and second.
4c0b746e5SOllivier Robert */
5*2b15cb3dSCy Schubert #include <config.h>
6c0b746e5SOllivier Robert #include "ntp_fp.h"
7c0b746e5SOllivier Robert #include "ntp_unixtime.h"
8c0b746e5SOllivier Robert #include "ntp_stdlib.h"
9*2b15cb3dSCy Schubert #include "ntp_calendar.h"
10c0b746e5SOllivier Robert
11c0b746e5SOllivier Robert /*
12*2b15cb3dSCy Schubert * We check that the time be within CLOSETIME seconds of the receive
13*2b15cb3dSCy Schubert * time stamp. This is about 4 hours, which hopefully should be wide
14*2b15cb3dSCy Schubert * enough to collect most data, while close enough to keep things from
15*2b15cb3dSCy Schubert * getting confused.
16c0b746e5SOllivier Robert */
17*2b15cb3dSCy Schubert #define CLOSETIME (4u*60u*60u)
18c0b746e5SOllivier Robert
19c0b746e5SOllivier Robert /*
20*2b15cb3dSCy Schubert * Since we try to match years, the result of a full search will not
21*2b15cb3dSCy Schubert * change when we are already less than a half year from the receive
22*2b15cb3dSCy Schubert * time stamp. Since the length of a year is variable we use a
23*2b15cb3dSCy Schubert * slightly narrower limit; this might require a full evaluation near
24*2b15cb3dSCy Schubert * the edge, but will make sure we always get the correct result.
25c0b746e5SOllivier Robert */
26*2b15cb3dSCy Schubert #define NEARTIME (182u * SECSPERDAY)
27c0b746e5SOllivier Robert
28c0b746e5SOllivier Robert /*
29*2b15cb3dSCy Schubert * local calendar helpers
30c0b746e5SOllivier Robert */
31*2b15cb3dSCy Schubert static int32 ntp_to_year(u_int32);
32*2b15cb3dSCy Schubert static u_int32 year_to_ntp(int32);
33c0b746e5SOllivier Robert
34*2b15cb3dSCy Schubert /*
35*2b15cb3dSCy Schubert * Take a time spec given as day-of-year, hour, minute and second as
36*2b15cb3dSCy Schubert * well as a GMT offset in hours and convert it to a NTP time stamp in
37*2b15cb3dSCy Schubert * '*ts_ui'. The value will be in the range (rec_ui-0.5yrs) to
38*2b15cb3dSCy Schubert * (rec_ui+0.5yrs). A hint for the current start-of-year will be
39*2b15cb3dSCy Schubert * read from '*yearstart'.
40*2b15cb3dSCy Schubert *
41*2b15cb3dSCy Schubert * On return '*ts_ui' will always the best matching solution, and
42*2b15cb3dSCy Schubert * '*yearstart' will receive the associated start-of-year.
43*2b15cb3dSCy Schubert *
44*2b15cb3dSCy Schubert * The function will tell if the result in 'ts_ui' is in CLOSETIME
45*2b15cb3dSCy Schubert * (+/-4hrs) around the receive time by returning a non-zero value.
46*2b15cb3dSCy Schubert *
47*2b15cb3dSCy Schubert * Note: The function puts no constraints on the value ranges for the
48*2b15cb3dSCy Schubert * time specification, but evaluates the effective seconds in
49*2b15cb3dSCy Schubert * 32-bit arithmetic.
50*2b15cb3dSCy Schubert */
51c0b746e5SOllivier Robert int
clocktime(int yday,int hour,int minute,int second,int tzoff,u_int32 rec_ui,u_long * yearstart,u_int32 * ts_ui)52c0b746e5SOllivier Robert clocktime(
53*2b15cb3dSCy Schubert int yday , /* day-of-year */
54*2b15cb3dSCy Schubert int hour , /* hour of day */
55*2b15cb3dSCy Schubert int minute , /* minute of hour */
56*2b15cb3dSCy Schubert int second , /* second of minute */
57*2b15cb3dSCy Schubert int tzoff , /* hours west of GMT */
58*2b15cb3dSCy Schubert u_int32 rec_ui , /* pivot value */
59*2b15cb3dSCy Schubert u_long *yearstart, /* cached start-of-year, should be fixed to u_int32 */
60*2b15cb3dSCy Schubert u_int32 *ts_ui ) /* effective time stamp */
61c0b746e5SOllivier Robert {
62*2b15cb3dSCy Schubert u_int32 ystt[3]; /* year start */
63*2b15cb3dSCy Schubert u_int32 test[3]; /* result time stamp */
64*2b15cb3dSCy Schubert u_int32 diff[3]; /* abs difference to receive */
65*2b15cb3dSCy Schubert int32 y, tmp, idx, min;
66c0b746e5SOllivier Robert
67c0b746e5SOllivier Robert /*
68c0b746e5SOllivier Robert * Compute the offset into the year in seconds. Note that
69c0b746e5SOllivier Robert * this could come out to be a negative number.
70c0b746e5SOllivier Robert */
71*2b15cb3dSCy Schubert tmp = ((int32)second +
72*2b15cb3dSCy Schubert SECSPERMIN * ((int32)minute +
73*2b15cb3dSCy Schubert MINSPERHR * ((int32)hour + (int32)tzoff +
74*2b15cb3dSCy Schubert HRSPERDAY * ((int32)yday - 1))));
75c0b746e5SOllivier Robert /*
76*2b15cb3dSCy Schubert * Based on the cached year start, do a first attempt. Be
77*2b15cb3dSCy Schubert * happy and return if this gets us better than NEARTIME to
78*2b15cb3dSCy Schubert * the receive time stamp. Do this only if the cached year
79*2b15cb3dSCy Schubert * start is not zero, which will not happen after 1900 for the
80*2b15cb3dSCy Schubert * next few thousand years.
81c0b746e5SOllivier Robert */
82*2b15cb3dSCy Schubert if (*yearstart) {
83*2b15cb3dSCy Schubert /* -- get time stamp of potential solution */
84*2b15cb3dSCy Schubert test[0] = (u_int32)(*yearstart) + tmp;
85*2b15cb3dSCy Schubert /* -- calc absolute difference to receive time */
86*2b15cb3dSCy Schubert diff[0] = test[0] - rec_ui;
87*2b15cb3dSCy Schubert if (diff[0] >= 0x80000000u)
88*2b15cb3dSCy Schubert diff[0] = ~diff[0] + 1;
89*2b15cb3dSCy Schubert /* -- can't get closer if diff < NEARTIME */
90*2b15cb3dSCy Schubert if (diff[0] < NEARTIME) {
91*2b15cb3dSCy Schubert *ts_ui = test[0];
92*2b15cb3dSCy Schubert return diff[0] < CLOSETIME;
93c0b746e5SOllivier Robert }
94c0b746e5SOllivier Robert }
95c0b746e5SOllivier Robert
96c0b746e5SOllivier Robert /*
97*2b15cb3dSCy Schubert * Now the dance begins. Based on the receive time stamp and
98*2b15cb3dSCy Schubert * the seconds offset in 'tmp', we make an educated guess
99*2b15cb3dSCy Schubert * about the year to start with. This takes us on the spot
100*2b15cb3dSCy Schubert * with a fuzz of +/-1 year.
101*2b15cb3dSCy Schubert *
102*2b15cb3dSCy Schubert * We calculate the effective timestamps for the three years
103*2b15cb3dSCy Schubert * around the guess and select the entry with the minimum
104*2b15cb3dSCy Schubert * absolute difference to the receive time stamp.
105c0b746e5SOllivier Robert */
106*2b15cb3dSCy Schubert y = ntp_to_year(rec_ui - tmp);
107*2b15cb3dSCy Schubert for (idx = 0; idx < 3; idx++) {
108*2b15cb3dSCy Schubert /* -- get year start of potential solution */
109*2b15cb3dSCy Schubert ystt[idx] = year_to_ntp(y + idx - 1);
110*2b15cb3dSCy Schubert /* -- get time stamp of potential solution */
111*2b15cb3dSCy Schubert test[idx] = ystt[idx] + tmp;
112*2b15cb3dSCy Schubert /* -- calc absolute difference to receive time */
113*2b15cb3dSCy Schubert diff[idx] = test[idx] - rec_ui;
114*2b15cb3dSCy Schubert if (diff[idx] >= 0x80000000u)
115*2b15cb3dSCy Schubert diff[idx] = ~diff[idx] + 1;
116c0b746e5SOllivier Robert }
117*2b15cb3dSCy Schubert /* -*- assume current year fits best, then search best fit */
118*2b15cb3dSCy Schubert for (min = 1, idx = 0; idx < 3; idx++)
119*2b15cb3dSCy Schubert if (diff[idx] < diff[min])
120*2b15cb3dSCy Schubert min = idx;
121*2b15cb3dSCy Schubert /* -*- store results and update year start */
122*2b15cb3dSCy Schubert *ts_ui = test[min];
123*2b15cb3dSCy Schubert *yearstart = ystt[min];
124*2b15cb3dSCy Schubert
125*2b15cb3dSCy Schubert /* -*- tell if we could get into CLOSETIME*/
126*2b15cb3dSCy Schubert return diff[min] < CLOSETIME;
127c0b746e5SOllivier Robert }
128c0b746e5SOllivier Robert
129*2b15cb3dSCy Schubert static int32
ntp_to_year(u_int32 ntp)130*2b15cb3dSCy Schubert ntp_to_year(
131*2b15cb3dSCy Schubert u_int32 ntp)
132*2b15cb3dSCy Schubert {
133*2b15cb3dSCy Schubert vint64 t;
134*2b15cb3dSCy Schubert ntpcal_split s;
135*2b15cb3dSCy Schubert
136*2b15cb3dSCy Schubert t = ntpcal_ntp_to_ntp(ntp, NULL);
137*2b15cb3dSCy Schubert s = ntpcal_daysplit(&t);
138*2b15cb3dSCy Schubert s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL);
139*2b15cb3dSCy Schubert return s.hi + 1;
140c0b746e5SOllivier Robert }
141c0b746e5SOllivier Robert
142*2b15cb3dSCy Schubert static u_int32
year_to_ntp(int32 year)143*2b15cb3dSCy Schubert year_to_ntp(
144*2b15cb3dSCy Schubert int32 year)
145*2b15cb3dSCy Schubert {
146*2b15cb3dSCy Schubert u_int32 days;
147*2b15cb3dSCy Schubert days = ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1;
148*2b15cb3dSCy Schubert return days * SECSPERDAY;
149c0b746e5SOllivier Robert }
150