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