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