1ef64b99roberto/*
2ef64b99roberto * clocktime - compute the NTP date from a day of year, hour, minute
3ef64b99roberto *	       and second.
4ef64b99roberto */
5047f369cy#include <config.h>
6ef64b99roberto#include "ntp_fp.h"
7ef64b99roberto#include "ntp_unixtime.h"
8ef64b99roberto#include "ntp_stdlib.h"
9047f369cy#include "ntp_calendar.h"
10ef64b99roberto
11ef64b99roberto/*
12047f369cy * We check that the time be within CLOSETIME seconds of the receive
13047f369cy * time stamp.	This is about 4 hours, which hopefully should be wide
14047f369cy * enough to collect most data, while close enough to keep things from
15047f369cy * getting confused.
16ef64b99roberto */
17047f369cy#define	CLOSETIME	(4u*60u*60u)
18ef64b99roberto
19ef64b99roberto/*
20047f369cy * Since we try to match years, the result of a full search will not
21047f369cy * change when we are already less than a half year from the receive
22047f369cy * time stamp.	Since the length of a year is variable we use a
23047f369cy * slightly narrower limit; this might require a full evaluation near
24047f369cy * the edge, but will make sure we always get the correct result.
25ef64b99roberto */
26047f369cy#define NEARTIME	(182u * SECSPERDAY)
27ef64b99roberto
28ef64b99roberto/*
29047f369cy * local calendar helpers
30ef64b99roberto */
31047f369cystatic int32   ntp_to_year(u_int32);
32047f369cystatic u_int32 year_to_ntp(int32);
33ef64b99roberto
34047f369cy/*
35047f369cy * Take a time spec given as day-of-year, hour, minute and second as
36047f369cy * well as a GMT offset in hours and convert it to a NTP time stamp in
37047f369cy * '*ts_ui'. The value will be in the range (rec_ui-0.5yrs) to
38047f369cy * (rec_ui+0.5yrs). A hint for the current start-of-year will be
39047f369cy * read from '*yearstart'.
40047f369cy *
41047f369cy * On return '*ts_ui' will always the best matching solution, and
42047f369cy * '*yearstart' will receive the associated start-of-year.
43047f369cy *
44047f369cy * The function will tell if the result in 'ts_ui' is in CLOSETIME
45047f369cy * (+/-4hrs) around the receive time by returning a non-zero value.
46047f369cy *
47047f369cy * Note: The function puts no constraints on the value ranges for the
48047f369cy * time specification, but evaluates the effective seconds in
49047f369cy * 32-bit arithmetic.
50047f369cy */
51ef64b99robertoint
52ef64b99robertoclocktime(
53047f369cy	int	yday	 ,	/* day-of-year */
54047f369cy	int	hour	 ,	/* hour of day */
55047f369cy	int	minute	 ,	/* minute of hour */
56047f369cy	int	second	 ,	/* second of minute */
57047f369cy	int	tzoff	 ,	/* hours west of GMT */
58047f369cy	u_int32 rec_ui	 ,	/* pivot value */
59047f369cy	u_long *yearstart,	/* cached start-of-year, should be fixed to u_int32 */
60047f369cy	u_int32 *ts_ui	 )	/* effective time stamp */
61ef64b99roberto{
62047f369cy	u_int32 ystt[3];	/* year start */
63047f369cy	u_int32 test[3];	/* result time stamp */
64047f369cy	u_int32 diff[3];	/* abs difference to receive */
65047f369cy	int32 y, tmp, idx, min;
66047f369cy
67ef64b99roberto	/*
68047f369cy	 * Compute the offset into the year in seconds.	 Note that
69ef64b99roberto	 * this could come out to be a negative number.
70ef64b99roberto	 */
71047f369cy	tmp = ((int32)second +
72047f369cy	       SECSPERMIN * ((int32)minute +
73047f369cy			     MINSPERHR * ((int32)hour + (int32)tzoff +
74047f369cy					  HRSPERDAY * ((int32)yday - 1))));
75ef64b99roberto	/*
76047f369cy	 * Based on the cached year start, do a first attempt. Be
77047f369cy	 * happy and return if this gets us better than NEARTIME to
78047f369cy	 * the receive time stamp. Do this only if the cached year
79047f369cy	 * start is not zero, which will not happen after 1900 for the
80047f369cy	 * next few thousand years.
81ef64b99roberto	 */
82047f369cy	if (*yearstart) {
83047f369cy		/* -- get time stamp of potential solution */
84047f369cy		test[0] = (u_int32)(*yearstart) + tmp;
85047f369cy		/* -- calc absolute difference to receive time */
86047f369cy		diff[0] = test[0] - rec_ui;
87047f369cy		if (diff[0] >= 0x80000000u)
88047f369cy			diff[0] = ~diff[0] + 1;
89047f369cy		/* -- can't get closer if diff < NEARTIME */
90047f369cy		if (diff[0] < NEARTIME) {
91047f369cy			*ts_ui = test[0];
92047f369cy			return diff[0] < CLOSETIME;
93047f369cy		}
94ef64b99roberto	}
95ef64b99roberto
96ef64b99roberto	/*
97047f369cy	 * Now the dance begins. Based on the receive time stamp and
98047f369cy	 * the seconds offset in 'tmp', we make an educated guess
99047f369cy	 * about the year to start with. This takes us on the spot
100047f369cy	 * with a fuzz of +/-1 year.
101047f369cy	 *
102047f369cy	 * We calculate the effective timestamps for the three years
103047f369cy	 * around the guess and select the entry with the minimum
104047f369cy	 * absolute difference to the receive time stamp.
105ef64b99roberto	 */
106047f369cy	y = ntp_to_year(rec_ui - tmp);
107047f369cy	for (idx = 0; idx < 3; idx++) {
108047f369cy		/* -- get year start of potential solution */
109047f369cy		ystt[idx] = year_to_ntp(y + idx - 1);
110047f369cy		/* -- get time stamp of potential solution */
111047f369cy		test[idx] = ystt[idx] + tmp;
112047f369cy		/* -- calc absolute difference to receive time */
113047f369cy		diff[idx] = test[idx] - rec_ui;
114047f369cy		if (diff[idx] >= 0x80000000u)
115047f369cy			diff[idx] = ~diff[idx] + 1;
116ef64b99roberto	}
117047f369cy	/* -*- assume current year fits best, then search best fit */
118047f369cy	for (min = 1, idx = 0; idx < 3; idx++)
119047f369cy		if (diff[idx] < diff[min])
120047f369cy			min = idx;
121047f369cy	/* -*- store results and update year start */
122047f369cy	*ts_ui	   = test[min];
123047f369cy	*yearstart = ystt[min];
124ef64b99roberto
125047f369cy	/* -*- tell if we could get into CLOSETIME*/
126047f369cy	return diff[min] < CLOSETIME;
127047f369cy}
128ef64b99roberto
129047f369cystatic int32
130047f369cyntp_to_year(
131047f369cy	u_int32 ntp)
132047f369cy{
133047f369cy	vint64	     t;
134047f369cy	ntpcal_split s;
135ef64b99roberto
136047f369cy	t = ntpcal_ntp_to_ntp(ntp, NULL);
137047f369cy	s = ntpcal_daysplit(&t);
138047f369cy	s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL);
139047f369cy	return s.hi + 1;
140047f369cy}
141ef64b99roberto
142047f369cystatic u_int32
143047f369cyyear_to_ntp(
144047f369cy	int32 year)
145047f369cy{
146047f369cy	u_int32 days;
147047f369cy	days = ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1;
148047f369cy	return days * SECSPERDAY;
149ef64b99roberto}
150