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 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 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 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