ntp_calendar.c revision 282408
1/*
2 * ntp_calendar.c - calendar and helper functions
3 *
4 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5 * The contents of 'html/copyright.html' apply.
6 */
7#include <config.h>
8#include <sys/types.h>
9
10#include "ntp_types.h"
11#include "ntp_calendar.h"
12#include "ntp_stdlib.h"
13#include "ntp_fp.h"
14#include "ntp_unixtime.h"
15
16/*
17 *---------------------------------------------------------------------
18 * replacing the 'time()' function
19 * --------------------------------------------------------------------
20 */
21
22static systime_func_ptr systime_func = &time;
23static inline time_t now(void);
24
25
26systime_func_ptr
27ntpcal_set_timefunc(
28	systime_func_ptr nfunc
29	)
30{
31	systime_func_ptr res;
32
33	res = systime_func;
34	if (NULL == nfunc)
35		nfunc = &time;
36	systime_func = nfunc;
37
38	return res;
39}
40
41
42static inline time_t
43now(void)
44{
45	return (*systime_func)(NULL);
46}
47
48/*
49 *---------------------------------------------------------------------
50 * Convert between 'time_t' and 'vint64'
51 *---------------------------------------------------------------------
52 */
53vint64
54time_to_vint64(
55	const time_t * ptt
56	)
57{
58	vint64 res;
59	time_t tt;
60
61	tt = *ptt;
62
63#if SIZEOF_TIME_T <= 4
64
65	res.D_s.hi = 0;
66	if (tt < 0) {
67		res.D_s.lo = (uint32_t)-tt;
68		M_NEG(res.D_s.hi, res.D_s.lo);
69	} else {
70		res.D_s.lo = (uint32_t)tt;
71	}
72
73#elif defined(HAVE_INT64)
74
75	res.q_s = tt;
76
77#else
78	/*
79	 * shifting negative signed quantities is compiler-dependent, so
80	 * we better avoid it and do it all manually. And shifting more
81	 * than the width of a quantity is undefined. Also a don't do!
82	 */
83	if (tt < 0) {
84		tt = -tt;
85		res.D_s.lo = (uint32_t)tt;
86		res.D_s.hi = (uint32_t)(tt >> 32);
87		M_NEG(res.D_s.hi, res.D_s.lo);
88	} else {
89		res.D_s.lo = (uint32_t)tt;
90		res.D_s.hi = (uint32_t)(tt >> 32);
91	}
92
93#endif
94
95	return res;
96}
97
98
99time_t
100vint64_to_time(
101	const vint64 *tv
102	)
103{
104	time_t res;
105
106#if SIZEOF_TIME_T <= 4
107
108	res = (time_t)tv->D_s.lo;
109
110#elif defined(HAVE_INT64)
111
112	res = (time_t)tv->q_s;
113
114#else
115
116	res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo;
117
118#endif
119
120	return res;
121}
122
123/*
124 *---------------------------------------------------------------------
125 * Get the build date & time
126 *---------------------------------------------------------------------
127 */
128int
129ntpcal_get_build_date(
130	struct calendar * jd
131	)
132{
133	/* The C standard tells us the format of '__DATE__':
134	 *
135	 * __DATE__ The date of translation of the preprocessing
136	 * translation unit: a character string literal of the form "Mmm
137	 * dd yyyy", where the names of the months are the same as those
138	 * generated by the asctime function, and the first character of
139	 * dd is a space character if the value is less than 10. If the
140	 * date of translation is not available, an
141	 * implementation-defined valid date shall be supplied.
142	 *
143	 * __TIME__ The time of translation of the preprocessing
144	 * translation unit: a character string literal of the form
145	 * "hh:mm:ss" as in the time generated by the asctime
146	 * function. If the time of translation is not available, an
147	 * implementation-defined valid time shall be supplied.
148	 *
149	 * Note that MSVC declares DATE and TIME to be in the local time
150	 * zone, while neither the C standard nor the GCC docs make any
151	 * statement about this. As a result, we may be +/-12hrs off
152	 * UTC.  But for practical purposes, this should not be a
153	 * problem.
154	 *
155	 */
156#ifdef MKREPRO_DATE
157	static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE;
158#else
159	static const char build[] = __TIME__ "/" __DATE__;
160#endif
161	static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
162
163	char		  monstr[4];
164	const char *	  cp;
165	unsigned short	  hour, minute, second, day, year;
166 	/* Note: The above quantities are used for sscanf 'hu' format,
167	 * so using 'uint16_t' is contra-indicated!
168	 */
169
170#ifdef DEBUG
171	static int        ignore  = 0;
172#endif
173
174	ZERO(*jd);
175	jd->year     = 1970;
176	jd->month    = 1;
177	jd->monthday = 1;
178
179#ifdef DEBUG
180	/* check environment if build date should be ignored */
181	if (0 == ignore) {
182	    const char * envstr;
183	    envstr = getenv("NTPD_IGNORE_BUILD_DATE");
184	    ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes")));
185	}
186	if (ignore > 1)
187	    return FALSE;
188#endif
189
190	if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu",
191			&hour, &minute, &second, monstr, &day, &year)) {
192		cp = strstr(mlist, monstr);
193		if (NULL != cp) {
194			jd->year     = year;
195			jd->month    = (uint8_t)((cp - mlist) / 3 + 1);
196			jd->monthday = (uint8_t)day;
197			jd->hour     = (uint8_t)hour;
198			jd->minute   = (uint8_t)minute;
199			jd->second   = (uint8_t)second;
200
201			return TRUE;
202		}
203	}
204
205	return FALSE;
206}
207
208
209/*
210 *---------------------------------------------------------------------
211 * basic calendar stuff
212 * --------------------------------------------------------------------
213 */
214
215/* month table for a year starting with March,1st */
216static const uint16_t shift_month_table[13] = {
217	0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366
218};
219
220/* month tables for years starting with January,1st; regular & leap */
221static const uint16_t real_month_table[2][13] = {
222	/* -*- table for regular years -*- */
223	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
224	/* -*- table for leap years -*- */
225	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
226};
227
228/*
229 * Some notes on the terminology:
230 *
231 * We use the proleptic Gregorian calendar, which is the Gregorian
232 * calendar extended in both directions ad infinitum. This totally
233 * disregards the fact that this calendar was invented in 1582, and
234 * was adopted at various dates over the world; sometimes even after
235 * the start of the NTP epoch.
236 *
237 * Normally date parts are given as current cycles, while time parts
238 * are given as elapsed cycles:
239 *
240 * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month,
241 * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed.
242 *
243 * The basic calculations for this calendar implementation deal with
244 * ELAPSED date units, which is the number of full years, full months
245 * and full days before a date: 1970-01-01 would be (1969, 0, 0) in
246 * that notation.
247 *
248 * To ease the numeric computations, month and day values outside the
249 * normal range are acceptable: 2001-03-00 will be treated as the day
250 * before 2001-03-01, 2000-13-32 will give the same result as
251 * 2001-02-01 and so on.
252 *
253 * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die'
254 * (day number).  This is the number of days elapsed since 0000-12-31
255 * in the proleptic Gregorian calendar. The begin of the Christian Era
256 * (0001-01-01) is RD(1).
257 *
258 *
259 * Some notes on the implementation:
260 *
261 * Calendar algorithms thrive on the division operation, which is one of
262 * the slowest numerical operations in any CPU. What saves us here from
263 * abysmal performance is the fact that all divisions are divisions by
264 * constant numbers, and most compilers can do this by a multiplication
265 * operation.  But this might not work when using the div/ldiv/lldiv
266 * function family, because many compilers are not able to do inline
267 * expansion of the code with following optimisation for the
268 * constant-divider case.
269 *
270 * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which
271 * are inherently target dependent. Nothing that could not be cured with
272 * autoconf, but still a mess...
273 *
274 * Furthermore, we need floor division while C demands truncation to
275 * zero, so additional steps are required to make sure the algorithms
276 * work.
277 *
278 * For all this, all divisions by constant are coded manually, even when
279 * there is a joined div/mod operation: The optimiser should sort that
280 * out, if possible.
281 *
282 * Finally, the functions do not check for overflow conditions. This
283 * is a sacrifice made for execution speed; since a 32-bit day counter
284 * covers +/- 5,879,610 years, this should not pose a problem here.
285 */
286
287
288/*
289 * ==================================================================
290 *
291 * General algorithmic stuff
292 *
293 * ==================================================================
294 */
295
296/*
297 *---------------------------------------------------------------------
298 * Do a periodic extension of 'value' around 'pivot' with a period of
299 * 'cycle'.
300 *
301 * The result 'res' is a number that holds to the following properties:
302 *
303 *   1)	 res MOD cycle == value MOD cycle
304 *   2)	 pivot <= res < pivot + cycle
305 *	 (replace </<= with >/>= for negative cycles)
306 *
307 * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which
308 * is not the same as the '%' operator in C: C requires division to be
309 * a truncated division, where remainder and dividend have the same
310 * sign if the remainder is not zero, whereas floor division requires
311 * divider and modulus to have the same sign for a non-zero modulus.
312 *
313 * This function has some useful applications:
314 *
315 * + let Y be a calendar year and V a truncated 2-digit year: then
316 *	periodic_extend(Y-50, V, 100)
317 *   is the closest expansion of the truncated year with respect to
318 *   the full year, that is a 4-digit year with a difference of less
319 *   than 50 years to the year Y. ("century unfolding")
320 *
321 * + let T be a UN*X time stamp and V be seconds-of-day: then
322 *	perodic_extend(T-43200, V, 86400)
323 *   is a time stamp that has the same seconds-of-day as the input
324 *   value, with an absolute difference to T of <= 12hrs.  ("day
325 *   unfolding")
326 *
327 * + Wherever you have a truncated periodic value and a non-truncated
328 *   base value and you want to match them somehow...
329 *
330 * Basically, the function delivers 'pivot + (value - pivot) % cycle',
331 * but the implementation takes some pains to avoid internal signed
332 * integer overflows in the '(value - pivot) % cycle' part and adheres
333 * to the floor division convention.
334 *
335 * If 64bit scalars where available on all intended platforms, writing a
336 * version that uses 64 bit ops would be easy; writing a general
337 * division routine for 64bit ops on a platform that can only do
338 * 32/16bit divisions and is still performant is a bit more
339 * difficult. Since most usecases can be coded in a way that does only
340 * require the 32-bit version a 64bit version is NOT provided here.
341 * ---------------------------------------------------------------------
342 */
343int32_t
344ntpcal_periodic_extend(
345	int32_t pivot,
346	int32_t value,
347	int32_t cycle
348	)
349{
350	uint32_t diff;
351	char	 cpl = 0; /* modulo complement flag */
352	char	 neg = 0; /* sign change flag	    */
353
354	/* make the cycle positive and adjust the flags */
355	if (cycle < 0) {
356		cycle = - cycle;
357		neg ^= 1;
358		cpl ^= 1;
359	}
360	/* guard against div by zero or one */
361	if (cycle > 1) {
362		/*
363		 * Get absolute difference as unsigned quantity and
364		 * the complement flag. This is done by always
365		 * subtracting the smaller value from the bigger
366		 * one. This implementation works only on a two's
367		 * complement machine!
368		 */
369		if (value >= pivot) {
370			diff = (uint32_t)value - (uint32_t)pivot;
371		} else {
372			diff = (uint32_t)pivot - (uint32_t)value;
373			cpl ^= 1;
374		}
375		diff %= (uint32_t)cycle;
376		if (diff) {
377			if (cpl)
378				diff = cycle - diff;
379			if (neg)
380				diff = ~diff + 1;
381			pivot += diff;
382		}
383	}
384	return pivot;
385}
386
387/*
388 *-------------------------------------------------------------------
389 * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X
390 * scale with proper epoch unfolding around a given pivot or the current
391 * system time. This function happily accepts negative pivot values as
392 * timestamps befor 1970-01-01, so be aware of possible trouble on
393 * platforms with 32bit 'time_t'!
394 *
395 * This is also a periodic extension, but since the cycle is 2^32 and
396 * the shift is 2^31, we can do some *very* fast math without explicit
397 * divisions.
398 *-------------------------------------------------------------------
399 */
400vint64
401ntpcal_ntp_to_time(
402	uint32_t	ntp,
403	const time_t *	pivot
404	)
405{
406	vint64 res;
407
408#ifdef HAVE_INT64
409
410	res.q_s = (pivot != NULL)
411		      ? *pivot
412		      : now();
413	res.Q_s -= 0x80000000;		/* unshift of half range */
414	ntp	-= (uint32_t)JAN_1970;	/* warp into UN*X domain */
415	ntp	-= res.D_s.lo;		/* cycle difference	 */
416	res.Q_s += (uint64_t)ntp;	/* get expanded time	 */
417
418#else /* no 64bit scalars */
419
420	time_t tmp;
421
422	tmp = (pivot != NULL)
423		  ? *pivot
424		  : now();
425	res = time_to_vint64(&tmp);
426	M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000);
427	ntp -= (uint32_t)JAN_1970;	/* warp into UN*X domain */
428	ntp -= res.D_s.lo;		/* cycle difference	 */
429	M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp);
430
431#endif /* no 64bit scalars */
432
433	return res;
434}
435
436/*
437 *-------------------------------------------------------------------
438 * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP
439 * scale with proper epoch unfolding around a given pivot or the current
440 * system time.
441 *
442 * Note: The pivot must be given in the UN*X time domain!
443 *
444 * This is also a periodic extension, but since the cycle is 2^32 and
445 * the shift is 2^31, we can do some *very* fast math without explicit
446 * divisions.
447 *-------------------------------------------------------------------
448 */
449vint64
450ntpcal_ntp_to_ntp(
451	uint32_t      ntp,
452	const time_t *pivot
453	)
454{
455	vint64 res;
456
457#ifdef HAVE_INT64
458
459	res.q_s = (pivot)
460		      ? *pivot
461		      : now();
462	res.Q_s -= 0x80000000;		/* unshift of half range */
463	res.Q_s += (uint32_t)JAN_1970;	/* warp into NTP domain	 */
464	ntp	-= res.D_s.lo;		/* cycle difference	 */
465	res.Q_s += (uint64_t)ntp;	/* get expanded time	 */
466
467#else /* no 64bit scalars */
468
469	time_t tmp;
470
471	tmp = (pivot)
472		  ? *pivot
473		  : now();
474	res = time_to_vint64(&tmp);
475	M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u);
476	M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */
477	ntp -= res.D_s.lo;		/* cycle difference	 */
478	M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp);
479
480#endif /* no 64bit scalars */
481
482	return res;
483}
484
485
486/*
487 * ==================================================================
488 *
489 * Splitting values to composite entities
490 *
491 * ==================================================================
492 */
493
494/*
495 *-------------------------------------------------------------------
496 * Split a 64bit seconds value into elapsed days in 'res.hi' and
497 * elapsed seconds since midnight in 'res.lo' using explicit floor
498 * division. This function happily accepts negative time values as
499 * timestamps before the respective epoch start.
500 * -------------------------------------------------------------------
501 */
502ntpcal_split
503ntpcal_daysplit(
504	const vint64 *ts
505	)
506{
507	ntpcal_split res;
508
509#ifdef HAVE_INT64
510
511	/* manual floor division by SECSPERDAY */
512	res.hi = (int32_t)(ts->q_s / SECSPERDAY);
513	res.lo = (int32_t)(ts->q_s % SECSPERDAY);
514	if (res.lo < 0) {
515		res.hi -= 1;
516		res.lo += SECSPERDAY;
517	}
518
519#else
520
521	/*
522	 * since we do not have 64bit ops, we have to this by hand.
523	 * Luckily SECSPERDAY is 86400 is 675*128, so we do the division
524	 * using chained 32/16 bit divisions and shifts.
525	 */
526	vint64	 op;
527	uint32_t q, r, a;
528	int	 isneg;
529
530	memcpy(&op, ts, sizeof(op));
531	/* fix sign */
532	isneg = M_ISNEG(op.D_s.hi);
533	if (isneg)
534		M_NEG(op.D_s.hi, op.D_s.lo);
535
536	/* save remainder of DIV 128, shift for divide */
537	r  = op.D_s.lo & 127; /* save remainder bits */
538	op.D_s.lo = (op.D_s.lo >> 7) | (op.D_s.hi << 25);
539	op.D_s.hi = (op.D_s.hi >> 7);
540
541	/* now do a mnual division, trying to remove as many ops as
542	 * possible -- division is always slow! An since we do not have
543	 * the advantage of a specific 64/32 bit or even a specific 32/16
544	 * bit division op, but must use the general 32/32bit division
545	 * even if we *know* the divider fits into unsigned 16 bits, the
546	 * exra code pathes should pay off.
547	 */
548	a = op.D_s.hi;
549	if (a > 675u)
550		a = a % 675u;
551	if (a) {
552		a = (a << 16) | op.W_s.lh;
553		q = a / 675u;
554		a = a % 675u;
555
556		a = (a << 16) | op.W_s.ll;
557		q = (q << 16) | (a / 675u);
558	} else {
559		a = op.D_s.lo;
560		q = a / 675u;
561	}
562	a = a % 675u;
563
564	/* assemble remainder */
565	r |= a << 7;
566
567	/* fix sign of result */
568	if (isneg) {
569		if (r) {
570			r = SECSPERDAY - r;
571			q = ~q;
572		} else
573			q = ~q + 1;
574	}
575
576	res.hi = q;
577	res.lo = r;
578
579#endif
580	return res;
581}
582
583/*
584 *-------------------------------------------------------------------
585 * Split a 32bit seconds value into h/m/s and excessive days.  This
586 * function happily accepts negative time values as timestamps before
587 * midnight.
588 * -------------------------------------------------------------------
589 */
590static int32_t
591priv_timesplit(
592	int32_t split[3],
593	int32_t ts
594	)
595{
596	int32_t days = 0;
597
598	/* make sure we have a positive offset into a day */
599	if (ts < 0 || ts >= SECSPERDAY) {
600		days = ts / SECSPERDAY;
601		ts   = ts % SECSPERDAY;
602		if (ts < 0) {
603			days -= 1;
604			ts   += SECSPERDAY;
605		}
606	}
607
608	/* get secs, mins, hours */
609	split[2] = (uint8_t)(ts % SECSPERMIN);
610	ts /= SECSPERMIN;
611	split[1] = (uint8_t)(ts % MINSPERHR);
612	split[0] = (uint8_t)(ts / MINSPERHR);
613
614	return days;
615}
616
617/*
618 * ---------------------------------------------------------------------
619 * Given the number of elapsed days in the calendar era, split this
620 * number into the number of elapsed years in 'res.hi' and the number
621 * of elapsed days of that year in 'res.lo'.
622 *
623 * if 'isleapyear' is not NULL, it will receive an integer that is 0 for
624 * regular years and a non-zero value for leap years.
625 *---------------------------------------------------------------------
626 */
627ntpcal_split
628ntpcal_split_eradays(
629	int32_t days,
630	int  *isleapyear
631	)
632{
633	ntpcal_split res;
634	int32_t	     n400, n100, n004, n001, yday; /* calendar year cycles */
635
636	/*
637	 * Split off calendar cycles, using floor division in the first
638	 * step. After that first step, simple division does it because
639	 * all operands are positive; alas, we have to be aware of the
640	 * possibe cycle overflows for 100 years and 1 year, caused by
641	 * the additional leap day.
642	 */
643	n400 = days / GREGORIAN_CYCLE_DAYS;
644	yday = days % GREGORIAN_CYCLE_DAYS;
645	if (yday < 0) {
646		n400 -= 1;
647		yday += GREGORIAN_CYCLE_DAYS;
648	}
649	n100 = yday / GREGORIAN_NORMAL_CENTURY_DAYS;
650	yday = yday % GREGORIAN_NORMAL_CENTURY_DAYS;
651	n004 = yday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
652	yday = yday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
653	n001 = yday / DAYSPERYEAR;
654	yday = yday % DAYSPERYEAR;
655
656	/*
657	 * check for leap cycle overflows and calculate the leap flag
658	 * if needed
659	 */
660	if ((n001 | n100) > 3) {
661		/* hit last day of leap year */
662		n001 -= 1;
663		yday += DAYSPERYEAR;
664		if (isleapyear)
665			*isleapyear = 1;
666	} else if (isleapyear)
667		*isleapyear = (n001 == 3) && ((n004 != 24) || (n100 == 3));
668
669	/* now merge the cycles to elapsed years, using horner scheme */
670	res.hi = ((4*n400 + n100)*25 + n004)*4 + n001;
671	res.lo = yday;
672
673	return res;
674}
675
676/*
677 *---------------------------------------------------------------------
678 * Given a number of elapsed days in a year and a leap year indicator,
679 * split the number of elapsed days into the number of elapsed months in
680 * 'res.hi' and the number of elapsed days of that month in 'res.lo'.
681 *
682 * This function will fail and return {-1,-1} if the number of elapsed
683 * days is not in the valid range!
684 *---------------------------------------------------------------------
685 */
686ntpcal_split
687ntpcal_split_yeardays(
688	int32_t eyd,
689	int     isleapyear
690	)
691{
692	ntpcal_split    res;
693	const uint16_t *lt;	/* month length table	*/
694
695	/* check leap year flag and select proper table */
696	lt = real_month_table[(isleapyear != 0)];
697	if (0 <= eyd && eyd < lt[12]) {
698		/* get zero-based month by approximation & correction step */
699		res.hi = eyd >> 5;	   /* approx month; might be 1 too low */
700		if (lt[res.hi + 1] <= eyd) /* fixup approximative month value  */
701			res.hi += 1;
702		res.lo = eyd - lt[res.hi];
703	} else {
704		res.lo = res.hi = -1;
705	}
706
707	return res;
708}
709
710/*
711 *---------------------------------------------------------------------
712 * Convert a RD into the date part of a 'struct calendar'.
713 *---------------------------------------------------------------------
714 */
715int
716ntpcal_rd_to_date(
717	struct calendar *jd,
718	int32_t		 rd
719	)
720{
721	ntpcal_split split;
722	int	     leaps;
723	int	     retv;
724
725	leaps = 0;
726	retv = 0;
727	/* Get day-of-week first. Since rd is signed, the remainder can
728	 * be in the range [-6..+6], but the assignment to an unsigned
729	 * variable maps the negative values to positive values >=7.
730	 * This makes the sign correction look strange, but adding 7
731	 * causes the needed wrap-around into the desired value range of
732	 * zero to six, both inclusive.
733	 */
734	jd->weekday = rd % 7;
735	if (jd->weekday >= 7)	/* unsigned! */
736		jd->weekday += 7;
737
738	split = ntpcal_split_eradays(rd - 1, &leaps);
739	retv  = leaps;
740	/* get year and day-of-year */
741	jd->year = (uint16_t)split.hi + 1;
742	if (jd->year != split.hi + 1) {
743		jd->year = 0;
744		retv	 = -1;	/* bletch. overflow trouble. */
745	}
746	jd->yearday = (uint16_t)split.lo + 1;
747
748	/* convert to month and mday */
749	split = ntpcal_split_yeardays(split.lo, leaps);
750	jd->month    = (uint8_t)split.hi + 1;
751	jd->monthday = (uint8_t)split.lo + 1;
752
753	return retv ? retv : leaps;
754}
755
756/*
757 *---------------------------------------------------------------------
758 * Convert a RD into the date part of a 'struct tm'.
759 *---------------------------------------------------------------------
760 */
761int
762ntpcal_rd_to_tm(
763	struct tm  *utm,
764	int32_t	    rd
765	)
766{
767	ntpcal_split split;
768	int	     leaps;
769
770	leaps = 0;
771	/* get day-of-week first */
772	utm->tm_wday = rd % 7;
773	if (utm->tm_wday < 0)
774		utm->tm_wday += 7;
775
776	/* get year and day-of-year */
777	split = ntpcal_split_eradays(rd - 1, &leaps);
778	utm->tm_year = split.hi - 1899;
779	utm->tm_yday = split.lo;	/* 0-based */
780
781	/* convert to month and mday */
782	split = ntpcal_split_yeardays(split.lo, leaps);
783	utm->tm_mon  = split.hi;	/* 0-based */
784	utm->tm_mday = split.lo + 1;	/* 1-based */
785
786	return leaps;
787}
788
789/*
790 *---------------------------------------------------------------------
791 * Take a value of seconds since midnight and split it into hhmmss in a
792 * 'struct calendar'.
793 *---------------------------------------------------------------------
794 */
795int32_t
796ntpcal_daysec_to_date(
797	struct calendar *jd,
798	int32_t		sec
799	)
800{
801	int32_t days;
802	int   ts[3];
803
804	days = priv_timesplit(ts, sec);
805	jd->hour   = (uint8_t)ts[0];
806	jd->minute = (uint8_t)ts[1];
807	jd->second = (uint8_t)ts[2];
808
809	return days;
810}
811
812/*
813 *---------------------------------------------------------------------
814 * Take a value of seconds since midnight and split it into hhmmss in a
815 * 'struct tm'.
816 *---------------------------------------------------------------------
817 */
818int32_t
819ntpcal_daysec_to_tm(
820	struct tm *utm,
821	int32_t	   sec
822	)
823{
824	int32_t days;
825	int32_t ts[3];
826
827	days = priv_timesplit(ts, sec);
828	utm->tm_hour = ts[0];
829	utm->tm_min  = ts[1];
830	utm->tm_sec  = ts[2];
831
832	return days;
833}
834
835/*
836 *---------------------------------------------------------------------
837 * take a split representation for day/second-of-day and day offset
838 * and convert it to a 'struct calendar'. The seconds will be normalised
839 * into the range of a day, and the day will be adjusted accordingly.
840 *
841 * returns >0 if the result is in a leap year, 0 if in a regular
842 * year and <0 if the result did not fit into the calendar struct.
843 *---------------------------------------------------------------------
844 */
845int
846ntpcal_daysplit_to_date(
847	struct calendar	   *jd,
848	const ntpcal_split *ds,
849	int32_t		    dof
850	)
851{
852	dof += ntpcal_daysec_to_date(jd, ds->lo);
853	return ntpcal_rd_to_date(jd, ds->hi + dof);
854}
855
856/*
857 *---------------------------------------------------------------------
858 * take a split representation for day/second-of-day and day offset
859 * and convert it to a 'struct tm'. The seconds will be normalised
860 * into the range of a day, and the day will be adjusted accordingly.
861 *
862 * returns 1 if the result is in a leap year and zero if in a regular
863 * year.
864 *---------------------------------------------------------------------
865 */
866int
867ntpcal_daysplit_to_tm(
868	struct tm	   *utm,
869	const ntpcal_split *ds ,
870	int32_t		    dof
871	)
872{
873	dof += ntpcal_daysec_to_tm(utm, ds->lo);
874
875	return ntpcal_rd_to_tm(utm, ds->hi + dof);
876}
877
878/*
879 *---------------------------------------------------------------------
880 * Take a UN*X time and convert to a calendar structure.
881 *---------------------------------------------------------------------
882 */
883int
884ntpcal_time_to_date(
885	struct calendar	*jd,
886	const vint64	*ts
887	)
888{
889	ntpcal_split ds;
890
891	ds = ntpcal_daysplit(ts);
892	ds.hi += ntpcal_daysec_to_date(jd, ds.lo);
893	ds.hi += DAY_UNIX_STARTS;
894
895	return ntpcal_rd_to_date(jd, ds.hi);
896}
897
898
899/*
900 * ==================================================================
901 *
902 * merging composite entities
903 *
904 * ==================================================================
905 */
906
907/*
908 *---------------------------------------------------------------------
909 * Merge a number of days and a number of seconds into seconds,
910 * expressed in 64 bits to avoid overflow.
911 *---------------------------------------------------------------------
912 */
913vint64
914ntpcal_dayjoin(
915	int32_t days,
916	int32_t secs
917	)
918{
919	vint64 res;
920
921#ifdef HAVE_INT64
922
923	res.q_s	 = days;
924	res.q_s *= SECSPERDAY;
925	res.q_s += secs;
926
927#else
928
929	uint32_t p1, p2;
930	int	 isneg;
931
932	/*
933	 * res = days *86400 + secs, using manual 16/32 bit
934	 * multiplications and shifts.
935	 */
936	isneg = (days < 0);
937	if (isneg)
938		days = -days;
939
940	/* assemble days * 675 */
941	res.D_s.lo = (days & 0xFFFF) * 675u;
942	res.D_s.hi = 0;
943	p1 = (days >> 16) * 675u;
944	p2 = p1 >> 16;
945	p1 = p1 << 16;
946	M_ADD(res.D_s.hi, res.D_s.lo, p2, p1);
947
948	/* mul by 128, using shift */
949	res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25);
950	res.D_s.lo = (res.D_s.lo << 7);
951
952	/* fix sign */
953	if (isneg)
954		M_NEG(res.D_s.hi, res.D_s.lo);
955
956	/* properly add seconds */
957	p2 = 0;
958	if (secs < 0) {
959		p1 = (uint32_t)-secs;
960		M_NEG(p2, p1);
961	} else {
962		p1 = (uint32_t)secs;
963	}
964	M_ADD(res.D_s.hi, res.D_s.lo, p2, p1);
965
966#endif
967
968	return res;
969}
970
971/*
972 *---------------------------------------------------------------------
973 * Convert elapsed years in Era into elapsed days in Era.
974 *
975 * To accomodate for negative values of years, floor division would be
976 * required for all division operations. This can be eased by first
977 * splitting the years into full 400-year cycles and years in the
978 * cycle. Only this operation must be coded as a full floor division; as
979 * the years in the cycle is a non-negative number, all other divisions
980 * can be regular truncated divisions.
981 *---------------------------------------------------------------------
982 */
983int32_t
984ntpcal_days_in_years(
985	int32_t years
986	)
987{
988	int32_t cycle; /* full gregorian cycle */
989
990	/* split off full calendar cycles, using floor division */
991	cycle = years / 400;
992	years = years % 400;
993	if (years < 0) {
994		cycle -= 1;
995		years += 400;
996	}
997
998	/*
999	 * Calculate days in cycle. years now is a non-negative number,
1000	 * holding the number of years in the 400-year cycle.
1001	 */
1002	return cycle * GREGORIAN_CYCLE_DAYS
1003	     + years * DAYSPERYEAR	/* days inregular years	*/
1004	     + years / 4		/* 4 year leap rule	*/
1005	     - years / 100;		/* 100 year leap rule	*/
1006	/* the 400-year rule does not apply due to full-cycle split-off */
1007}
1008
1009/*
1010 *---------------------------------------------------------------------
1011 * Convert a number of elapsed month in a year into elapsed days in year.
1012 *
1013 * The month will be normalized, and 'res.hi' will contain the
1014 * excessive years that must be considered when converting the years,
1015 * while 'res.lo' will contain the number of elapsed days since start
1016 * of the year.
1017 *
1018 * This code uses the shifted-month-approach to convert month to days,
1019 * because then there is no need to have explicit leap year
1020 * information.	 The slight disadvantage is that for most month values
1021 * the result is a negative value, and the year excess is one; the
1022 * conversion is then simply based on the start of the following year.
1023 *---------------------------------------------------------------------
1024 */
1025ntpcal_split
1026ntpcal_days_in_months(
1027	int32_t m
1028	)
1029{
1030	ntpcal_split res;
1031
1032	/* normalize month into range */
1033	res.hi = 0;
1034	res.lo = m;
1035	if (res.lo < 0 || res.lo >= 12) {
1036		res.hi = res.lo / 12;
1037		res.lo = res.lo % 12;
1038		if (res.lo < 0) {
1039			res.hi -= 1;
1040			res.lo += 12;
1041		}
1042	}
1043
1044	/* add 10 month for year starting with march */
1045	if (res.lo < 2)
1046		res.lo += 10;
1047	else {
1048		res.hi += 1;
1049		res.lo -= 2;
1050	}
1051
1052	/* get cummulated days in year with unshift */
1053	res.lo = shift_month_table[res.lo] - 306;
1054
1055	return res;
1056}
1057
1058/*
1059 *---------------------------------------------------------------------
1060 * Convert ELAPSED years/months/days of gregorian calendar to elapsed
1061 * days in Gregorian epoch.
1062 *
1063 * If you want to convert years and days-of-year, just give a month of
1064 * zero.
1065 *---------------------------------------------------------------------
1066 */
1067int32_t
1068ntpcal_edate_to_eradays(
1069	int32_t years,
1070	int32_t mons,
1071	int32_t mdays
1072	)
1073{
1074	ntpcal_split tmp;
1075	int32_t	     res;
1076
1077	if (mons) {
1078		tmp = ntpcal_days_in_months(mons);
1079		res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo;
1080	} else
1081		res = ntpcal_days_in_years(years);
1082	res += mdays;
1083
1084	return res;
1085}
1086
1087/*
1088 *---------------------------------------------------------------------
1089 * Convert ELAPSED years/months/days of gregorian calendar to elapsed
1090 * days in year.
1091 *
1092 * Note: This will give the true difference to the start of the given year,
1093 * even if months & days are off-scale.
1094 *---------------------------------------------------------------------
1095 */
1096int32_t
1097ntpcal_edate_to_yeardays(
1098	int32_t years,
1099	int32_t mons,
1100	int32_t mdays
1101	)
1102{
1103	ntpcal_split tmp;
1104
1105	if (0 <= mons && mons < 12) {
1106		years += 1;
1107		mdays += real_month_table[is_leapyear(years)][mons];
1108	} else {
1109		tmp = ntpcal_days_in_months(mons);
1110		mdays += tmp.lo
1111		       + ntpcal_days_in_years(years + tmp.hi)
1112		       - ntpcal_days_in_years(years);
1113	}
1114
1115	return mdays;
1116}
1117
1118/*
1119 *---------------------------------------------------------------------
1120 * Convert elapsed days and the hour/minute/second information into
1121 * total seconds.
1122 *
1123 * If 'isvalid' is not NULL, do a range check on the time specification
1124 * and tell if the time input is in the normal range, permitting for a
1125 * single leapsecond.
1126 *---------------------------------------------------------------------
1127 */
1128int32_t
1129ntpcal_etime_to_seconds(
1130	int32_t hours,
1131	int32_t minutes,
1132	int32_t seconds
1133	)
1134{
1135	int32_t res;
1136
1137	res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds;
1138
1139	return res;
1140}
1141
1142/*
1143 *---------------------------------------------------------------------
1144 * Convert the date part of a 'struct tm' (that is, year, month,
1145 * day-of-month) into the RD of that day.
1146 *---------------------------------------------------------------------
1147 */
1148int32_t
1149ntpcal_tm_to_rd(
1150	const struct tm *utm
1151	)
1152{
1153	return ntpcal_edate_to_eradays(utm->tm_year + 1899,
1154				       utm->tm_mon,
1155				       utm->tm_mday - 1) + 1;
1156}
1157
1158/*
1159 *---------------------------------------------------------------------
1160 * Convert the date part of a 'struct calendar' (that is, year, month,
1161 * day-of-month) into the RD of that day.
1162 *---------------------------------------------------------------------
1163 */
1164int32_t
1165ntpcal_date_to_rd(
1166	const struct calendar *jd
1167	)
1168{
1169	return ntpcal_edate_to_eradays((int32_t)jd->year - 1,
1170				       (int32_t)jd->month - 1,
1171				       (int32_t)jd->monthday - 1) + 1;
1172}
1173
1174/*
1175 *---------------------------------------------------------------------
1176 * convert a year number to rata die of year start
1177 *---------------------------------------------------------------------
1178 */
1179int32_t
1180ntpcal_year_to_ystart(
1181	int32_t year
1182	)
1183{
1184	return ntpcal_days_in_years(year - 1) + 1;
1185}
1186
1187/*
1188 *---------------------------------------------------------------------
1189 * For a given RD, get the RD of the associated year start,
1190 * that is, the RD of the last January,1st on or before that day.
1191 *---------------------------------------------------------------------
1192 */
1193int32_t
1194ntpcal_rd_to_ystart(
1195	int32_t rd
1196	)
1197{
1198	/*
1199	 * Rather simple exercise: split the day number into elapsed
1200	 * years and elapsed days, then remove the elapsed days from the
1201	 * input value. Nice'n sweet...
1202	 */
1203	return rd - ntpcal_split_eradays(rd - 1, NULL).lo;
1204}
1205
1206/*
1207 *---------------------------------------------------------------------
1208 * For a given RD, get the RD of the associated month start.
1209 *---------------------------------------------------------------------
1210 */
1211int32_t
1212ntpcal_rd_to_mstart(
1213	int32_t rd
1214	)
1215{
1216	ntpcal_split split;
1217	int	     leaps;
1218
1219	split = ntpcal_split_eradays(rd - 1, &leaps);
1220	split = ntpcal_split_yeardays(split.lo, leaps);
1221
1222	return rd - split.lo;
1223}
1224
1225/*
1226 *---------------------------------------------------------------------
1227 * take a 'struct calendar' and get the seconds-of-day from it.
1228 *---------------------------------------------------------------------
1229 */
1230int32_t
1231ntpcal_date_to_daysec(
1232	const struct calendar *jd
1233	)
1234{
1235	return ntpcal_etime_to_seconds(jd->hour, jd->minute,
1236				       jd->second);
1237}
1238
1239/*
1240 *---------------------------------------------------------------------
1241 * take a 'struct tm' and get the seconds-of-day from it.
1242 *---------------------------------------------------------------------
1243 */
1244int32_t
1245ntpcal_tm_to_daysec(
1246	const struct tm *utm
1247	)
1248{
1249	return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min,
1250				       utm->tm_sec);
1251}
1252
1253/*
1254 *---------------------------------------------------------------------
1255 * take a 'struct calendar' and convert it to a 'time_t'
1256 *---------------------------------------------------------------------
1257 */
1258time_t
1259ntpcal_date_to_time(
1260	const struct calendar *jd
1261	)
1262{
1263	vint64  join;
1264	int32_t days, secs;
1265
1266	days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS;
1267	secs = ntpcal_date_to_daysec(jd);
1268	join = ntpcal_dayjoin(days, secs);
1269
1270	return vint64_to_time(&join);
1271}
1272
1273
1274/*
1275 * ==================================================================
1276 *
1277 * extended and unchecked variants of caljulian/caltontp
1278 *
1279 * ==================================================================
1280 */
1281int
1282ntpcal_ntp64_to_date(
1283	struct calendar *jd,
1284	const vint64    *ntp
1285	)
1286{
1287	ntpcal_split ds;
1288
1289	ds = ntpcal_daysplit(ntp);
1290	ds.hi += ntpcal_daysec_to_date(jd, ds.lo);
1291
1292	return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS);
1293}
1294
1295int
1296ntpcal_ntp_to_date(
1297	struct calendar *jd,
1298	uint32_t	 ntp,
1299	const time_t	*piv
1300	)
1301{
1302	vint64	ntp64;
1303
1304	/*
1305	 * Unfold ntp time around current time into NTP domain. Split
1306	 * into days and seconds, shift days into CE domain and
1307	 * process the parts.
1308	 */
1309	ntp64 = ntpcal_ntp_to_ntp(ntp, piv);
1310	return ntpcal_ntp64_to_date(jd, &ntp64);
1311}
1312
1313
1314vint64
1315ntpcal_date_to_ntp64(
1316	const struct calendar *jd
1317	)
1318{
1319	/*
1320	 * Convert date to NTP. Ignore yearday, use d/m/y only.
1321	 */
1322	return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS,
1323			      ntpcal_date_to_daysec(jd));
1324}
1325
1326
1327uint32_t
1328ntpcal_date_to_ntp(
1329	const struct calendar *jd
1330	)
1331{
1332	/*
1333	 * Get lower half of 64-bit NTP timestamp from date/time.
1334	 */
1335	return ntpcal_date_to_ntp64(jd).d_s.lo;
1336}
1337
1338
1339
1340/*
1341 * ==================================================================
1342 *
1343 * day-of-week calculations
1344 *
1345 * ==================================================================
1346 */
1347/*
1348 * Given a RataDie and a day-of-week, calculate a RDN that is reater-than,
1349 * greater-or equal, closest, less-or-equal or less-than the given RDN
1350 * and denotes the given day-of-week
1351 */
1352int32_t
1353ntpcal_weekday_gt(
1354	int32_t rdn,
1355	int32_t dow
1356	)
1357{
1358	return ntpcal_periodic_extend(rdn+1, dow, 7);
1359}
1360
1361int32_t
1362ntpcal_weekday_ge(
1363	int32_t rdn,
1364	int32_t dow
1365	)
1366{
1367	return ntpcal_periodic_extend(rdn, dow, 7);
1368}
1369
1370int32_t
1371ntpcal_weekday_close(
1372	int32_t rdn,
1373	int32_t dow
1374	)
1375{
1376	return ntpcal_periodic_extend(rdn-3, dow, 7);
1377}
1378
1379int32_t
1380ntpcal_weekday_le(
1381	int32_t rdn,
1382	int32_t dow
1383	)
1384{
1385	return ntpcal_periodic_extend(rdn, dow, -7);
1386}
1387
1388int32_t
1389ntpcal_weekday_lt(
1390	int32_t rdn,
1391	int32_t dow
1392	)
1393{
1394	return ntpcal_periodic_extend(rdn-1, dow, -7);
1395}
1396
1397/*
1398 * ==================================================================
1399 *
1400 * ISO week-calendar conversions
1401 *
1402 * The ISO8601 calendar defines a calendar of years, weeks and weekdays.
1403 * It is related to the Gregorian calendar, and a ISO year starts at the
1404 * Monday closest to Jan,1st of the corresponding Gregorian year.  A ISO
1405 * calendar year has always 52 or 53 weeks, and like the Grogrian
1406 * calendar the ISO8601 calendar repeats itself every 400 years, or
1407 * 146097 days, or 20871 weeks.
1408 *
1409 * While it is possible to write ISO calendar functions based on the
1410 * Gregorian calendar functions, the following implementation takes a
1411 * different approach, based directly on years and weeks.
1412 *
1413 * Analysis of the tabulated data shows that it is not possible to
1414 * interpolate from years to weeks over a full 400 year range; cyclic
1415 * shifts over 400 years do not provide a solution here. But it *is*
1416 * possible to interpolate over every single century of the 400-year
1417 * cycle. (The centennial leap year rule seems to be the culprit here.)
1418 *
1419 * It can be shown that a conversion from years to weeks can be done
1420 * using a linear transformation of the form
1421 *
1422 *   w = floor( y * a + b )
1423 *
1424 * where the slope a must hold to
1425 *
1426 *  52.1780821918 <= a < 52.1791044776
1427 *
1428 * and b must be chosen according to the selected slope and the number
1429 * of the century in a 400-year period.
1430 *
1431 * The inverse calculation can also be done in this way. Careful scaling
1432 * provides an unlimited set of integer coefficients a,k,b that enable
1433 * us to write the calulation in the form
1434 *
1435 *   w = (y * a	 + b ) / k
1436 *   y = (w * a' + b') / k'
1437 *
1438 * In this implementation the values of k and k' are chosen to be
1439 * smallest possible powers of two, so the division can be implemented
1440 * as shifts if the optimiser chooses to do so.
1441 *
1442 * ==================================================================
1443 */
1444
1445/*
1446 * Given a number of elapsed (ISO-)years since the begin of the
1447 * christian era, return the number of elapsed weeks corresponding to
1448 * the number of years.
1449 */
1450int32_t
1451isocal_weeks_in_years(
1452	int32_t years
1453	)
1454{
1455	/*
1456	 * use: w = (y * 53431 + b[c]) / 1024 as interpolation
1457	 */
1458	static const int32_t bctab[4] = { 449, 157, 889, 597 };
1459	int32_t cycle; /* full gregorian cycle */
1460	int32_t cents; /* full centuries	   */
1461	int32_t weeks; /* accumulated weeks	   */
1462
1463	/* split off full calendar cycles, using floor division */
1464	cycle = years / 400;
1465	years = years % 400;
1466	if (years < 0) {
1467		cycle -= 1;
1468		years += 400;
1469	}
1470
1471	/* split off full centuries */
1472	cents = years / 100;
1473	years = years % 100;
1474
1475	/*
1476	 * calculate elapsed weeks, taking into account that the
1477	 * first, third and fourth century have 5218 weeks but the
1478	 * second century falls short by one week.
1479	 */
1480	weeks = (years * 53431 + bctab[cents]) / 1024;
1481
1482	return cycle * GREGORIAN_CYCLE_WEEKS
1483	     + cents * 5218 - (cents > 1)
1484	     + weeks;
1485}
1486
1487/*
1488 * Given a number of elapsed weeks since the begin of the christian
1489 * era, split this number into the number of elapsed years in res.hi
1490 * and the excessive number of weeks in res.lo. (That is, res.lo is
1491 * the number of elapsed weeks in the remaining partial year.)
1492 */
1493ntpcal_split
1494isocal_split_eraweeks(
1495	int32_t weeks
1496	)
1497{
1498	/*
1499	 * use: y = (w * 157 + b[c]) / 8192 as interpolation
1500	 */
1501	static const int32_t bctab[4] = { 85, 131, 17, 62 };
1502	ntpcal_split res;
1503	int32_t	     cents;
1504
1505	/*
1506	 * split off 400-year cycles, using the fact that a 400-year
1507	 * cycle has 146097 days, which is exactly 20871 weeks.
1508	 */
1509	res.hi = weeks / GREGORIAN_CYCLE_WEEKS;
1510	res.lo = weeks % GREGORIAN_CYCLE_WEEKS;
1511	if (res.lo < 0) {
1512		res.hi -= 1;
1513		res.lo += GREGORIAN_CYCLE_WEEKS;
1514	}
1515	res.hi *= 400;
1516
1517	/*
1518	 * split off centuries, taking into account that the first,
1519	 * third and fourth century have 5218 weeks but that the
1520	 * second century falls short by one week.
1521	 */
1522	res.lo += (res.lo >= 10435);
1523	cents	= res.lo / 5218;
1524	res.lo %= 5218;		/* res.lo is weeks in century now */
1525
1526	/* convert elapsed weeks in century to elapsed years and weeks */
1527	res.lo	= res.lo * 157 + bctab[cents];
1528	res.hi += cents * 100 + res.lo / 8192;
1529	res.lo	= (res.lo % 8192) / 157;
1530
1531	return res;
1532}
1533
1534/*
1535 * Given a second in the NTP time scale and a pivot, expand the NTP
1536 * time stamp around the pivot and convert into an ISO calendar time
1537 * stamp.
1538 */
1539int
1540isocal_ntp64_to_date(
1541	struct isodate *id,
1542	const vint64   *ntp
1543	)
1544{
1545	ntpcal_split ds;
1546	int32_t      ts[3];
1547
1548	/*
1549	 * Split NTP time into days and seconds, shift days into CE
1550	 * domain and process the parts.
1551	 */
1552	ds = ntpcal_daysplit(ntp);
1553
1554	/* split time part */
1555	ds.hi += priv_timesplit(ts, ds.lo);
1556	id->hour   = (uint8_t)ts[0];
1557	id->minute = (uint8_t)ts[1];
1558	id->second = (uint8_t)ts[2];
1559
1560	/* split date part */
1561	ds.lo = ds.hi + DAY_NTP_STARTS - 1;	/* elapsed era days  */
1562	ds.hi = ds.lo / 7;			/* elapsed era weeks */
1563	ds.lo = ds.lo % 7;			/* elapsed week days */
1564	if (ds.lo < 0) {			/* floor division!   */
1565		ds.hi -= 1;
1566		ds.lo += 7;
1567	}
1568	id->weekday = (uint8_t)ds.lo + 1;	/* weekday result    */
1569
1570	ds = isocal_split_eraweeks(ds.hi);	/* elapsed years&week*/
1571	id->year = (uint16_t)ds.hi + 1;		/* shift to current  */
1572	id->week = (uint8_t )ds.lo + 1;
1573
1574	return (ds.hi >= 0 && ds.hi < 0x0000FFFF);
1575}
1576
1577int
1578isocal_ntp_to_date(
1579	struct isodate *id,
1580	uint32_t	ntp,
1581	const time_t   *piv
1582	)
1583{
1584	vint64	ntp64;
1585
1586	/*
1587	 * Unfold ntp time around current time into NTP domain, then
1588	 * convert the full time stamp.
1589	 */
1590	ntp64 = ntpcal_ntp_to_ntp(ntp, piv);
1591	return isocal_ntp64_to_date(id, &ntp64);
1592}
1593
1594/*
1595 * Convert a ISO date spec into a second in the NTP time scale,
1596 * properly truncated to 32 bit.
1597 */
1598vint64
1599isocal_date_to_ntp64(
1600	const struct isodate *id
1601	)
1602{
1603	int32_t weeks, days, secs;
1604
1605	weeks = isocal_weeks_in_years((int32_t)id->year - 1)
1606	      + (int32_t)id->week - 1;
1607	days = weeks * 7 + (int32_t)id->weekday;
1608	/* days is RDN of ISO date now */
1609	secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second);
1610
1611	return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs);
1612}
1613
1614uint32_t
1615isocal_date_to_ntp(
1616	const struct isodate *id
1617	)
1618{
1619	/*
1620	 * Get lower half of 64-bit NTP timestamp from date/time.
1621	 */
1622	return isocal_date_to_ntp64(id).d_s.lo;
1623}
1624
1625/* -*-EOF-*- */
1626