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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
/*
* caljulian - determine the Julian date from an NTP time.
*/
#include <sys/types.h>
#include "ntp_types.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_fp.h"
#include "ntp_unixtime.h"
#if !(defined(ISC_CHECK_ALL) || defined(ISC_CHECK_NONE) || \
defined(ISC_CHECK_ENSURE) || defined(ISC_CHECK_INSIST) || \
defined(ISC_CHECK_INVARIANT))
# define ISC_CHECK_ALL
#endif
#include "ntp_assert.h"
#if 1
/* Updated 2008-11-10 Juergen Perlinger <juergen.perlinger@t-online.de>
*
* Make the conversion 2038-proof with proper NTP epoch unfolding and extended
* precision calculations. Though we should really get a 'time_t' with more
* than 32 bits at least until 2037, because the unfolding cannot work after
* the wrap of the 32-bit 'time_t'.
*/
void
caljulian(
u_long ntptime,
register struct calendar *jt
)
{
u_long saved_time = ntptime;
u_long ntp_day; /* days (since christian era or in year) */
u_long n400; /* # of Gregorian cycles */
u_long n100; /* # of normal centuries */
u_long n4; /* # of 4-year cycles */
u_long n1; /* # of years into a leap year cycle */
u_long sclday; /* scaled days for month conversion */
int leaps; /* # of leaps days in year */
time_t now; /* current system time */
u_int32 tmplo; /* double precision tmp value / lo part */
int32 tmphi; /* double precision tmp value / hi part */
NTP_INSIST(NULL != jt);
/*
* First we have to unfold the ntp time stamp around the current time
* to make sure we are in the right epoch. Also we we do *NOT* fold
* before the begin of the first NTP epoch, so we WILL have a
* non-negative time stamp afterwards. Though at the time of this
* writing (2008 A.D.) it would be really strange to have systems
* running with clock set to he 1960's or before...
*
* But's important to use a 32 bit max signed value -- LONG_MAX is 64
* bit on a 64-bit system, and it will give wrong results.
*/
now = time(NULL);
tmplo = (u_int32)now;
#if ( SIZEOF_TIME_T > 4 )
tmphi = (int32)(now >> 16 >> 16);
#else
/*
* Get the correct sign extension in the high part.
* (now >> 32) may not work correctly on every 32 bit
* system, e.g. it yields garbage under Win32/VC6.
*/
tmphi = (int32)(now >> 31);
#endif
M_ADD(tmphi, tmplo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
M_ADD(tmphi, tmplo, 0, JAN_1970);
if ((ntptime > tmplo) && (tmphi > 0))
--tmphi;
tmplo = ntptime;
/*
* Now split into days and seconds-of-day, using the fact that
* SECSPERDAY (86400) == 675 * 128; we can get roughly 17000 years of
* time scale, using only 32-bit calculations. Some magic numbers here,
* sorry for that. (This could be streamlined for 64 bit machines, but
* is worth the trouble?)
*/
ntptime = tmplo & 127; /* save remainder bits */
tmplo = (tmplo >> 7) | (tmphi << 25);
ntp_day = (u_int32)tmplo / 675;
ntptime += ((u_int32)tmplo % 675) << 7;
/* some checks for the algorithm
* There's some 64-bit trouble out there: the original NTP time stamp
* had only 32 bits, so our calculation invariant only holds in 32 bits!
*/
NTP_ENSURE(ntptime < SECSPERDAY);
NTP_INVARIANT((u_int32)(ntptime + ntp_day * SECSPERDAY) == (u_int32)saved_time);
/*
* Do the easy stuff first: take care of hh:mm:ss, ignoring leap
* seconds
*/
jt->second = (u_char)(ntptime % SECSPERMIN);
ntptime /= SECSPERMIN;
jt->minute = (u_char)(ntptime % MINSPERHR);
ntptime /= MINSPERHR;
jt->hour = (u_char)(ntptime);
/* check time invariants */
NTP_ENSURE(jt->second < SECSPERMIN);
NTP_ENSURE(jt->minute < MINSPERHR);
NTP_ENSURE(jt->hour < HRSPERDAY);
/*
* Find the day past 1900/01/01 00:00 UTC
*/
ntp_day += DAY_NTP_STARTS - 1; /* convert to days in CE */
n400 = ntp_day / GREGORIAN_CYCLE_DAYS; /* split off cycles */
ntp_day %= GREGORIAN_CYCLE_DAYS;
n100 = ntp_day / GREGORIAN_NORMAL_CENTURY_DAYS;
ntp_day %= GREGORIAN_NORMAL_CENTURY_DAYS;
n4 = ntp_day / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
ntp_day %= GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
n1 = ntp_day / DAYSPERYEAR;
ntp_day %= DAYSPERYEAR; /* now zero-based day-of-year */
NTP_ENSURE(ntp_day < 366);
/*
* Calculate the year and day-of-year
*/
jt->year = (u_short)(400*n400 + 100*n100 + 4*n4 + n1);
if ((n100 | n1) > 3) {
/*
* If the cycle year ever comes out to 4, it must be December
* 31st of a leap year.
*/
jt->month = 12;
jt->monthday = 31;
jt->yearday = 366;
} else {
/*
* The following code is according to the excellent book
* 'Calendrical Calculations' by Nachum Dershowitz and Edward
* Reingold. It converts the day-of-year into month and
* day-of-month, using a linear transformation with integer
* truncation. Magic numbers again, but they will not be used
* anywhere else.
*/
sclday = ntp_day * 7 + 217;
leaps = ((n1 == 3) && ((n4 != 24) || (n100 == 3))) ? 1 : 0;
if (ntp_day >= (u_long)(JAN + FEB + leaps))
sclday += (2 - leaps) * 7;
++jt->year;
jt->month = (u_char)(sclday / 214);
jt->monthday = (u_char)((sclday % 214) / 7 + 1);
jt->yearday = (u_short)(1 + ntp_day);
}
/* check date invariants */
NTP_ENSURE(1 <= jt->month && jt->month <= 12);
NTP_ENSURE(1 <= jt->monthday && jt->monthday <= 31);
NTP_ENSURE(1 <= jt->yearday && jt->yearday <= 366);
}
#else
/* Updated 2003-12-30 TMa
Uses common code with the *prettydate functions to convert an ntp
seconds count into a calendar date.
Will handle ntp epoch wraparound as long as the underlying os/library
does so for the unix epoch, i.e. works after 2038.
*/
void
caljulian(
u_long ntptime,
register struct calendar *jt
)
{
struct tm *tm;
NTP_REQUIRE(jt != NULL);
tm = ntp2unix_tm(ntptime, 0);
NTP_INSIST(tm != NULL);
jt->hour = (u_char) tm->tm_hour;
jt->minute = (u_char) tm->tm_min;
jt->month = (u_char) (tm->tm_mon + 1);
jt->monthday = (u_char) tm->tm_mday;
jt->second = (u_char) tm->tm_sec;
jt->year = (u_short) (tm->tm_year + 1900);
jt->yearday = (u_short) (tm->tm_yday + 1); /* Assumes tm_yday starts with day 0! */
}
#endif
|