1 /* $NetBSD: caljulian.c,v 1.1.1.1 2009/12/13 16:55:02 kardel Exp $ */ 2 3 /* 4 * caljulian - determine the Julian date from an NTP time. 5 */ 6 #include <sys/types.h> 7 8 #include "ntp_types.h" 9 #include "ntp_calendar.h" 10 #include "ntp_stdlib.h" 11 #include "ntp_fp.h" 12 #include "ntp_unixtime.h" 13 14 #if !(defined(ISC_CHECK_ALL) || defined(ISC_CHECK_NONE) || \ 15 defined(ISC_CHECK_ENSURE) || defined(ISC_CHECK_INSIST) || \ 16 defined(ISC_CHECK_INVARIANT)) 17 # define ISC_CHECK_ALL 18 #endif 19 20 #include "ntp_assert.h" 21 22 #if 1 23 24 /* Updated 2008-11-10 Juergen Perlinger <juergen.perlinger@t-online.de> 25 * 26 * Make the conversion 2038-proof with proper NTP epoch unfolding and extended 27 * precision calculations. Though we should really get a 'time_t' with more 28 * than 32 bits at least until 2037, because the unfolding cannot work after 29 * the wrap of the 32-bit 'time_t'. 30 */ 31 32 void 33 caljulian( 34 u_long ntptime, 35 register struct calendar *jt 36 ) 37 { 38 u_long saved_time = ntptime; 39 u_long ntp_day; /* days (since christian era or in year) */ 40 u_long n400; /* # of Gregorian cycles */ 41 u_long n100; /* # of normal centuries */ 42 u_long n4; /* # of 4-year cycles */ 43 u_long n1; /* # of years into a leap year cycle */ 44 u_long sclday; /* scaled days for month conversion */ 45 int leaps; /* # of leaps days in year */ 46 time_t now; /* current system time */ 47 u_int32 tmplo; /* double precision tmp value / lo part */ 48 int32 tmphi; /* double precision tmp value / hi part */ 49 50 NTP_INSIST(NULL != jt); 51 52 /* 53 * First we have to unfold the ntp time stamp around the current time 54 * to make sure we are in the right epoch. Also we we do *NOT* fold 55 * before the begin of the first NTP epoch, so we WILL have a 56 * non-negative time stamp afterwards. Though at the time of this 57 * writing (2008 A.D.) it would be really strange to have systems 58 * running with clock set to he 1960's or before... 59 * 60 * But's important to use a 32 bit max signed value -- LONG_MAX is 64 61 * bit on a 64-bit system, and it will give wrong results. 62 */ 63 now = time(NULL); 64 tmplo = (u_int32)now; 65 #if ( SIZEOF_TIME_T > 4 ) 66 tmphi = (int32)(now >> 16 >> 16); 67 #else 68 /* 69 * Get the correct sign extension in the high part. 70 * (now >> 32) may not work correctly on every 32 bit 71 * system, e.g. it yields garbage under Win32/VC6. 72 */ 73 tmphi = (int32)(now >> 31); 74 #endif 75 76 M_ADD(tmphi, tmplo, 0, ((1UL << 31)-1)); /* 32-bit max signed */ 77 M_ADD(tmphi, tmplo, 0, JAN_1970); 78 if ((ntptime > tmplo) && (tmphi > 0)) 79 --tmphi; 80 tmplo = ntptime; 81 82 /* 83 * Now split into days and seconds-of-day, using the fact that 84 * SECSPERDAY (86400) == 675 * 128; we can get roughly 17000 years of 85 * time scale, using only 32-bit calculations. Some magic numbers here, 86 * sorry for that. (This could be streamlined for 64 bit machines, but 87 * is worth the trouble?) 88 */ 89 ntptime = tmplo & 127; /* save remainder bits */ 90 tmplo = (tmplo >> 7) | (tmphi << 25); 91 ntp_day = (u_int32)tmplo / 675; 92 ntptime += ((u_int32)tmplo % 675) << 7; 93 94 /* some checks for the algorithm 95 * There's some 64-bit trouble out there: the original NTP time stamp 96 * had only 32 bits, so our calculation invariant only holds in 32 bits! 97 */ 98 NTP_ENSURE(ntptime < SECSPERDAY); 99 NTP_INVARIANT((u_int32)(ntptime + ntp_day * SECSPERDAY) == (u_int32)saved_time); 100 101 /* 102 * Do the easy stuff first: take care of hh:mm:ss, ignoring leap 103 * seconds 104 */ 105 jt->second = (u_char)(ntptime % SECSPERMIN); 106 ntptime /= SECSPERMIN; 107 jt->minute = (u_char)(ntptime % MINSPERHR); 108 ntptime /= MINSPERHR; 109 jt->hour = (u_char)(ntptime); 110 111 /* check time invariants */ 112 NTP_ENSURE(jt->second < SECSPERMIN); 113 NTP_ENSURE(jt->minute < MINSPERHR); 114 NTP_ENSURE(jt->hour < HRSPERDAY); 115 116 /* 117 * Find the day past 1900/01/01 00:00 UTC 118 */ 119 ntp_day += DAY_NTP_STARTS - 1; /* convert to days in CE */ 120 n400 = ntp_day / GREGORIAN_CYCLE_DAYS; /* split off cycles */ 121 ntp_day %= GREGORIAN_CYCLE_DAYS; 122 n100 = ntp_day / GREGORIAN_NORMAL_CENTURY_DAYS; 123 ntp_day %= GREGORIAN_NORMAL_CENTURY_DAYS; 124 n4 = ntp_day / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 125 ntp_day %= GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 126 n1 = ntp_day / DAYSPERYEAR; 127 ntp_day %= DAYSPERYEAR; /* now zero-based day-of-year */ 128 129 NTP_ENSURE(ntp_day < 366); 130 131 /* 132 * Calculate the year and day-of-year 133 */ 134 jt->year = (u_short)(400*n400 + 100*n100 + 4*n4 + n1); 135 136 if ((n100 | n1) > 3) { 137 /* 138 * If the cycle year ever comes out to 4, it must be December 139 * 31st of a leap year. 140 */ 141 jt->month = 12; 142 jt->monthday = 31; 143 jt->yearday = 366; 144 } else { 145 /* 146 * The following code is according to the excellent book 147 * 'Calendrical Calculations' by Nachum Dershowitz and Edward 148 * Reingold. It converts the day-of-year into month and 149 * day-of-month, using a linear transformation with integer 150 * truncation. Magic numbers again, but they will not be used 151 * anywhere else. 152 */ 153 sclday = ntp_day * 7 + 217; 154 leaps = ((n1 == 3) && ((n4 != 24) || (n100 == 3))) ? 1 : 0; 155 if (ntp_day >= (u_long)(JAN + FEB + leaps)) 156 sclday += (2 - leaps) * 7; 157 ++jt->year; 158 jt->month = (u_char)(sclday / 214); 159 jt->monthday = (u_char)((sclday % 214) / 7 + 1); 160 jt->yearday = (u_short)(1 + ntp_day); 161 } 162 163 /* check date invariants */ 164 NTP_ENSURE(1 <= jt->month && jt->month <= 12); 165 NTP_ENSURE(1 <= jt->monthday && jt->monthday <= 31); 166 NTP_ENSURE(1 <= jt->yearday && jt->yearday <= 366); 167 } 168 169 #else 170 171 /* Updated 2003-12-30 TMa 172 173 Uses common code with the *prettydate functions to convert an ntp 174 seconds count into a calendar date. 175 Will handle ntp epoch wraparound as long as the underlying os/library 176 does so for the unix epoch, i.e. works after 2038. 177 */ 178 179 void 180 caljulian( 181 u_long ntptime, 182 register struct calendar *jt 183 ) 184 { 185 struct tm *tm; 186 NTP_REQUIRE(jt != NULL); 187 188 tm = ntp2unix_tm(ntptime, 0); 189 NTP_INSIST(tm != NULL); 190 191 jt->hour = (u_char) tm->tm_hour; 192 jt->minute = (u_char) tm->tm_min; 193 jt->month = (u_char) (tm->tm_mon + 1); 194 jt->monthday = (u_char) tm->tm_mday; 195 jt->second = (u_char) tm->tm_sec; 196 jt->year = (u_short) (tm->tm_year + 1900); 197 jt->yearday = (u_short) (tm->tm_yday + 1); /* Assumes tm_yday starts with day 0! */ 198 } 199 #endif 200