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