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