Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / libntp / caljulian.c
blob379a7f6d04a8ade424442e833d63edb471d04e29
1 /* $NetBSD$ */
3 /*
4 * caljulian - determine the Julian date from an NTP time.
5 */
6 #include <sys/types.h>
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"
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
20 #include "ntp_assert.h"
22 #if 1
24 /* Updated 2008-11-10 Juergen Perlinger <juergen.perlinger@t-online.de>
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'.
32 void
33 caljulian(
34 u_long ntptime,
35 register struct calendar *jt
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 */
50 NTP_INSIST(NULL != jt);
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...
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.
63 now = time(NULL);
64 tmplo = (u_int32)now;
65 #if ( SIZEOF_TIME_T > 4 )
66 tmphi = (int32)(now >> 16 >> 16);
67 #else
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.
73 tmphi = (int32)(now >> 31);
74 #endif
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;
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?)
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;
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!
98 NTP_ENSURE(ntptime < SECSPERDAY);
99 NTP_INVARIANT((u_int32)(ntptime + ntp_day * SECSPERDAY) == (u_int32)saved_time);
102 * Do the easy stuff first: take care of hh:mm:ss, ignoring leap
103 * seconds
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);
111 /* check time invariants */
112 NTP_ENSURE(jt->second < SECSPERMIN);
113 NTP_ENSURE(jt->minute < MINSPERHR);
114 NTP_ENSURE(jt->hour < HRSPERDAY);
117 * Find the day past 1900/01/01 00:00 UTC
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 */
129 NTP_ENSURE(ntp_day < 366);
132 * Calculate the year and day-of-year
134 jt->year = (u_short)(400*n400 + 100*n100 + 4*n4 + n1);
136 if ((n100 | n1) > 3) {
138 * If the cycle year ever comes out to 4, it must be December
139 * 31st of a leap year.
141 jt->month = 12;
142 jt->monthday = 31;
143 jt->yearday = 366;
144 } else {
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.
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);
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);
169 #else
171 /* Updated 2003-12-30 TMa
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.
179 void
180 caljulian(
181 u_long ntptime,
182 register struct calendar *jt
185 struct tm *tm;
186 NTP_REQUIRE(jt != NULL);
188 tm = ntp2unix_tm(ntptime, 0);
189 NTP_INSIST(tm != NULL);
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! */
199 #endif