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