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