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 */
31static int32   ntp_to_year(u_int32);
32static 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 */
51int
52clocktime(
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
129static int32
130ntp_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
142static u_int32
143year_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}
150