ntp_calendar.c revision 338530
138032Speter/*
2182352Sgshapiro * ntp_calendar.c - calendar and helper functions
364562Sgshapiro *
438032Speter * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
538032Speter * The contents of 'html/copyright.html' apply.
638032Speter *
738032Speter * --------------------------------------------------------------------
838032Speter * Some notes on the implementation:
938032Speter *
1038032Speter * Calendar algorithms thrive on the division operation, which is one of
1138032Speter * the slowest numerical operations in any CPU. What saves us here from
1238032Speter * abysmal performance is the fact that all divisions are divisions by
1338032Speter * constant numbers, and most compilers can do this by a multiplication
1490792Sgshapiro * operation.  But this might not work when using the div/ldiv/lldiv
1590792Sgshapiro * function family, because many compilers are not able to do inline
16168515Sgshapiro * expansion of the code with following optimisation for the
1790792Sgshapiro * constant-divider case.
1890792Sgshapiro *
1990792Sgshapiro * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which
2038032Speter * are inherently target dependent. Nothing that could not be cured with
2190792Sgshapiro * autoconf, but still a mess...
22132943Sgshapiro *
2364562Sgshapiro * Furthermore, we need floor division in many places. C either leaves
2438032Speter * the division behaviour undefined (< C99) or demands truncation to
2538032Speter * zero (>= C99), so additional steps are required to make sure the
2638032Speter * algorithms work. The {l,ll}div function family is requested to
2764562Sgshapiro * truncate towards zero, which is also the wrong direction for our
2838032Speter * purpose.
29182352Sgshapiro *
3038032Speter * For all this, all divisions by constant are coded manually, even when
3138032Speter * there is a joined div/mod operation: The optimiser should sort that
3264562Sgshapiro * out, if possible. Most of the calculations are done with unsigned
3364562Sgshapiro * types, explicitely using two's complement arithmetics where
3464562Sgshapiro * necessary. This minimises the dependecies to compiler and target,
3564562Sgshapiro * while still giving reasonable to good performance.
3690792Sgshapiro *
3790792Sgshapiro * The implementation uses a few tricks that exploit properties of the
3890792Sgshapiro * two's complement: Floor division on negative dividents can be
3990792Sgshapiro * executed by using the one's complement of the divident. One's
4090792Sgshapiro * complement can be easily created using XOR and a mask.
4190792Sgshapiro *
4290792Sgshapiro * Finally, check for overflow conditions is minimal. There are only two
4390792Sgshapiro * calculation steps in the whole calendar that suffer from an internal
4490792Sgshapiro * overflow, and these conditions are checked: errno is set to EDOM and
4590792Sgshapiro * the results are clamped/saturated in this case.  All other functions
4690792Sgshapiro * do not suffer from internal overflow and simply return the result
4790792Sgshapiro * truncated to 32 bits.
4877349Sgshapiro *
4990792Sgshapiro * This is a sacrifice made for execution speed.  Since a 32-bit day
5090792Sgshapiro * counter covers +/- 5,879,610 years and the clamp limits the effective
5190792Sgshapiro * range to +/-2.9 million years, this should not pose a problem here.
5280785Sgshapiro *
5377349Sgshapiro */
5490792Sgshapiro
5564562Sgshapiro#include <config.h>
5638032Speter#include <sys/types.h>
5738032Speter
5838032Speter#include "ntp_types.h"
5938032Speter#include "ntp_calendar.h"
6038032Speter#include "ntp_stdlib.h"
6138032Speter#include "ntp_fp.h"
6238032Speter#include "ntp_unixtime.h"
6338032Speter
6464562Sgshapiro/* For now, let's take the conservative approach: if the target property
6538032Speter * macros are not defined, check a few well-known compiler/architecture
6638032Speter * settings. Default is to assume that the representation of signed
6738032Speter * integers is unknown and shift-arithmetic-right is not available.
6838032Speter */
6938032Speter#ifndef TARGET_HAS_2CPL
7038032Speter# if defined(__GNUC__)
7138032Speter#  if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
7290792Sgshapiro#   define TARGET_HAS_2CPL 1
7338032Speter#  else
7438032Speter#   define TARGET_HAS_2CPL 0
7538032Speter#  endif
7638032Speter# elif defined(_MSC_VER)
7738032Speter#  if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM)
7838032Speter#   define TARGET_HAS_2CPL 1
7938032Speter#  else
8038032Speter#   define TARGET_HAS_2CPL 0
81111823Sgshapiro#  endif
8238032Speter# else
8338032Speter#  define TARGET_HAS_2CPL 0
8490792Sgshapiro# endif
8590792Sgshapiro#endif
8690792Sgshapiro
8790792Sgshapiro#ifndef TARGET_HAS_SAR
8890792Sgshapiro# define TARGET_HAS_SAR 0
8990792Sgshapiro#endif
9090792Sgshapiro
9138032Speter/*
9238032Speter *---------------------------------------------------------------------
9338032Speter * replacing the 'time()' function
9438032Speter *---------------------------------------------------------------------
9564562Sgshapiro */
9638032Speter
9738032Speterstatic systime_func_ptr systime_func = &time;
9838032Speterstatic inline time_t now(void);
9990792Sgshapiro
10064562Sgshapiro
10190792Sgshapirosystime_func_ptr
10238032Speterntpcal_set_timefunc(
10338032Speter	systime_func_ptr nfunc
10438032Speter	)
10564562Sgshapiro{
10638032Speter	systime_func_ptr res;
10790792Sgshapiro
10838032Speter	res = systime_func;
10964562Sgshapiro	if (NULL == nfunc)
11064562Sgshapiro		nfunc = &time;
11164562Sgshapiro	systime_func = nfunc;
11264562Sgshapiro
11364562Sgshapiro	return res;
11464562Sgshapiro}
11564562Sgshapiro
11664562Sgshapiro
11764562Sgshapirostatic inline time_t
11890792Sgshapironow(void)
11990792Sgshapiro{
12090792Sgshapiro	return (*systime_func)(NULL);
12190792Sgshapiro}
12290792Sgshapiro
12390792Sgshapiro/*
12490792Sgshapiro *---------------------------------------------------------------------
12564562Sgshapiro * Get sign extension mask and unsigned 2cpl rep for a signed integer
12690792Sgshapiro *---------------------------------------------------------------------
12790792Sgshapiro */
12890792Sgshapiro
12990792Sgshapirostatic inline uint32_t
13090792Sgshapiroint32_sflag(
13190792Sgshapiro	const int32_t v)
132112810Sgshapiro{
13390792Sgshapiro#   if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4
13490792Sgshapiro
13590792Sgshapiro	/* Let's assume that shift is the fastest way to get the sign
13690792Sgshapiro	 * extension of of a signed integer. This might not always be
13790792Sgshapiro	 * true, though -- On 8bit CPUs or machines without barrel
13890792Sgshapiro	 * shifter this will kill the performance. So we make sure
13990792Sgshapiro	 * we do this only if 'int' has at least 4 bytes.
14090792Sgshapiro	 */
14190792Sgshapiro	return (uint32_t)(v >> 31);
14290792Sgshapiro
14390792Sgshapiro#   else
14490792Sgshapiro
14590792Sgshapiro	/* This should be a rather generic approach for getting a sign
14690792Sgshapiro	 * extension mask...
14790792Sgshapiro	 */
14890792Sgshapiro	return UINT32_C(0) - (uint32_t)(v < 0);
14938032Speter
15038032Speter#   endif
15138032Speter}
15238032Speter
15338032Speterstatic inline uint32_t
15438032Speterint32_to_uint32_2cpl(
15538032Speter	const int32_t v)
15638032Speter{
15738032Speter	uint32_t vu;
15838032Speter
15938032Speter#   if TARGET_HAS_2CPL
16038032Speter
16138032Speter	/* Just copy through the 32 bits from the signed value if we're
16264562Sgshapiro	 * on a two's complement target.
16390792Sgshapiro	 */
16490792Sgshapiro	vu = (uint32_t)v;
16590792Sgshapiro
16664562Sgshapiro#   else
16790792Sgshapiro
16890792Sgshapiro	/* Convert from signed int to unsigned int two's complement. Do
16938032Speter	 * not make any assumptions about the representation of signed
17090792Sgshapiro	 * integers, but make sure signed integer overflow cannot happen
17190792Sgshapiro	 * here. A compiler on a two's complement target *might* find
17238032Speter	 * out that this is just a complicated cast (as above), but your
17338032Speter	 * mileage might vary.
17438032Speter	 */
17564562Sgshapiro	if (v < 0)
17664562Sgshapiro		vu = ~(uint32_t)(-(v + 1));
17790792Sgshapiro	else
17890792Sgshapiro		vu = (uint32_t)v;
17990792Sgshapiro
18090792Sgshapiro#   endif
18190792Sgshapiro
18290792Sgshapiro	return vu;
18390792Sgshapiro}
18490792Sgshapiro
18590792Sgshapirostatic inline int32_t
186132943Sgshapirouint32_2cpl_to_int32(
18790792Sgshapiro	const uint32_t vu)
18864562Sgshapiro{
18990792Sgshapiro	int32_t v;
19038032Speter
19138032Speter#   if TARGET_HAS_2CPL
19238032Speter
19390792Sgshapiro	/* Just copy through the 32 bits from the unsigned value if
19466494Sgshapiro	 * we're on a two's complement target.
19590792Sgshapiro	 */
19638032Speter	v = (int32_t)vu;
19790792Sgshapiro
19838032Speter#   else
19938032Speter
20038032Speter	/* Convert to signed integer, making sure signed integer
20138032Speter	 * overflow cannot happen. Again, the optimiser might or might
20238032Speter	 * not find out that this is just a copy of 32 bits on a target
20390792Sgshapiro	 * with two's complement representation for signed integers.
20490792Sgshapiro	 */
20590792Sgshapiro	if (vu > INT32_MAX)
20638032Speter		v = -(int32_t)(~vu) - 1;
20790792Sgshapiro	else
20890792Sgshapiro		v = (int32_t)vu;
20990792Sgshapiro
21090792Sgshapiro#   endif
21190792Sgshapiro
21290792Sgshapiro	return v;
21390792Sgshapiro}
21490792Sgshapiro
21590792Sgshapiro/* Some of the calculations need to multiply the input by 4 before doing
21690792Sgshapiro * a division. This can cause overflow and strange results. Therefore we
217110560Sgshapiro * clamp / saturate the input operand. And since we do the calculations
218110560Sgshapiro * in unsigned int with an extra sign flag/mask, we only loose one bit
219110560Sgshapiro * of the input value range.
220110560Sgshapiro */
22138032Speterstatic inline uint32_t
22238032Speteruint32_saturate(
22338032Speter	uint32_t vu,
22438032Speter	uint32_t mu)
22538032Speter{
22638032Speter	static const uint32_t limit = UINT32_MAX/4u;
22790792Sgshapiro	if ((mu ^ vu) > limit) {
22838032Speter		vu    = mu ^ limit;
22938032Speter		errno = EDOM;
23038032Speter	}
23138032Speter	return vu;
23290792Sgshapiro}
23338032Speter
23438032Speter/*
235168515Sgshapiro *---------------------------------------------------------------------
23638032Speter * Convert between 'time_t' and 'vint64'
23790792Sgshapiro *---------------------------------------------------------------------
23890792Sgshapiro */
23977349Sgshapirovint64
24090792Sgshapirotime_to_vint64(
24190792Sgshapiro	const time_t * ptt
24290792Sgshapiro	)
24377349Sgshapiro{
24490792Sgshapiro	vint64 res;
24538032Speter	time_t tt;
24690792Sgshapiro
24790792Sgshapiro	tt = *ptt;
24864562Sgshapiro
24938032Speter#   if SIZEOF_TIME_T <= 4
25038032Speter
25138032Speter	res.D_s.hi = 0;
25238032Speter	if (tt < 0) {
25338032Speter		res.D_s.lo = (uint32_t)-tt;
25438032Speter		M_NEG(res.D_s.hi, res.D_s.lo);
25538032Speter	} else {
25638032Speter		res.D_s.lo = (uint32_t)tt;
25738032Speter	}
25838032Speter
25990792Sgshapiro#   elif defined(HAVE_INT64)
26090792Sgshapiro
26138032Speter	res.q_s = tt;
26290792Sgshapiro
26390792Sgshapiro#   else
26438032Speter	/*
26590792Sgshapiro	 * shifting negative signed quantities is compiler-dependent, so
26690792Sgshapiro	 * we better avoid it and do it all manually. And shifting more
26738032Speter	 * than the width of a quantity is undefined. Also a don't do!
26890792Sgshapiro	 */
26990792Sgshapiro	if (tt < 0) {
27038032Speter		tt = -tt;
271132943Sgshapiro		res.D_s.lo = (uint32_t)tt;
27238032Speter		res.D_s.hi = (uint32_t)(tt >> 32);
273132943Sgshapiro		M_NEG(res.D_s.hi, res.D_s.lo);
27438032Speter	} else {
27538032Speter		res.D_s.lo = (uint32_t)tt;
27690792Sgshapiro		res.D_s.hi = (uint32_t)(tt >> 32);
27790792Sgshapiro	}
27890792Sgshapiro
27964562Sgshapiro#   endif
28090792Sgshapiro
28164562Sgshapiro	return res;
28290792Sgshapiro}
28364562Sgshapiro
28464562Sgshapiro
28538032Spetertime_t
28690792Sgshapirovint64_to_time(
28790792Sgshapiro	const vint64 *tv
28890792Sgshapiro	)
28990792Sgshapiro{
29090792Sgshapiro	time_t res;
29138032Speter
29290792Sgshapiro#   if SIZEOF_TIME_T <= 4
29338032Speter
29490792Sgshapiro	res = (time_t)tv->D_s.lo;
29590792Sgshapiro
29690792Sgshapiro#   elif defined(HAVE_INT64)
29790792Sgshapiro
29890792Sgshapiro	res = (time_t)tv->q_s;
29990792Sgshapiro
30038032Speter#   else
30138032Speter
30238032Speter	res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo;
30338032Speter
30464562Sgshapiro#   endif
305182352Sgshapiro
306182352Sgshapiro	return res;
307182352Sgshapiro}
30838032Speter
30938032Speter/*
31064562Sgshapiro *---------------------------------------------------------------------
31138032Speter * Get the build date & time
312168515Sgshapiro *---------------------------------------------------------------------
31338032Speter */
31438032Speterint
31538032Speterntpcal_get_build_date(
31638032Speter	struct calendar * jd
31790792Sgshapiro	)
31890792Sgshapiro{
31938032Speter	/* The C standard tells us the format of '__DATE__':
32090792Sgshapiro	 *
32190792Sgshapiro	 * __DATE__ The date of translation of the preprocessing
32238032Speter	 * translation unit: a character string literal of the form "Mmm
32338032Speter	 * dd yyyy", where the names of the months are the same as those
32464562Sgshapiro	 * generated by the asctime function, and the first character of
32538032Speter	 * dd is a space character if the value is less than 10. If the
32638032Speter	 * date of translation is not available, an
32790792Sgshapiro	 * implementation-defined valid date shall be supplied.
32864562Sgshapiro	 *
32938032Speter	 * __TIME__ The time of translation of the preprocessing
33090792Sgshapiro	 * translation unit: a character string literal of the form
33177349Sgshapiro	 * "hh:mm:ss" as in the time generated by the asctime
33294334Sgshapiro	 * function. If the time of translation is not available, an
33377349Sgshapiro	 * implementation-defined valid time shall be supplied.
33477349Sgshapiro	 *
33590792Sgshapiro	 * Note that MSVC declares DATE and TIME to be in the local time
33677349Sgshapiro	 * zone, while neither the C standard nor the GCC docs make any
33794334Sgshapiro	 * statement about this. As a result, we may be +/-12hrs off
33894334Sgshapiro	 * UTC.  But for practical purposes, this should not be a
33994334Sgshapiro	 * problem.
34094334Sgshapiro	 *
34194334Sgshapiro	 */
34290792Sgshapiro#   ifdef MKREPRO_DATE
34338032Speter	static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE;
34438032Speter#   else
34538032Speter	static const char build[] = __TIME__ "/" __DATE__;
34638032Speter#   endif
34738032Speter	static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
34838032Speter
34938032Speter	char		  monstr[4];
35038032Speter	const char *	  cp;
35138032Speter	unsigned short	  hour, minute, second, day, year;
35238032Speter 	/* Note: The above quantities are used for sscanf 'hu' format,
35338032Speter	 * so using 'uint16_t' is contra-indicated!
35464562Sgshapiro	 */
35590792Sgshapiro
35690792Sgshapiro#   ifdef DEBUG
35790792Sgshapiro	static int        ignore  = 0;
35890792Sgshapiro#   endif
35990792Sgshapiro
36090792Sgshapiro	ZERO(*jd);
36190792Sgshapiro	jd->year     = 1970;
36290792Sgshapiro	jd->month    = 1;
36390792Sgshapiro	jd->monthday = 1;
36490792Sgshapiro
36590792Sgshapiro#   ifdef DEBUG
36690792Sgshapiro	/* check environment if build date should be ignored */
36790792Sgshapiro	if (0 == ignore) {
36890792Sgshapiro	    const char * envstr;
36990792Sgshapiro	    envstr = getenv("NTPD_IGNORE_BUILD_DATE");
37090792Sgshapiro	    ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes")));
37190792Sgshapiro	}
372132943Sgshapiro	if (ignore > 1)
373132943Sgshapiro	    return FALSE;
374132943Sgshapiro#   endif
375132943Sgshapiro
376132943Sgshapiro	if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu",
377132943Sgshapiro			&hour, &minute, &second, monstr, &day, &year)) {
378132943Sgshapiro		cp = strstr(mlist, monstr);
379132943Sgshapiro		if (NULL != cp) {
380132943Sgshapiro			jd->year     = year;
38190792Sgshapiro			jd->month    = (uint8_t)((cp - mlist) / 3 + 1);
382112810Sgshapiro			jd->monthday = (uint8_t)day;
38338032Speter			jd->hour     = (uint8_t)hour;
38438032Speter			jd->minute   = (uint8_t)minute;
38538032Speter			jd->second   = (uint8_t)second;
38638032Speter
38738032Speter			return TRUE;
38890792Sgshapiro		}
38994334Sgshapiro	}
39094334Sgshapiro
39138032Speter	return FALSE;
39290792Sgshapiro}
39390792Sgshapiro
39490792Sgshapiro
39590792Sgshapiro/*
39690792Sgshapiro *---------------------------------------------------------------------
39790792Sgshapiro * basic calendar stuff
39890792Sgshapiro *---------------------------------------------------------------------
39990792Sgshapiro */
40090792Sgshapiro
40190792Sgshapiro/* month table for a year starting with March,1st */
40290792Sgshapirostatic const uint16_t shift_month_table[13] = {
40390792Sgshapiro	0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366
40490792Sgshapiro};
40538032Speter
40690792Sgshapiro/* month tables for years starting with January,1st; regular & leap */
407182352Sgshapirostatic const uint16_t real_month_table[2][13] = {
408182352Sgshapiro	/* -*- table for regular years -*- */
409182352Sgshapiro	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
410182352Sgshapiro	/* -*- table for leap years -*- */
411182352Sgshapiro	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
412182352Sgshapiro};
413182352Sgshapiro
41490792Sgshapiro/*
41590792Sgshapiro * Some notes on the terminology:
41690792Sgshapiro *
41790792Sgshapiro * We use the proleptic Gregorian calendar, which is the Gregorian
41890792Sgshapiro * calendar extended in both directions ad infinitum. This totally
41990792Sgshapiro * disregards the fact that this calendar was invented in 1582, and
42090792Sgshapiro * was adopted at various dates over the world; sometimes even after
42190792Sgshapiro * the start of the NTP epoch.
42290792Sgshapiro *
42390792Sgshapiro * Normally date parts are given as current cycles, while time parts
42438032Speter * are given as elapsed cycles:
42590792Sgshapiro *
42690792Sgshapiro * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month,
427132943Sgshapiro * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed.
428132943Sgshapiro *
429132943Sgshapiro * The basic calculations for this calendar implementation deal with
430132943Sgshapiro * ELAPSED date units, which is the number of full years, full months
431132943Sgshapiro * and full days before a date: 1970-01-01 would be (1969, 0, 0) in
432132943Sgshapiro * that notation.
433132943Sgshapiro *
434132943Sgshapiro * To ease the numeric computations, month and day values outside the
435132943Sgshapiro * normal range are acceptable: 2001-03-00 will be treated as the day
436132943Sgshapiro * before 2001-03-01, 2000-13-32 will give the same result as
437132943Sgshapiro * 2001-02-01 and so on.
438132943Sgshapiro *
439132943Sgshapiro * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die'
440132943Sgshapiro * (day number).  This is the number of days elapsed since 0000-12-31
441132943Sgshapiro * in the proleptic Gregorian calendar. The begin of the Christian Era
442132943Sgshapiro * (0001-01-01) is RD(1).
443132943Sgshapiro */
444132943Sgshapiro
445132943Sgshapiro/*
446132943Sgshapiro * ====================================================================
447132943Sgshapiro *
44890792Sgshapiro * General algorithmic stuff
44990792Sgshapiro *
45038032Speter * ====================================================================
451132943Sgshapiro */
45290792Sgshapiro
45390792Sgshapiro/*
45438032Speter *---------------------------------------------------------------------
45564562Sgshapiro * Do a periodic extension of 'value' around 'pivot' with a period of
45664562Sgshapiro * 'cycle'.
45790792Sgshapiro *
45864562Sgshapiro * The result 'res' is a number that holds to the following properties:
45964562Sgshapiro *
46064562Sgshapiro *   1)	 res MOD cycle == value MOD cycle
461112810Sgshapiro *   2)	 pivot <= res < pivot + cycle
462112810Sgshapiro *	 (replace </<= with >/>= for negative cycles)
463112810Sgshapiro *
464112810Sgshapiro * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which
465112810Sgshapiro * is not the same as the '%' operator in C: C requires division to be
466112810Sgshapiro * a truncated division, where remainder and dividend have the same
467112810Sgshapiro * sign if the remainder is not zero, whereas floor division requires
468132943Sgshapiro * divider and modulus to have the same sign for a non-zero modulus.
46964562Sgshapiro *
47090792Sgshapiro * This function has some useful applications:
47190792Sgshapiro *
47290792Sgshapiro * + let Y be a calendar year and V a truncated 2-digit year: then
47364562Sgshapiro *	periodic_extend(Y-50, V, 100)
47464562Sgshapiro *   is the closest expansion of the truncated year with respect to
47590792Sgshapiro *   the full year, that is a 4-digit year with a difference of less
47690792Sgshapiro *   than 50 years to the year Y. ("century unfolding")
47790792Sgshapiro *
47890792Sgshapiro * + let T be a UN*X time stamp and V be seconds-of-day: then
47964562Sgshapiro *	perodic_extend(T-43200, V, 86400)
48038032Speter *   is a time stamp that has the same seconds-of-day as the input
48138032Speter *   value, with an absolute difference to T of <= 12hrs.  ("day
48238032Speter *   unfolding")
48338032Speter *
48490792Sgshapiro * + Wherever you have a truncated periodic value and a non-truncated
48590792Sgshapiro *   base value and you want to match them somehow...
48690792Sgshapiro *
48790792Sgshapiro * Basically, the function delivers 'pivot + (value - pivot) % cycle',
48890792Sgshapiro * but the implementation takes some pains to avoid internal signed
489132943Sgshapiro * integer overflows in the '(value - pivot) % cycle' part and adheres
49090792Sgshapiro * to the floor division convention.
491168515Sgshapiro *
49290792Sgshapiro * If 64bit scalars where available on all intended platforms, writing a
49390792Sgshapiro * version that uses 64 bit ops would be easy; writing a general
49477349Sgshapiro * division routine for 64bit ops on a platform that can only do
49564562Sgshapiro * 32/16bit divisions and is still performant is a bit more
49664562Sgshapiro * difficult. Since most usecases can be coded in a way that does only
49777349Sgshapiro * require the 32-bit version a 64bit version is NOT provided here.
49877349Sgshapiro *---------------------------------------------------------------------
49977349Sgshapiro */
50077349Sgshapiroint32_t
50177349Sgshapirontpcal_periodic_extend(
50277349Sgshapiro	int32_t pivot,
50364562Sgshapiro	int32_t value,
50464562Sgshapiro	int32_t cycle
50564562Sgshapiro	)
50664562Sgshapiro{
50764562Sgshapiro	uint32_t diff;
50864562Sgshapiro	char	 cpl = 0; /* modulo complement flag */
50977349Sgshapiro	char	 neg = 0; /* sign change flag	    */
51064562Sgshapiro
51164562Sgshapiro	/* make the cycle positive and adjust the flags */
51238032Speter	if (cycle < 0) {
51338032Speter		cycle = - cycle;
51438032Speter		neg ^= 1;
51538032Speter		cpl ^= 1;
51638032Speter	}
51738032Speter	/* guard against div by zero or one */
51838032Speter	if (cycle > 1) {
51938032Speter		/*
52038032Speter		 * Get absolute difference as unsigned quantity and
52138032Speter		 * the complement flag. This is done by always
52238032Speter		 * subtracting the smaller value from the bigger
52338032Speter		 * one.
52438032Speter		 */
52538032Speter		if (value >= pivot) {
52690792Sgshapiro			diff = int32_to_uint32_2cpl(value)
52738032Speter			     - int32_to_uint32_2cpl(pivot);
52890792Sgshapiro		} else {
52990792Sgshapiro			diff = int32_to_uint32_2cpl(pivot)
530159609Sgshapiro			     - int32_to_uint32_2cpl(value);
531159609Sgshapiro			cpl ^= 1;
53238032Speter		}
53338032Speter		diff %= (uint32_t)cycle;
53438032Speter		if (diff) {
535168515Sgshapiro			if (cpl)
53638032Speter				diff = (uint32_t)cycle - diff;
537168515Sgshapiro			if (neg)
53890792Sgshapiro				diff = ~diff + 1;
53964562Sgshapiro			pivot += uint32_2cpl_to_int32(diff);
54038032Speter		}
54138032Speter	}
54238032Speter	return pivot;
54338032Speter}
54490792Sgshapiro
54590792Sgshapiro/*---------------------------------------------------------------------
54690792Sgshapiro * Note to the casual reader
54738032Speter *
54838032Speter * In the next two functions you will find (or would have found...)
54938032Speter * the expression
55090792Sgshapiro *
55138032Speter *   res.Q_s -= 0x80000000;
55238032Speter *
55377349Sgshapiro * There was some ruckus about a possible programming error due to
55438032Speter * integer overflow and sign propagation.
55538032Speter *
55638032Speter * This assumption is based on a lack of understanding of the C
55790792Sgshapiro * standard. (Though this is admittedly not one of the most 'natural'
55890792Sgshapiro * aspects of the 'C' language and easily to get wrong.)
55938032Speter *
56038032Speter * see
56138032Speter *	http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
56238032Speter *	"ISO/IEC 9899:201x Committee Draft ��� April 12, 2011"
56338032Speter *	6.4.4.1 Integer constants, clause 5
56490792Sgshapiro *
56590792Sgshapiro * why there is no sign extension/overflow problem here.
56638032Speter *
56738032Speter * But to ease the minds of the doubtful, I added back the 'u' qualifiers
56838032Speter * that somehow got lost over the last years.
56938032Speter */
57090792Sgshapiro
57190792Sgshapiro
57290792Sgshapiro/*
57390792Sgshapiro *---------------------------------------------------------------------
57490792Sgshapiro * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X
57538032Speter * scale with proper epoch unfolding around a given pivot or the current
57638032Speter * system time. This function happily accepts negative pivot values as
57738032Speter * timestamps befor 1970-01-01, so be aware of possible trouble on
57864562Sgshapiro * platforms with 32bit 'time_t'!
57938032Speter *
58064562Sgshapiro * This is also a periodic extension, but since the cycle is 2^32 and
581168515Sgshapiro * the shift is 2^31, we can do some *very* fast math without explicit
58264562Sgshapiro * divisions.
58338032Speter *---------------------------------------------------------------------
58438032Speter */
58538032Spetervint64
58664562Sgshapirontpcal_ntp_to_time(
58764562Sgshapiro	uint32_t	ntp,
58838032Speter	const time_t *	pivot
58938032Speter	)
59038032Speter{
59190792Sgshapiro	vint64 res;
59264562Sgshapiro
59364562Sgshapiro#   if defined(HAVE_INT64)
59464562Sgshapiro
59538032Speter	res.q_s = (pivot != NULL)
59638032Speter		      ? *pivot
59738032Speter		      : now();
59838032Speter	res.Q_s -= 0x80000000u;		/* unshift of half range */
59938032Speter	ntp	-= (uint32_t)JAN_1970;	/* warp into UN*X domain */
60038032Speter	ntp	-= res.D_s.lo;		/* cycle difference	 */
60138032Speter	res.Q_s += (uint64_t)ntp;	/* get expanded time	 */
60290792Sgshapiro
60390792Sgshapiro#   else /* no 64bit scalars */
60438032Speter
60538032Speter	time_t tmp;
60638032Speter
60738032Speter	tmp = (pivot != NULL)
60838032Speter		  ? *pivot
60990792Sgshapiro		  : now();
61090792Sgshapiro	res = time_to_vint64(&tmp);
61138032Speter	M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u);
61290792Sgshapiro	ntp -= (uint32_t)JAN_1970;	/* warp into UN*X domain */
61364562Sgshapiro	ntp -= res.D_s.lo;		/* cycle difference	 */
61490792Sgshapiro	M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp);
61590792Sgshapiro
61690792Sgshapiro#   endif /* no 64bit scalars */
61790792Sgshapiro
61890792Sgshapiro	return res;
61990792Sgshapiro}
62090792Sgshapiro
62190792Sgshapiro/*
62238032Speter *---------------------------------------------------------------------
62338032Speter * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP
62490792Sgshapiro * scale with proper epoch unfolding around a given pivot or the current
62590792Sgshapiro * system time.
62690792Sgshapiro *
62738032Speter * Note: The pivot must be given in the UN*X time domain!
62890792Sgshapiro *
62990792Sgshapiro * This is also a periodic extension, but since the cycle is 2^32 and
63090792Sgshapiro * the shift is 2^31, we can do some *very* fast math without explicit
63190792Sgshapiro * divisions.
63290792Sgshapiro *---------------------------------------------------------------------
63390792Sgshapiro */
63490792Sgshapirovint64
63590792Sgshapirontpcal_ntp_to_ntp(
63690792Sgshapiro	uint32_t      ntp,
63790792Sgshapiro	const time_t *pivot
63890792Sgshapiro	)
63990792Sgshapiro{
64038032Speter	vint64 res;
64138032Speter
64238032Speter#   if defined(HAVE_INT64)
64338032Speter
64438032Speter	res.q_s = (pivot)
64538032Speter		      ? *pivot
64642575Speter		      : now();
64742575Speter	res.Q_s -= 0x80000000u;		/* unshift of half range */
64838032Speter	res.Q_s += (uint32_t)JAN_1970;	/* warp into NTP domain	 */
64990792Sgshapiro	ntp	-= res.D_s.lo;		/* cycle difference	 */
65038032Speter	res.Q_s += (uint64_t)ntp;	/* get expanded time	 */
65138032Speter
65238032Speter#   else /* no 64bit scalars */
65338032Speter
65438032Speter	time_t tmp;
65590792Sgshapiro
65638032Speter	tmp = (pivot)
65738032Speter		  ? *pivot
65890792Sgshapiro		  : now();
65990792Sgshapiro	res = time_to_vint64(&tmp);
66090792Sgshapiro	M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u);
66164562Sgshapiro	M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */
66238032Speter	ntp -= res.D_s.lo;		/* cycle difference	 */
66338032Speter	M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp);
66438032Speter
665157001Sgshapiro#   endif /* no 64bit scalars */
66638032Speter
66790792Sgshapiro	return res;
66838032Speter}
66938032Speter
67095154Sgshapiro
67195154Sgshapiro/*
67238032Speter * ====================================================================
67338032Speter *
67438032Speter * Splitting values to composite entities
67538032Speter *
67638032Speter * ====================================================================
67738032Speter */
67838032Speter
67964562Sgshapiro/*
68038032Speter *---------------------------------------------------------------------
68138032Speter * Split a 64bit seconds value into elapsed days in 'res.hi' and
68238032Speter * elapsed seconds since midnight in 'res.lo' using explicit floor
68338032Speter * division. This function happily accepts negative time values as
68438032Speter * timestamps before the respective epoch start.
68538032Speter *---------------------------------------------------------------------
68664562Sgshapiro */
68764562Sgshapirontpcal_split
68864562Sgshapirontpcal_daysplit(
68964562Sgshapiro	const vint64 *ts
69064562Sgshapiro	)
69164562Sgshapiro{
69264562Sgshapiro	ntpcal_split res;
69364562Sgshapiro	uint32_t Q;
69438032Speter
69538032Speter#   if defined(HAVE_INT64)
69638032Speter
69738032Speter	/* Manual floor division by SECSPERDAY. This uses the one's
69838032Speter	 * complement trick, too, but without an extra flag value: The
69990792Sgshapiro	 * flag would be 64bit, and that's a bit of overkill on a 32bit
70038032Speter	 * target that has to use a register pair for a 64bit number.
70138032Speter	 */
70290792Sgshapiro	if (ts->q_s < 0)
70338032Speter		Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY);
70438032Speter	else
705168515Sgshapiro		Q = (uint32_t)(ts->Q_s / SECSPERDAY);
70638032Speter
70738032Speter#   else
70890792Sgshapiro
70938032Speter	uint32_t ah, al, sflag, A;
71038032Speter
71190792Sgshapiro	/* get operand into ah/al (either ts or ts' one's complement,
71290792Sgshapiro	 * for later floor division)
71390792Sgshapiro	 */
71438032Speter	sflag = int32_sflag(ts->d_s.hi);
71538032Speter	ah = sflag ^ ts->D_s.hi;
71638032Speter	al = sflag ^ ts->D_s.lo;
717132943Sgshapiro
718132943Sgshapiro	/* Since 86400 == 128*675 we can drop the least 7 bits and
71938032Speter	 * divide by 675 instead of 86400. Then the maximum remainder
72038032Speter	 * after each devision step is 674, and we need 10 bits for
72138032Speter	 * that. So in the next step we can shift in 22 bits from the
72238032Speter	 * numerator.
72338032Speter	 *
72438032Speter	 * Therefore we load the accu with the top 13 bits (51..63) in
72590792Sgshapiro	 * the first shot. We don't have to remember the quotient -- it
72690792Sgshapiro	 * would be shifted out anyway.
72738032Speter	 */
72838032Speter	A = ah >> 19;
72938032Speter	if (A >= 675)
73038032Speter		A = (A % 675u);
73190792Sgshapiro
73290792Sgshapiro	/* Now assemble the remainder with bits 29..50 from the
73338032Speter	 * numerator and divide. This creates the upper ten bits of the
73438032Speter	 * quotient. (Well, the top 22 bits of a 44bit result. But that
73538032Speter	 * will be truncated to 32 bits anyway.)
73638032Speter	 */
73738032Speter	A = (A << 19) | (ah & 0x0007FFFFu);
73838032Speter	A = (A <<  3) | (al >> 29);
73938032Speter	Q = A / 675u;
74038032Speter	A = A % 675u;
74190792Sgshapiro
74238032Speter	/* Now assemble the remainder with bits 7..28 from the numerator
74338032Speter	 * and do a final division step.
74464562Sgshapiro	 */
74590792Sgshapiro	A = (A << 22) | ((al >> 7) & 0x003FFFFFu);
74638032Speter	Q = (Q << 22) | (A / 675u);
74764562Sgshapiro
74864562Sgshapiro	/* The last 7 bits get simply dropped, as they have no affect on
74964562Sgshapiro	 * the quotient when dividing by 86400.
75064562Sgshapiro	 */
75164562Sgshapiro
75264562Sgshapiro	/* apply sign correction and calculate the true floor
75364562Sgshapiro	 * remainder.
75464562Sgshapiro	 */
75564562Sgshapiro	Q ^= sflag;
75664562Sgshapiro
75764562Sgshapiro#   endif
75864562Sgshapiro
75938032Speter	res.hi = uint32_2cpl_to_int32(Q);
76064562Sgshapiro	res.lo = ts->D_s.lo - Q * SECSPERDAY;
76164562Sgshapiro
76264562Sgshapiro	return res;
76364562Sgshapiro}
76438032Speter
76564562Sgshapiro/*
766168515Sgshapiro *---------------------------------------------------------------------
76790792Sgshapiro * Split a 32bit seconds value into h/m/s and excessive days.  This
76864562Sgshapiro * function happily accepts negative time values as timestamps before
76964562Sgshapiro * midnight.
77064562Sgshapiro *---------------------------------------------------------------------
77164562Sgshapiro */
77264562Sgshapirostatic int32_t
77364562Sgshapiropriv_timesplit(
77464562Sgshapiro	int32_t split[3],
77564562Sgshapiro	int32_t ts
77664562Sgshapiro	)
777168515Sgshapiro{
77864562Sgshapiro	/* Do 3 chained floor divisions by positive constants, using the
779168515Sgshapiro	 * one's complement trick and factoring out the intermediate XOR
78090792Sgshapiro	 * ops to reduce the number of operations.
78164562Sgshapiro	 */
78264562Sgshapiro	uint32_t us, um, uh, ud, sflag;
78338032Speter
78464562Sgshapiro	sflag = int32_sflag(ts);
78564562Sgshapiro	us    = int32_to_uint32_2cpl(ts);
78664562Sgshapiro
78764562Sgshapiro	um = (sflag ^ us) / SECSPERMIN;
78890792Sgshapiro	uh = um / MINSPERHR;
78964562Sgshapiro	ud = uh / HRSPERDAY;
79038032Speter
79164562Sgshapiro	um ^= sflag;
79290792Sgshapiro	uh ^= sflag;
79371345Sgshapiro	ud ^= sflag;
79471345Sgshapiro
79590792Sgshapiro	split[0] = (int32_t)(uh - ud * HRSPERDAY );
79638032Speter	split[1] = (int32_t)(um - uh * MINSPERHR );
79738032Speter	split[2] = (int32_t)(us - um * SECSPERMIN);
79838032Speter
79990792Sgshapiro	return uint32_2cpl_to_int32(ud);
80090792Sgshapiro}
80164562Sgshapiro
80290792Sgshapiro/*
80338032Speter *---------------------------------------------------------------------
80438032Speter * Given the number of elapsed days in the calendar era, split this
80538032Speter * number into the number of elapsed years in 'res.hi' and the number
80638032Speter * of elapsed days of that year in 'res.lo'.
80790792Sgshapiro *
80838032Speter * if 'isleapyear' is not NULL, it will receive an integer that is 0 for
80938032Speter * regular years and a non-zero value for leap years.
81042575Speter *---------------------------------------------------------------------
81138032Speter */
81238032Speterntpcal_split
81338032Speterntpcal_split_eradays(
81438032Speter	int32_t days,
81538032Speter	int  *isleapyear
81638032Speter	)
81738032Speter{
81838032Speter	/* Use the fast cyclesplit algorithm here, to calculate the
81990792Sgshapiro	 * centuries and years in a century with one division each. This
82090792Sgshapiro	 * reduces the number of division operations to two, but is
82138032Speter	 * susceptible to internal range overflow. We make sure the
82290792Sgshapiro	 * input operands are in the safe range; this still gives us
82390792Sgshapiro	 * approx +/-2.9 million years.
82490792Sgshapiro	 */
82538032Speter	ntpcal_split res;
82638032Speter	int32_t	 n100, n001; /* calendar year cycles */
82738032Speter	uint32_t uday, Q, sflag;
82890792Sgshapiro
82990792Sgshapiro	/* split off centuries first */
83038032Speter	sflag = int32_sflag(days);
83138032Speter	uday  = uint32_saturate(int32_to_uint32_2cpl(days), sflag);
83238032Speter	uday  = (4u * uday) | 3u;
83338032Speter	Q    = sflag ^ ((sflag ^ uday) / GREGORIAN_CYCLE_DAYS);
83490792Sgshapiro	uday = uday - Q * GREGORIAN_CYCLE_DAYS;
83590792Sgshapiro	n100 = uint32_2cpl_to_int32(Q);
83690792Sgshapiro
83764562Sgshapiro	/* Split off years in century -- days >= 0 here, and we're far
83890792Sgshapiro	 * away from integer overflow trouble now. */
83938032Speter	uday |= 3;
84038032Speter	n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
841132943Sgshapiro	uday = uday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
84290792Sgshapiro
84390792Sgshapiro	/* Assemble the year and day in year */
84438032Speter	res.hi = n100 * 100 + n001;
84538032Speter	res.lo = uday / 4u;
84638032Speter
84738032Speter	/* Eventually set the leap year flag. Note: 0 <= n001 <= 99 and
84890792Sgshapiro	 * Q is still the two's complement representation of the
84938032Speter	 * centuries: The modulo 4 ops can be done with masking here.
85038032Speter	 * We also shift the year and the century by one, so the tests
85138032Speter	 * can be done against zero instead of 3.
85238032Speter	 */
85338032Speter	if (isleapyear)
85438032Speter		*isleapyear = !((n001+1) & 3)
855110560Sgshapiro		    && ((n001 != 99) || !((Q+1) & 3));
856110560Sgshapiro
857110560Sgshapiro	return res;
858110560Sgshapiro}
85938032Speter
86038032Speter/*
86138032Speter *---------------------------------------------------------------------
86238032Speter * Given a number of elapsed days in a year and a leap year indicator,
86338032Speter * split the number of elapsed days into the number of elapsed months in
86490792Sgshapiro * 'res.hi' and the number of elapsed days of that month in 'res.lo'.
86538032Speter *
86638032Speter * This function will fail and return {-1,-1} if the number of elapsed
86738032Speter * days is not in the valid range!
86864562Sgshapiro *---------------------------------------------------------------------
86964562Sgshapiro */
87090792Sgshapirontpcal_split
87164562Sgshapirontpcal_split_yeardays(
87264562Sgshapiro	int32_t eyd,
87338032Speter	int     isleapyear
87490792Sgshapiro	)
87590792Sgshapiro{
87690792Sgshapiro	ntpcal_split    res;
877168515Sgshapiro	const uint16_t *lt;	/* month length table	*/
87890792Sgshapiro
87990792Sgshapiro	/* check leap year flag and select proper table */
88090792Sgshapiro	lt = real_month_table[(isleapyear != 0)];
88138032Speter	if (0 <= eyd && eyd < lt[12]) {
88238032Speter		/* get zero-based month by approximation & correction step */
88338032Speter		res.hi = eyd >> 5;	   /* approx month; might be 1 too low */
88438032Speter		if (lt[res.hi + 1] <= eyd) /* fixup approximative month value  */
88538032Speter			res.hi += 1;
88638032Speter		res.lo = eyd - lt[res.hi];
88764562Sgshapiro	} else {
88864562Sgshapiro		res.lo = res.hi = -1;
88964562Sgshapiro	}
89064562Sgshapiro
89164562Sgshapiro	return res;
89238032Speter}
89390792Sgshapiro
89490792Sgshapiro/*
89538032Speter *---------------------------------------------------------------------
89638032Speter * Convert a RD into the date part of a 'struct calendar'.
89738032Speter *---------------------------------------------------------------------
89890792Sgshapiro */
89938032Speterint
90090792Sgshapirontpcal_rd_to_date(
90190792Sgshapiro	struct calendar *jd,
90290792Sgshapiro	int32_t		 rd
90338032Speter	)
90438032Speter{
90538032Speter	ntpcal_split split;
90638032Speter	int	     leapy;
90738032Speter	u_int	     ymask;
90838032Speter
90990792Sgshapiro	/* Get day-of-week first. Since rd is signed, the remainder can
91038032Speter	 * be in the range [-6..+6], but the assignment to an unsigned
91190792Sgshapiro	 * variable maps the negative values to positive values >=7.
91238032Speter	 * This makes the sign correction look strange, but adding 7
91390792Sgshapiro	 * causes the needed wrap-around into the desired value range of
91438032Speter	 * zero to six, both inclusive.
91538032Speter	 */
91638032Speter	jd->weekday = rd % DAYSPERWEEK;
91738032Speter	if (jd->weekday >= DAYSPERWEEK)	/* weekday is unsigned! */
91838032Speter		jd->weekday += DAYSPERWEEK;
91938032Speter
92038032Speter	split = ntpcal_split_eradays(rd - 1, &leapy);
92138032Speter	/* Get year and day-of-year, with overflow check. If any of the
92238032Speter	 * upper 16 bits is set after shifting to unity-based years, we
92338032Speter	 * will have an overflow when converting to an unsigned 16bit
92490792Sgshapiro	 * year. Shifting to the right is OK here, since it does not
92590792Sgshapiro	 * matter if the shift is logic or arithmetic.
92638032Speter	 */
92738032Speter	split.hi += 1;
92838032Speter	ymask = 0u - ((split.hi >> 16) == 0);
92990792Sgshapiro	jd->year = (uint16_t)(split.hi & ymask);
93038032Speter	jd->yearday = (uint16_t)split.lo + 1;
93138032Speter
93238032Speter	/* convert to month and mday */
93390792Sgshapiro	split = ntpcal_split_yeardays(split.lo, leapy);
93438032Speter	jd->month    = (uint8_t)split.hi + 1;
93538032Speter	jd->monthday = (uint8_t)split.lo + 1;
93638032Speter
93738032Speter	return ymask ? leapy : -1;
93838032Speter}
93938032Speter
940120256Sgshapiro/*
941120256Sgshapiro *---------------------------------------------------------------------
942120256Sgshapiro * Convert a RD into the date part of a 'struct tm'.
94390792Sgshapiro *---------------------------------------------------------------------
94490792Sgshapiro */
94538032Speterint
94638032Speterntpcal_rd_to_tm(
94738032Speter	struct tm  *utm,
94838032Speter	int32_t	    rd
949120256Sgshapiro	)
950120256Sgshapiro{
951120256Sgshapiro	ntpcal_split split;
95290792Sgshapiro	int	     leapy;
95390792Sgshapiro
95438032Speter	/* get day-of-week first */
95538032Speter	utm->tm_wday = rd % DAYSPERWEEK;
95638032Speter	if (utm->tm_wday < 0)
95790792Sgshapiro		utm->tm_wday += DAYSPERWEEK;
95890792Sgshapiro
95990792Sgshapiro	/* get year and day-of-year */
96090792Sgshapiro	split = ntpcal_split_eradays(rd - 1, &leapy);
96190792Sgshapiro	utm->tm_year = split.hi - 1899;
96290792Sgshapiro	utm->tm_yday = split.lo;	/* 0-based */
96390792Sgshapiro
96490792Sgshapiro	/* convert to month and mday */
96590792Sgshapiro	split = ntpcal_split_yeardays(split.lo, leapy);
96690792Sgshapiro	utm->tm_mon  = split.hi;	/* 0-based */
96790792Sgshapiro	utm->tm_mday = split.lo + 1;	/* 1-based */
96890792Sgshapiro
96990792Sgshapiro	return leapy;
97090792Sgshapiro}
97190792Sgshapiro
97290792Sgshapiro/*
97390792Sgshapiro *---------------------------------------------------------------------
97490792Sgshapiro * Take a value of seconds since midnight and split it into hhmmss in a
97538032Speter * 'struct calendar'.
97664562Sgshapiro *---------------------------------------------------------------------
97764562Sgshapiro */
97864562Sgshapiroint32_t
97964562Sgshapirontpcal_daysec_to_date(
98064562Sgshapiro	struct calendar *jd,
98190792Sgshapiro	int32_t		sec
98264562Sgshapiro	)
98364562Sgshapiro{
98464562Sgshapiro	int32_t days;
98564562Sgshapiro	int   ts[3];
98664562Sgshapiro
98764562Sgshapiro	days = priv_timesplit(ts, sec);
98864562Sgshapiro	jd->hour   = (uint8_t)ts[0];
98964562Sgshapiro	jd->minute = (uint8_t)ts[1];
99064562Sgshapiro	jd->second = (uint8_t)ts[2];
99190792Sgshapiro
99264562Sgshapiro	return days;
99338032Speter}
99490792Sgshapiro
99590792Sgshapiro/*
99690792Sgshapiro *---------------------------------------------------------------------
99790792Sgshapiro * Take a value of seconds since midnight and split it into hhmmss in a
99890792Sgshapiro * 'struct tm'.
99990792Sgshapiro *---------------------------------------------------------------------
100064562Sgshapiro */
100138032Speterint32_t
100238032Speterntpcal_daysec_to_tm(
100390792Sgshapiro	struct tm *utm,
100490792Sgshapiro	int32_t	   sec
100590792Sgshapiro	)
100690792Sgshapiro{
100790792Sgshapiro	int32_t days;
100890792Sgshapiro	int32_t ts[3];
100990792Sgshapiro
101090792Sgshapiro	days = priv_timesplit(ts, sec);
101190792Sgshapiro	utm->tm_hour = ts[0];
101290792Sgshapiro	utm->tm_min  = ts[1];
101390792Sgshapiro	utm->tm_sec  = ts[2];
101490792Sgshapiro
101590792Sgshapiro	return days;
101690792Sgshapiro}
101790792Sgshapiro
101890792Sgshapiro/*
101990792Sgshapiro *---------------------------------------------------------------------
1020168515Sgshapiro * take a split representation for day/second-of-day and day offset
102138032Speter * and convert it to a 'struct calendar'. The seconds will be normalised
102290792Sgshapiro * into the range of a day, and the day will be adjusted accordingly.
102338032Speter *
102438032Speter * returns >0 if the result is in a leap year, 0 if in a regular
102538032Speter * year and <0 if the result did not fit into the calendar struct.
102638032Speter *---------------------------------------------------------------------
102790792Sgshapiro */
1028168515Sgshapiroint
102938032Speterntpcal_daysplit_to_date(
103090792Sgshapiro	struct calendar	   *jd,
103138032Speter	const ntpcal_split *ds,
103238032Speter	int32_t		    dof
103338032Speter	)
103438032Speter{
103590792Sgshapiro	dof += ntpcal_daysec_to_date(jd, ds->lo);
1036168515Sgshapiro	return ntpcal_rd_to_date(jd, ds->hi + dof);
103738032Speter}
103890792Sgshapiro
103938032Speter/*
104038032Speter *---------------------------------------------------------------------
104138032Speter * take a split representation for day/second-of-day and day offset
104238032Speter * and convert it to a 'struct tm'. The seconds will be normalised
104390792Sgshapiro * into the range of a day, and the day will be adjusted accordingly.
104490792Sgshapiro *
104590792Sgshapiro * returns 1 if the result is in a leap year and zero if in a regular
104690792Sgshapiro * year.
104790792Sgshapiro *---------------------------------------------------------------------
104890792Sgshapiro */
104990792Sgshapiroint
1050168515Sgshapirontpcal_daysplit_to_tm(
105190792Sgshapiro	struct tm	   *utm,
105290792Sgshapiro	const ntpcal_split *ds ,
105390792Sgshapiro	int32_t		    dof
105490792Sgshapiro	)
105590792Sgshapiro{
105690792Sgshapiro	dof += ntpcal_daysec_to_tm(utm, ds->lo);
105790792Sgshapiro
105890792Sgshapiro	return ntpcal_rd_to_tm(utm, ds->hi + dof);
105990792Sgshapiro}
106090792Sgshapiro
106190792Sgshapiro/*
106290792Sgshapiro *---------------------------------------------------------------------
106390792Sgshapiro * Take a UN*X time and convert to a calendar structure.
106490792Sgshapiro *---------------------------------------------------------------------
106590792Sgshapiro */
106690792Sgshapiroint
106790792Sgshapirontpcal_time_to_date(
106890792Sgshapiro	struct calendar	*jd,
106990792Sgshapiro	const vint64	*ts
107090792Sgshapiro	)
107190792Sgshapiro{
107238032Speter	ntpcal_split ds;
107364562Sgshapiro
107438032Speter	ds = ntpcal_daysplit(ts);
107598841Sgshapiro	ds.hi += ntpcal_daysec_to_date(jd, ds.lo);
107698841Sgshapiro	ds.hi += DAY_UNIX_STARTS;
107798841Sgshapiro
107898841Sgshapiro	return ntpcal_rd_to_date(jd, ds.hi);
107998841Sgshapiro}
108064562Sgshapiro
108164562Sgshapiro
108264562Sgshapiro/*
108364562Sgshapiro * ====================================================================
108438032Speter *
108538032Speter * merging composite entities
108638032Speter *
108738032Speter * ====================================================================
108838032Speter */
108990792Sgshapiro
109090792Sgshapiro/*
109138032Speter *---------------------------------------------------------------------
109238032Speter * Merge a number of days and a number of seconds into seconds,
109338032Speter * expressed in 64 bits to avoid overflow.
109438032Speter *---------------------------------------------------------------------
109538032Speter */
109690792Sgshapirovint64
109790792Sgshapirontpcal_dayjoin(
109890792Sgshapiro	int32_t days,
109990792Sgshapiro	int32_t secs
110038032Speter	)
110138032Speter{
110238032Speter	vint64 res;
110338032Speter
110490792Sgshapiro#   if defined(HAVE_INT64)
110590792Sgshapiro
110638032Speter	res.q_s	 = days;
110738032Speter	res.q_s *= SECSPERDAY;
110838032Speter	res.q_s += secs;
110990792Sgshapiro
111090792Sgshapiro#   else
111138032Speter
111238032Speter	uint32_t p1, p2;
111338032Speter	int	 isneg;
111490792Sgshapiro
111538032Speter	/*
111638032Speter	 * res = days *86400 + secs, using manual 16/32 bit
111738032Speter	 * multiplications and shifts.
111838032Speter	 */
111938032Speter	isneg = (days < 0);
112038032Speter	if (isneg)
112164562Sgshapiro		days = -days;
112290792Sgshapiro
112390792Sgshapiro	/* assemble days * 675 */
112490792Sgshapiro	res.D_s.lo = (days & 0xFFFF) * 675u;
112564562Sgshapiro	res.D_s.hi = 0;
112638032Speter	p1 = (days >> 16) * 675u;
112738032Speter	p2 = p1 >> 16;
112838032Speter	p1 = p1 << 16;
112990792Sgshapiro	M_ADD(res.D_s.hi, res.D_s.lo, p2, p1);
113064562Sgshapiro
113164562Sgshapiro	/* mul by 128, using shift */
113264562Sgshapiro	res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25);
113390792Sgshapiro	res.D_s.lo = (res.D_s.lo << 7);
113490792Sgshapiro
113590792Sgshapiro	/* fix sign */
113690792Sgshapiro	if (isneg)
113764562Sgshapiro		M_NEG(res.D_s.hi, res.D_s.lo);
113890792Sgshapiro
113990792Sgshapiro	/* properly add seconds */
114090792Sgshapiro	p2 = 0;
114190792Sgshapiro	if (secs < 0) {
114238032Speter		p1 = (uint32_t)-secs;
114338032Speter		M_NEG(p2, p1);
114438032Speter	} else {
114538032Speter		p1 = (uint32_t)secs;
114638032Speter	}
114738032Speter	M_ADD(res.D_s.hi, res.D_s.lo, p2, p1);
114890792Sgshapiro
114990792Sgshapiro#   endif
115038032Speter
115138032Speter	return res;
115238032Speter}
115338032Speter
115438032Speter/*
115538032Speter *---------------------------------------------------------------------
115638032Speter * get leap years since epoch in elapsed years
115738032Speter *---------------------------------------------------------------------
115890792Sgshapiro */
115938032Speterint32_t
116038032Speterntpcal_leapyears_in_years(
116138032Speter	int32_t years
116238032Speter	)
116390792Sgshapiro{
116438032Speter	/* We use the in-out-in algorithm here, using the one's
116538032Speter	 * complement division trick for negative numbers. The chained
116638032Speter	 * division sequence by 4/25/4 gives the compiler the chance to
116790792Sgshapiro	 * get away with only one true division and doing shifts otherwise.
116838032Speter	 */
116938032Speter
117064562Sgshapiro	uint32_t sflag, sum, uyear;
117138032Speter
117290792Sgshapiro	sflag = int32_sflag(years);
117338032Speter	uyear = int32_to_uint32_2cpl(years);
117464562Sgshapiro	uyear ^= sflag;
117538032Speter
117664562Sgshapiro	sum  = (uyear /=  4u);	/*   4yr rule --> IN  */
117738032Speter	sum -= (uyear /= 25u);	/* 100yr rule --> OUT */
117838032Speter	sum += (uyear /=  4u);	/* 400yr rule --> IN  */
117964562Sgshapiro
118064562Sgshapiro	/* Thanks to the alternation of IN/OUT/IN we can do the sum
118138032Speter	 * directly and have a single one's complement operation
118238032Speter	 * here. (Only if the years are negative, of course.) Otherwise
118364562Sgshapiro	 * the one's complement would have to be done when
118438032Speter	 * adding/subtracting the terms.
118564562Sgshapiro	 */
118638032Speter	return uint32_2cpl_to_int32(sflag ^ sum);
118738032Speter}
118890792Sgshapiro
118990792Sgshapiro/*
119038032Speter *---------------------------------------------------------------------
119138032Speter * Convert elapsed years in Era into elapsed days in Era.
119238032Speter *---------------------------------------------------------------------
119338032Speter */
119490792Sgshapiroint32_t
119590792Sgshapirontpcal_days_in_years(
119690792Sgshapiro	int32_t years
119764562Sgshapiro	)
119890792Sgshapiro{
119990792Sgshapiro	return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years);
120064562Sgshapiro}
120190792Sgshapiro
120290792Sgshapiro/*
120364562Sgshapiro *---------------------------------------------------------------------
120498841Sgshapiro * Convert a number of elapsed month in a year into elapsed days in year.
120598841Sgshapiro *
120698841Sgshapiro * The month will be normalized, and 'res.hi' will contain the
120798841Sgshapiro * excessive years that must be considered when converting the years,
120864562Sgshapiro * while 'res.lo' will contain the number of elapsed days since start
120990792Sgshapiro * of the year.
121064562Sgshapiro *
121190792Sgshapiro * This code uses the shifted-month-approach to convert month to days,
121298841Sgshapiro * because then there is no need to have explicit leap year
121398841Sgshapiro * information.	 The slight disadvantage is that for most month values
121498841Sgshapiro * the result is a negative value, and the year excess is one; the
121598841Sgshapiro * conversion is then simply based on the start of the following year.
121698841Sgshapiro *---------------------------------------------------------------------
121764562Sgshapiro */
121864562Sgshapirontpcal_split
121938032Speterntpcal_days_in_months(
122038032Speter	int32_t m
122138032Speter	)
122238032Speter{
122338032Speter	ntpcal_split res;
122438032Speter
122538032Speter	/* Add ten months and correct if needed. (It likely is...) */
122638032Speter	res.lo  = m + 10;
122764562Sgshapiro	res.hi  = (res.lo >= 12);
122890792Sgshapiro	if (res.hi)
122964562Sgshapiro		res.lo -= 12;
123090792Sgshapiro
123190792Sgshapiro	/* if still out of range, normalise by floor division ... */
123290792Sgshapiro	if (res.lo < 0 || res.lo >= 12) {
123390792Sgshapiro		uint32_t mu, Q, sflag;
123490792Sgshapiro		sflag = int32_sflag(res.lo);
123590792Sgshapiro		mu    = int32_to_uint32_2cpl(res.lo);
123638032Speter		Q     = sflag ^ ((sflag ^ mu) / 12u);
123790792Sgshapiro		res.hi += uint32_2cpl_to_int32(Q);
123890792Sgshapiro		res.lo  = mu - Q * 12u;
123990792Sgshapiro	}
124090792Sgshapiro
124190792Sgshapiro	/* get cummulated days in year with unshift */
124290792Sgshapiro	res.lo = shift_month_table[res.lo] - 306;
124390792Sgshapiro
1244168515Sgshapiro	return res;
124590792Sgshapiro}
1246168515Sgshapiro
124790792Sgshapiro/*
1248168515Sgshapiro *---------------------------------------------------------------------
124990792Sgshapiro * Convert ELAPSED years/months/days of gregorian calendar to elapsed
125090792Sgshapiro * days in Gregorian epoch.
125190792Sgshapiro *
125290792Sgshapiro * If you want to convert years and days-of-year, just give a month of
125390792Sgshapiro * zero.
125490792Sgshapiro *---------------------------------------------------------------------
125590792Sgshapiro */
125677349Sgshapiroint32_t
125790792Sgshapirontpcal_edate_to_eradays(
125877349Sgshapiro	int32_t years,
125977349Sgshapiro	int32_t mons,
126077349Sgshapiro	int32_t mdays
126177349Sgshapiro	)
126277349Sgshapiro{
126377349Sgshapiro	ntpcal_split tmp;
126477349Sgshapiro	int32_t	     res;
126577349Sgshapiro
126677349Sgshapiro	if (mons) {
126777349Sgshapiro		tmp = ntpcal_days_in_months(mons);
126877349Sgshapiro		res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo;
126977349Sgshapiro	} else
127077349Sgshapiro		res = ntpcal_days_in_years(years);
127177349Sgshapiro	res += mdays;
127290792Sgshapiro
127377349Sgshapiro	return res;
127477349Sgshapiro}
127577349Sgshapiro
127677349Sgshapiro/*
127790792Sgshapiro *---------------------------------------------------------------------
127877349Sgshapiro * Convert ELAPSED years/months/days of gregorian calendar to elapsed
127977349Sgshapiro * days in year.
128077349Sgshapiro *
128190792Sgshapiro * Note: This will give the true difference to the start of the given
128290792Sgshapiro * year, even if months & days are off-scale.
128377349Sgshapiro *---------------------------------------------------------------------
128477349Sgshapiro */
128577349Sgshapiroint32_t
128677349Sgshapirontpcal_edate_to_yeardays(
128777349Sgshapiro	int32_t years,
128877349Sgshapiro	int32_t mons,
128977349Sgshapiro	int32_t mdays
129090792Sgshapiro	)
129177349Sgshapiro{
129277349Sgshapiro	ntpcal_split tmp;
129377349Sgshapiro
129490792Sgshapiro	if (0 <= mons && mons < 12) {
129577349Sgshapiro		years += 1;
129677349Sgshapiro		mdays += real_month_table[is_leapyear(years)][mons];
129777349Sgshapiro	} else {
129890792Sgshapiro		tmp = ntpcal_days_in_months(mons);
129990792Sgshapiro		mdays += tmp.lo
130077349Sgshapiro		       + ntpcal_days_in_years(years + tmp.hi)
130177349Sgshapiro		       - ntpcal_days_in_years(years);
130277349Sgshapiro	}
130390792Sgshapiro
1304132943Sgshapiro	return mdays;
1305132943Sgshapiro}
130690792Sgshapiro
130790792Sgshapiro/*
130890792Sgshapiro *---------------------------------------------------------------------
130990792Sgshapiro * Convert elapsed days and the hour/minute/second information into
131090792Sgshapiro * total seconds.
131190792Sgshapiro *
131290792Sgshapiro * If 'isvalid' is not NULL, do a range check on the time specification
131390792Sgshapiro * and tell if the time input is in the normal range, permitting for a
131490792Sgshapiro * single leapsecond.
131590792Sgshapiro *---------------------------------------------------------------------
131690792Sgshapiro */
131790792Sgshapiroint32_t
131890792Sgshapirontpcal_etime_to_seconds(
131990792Sgshapiro	int32_t hours,
132090792Sgshapiro	int32_t minutes,
132190792Sgshapiro	int32_t seconds
132290792Sgshapiro	)
132390792Sgshapiro{
132490792Sgshapiro	int32_t res;
132577349Sgshapiro
132690792Sgshapiro	res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds;
132790792Sgshapiro
132890792Sgshapiro	return res;
132977349Sgshapiro}
133038032Speter
133138032Speter/*
133238032Speter *---------------------------------------------------------------------
133338032Speter * Convert the date part of a 'struct tm' (that is, year, month,
1334157001Sgshapiro * day-of-month) into the RD of that day.
133538032Speter *---------------------------------------------------------------------
1336157001Sgshapiro */
133738032Speterint32_t
133838032Speterntpcal_tm_to_rd(
133990792Sgshapiro	const struct tm *utm
134090792Sgshapiro	)
134190792Sgshapiro{
134290792Sgshapiro	return ntpcal_edate_to_eradays(utm->tm_year + 1899,
134390792Sgshapiro				       utm->tm_mon,
134490792Sgshapiro				       utm->tm_mday - 1) + 1;
134590792Sgshapiro}
134690792Sgshapiro
134790792Sgshapiro/*
134838032Speter *---------------------------------------------------------------------
134938032Speter * Convert the date part of a 'struct calendar' (that is, year, month,
135038032Speter * day-of-month) into the RD of that day.
135190792Sgshapiro *---------------------------------------------------------------------
135238032Speter */
135390792Sgshapiroint32_t
135490792Sgshapirontpcal_date_to_rd(
135590792Sgshapiro	const struct calendar *jd
135690792Sgshapiro	)
135790792Sgshapiro{
135890792Sgshapiro	return ntpcal_edate_to_eradays((int32_t)jd->year - 1,
135990792Sgshapiro				       (int32_t)jd->month - 1,
136090792Sgshapiro				       (int32_t)jd->monthday - 1) + 1;
136190792Sgshapiro}
136290792Sgshapiro
136338032Speter/*
136490792Sgshapiro *---------------------------------------------------------------------
136564562Sgshapiro * convert a year number to rata die of year start
136690792Sgshapiro *---------------------------------------------------------------------
136790792Sgshapiro */
136890792Sgshapiroint32_t
136990792Sgshapirontpcal_year_to_ystart(
137090792Sgshapiro	int32_t year
137190792Sgshapiro	)
137238032Speter{
137338032Speter	return ntpcal_days_in_years(year - 1) + 1;
137464562Sgshapiro}
137564562Sgshapiro
137664562Sgshapiro/*
137764562Sgshapiro *---------------------------------------------------------------------
137864562Sgshapiro * For a given RD, get the RD of the associated year start,
137938032Speter * that is, the RD of the last January,1st on or before that day.
138038032Speter *---------------------------------------------------------------------
138138032Speter */
138238032Speterint32_t
138364562Sgshapirontpcal_rd_to_ystart(
138490792Sgshapiro	int32_t rd
138538032Speter	)
138638032Speter{
138790792Sgshapiro	/*
138890792Sgshapiro	 * Rather simple exercise: split the day number into elapsed
138938032Speter	 * years and elapsed days, then remove the elapsed days from the
139038032Speter	 * input value. Nice'n sweet...
1391168515Sgshapiro	 */
139273188Sgshapiro	return rd - ntpcal_split_eradays(rd - 1, NULL).lo;
139373188Sgshapiro}
139438032Speter
139538032Speter/*
139690792Sgshapiro *---------------------------------------------------------------------
139738032Speter * For a given RD, get the RD of the associated month start.
139838032Speter *---------------------------------------------------------------------
139990792Sgshapiro */
140090792Sgshapiroint32_t
1401110560Sgshapirontpcal_rd_to_mstart(
1402110560Sgshapiro	int32_t rd
140390792Sgshapiro	)
140490792Sgshapiro{
140590792Sgshapiro	ntpcal_split split;
1406168515Sgshapiro	int	     leaps;
1407110560Sgshapiro
140890792Sgshapiro	split = ntpcal_split_eradays(rd - 1, &leaps);
140990792Sgshapiro	split = ntpcal_split_yeardays(split.lo, leaps);
141038032Speter
141138032Speter	return rd - split.lo;
141290792Sgshapiro}
141390792Sgshapiro
1414132943Sgshapiro/*
141590792Sgshapiro *---------------------------------------------------------------------
1416132943Sgshapiro * take a 'struct calendar' and get the seconds-of-day from it.
141790792Sgshapiro *---------------------------------------------------------------------
1418132943Sgshapiro */
141990792Sgshapiroint32_t
1420132943Sgshapirontpcal_date_to_daysec(
142190792Sgshapiro	const struct calendar *jd
142238032Speter	)
142338032Speter{
142438032Speter	return ntpcal_etime_to_seconds(jd->hour, jd->minute,
142538032Speter				       jd->second);
142638032Speter}
142738032Speter
142838032Speter/*
142938032Speter *---------------------------------------------------------------------
143038032Speter * take a 'struct tm' and get the seconds-of-day from it.
143190792Sgshapiro *---------------------------------------------------------------------
143290792Sgshapiro */
143364562Sgshapiroint32_t
143490792Sgshapirontpcal_tm_to_daysec(
143590792Sgshapiro	const struct tm *utm
143690792Sgshapiro	)
143790792Sgshapiro{
143890792Sgshapiro	return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min,
143990792Sgshapiro				       utm->tm_sec);
144038032Speter}
144138032Speter
144290792Sgshapiro/*
144390792Sgshapiro *---------------------------------------------------------------------
144438032Speter * take a 'struct calendar' and convert it to a 'time_t'
144590792Sgshapiro *---------------------------------------------------------------------
144690792Sgshapiro */
144738032Spetertime_t
144890792Sgshapirontpcal_date_to_time(
144990792Sgshapiro	const struct calendar *jd
145038032Speter	)
145138032Speter{
145238032Speter	vint64  join;
145338032Speter	int32_t days, secs;
145438032Speter
145538032Speter	days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS;
145638032Speter	secs = ntpcal_date_to_daysec(jd);
145738032Speter	join = ntpcal_dayjoin(days, secs);
145838032Speter
145990792Sgshapiro	return vint64_to_time(&join);
146038032Speter}
146138032Speter
146238032Speter
146338032Speter/*
146438032Speter * ====================================================================
146538032Speter *
146690792Sgshapiro * extended and unchecked variants of caljulian/caltontp
146790792Sgshapiro *
146838032Speter * ====================================================================
146938032Speter */
147038032Speterint
147138032Speterntpcal_ntp64_to_date(
147238032Speter	struct calendar *jd,
147390792Sgshapiro	const vint64    *ntp
147490792Sgshapiro	)
147590792Sgshapiro{
147638032Speter	ntpcal_split ds;
147738032Speter
1478157001Sgshapiro	ds = ntpcal_daysplit(ntp);
1479157001Sgshapiro	ds.hi += ntpcal_daysec_to_date(jd, ds.lo);
1480157001Sgshapiro
1481157001Sgshapiro	return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS);
1482157001Sgshapiro}
1483157001Sgshapiro
1484157001Sgshapiroint
1485157001Sgshapirontpcal_ntp_to_date(
1486157001Sgshapiro	struct calendar *jd,
1487157001Sgshapiro	uint32_t	 ntp,
148838032Speter	const time_t	*piv
148990792Sgshapiro	)
149042575Speter{
149138032Speter	vint64	ntp64;
149290792Sgshapiro
149390792Sgshapiro	/*
149490792Sgshapiro	 * Unfold ntp time around current time into NTP domain. Split
149590792Sgshapiro	 * into days and seconds, shift days into CE domain and
149690792Sgshapiro	 * process the parts.
149790792Sgshapiro	 */
149890792Sgshapiro	ntp64 = ntpcal_ntp_to_ntp(ntp, piv);
149990792Sgshapiro	return ntpcal_ntp64_to_date(jd, &ntp64);
1500132943Sgshapiro}
1501132943Sgshapiro
1502132943Sgshapiro
1503132943Sgshapirovint64
1504132943Sgshapirontpcal_date_to_ntp64(
150590792Sgshapiro	const struct calendar *jd
150690792Sgshapiro	)
150790792Sgshapiro{
150890792Sgshapiro	/*
150990792Sgshapiro	 * Convert date to NTP. Ignore yearday, use d/m/y only.
151090792Sgshapiro	 */
151190792Sgshapiro	return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS,
151290792Sgshapiro			      ntpcal_date_to_daysec(jd));
151390792Sgshapiro}
151490792Sgshapiro
151590792Sgshapiro
151690792Sgshapirouint32_t
151790792Sgshapirontpcal_date_to_ntp(
151890792Sgshapiro	const struct calendar *jd
151990792Sgshapiro	)
152090792Sgshapiro{
152190792Sgshapiro	/*
152290792Sgshapiro	 * Get lower half of 64-bit NTP timestamp from date/time.
152390792Sgshapiro	 */
152490792Sgshapiro	return ntpcal_date_to_ntp64(jd).d_s.lo;
152590792Sgshapiro}
152690792Sgshapiro
152790792Sgshapiro
152890792Sgshapiro
152990792Sgshapiro/*
153090792Sgshapiro * ====================================================================
153190792Sgshapiro *
153290792Sgshapiro * day-of-week calculations
153390792Sgshapiro *
153490792Sgshapiro * ====================================================================
153590792Sgshapiro */
153690792Sgshapiro/*
153790792Sgshapiro * Given a RataDie and a day-of-week, calculate a RDN that is reater-than,
153890792Sgshapiro * greater-or equal, closest, less-or-equal or less-than the given RDN
153990792Sgshapiro * and denotes the given day-of-week
154090792Sgshapiro */
154190792Sgshapiroint32_t
154290792Sgshapirontpcal_weekday_gt(
154390792Sgshapiro	int32_t rdn,
154490792Sgshapiro	int32_t dow
154590792Sgshapiro	)
154690792Sgshapiro{
154790792Sgshapiro	return ntpcal_periodic_extend(rdn+1, dow, 7);
154890792Sgshapiro}
154990792Sgshapiro
155090792Sgshapiroint32_t
155190792Sgshapirontpcal_weekday_ge(
155290792Sgshapiro	int32_t rdn,
155390792Sgshapiro	int32_t dow
155490792Sgshapiro	)
155590792Sgshapiro{
155690792Sgshapiro	return ntpcal_periodic_extend(rdn, dow, 7);
155790792Sgshapiro}
155890792Sgshapiro
155990792Sgshapiroint32_t
156090792Sgshapirontpcal_weekday_close(
156190792Sgshapiro	int32_t rdn,
156290792Sgshapiro	int32_t dow
156390792Sgshapiro	)
156490792Sgshapiro{
156590792Sgshapiro	return ntpcal_periodic_extend(rdn-3, dow, 7);
156690792Sgshapiro}
156790792Sgshapiro
156890792Sgshapiroint32_t
156990792Sgshapirontpcal_weekday_le(
157090792Sgshapiro	int32_t rdn,
157190792Sgshapiro	int32_t dow
157290792Sgshapiro	)
157390792Sgshapiro{
157490792Sgshapiro	return ntpcal_periodic_extend(rdn, dow, -7);
157590792Sgshapiro}
157690792Sgshapiro
157790792Sgshapiroint32_t
157890792Sgshapirontpcal_weekday_lt(
157990792Sgshapiro	int32_t rdn,
158090792Sgshapiro	int32_t dow
158190792Sgshapiro	)
158290792Sgshapiro{
158390792Sgshapiro	return ntpcal_periodic_extend(rdn-1, dow, -7);
158490792Sgshapiro}
158590792Sgshapiro
158690792Sgshapiro/*
158790792Sgshapiro * ====================================================================
158890792Sgshapiro *
158990792Sgshapiro * ISO week-calendar conversions
159090792Sgshapiro *
159190792Sgshapiro * The ISO8601 calendar defines a calendar of years, weeks and weekdays.
159290792Sgshapiro * It is related to the Gregorian calendar, and a ISO year starts at the
159390792Sgshapiro * Monday closest to Jan,1st of the corresponding Gregorian year.  A ISO
159490792Sgshapiro * calendar year has always 52 or 53 weeks, and like the Grogrian
159590792Sgshapiro * calendar the ISO8601 calendar repeats itself every 400 years, or
159690792Sgshapiro * 146097 days, or 20871 weeks.
159790792Sgshapiro *
159890792Sgshapiro * While it is possible to write ISO calendar functions based on the
159990792Sgshapiro * Gregorian calendar functions, the following implementation takes a
160090792Sgshapiro * different approach, based directly on years and weeks.
160190792Sgshapiro *
160290792Sgshapiro * Analysis of the tabulated data shows that it is not possible to
160390792Sgshapiro * interpolate from years to weeks over a full 400 year range; cyclic
160490792Sgshapiro * shifts over 400 years do not provide a solution here. But it *is*
160590792Sgshapiro * possible to interpolate over every single century of the 400-year
160690792Sgshapiro * cycle. (The centennial leap year rule seems to be the culprit here.)
160790792Sgshapiro *
160890792Sgshapiro * It can be shown that a conversion from years to weeks can be done
160990792Sgshapiro * using a linear transformation of the form
161090792Sgshapiro *
161190792Sgshapiro *   w = floor( y * a + b )
161290792Sgshapiro *
161390792Sgshapiro * where the slope a must hold to
161490792Sgshapiro *
161590792Sgshapiro *  52.1780821918 <= a < 52.1791044776
161690792Sgshapiro *
161790792Sgshapiro * and b must be chosen according to the selected slope and the number
161890792Sgshapiro * of the century in a 400-year period.
161938032Speter *
162038032Speter * The inverse calculation can also be done in this way. Careful scaling
162138032Speter * provides an unlimited set of integer coefficients a,k,b that enable
162238032Speter * us to write the calulation in the form
162338032Speter *
162438032Speter *   w = (y * a	 + b ) / k
162538032Speter *   y = (w * a' + b') / k'
162638032Speter *
162738032Speter * In this implementation the values of k and k' are chosen to be
162838032Speter * smallest possible powers of two, so the division can be implemented
162938032Speter * as shifts if the optimiser chooses to do so.
163038032Speter *
163190792Sgshapiro * ====================================================================
163290792Sgshapiro */
163338032Speter
163438032Speter/*
163538032Speter * Given a number of elapsed (ISO-)years since the begin of the
163690792Sgshapiro * christian era, return the number of elapsed weeks corresponding to
163790792Sgshapiro * the number of years.
163838032Speter */
163990792Sgshapiroint32_t
164090792Sgshapiroisocal_weeks_in_years(
164190792Sgshapiro	int32_t years
164290792Sgshapiro	)
164390792Sgshapiro{
164438032Speter	/*
164538032Speter	 * use: w = (y * 53431 + b[c]) / 1024 as interpolation
164638032Speter	 */
164790792Sgshapiro	static const uint16_t bctab[4] = { 157, 449, 597, 889 };
164890792Sgshapiro
164964562Sgshapiro	int32_t  cs, cw;
165038032Speter	uint32_t cc, ci, yu, sflag;
165138032Speter
165290792Sgshapiro	sflag = int32_sflag(years);
165338032Speter	yu    = int32_to_uint32_2cpl(years);
165438032Speter
165538032Speter	/* split off centuries, using floor division */
165690792Sgshapiro	cc  = sflag ^ ((sflag ^ yu) / 100u);
165738032Speter	yu -= cc * 100u;
165838032Speter
165938032Speter	/* calculate century cycles shift and cycle index:
166038032Speter	 * Assuming a century is 5217 weeks, we have to add a cycle
166164562Sgshapiro	 * shift that is 3 for every 4 centuries, because 3 of the four
166238032Speter	 * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual
166338032Speter	 * correction, and the second century is the defective one.
166438032Speter	 *
166538032Speter	 * Needs floor division by 4, which is done with masking and
166690792Sgshapiro	 * shifting.
166790792Sgshapiro	 */
166864562Sgshapiro	ci = cc * 3u + 1;
166938032Speter	cs = uint32_2cpl_to_int32(sflag ^ ((sflag ^ ci) / 4u));
167038032Speter	ci = ci % 4u;
167138032Speter
167290792Sgshapiro	/* Get weeks in century. Can use plain division here as all ops
167390792Sgshapiro	 * are >= 0,  and let the compiler sort out the possible
167438032Speter	 * optimisations.
167538032Speter	 */
167638032Speter	cw = (yu * 53431u + bctab[ci]) / 1024u;
167738032Speter
167838032Speter	return uint32_2cpl_to_int32(cc) * 5217 + cs + cw;
167938032Speter}
168038032Speter
168138032Speter/*
168238032Speter * Given a number of elapsed weeks since the begin of the christian
168364562Sgshapiro * era, split this number into the number of elapsed years in res.hi
168438032Speter * and the excessive number of weeks in res.lo. (That is, res.lo is
168590792Sgshapiro * the number of elapsed weeks in the remaining partial year.)
168673188Sgshapiro */
168738032Speterntpcal_split
168873188Sgshapiroisocal_split_eraweeks(
168938032Speter	int32_t weeks
169038032Speter	)
169138032Speter{
169238032Speter	/*
169338032Speter	 * use: y = (w * 157 + b[c]) / 8192 as interpolation
169438032Speter	 */
169538032Speter
169638032Speter	static const uint16_t bctab[4] = { 85, 130, 17, 62 };
169773188Sgshapiro
169890792Sgshapiro	ntpcal_split res;
169938032Speter	int32_t  cc, ci;
170090792Sgshapiro	uint32_t sw, cy, Q, sflag;
170138032Speter
170238032Speter	/* Use two fast cycle-split divisions here. This is again
170338032Speter	 * susceptible to internal overflow, so we check the range. This
170438032Speter	 * still permits more than +/-20 million years, so this is
170538032Speter	 * likely a pure academical problem.
170638032Speter	 *
170738032Speter	 * We want to execute '(weeks * 4 + 2) /% 20871' under floor
170890792Sgshapiro	 * division rules in the first step.
170938032Speter	 */
171038032Speter	sflag = int32_sflag(weeks);
171190792Sgshapiro	sw  = uint32_saturate(int32_to_uint32_2cpl(weeks), sflag);
171238032Speter	sw  = 4u * sw + 2;
171338032Speter	Q   = sflag ^ ((sflag ^ sw) / GREGORIAN_CYCLE_WEEKS);
171442575Speter	sw -= Q * GREGORIAN_CYCLE_WEEKS;
171542575Speter	ci  = Q % 4u;
171642575Speter	cc  = uint32_2cpl_to_int32(Q);
171742575Speter
171842575Speter	/* Split off years; sw >= 0 here! The scaled weeks in the years
171942575Speter	 * are scaled up by 157 afterwards.
172042575Speter	 */
172164562Sgshapiro	sw  = (sw / 4u) * 157u + bctab[ci];
172264562Sgshapiro	cy  = sw / 8192u;	/* ws >> 13 , let the compiler sort it out */
172338032Speter	sw  = sw % 8192u;	/* ws & 8191, let the compiler sort it out */
172438032Speter
172538032Speter	/* assemble elapsed years and downscale the elapsed weeks in
172638032Speter	 * the year.
172738032Speter	 */
172838032Speter	res.hi = 100*cc + cy;
172938032Speter	res.lo = sw / 157u;
173038032Speter
173190792Sgshapiro	return res;
173238032Speter}
173338032Speter
173438032Speter/*
173538032Speter * Given a second in the NTP time scale and a pivot, expand the NTP
173690792Sgshapiro * time stamp around the pivot and convert into an ISO calendar time
173790792Sgshapiro * stamp.
173838032Speter */
173938032Speterint
174038032Speterisocal_ntp64_to_date(
174138032Speter	struct isodate *id,
174238032Speter	const vint64   *ntp
174338032Speter	)
174490792Sgshapiro{
174590792Sgshapiro	ntpcal_split ds;
174638032Speter	int32_t      ts[3];
174738032Speter	uint32_t     uw, ud, sflag;
174838032Speter
174938032Speter	/*
175064562Sgshapiro	 * Split NTP time into days and seconds, shift days into CE
175138032Speter	 * domain and process the parts.
175238032Speter	 */
1753168515Sgshapiro	ds = ntpcal_daysplit(ntp);
175473188Sgshapiro
175590792Sgshapiro	/* split time part */
175673188Sgshapiro	ds.hi += priv_timesplit(ts, ds.lo);
175790792Sgshapiro	id->hour   = (uint8_t)ts[0];
175873188Sgshapiro	id->minute = (uint8_t)ts[1];
1759132943Sgshapiro	id->second = (uint8_t)ts[2];
176073188Sgshapiro
176138032Speter	/* split days into days and weeks, using floor division in unsigned */
176238032Speter	ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */
176338032Speter	sflag = int32_sflag(ds.hi);
176438032Speter	ud  = int32_to_uint32_2cpl(ds.hi);
176590792Sgshapiro	uw  = sflag ^ ((sflag ^ ud) / DAYSPERWEEK);
176690792Sgshapiro	ud -= uw * DAYSPERWEEK;
176790792Sgshapiro	ds.hi = uint32_2cpl_to_int32(uw);
176890792Sgshapiro	ds.lo = ud;
176990792Sgshapiro
177090792Sgshapiro	id->weekday = (uint8_t)ds.lo + 1;	/* weekday result    */
177190792Sgshapiro
177238032Speter	/* get year and week in year */
177338032Speter	ds = isocal_split_eraweeks(ds.hi);	/* elapsed years&week*/
177438032Speter	id->year = (uint16_t)ds.hi + 1;		/* shift to current  */
177538032Speter	id->week = (uint8_t )ds.lo + 1;
177638032Speter
177738032Speter	return (ds.hi >= 0 && ds.hi < 0x0000FFFF);
177838032Speter}
177938032Speter
178038032Speterint
178138032Speterisocal_ntp_to_date(
178238032Speter	struct isodate *id,
178338032Speter	uint32_t	ntp,
178438032Speter	const time_t   *piv
178538032Speter	)
178638032Speter{
178738032Speter	vint64	ntp64;
178838032Speter
178938032Speter	/*
179038032Speter	 * Unfold ntp time around current time into NTP domain, then
179138032Speter	 * convert the full time stamp.
179238032Speter	 */
179338032Speter	ntp64 = ntpcal_ntp_to_ntp(ntp, piv);
179438032Speter	return isocal_ntp64_to_date(id, &ntp64);
179538032Speter}
179638032Speter
179738032Speter/*
179838032Speter * Convert a ISO date spec into a second in the NTP time scale,
179938032Speter * properly truncated to 32 bit.
180038032Speter */
180138032Spetervint64
180238032Speterisocal_date_to_ntp64(
180338032Speter	const struct isodate *id
180438032Speter	)
180538032Speter{
180638032Speter	int32_t weeks, days, secs;
180738032Speter
180838032Speter	weeks = isocal_weeks_in_years((int32_t)id->year - 1)
180938032Speter	      + (int32_t)id->week - 1;
181038032Speter	days = weeks * 7 + (int32_t)id->weekday;
181138032Speter	/* days is RDN of ISO date now */
181238032Speter	secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second);
181338032Speter
181438032Speter	return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs);
181538032Speter}
181638032Speter
181738032Speteruint32_t
181838032Speterisocal_date_to_ntp(
181938032Speter	const struct isodate *id
182038032Speter	)
182138032Speter{
182238032Speter	/*
182338032Speter	 * Get lower half of 64-bit NTP timestamp from date/time.
182438032Speter	 */
182538032Speter	return isocal_date_to_ntp64(id).d_s.lo;
182638032Speter}
182738032Speter
182838032Speter/*
182938032Speter * ====================================================================
183038032Speter * 'basedate' support functions
183138032Speter * ====================================================================
183238032Speter */
183338032Speter
183438032Speterstatic int32_t s_baseday = NTP_TO_UNIX_DAYS;
183538032Speter
183638032Speterint32_t
183738032Speterbasedate_eval_buildstamp(void)
183838032Speter{
183938032Speter	struct calendar jd;
184038032Speter	int32_t		ed;
184138032Speter
184238032Speter	if (!ntpcal_get_build_date(&jd))
184338032Speter		return NTP_TO_UNIX_DAYS;
184438032Speter
184538032Speter	/* The time zone of the build stamp is unspecified; we remove
184638032Speter	 * one day to provide a certain slack. And in case somebody
184738032Speter	 * fiddled with the system clock, we make sure we do not go
184864562Sgshapiro	 * before the UNIX epoch (1970-01-01). It's probably not possible
184938032Speter	 * to do this to the clock on most systems, but there are other
185042575Speter	 * ways to tweak the build stamp.
185190792Sgshapiro	 */
185290792Sgshapiro	jd.monthday -= 1;
185342575Speter	ed = ntpcal_date_to_rd(&jd) - DAY_NTP_STARTS;
185442575Speter	return (ed < NTP_TO_UNIX_DAYS) ? NTP_TO_UNIX_DAYS : ed;
185590792Sgshapiro}
185642575Speter
185742575Speterint32_t
185890792Sgshapirobasedate_eval_string(
185990792Sgshapiro	const char * str
186090792Sgshapiro	)
186190792Sgshapiro{
186290792Sgshapiro	u_short	y,m,d;
186342575Speter	u_long	ned;
186490792Sgshapiro	int	rc, nc;
186590792Sgshapiro	size_t	sl;
186690792Sgshapiro
186790792Sgshapiro	sl = strlen(str);
186890792Sgshapiro	rc = sscanf(str, "%4hu-%2hu-%2hu%n", &y, &m, &d, &nc);
186990792Sgshapiro	if (rc == 3 && (size_t)nc == sl) {
187064562Sgshapiro		if (m >= 1 && m <= 12 && d >= 1 && d <= 31)
187138032Speter			return ntpcal_edate_to_eradays(y-1, m-1, d)
187290792Sgshapiro			    - DAY_NTP_STARTS;
187338032Speter		goto buildstamp;
187438032Speter	}
187538032Speter
187638032Speter	rc = sscanf(str, "%lu%n", &ned, &nc);
187738032Speter	if (rc == 1 && (size_t)nc == sl) {
187838032Speter		if (ned <= INT32_MAX)
187938032Speter			return (int32_t)ned;
188038032Speter		goto buildstamp;
188138032Speter	}
188264562Sgshapiro
188390792Sgshapiro  buildstamp:
188438032Speter	msyslog(LOG_WARNING,
188538032Speter		"basedate string \"%s\" invalid, build date substituted!",
188638032Speter		str);
188790792Sgshapiro	return basedate_eval_buildstamp();
188838032Speter}
188938032Speter
189090792Sgshapirouint32_t
189190792Sgshapirobasedate_get_day(void)
189290792Sgshapiro{
189338032Speter	return s_baseday;
189438032Speter}
189538032Speter
189690792Sgshapiroint32_t
189790792Sgshapirobasedate_set_day(
189838032Speter	int32_t day
189938032Speter	)
190038032Speter{
190138032Speter	struct calendar	jd;
190238032Speter	int32_t		retv;
190338032Speter
190438032Speter	if (day < NTP_TO_UNIX_DAYS) {
190538032Speter		msyslog(LOG_WARNING,
190638032Speter			"baseday_set_day: invalid day (%lu), UNIX epoch substituted",
190790792Sgshapiro			(unsigned long)day);
190838032Speter		day = NTP_TO_UNIX_DAYS;
190990792Sgshapiro	}
191090792Sgshapiro	retv = s_baseday;
191138032Speter	s_baseday = day;
191238032Speter	ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS);
191338032Speter	msyslog(LOG_INFO, "basedate set to %04hu-%02hu-%02hu",
191490792Sgshapiro		jd.year, (u_short)jd.month, (u_short)jd.monthday);
191564562Sgshapiro	return retv;
191664562Sgshapiro}
191790792Sgshapiro
191890792Sgshapirotime_t
191990792Sgshapirobasedate_get_eracenter(void)
192090792Sgshapiro{
192190792Sgshapiro	time_t retv;
192264562Sgshapiro	retv  = (time_t)(s_baseday - NTP_TO_UNIX_DAYS);
192390792Sgshapiro	retv *= SECSPERDAY;
192490792Sgshapiro	retv += (UINT32_C(1) << 31);
192590792Sgshapiro	return retv;
192690792Sgshapiro}
192790792Sgshapiro
192890792Sgshapirotime_t
192990792Sgshapirobasedate_get_erabase(void)
193090792Sgshapiro{
193190792Sgshapiro	time_t retv;
193290792Sgshapiro	retv  = (time_t)(s_baseday - NTP_TO_UNIX_DAYS);
193390792Sgshapiro	retv *= SECSPERDAY;
193490792Sgshapiro	return retv;
193566494Sgshapiro}
193638032Speter
193738032Speter/* -*-EOF-*- */
193890792Sgshapiro