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