154359Sroberto/* 254359Sroberto * clocktime - compute the NTP date from a day of year, hour, minute 354359Sroberto * and second. 454359Sroberto */ 5285612Sdelphij#include <config.h> 654359Sroberto#include "ntp_fp.h" 754359Sroberto#include "ntp_unixtime.h" 854359Sroberto#include "ntp_stdlib.h" 9285612Sdelphij#include "ntp_calendar.h" 1054359Sroberto 1154359Sroberto/* 12285612Sdelphij * We check that the time be within CLOSETIME seconds of the receive 13285612Sdelphij * time stamp. This is about 4 hours, which hopefully should be wide 14285612Sdelphij * enough to collect most data, while close enough to keep things from 15285612Sdelphij * getting confused. 1654359Sroberto */ 17285612Sdelphij#define CLOSETIME (4u*60u*60u) 1854359Sroberto 1954359Sroberto/* 20285612Sdelphij * Since we try to match years, the result of a full search will not 21285612Sdelphij * change when we are already less than a half year from the receive 22285612Sdelphij * time stamp. Since the length of a year is variable we use a 23285612Sdelphij * slightly narrower limit; this might require a full evaluation near 24285612Sdelphij * the edge, but will make sure we always get the correct result. 2554359Sroberto */ 26285612Sdelphij#define NEARTIME (182u * SECSPERDAY) 2754359Sroberto 2854359Sroberto/* 29285612Sdelphij * local calendar helpers 3054359Sroberto */ 31285612Sdelphijstatic int32 ntp_to_year(u_int32); 32285612Sdelphijstatic u_int32 year_to_ntp(int32); 3354359Sroberto 34285612Sdelphij/* 35285612Sdelphij * Take a time spec given as day-of-year, hour, minute and second as 36285612Sdelphij * well as a GMT offset in hours and convert it to a NTP time stamp in 37285612Sdelphij * '*ts_ui'. The value will be in the range (rec_ui-0.5yrs) to 38285612Sdelphij * (rec_ui+0.5yrs). A hint for the current start-of-year will be 39285612Sdelphij * read from '*yearstart'. 40285612Sdelphij * 41285612Sdelphij * On return '*ts_ui' will always the best matching solution, and 42285612Sdelphij * '*yearstart' will receive the associated start-of-year. 43285612Sdelphij * 44285612Sdelphij * The function will tell if the result in 'ts_ui' is in CLOSETIME 45285612Sdelphij * (+/-4hrs) around the receive time by returning a non-zero value. 46285612Sdelphij * 47285612Sdelphij * Note: The function puts no constraints on the value ranges for the 48285612Sdelphij * time specification, but evaluates the effective seconds in 49285612Sdelphij * 32-bit arithmetic. 50285612Sdelphij */ 5154359Srobertoint 5254359Srobertoclocktime( 53285612Sdelphij int yday , /* day-of-year */ 54285612Sdelphij int hour , /* hour of day */ 55285612Sdelphij int minute , /* minute of hour */ 56285612Sdelphij int second , /* second of minute */ 57285612Sdelphij int tzoff , /* hours west of GMT */ 58285612Sdelphij u_int32 rec_ui , /* pivot value */ 59285612Sdelphij u_long *yearstart, /* cached start-of-year, should be fixed to u_int32 */ 60285612Sdelphij u_int32 *ts_ui ) /* effective time stamp */ 6154359Sroberto{ 62285612Sdelphij u_int32 ystt[3]; /* year start */ 63285612Sdelphij u_int32 test[3]; /* result time stamp */ 64285612Sdelphij u_int32 diff[3]; /* abs difference to receive */ 65285612Sdelphij int32 y, tmp, idx, min; 66285612Sdelphij 6754359Sroberto /* 68285612Sdelphij * Compute the offset into the year in seconds. Note that 6954359Sroberto * this could come out to be a negative number. 7054359Sroberto */ 71285612Sdelphij tmp = ((int32)second + 72285612Sdelphij SECSPERMIN * ((int32)minute + 73285612Sdelphij MINSPERHR * ((int32)hour + (int32)tzoff + 74285612Sdelphij HRSPERDAY * ((int32)yday - 1)))); 7554359Sroberto /* 76285612Sdelphij * Based on the cached year start, do a first attempt. Be 77285612Sdelphij * happy and return if this gets us better than NEARTIME to 78285612Sdelphij * the receive time stamp. Do this only if the cached year 79285612Sdelphij * start is not zero, which will not happen after 1900 for the 80285612Sdelphij * next few thousand years. 8154359Sroberto */ 82285612Sdelphij if (*yearstart) { 83285612Sdelphij /* -- get time stamp of potential solution */ 84285612Sdelphij test[0] = (u_int32)(*yearstart) + tmp; 85285612Sdelphij /* -- calc absolute difference to receive time */ 86285612Sdelphij diff[0] = test[0] - rec_ui; 87285612Sdelphij if (diff[0] >= 0x80000000u) 88285612Sdelphij diff[0] = ~diff[0] + 1; 89285612Sdelphij /* -- can't get closer if diff < NEARTIME */ 90285612Sdelphij if (diff[0] < NEARTIME) { 91285612Sdelphij *ts_ui = test[0]; 92285612Sdelphij return diff[0] < CLOSETIME; 93285612Sdelphij } 9454359Sroberto } 9554359Sroberto 9654359Sroberto /* 97285612Sdelphij * Now the dance begins. Based on the receive time stamp and 98285612Sdelphij * the seconds offset in 'tmp', we make an educated guess 99285612Sdelphij * about the year to start with. This takes us on the spot 100285612Sdelphij * with a fuzz of +/-1 year. 101285612Sdelphij * 102285612Sdelphij * We calculate the effective timestamps for the three years 103285612Sdelphij * around the guess and select the entry with the minimum 104285612Sdelphij * absolute difference to the receive time stamp. 10554359Sroberto */ 106285612Sdelphij y = ntp_to_year(rec_ui - tmp); 107285612Sdelphij for (idx = 0; idx < 3; idx++) { 108285612Sdelphij /* -- get year start of potential solution */ 109285612Sdelphij ystt[idx] = year_to_ntp(y + idx - 1); 110285612Sdelphij /* -- get time stamp of potential solution */ 111285612Sdelphij test[idx] = ystt[idx] + tmp; 112285612Sdelphij /* -- calc absolute difference to receive time */ 113285612Sdelphij diff[idx] = test[idx] - rec_ui; 114285612Sdelphij if (diff[idx] >= 0x80000000u) 115285612Sdelphij diff[idx] = ~diff[idx] + 1; 11654359Sroberto } 117285612Sdelphij /* -*- assume current year fits best, then search best fit */ 118285612Sdelphij for (min = 1, idx = 0; idx < 3; idx++) 119285612Sdelphij if (diff[idx] < diff[min]) 120285612Sdelphij min = idx; 121285612Sdelphij /* -*- store results and update year start */ 122285612Sdelphij *ts_ui = test[min]; 123285612Sdelphij *yearstart = ystt[min]; 12454359Sroberto 125285612Sdelphij /* -*- tell if we could get into CLOSETIME*/ 126285612Sdelphij return diff[min] < CLOSETIME; 127285612Sdelphij} 12854359Sroberto 129285612Sdelphijstatic int32 130285612Sdelphijntp_to_year( 131285612Sdelphij u_int32 ntp) 132285612Sdelphij{ 133285612Sdelphij vint64 t; 134285612Sdelphij ntpcal_split s; 13554359Sroberto 136285612Sdelphij t = ntpcal_ntp_to_ntp(ntp, NULL); 137285612Sdelphij s = ntpcal_daysplit(&t); 138285612Sdelphij s = ntpcal_split_eradays(s.hi + DAY_NTP_STARTS - 1, NULL); 139285612Sdelphij return s.hi + 1; 140285612Sdelphij} 14154359Sroberto 142285612Sdelphijstatic u_int32 143285612Sdelphijyear_to_ntp( 144285612Sdelphij int32 year) 145285612Sdelphij{ 146285612Sdelphij u_int32 days; 147285612Sdelphij days = ntpcal_days_in_years(year-1) - DAY_NTP_STARTS + 1; 148285612Sdelphij return days * SECSPERDAY; 14954359Sroberto} 150