summaryrefslogtreecommitdiff
path: root/libntp/clocktime.c
blob: c1a3ba09c0af2c400838c2e0ed333137662304b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * clocktime - compute the NTP date from a day of year, hour, minute
 *	       and second.
 */
#include <config.h>
#include "ntp_fp.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
#include "ntp_calendar.h"

/*
 * We check that the time be within CLOSETIME seconds of the receive
 * time stamp.	This is about 4 hours, which hopefully should be wide
 * enough to collect most data, while close enough to keep things from
 * getting confused.
 */
#define	CLOSETIME	(4u*60u*60u)

/*
 * Since we try to match years, the result of a full search will not
 * change when we are already less than a half year from the receive
 * time stamp.	Since the length of a year is variable we use a
 * slightly narrower limit; this might require a full evaluation near
 * the edge, but will make sure we always get the correct result.
 */
#define NEARTIME	(182u * SECSPERDAY)

/*
 * local calendar helpers
 */
static int32   ntp_to_year(u_int32);
static u_int32 year_to_ntp(int32);

/*
 * Take a time spec given as day-of-year, hour, minute and second as
 * well as a GMT offset in hours and convert it to a NTP time stamp in
 * '*ts_ui'. The value will be in the range (rec_ui-0.5yrs) to
 * (rec_ui+0.5yrs). A hint for the current start-of-year will be
 * read from '*yearstart'.
 *
 * On return '*ts_ui' will always the best matching solution, and
 * '*yearstart' will receive the associated start-of-year.
 *
 * The function will tell if the result in 'ts_ui' is in CLOSETIME
 * (+/-4hrs) around the receive time by returning a non-zero value.
 *
 * Note: The function puts no constraints on the value ranges for the
 * time specification, but evaluates the effective seconds in
 * 32-bit arithmetic.
 */
int
clocktime(
	int	yday	 ,	/* day-of-year */
	int	hour	 ,	/* hour of day */
	int	minute	 ,	/* minute of hour */
	int	second	 ,	/* second of minute */
	int	tzoff	 ,	/* hours west of GMT */
	u_int32 rec_ui	 ,	/* pivot value */
	u_long *yearstart,	/* cached start-of-year, should be fixed to u_int32 */
	u_int32 *ts_ui	 )	/* effective time stamp */
{
	u_int32 ystt[3];	/* year start */
	u_int32 test[3];	/* result time stamp */
	u_int32 diff[3];	/* abs difference to receive */
	int32 y, tmp, idx, min;
	
	/*
	 * Compute the offset into the year in seconds.	 Note that
	 * this could come out to be a negative number.
	 */
	tmp = ((int32)second +
	       SECSPERMIN * ((int32)minute +
			     MINSPERHR * ((int32)hour + (int32)tzoff +
					  HRSPERDAY * ((int32)yday - 1))));
	/*
	 * Based on the cached year start, do a first attempt. Be
	 * happy and return if this gets us better than NEARTIME to
	 * the receive time stamp. Do this only if the cached year
	 * start is not zero, which will not happen after 1900 for the
	 * next few thousand years.
	 */
	if (*yearstart) {
		/* -- get time stamp of potential solution */
		test[0] = (u_int32)(*yearstart) + tmp;
		/* -- calc absolute difference to receive time */
		diff[0] = test[0] - rec_ui;
		if (diff[0] >= 0x80000000u)
			diff[0] = ~diff[0] + 1;
		/* -- can't get closer if diff < NEARTIME */
		if (diff[0] < NEARTIME) {
			*ts_ui = test[0];
			return diff[0] < CLOSETIME;
		}
	}

	/*
	 * Now the dance begins. Based on the receive time stamp and
	 * the seconds offset in 'tmp', we make an educated guess
	 * about the year to start with. This takes us on the spot
	 * with a fuzz of +/-1 year.
	 *
	 * We calculate the effective timestamps for the three years
	 * around the guess and select the entry with the minimum
	 * absolute difference to the receive time stamp.
	 */
	y = ntp_to_year(rec_ui - tmp);
	for (idx = 0; idx < 3; idx++) {
		/* -- get year start of potential solution */
		ystt[idx] = year_to_ntp(y + idx - 1);
		/* -- get time stamp of potential solution */
		test[idx] = ystt[idx] + tmp;
		/* -- calc absolute difference to receive time */
		diff[idx] = test[idx] - rec_ui;
		if (diff[idx] >= 0x80000000u)
			diff[idx] = ~diff[idx] + 1;
	}
	/* -*- assume current year fits best, then search best fit */
	for (min = 1, idx = 0; idx < 3; idx++)
		if (diff[idx] < diff[min])
			min = idx;
	/* -*- store results and update year start */
	*ts_ui	   = test[min];
	*yearstart = ystt[min];

	/* -*- tell if we could get into CLOSETIME*/
	return diff[min] < CLOSETIME;
}

static int32
ntp_to_year(
	u_int32 ntp)
{
	vint64	     t;
	ntpcal_split s;

	t = ntpcal_ntp_to_ntp(ntp, NULL);
	s = ntpcal_daysplit(&t);
	s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL);
	return s.hi + 1;
}

static u_int32
year_to_ntp(
	int32 year)
{
	u_int32 days;
	days = ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1;
	return days * SECSPERDAY;
}