1*eabc0478Schristos /* $NetBSD: ntp_calendar.c,v 1.12 2024/08/18 20:47:13 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * ntp_calendar.c - calendar and helper functions 58585484eSchristos * 68585484eSchristos * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 78585484eSchristos * The contents of 'html/copyright.html' apply. 8af12ab5eSchristos * 9af12ab5eSchristos * -------------------------------------------------------------------- 10af12ab5eSchristos * Some notes on the implementation: 11af12ab5eSchristos * 12af12ab5eSchristos * Calendar algorithms thrive on the division operation, which is one of 13af12ab5eSchristos * the slowest numerical operations in any CPU. What saves us here from 14af12ab5eSchristos * abysmal performance is the fact that all divisions are divisions by 15af12ab5eSchristos * constant numbers, and most compilers can do this by a multiplication 16af12ab5eSchristos * operation. But this might not work when using the div/ldiv/lldiv 17af12ab5eSchristos * function family, because many compilers are not able to do inline 18af12ab5eSchristos * expansion of the code with following optimisation for the 19af12ab5eSchristos * constant-divider case. 20af12ab5eSchristos * 21af12ab5eSchristos * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which 22af12ab5eSchristos * are inherently target dependent. Nothing that could not be cured with 23af12ab5eSchristos * autoconf, but still a mess... 24af12ab5eSchristos * 25af12ab5eSchristos * Furthermore, we need floor division in many places. C either leaves 26af12ab5eSchristos * the division behaviour undefined (< C99) or demands truncation to 27af12ab5eSchristos * zero (>= C99), so additional steps are required to make sure the 28af12ab5eSchristos * algorithms work. The {l,ll}div function family is requested to 29af12ab5eSchristos * truncate towards zero, which is also the wrong direction for our 30af12ab5eSchristos * purpose. 31af12ab5eSchristos * 32af12ab5eSchristos * For all this, all divisions by constant are coded manually, even when 33af12ab5eSchristos * there is a joined div/mod operation: The optimiser should sort that 34af12ab5eSchristos * out, if possible. Most of the calculations are done with unsigned 35af12ab5eSchristos * types, explicitely using two's complement arithmetics where 36af12ab5eSchristos * necessary. This minimises the dependecies to compiler and target, 37af12ab5eSchristos * while still giving reasonable to good performance. 38af12ab5eSchristos * 39af12ab5eSchristos * The implementation uses a few tricks that exploit properties of the 40af12ab5eSchristos * two's complement: Floor division on negative dividents can be 41af12ab5eSchristos * executed by using the one's complement of the divident. One's 42af12ab5eSchristos * complement can be easily created using XOR and a mask. 43af12ab5eSchristos * 44af12ab5eSchristos * Finally, check for overflow conditions is minimal. There are only two 45cdfa2a7eSchristos * calculation steps in the whole calendar that potentially suffer from 46cdfa2a7eSchristos * an internal overflow, and these are coded in a way that avoids 47cdfa2a7eSchristos * it. All other functions do not suffer from internal overflow and 48cdfa2a7eSchristos * simply return the result truncated to 32 bits. 498585484eSchristos */ 50af12ab5eSchristos 518585484eSchristos #include <config.h> 528585484eSchristos #include <sys/types.h> 538585484eSchristos 548585484eSchristos #include "ntp_types.h" 558585484eSchristos #include "ntp_calendar.h" 568585484eSchristos #include "ntp_stdlib.h" 578585484eSchristos #include "ntp_fp.h" 588585484eSchristos #include "ntp_unixtime.h" 598585484eSchristos 60cdfa2a7eSchristos #include "ntpd.h" 61cdfa2a7eSchristos 62af12ab5eSchristos /* For now, let's take the conservative approach: if the target property 63af12ab5eSchristos * macros are not defined, check a few well-known compiler/architecture 64af12ab5eSchristos * settings. Default is to assume that the representation of signed 65af12ab5eSchristos * integers is unknown and shift-arithmetic-right is not available. 66af12ab5eSchristos */ 67af12ab5eSchristos #ifndef TARGET_HAS_2CPL 68af12ab5eSchristos # if defined(__GNUC__) 69af12ab5eSchristos # if defined(__i386__) || defined(__x86_64__) || defined(__arm__) 70af12ab5eSchristos # define TARGET_HAS_2CPL 1 71af12ab5eSchristos # else 72af12ab5eSchristos # define TARGET_HAS_2CPL 0 73af12ab5eSchristos # endif 74af12ab5eSchristos # elif defined(_MSC_VER) 75af12ab5eSchristos # if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) 76af12ab5eSchristos # define TARGET_HAS_2CPL 1 77af12ab5eSchristos # else 78af12ab5eSchristos # define TARGET_HAS_2CPL 0 79af12ab5eSchristos # endif 80af12ab5eSchristos # else 81af12ab5eSchristos # define TARGET_HAS_2CPL 0 82af12ab5eSchristos # endif 83af12ab5eSchristos #endif 84af12ab5eSchristos 85af12ab5eSchristos #ifndef TARGET_HAS_SAR 86af12ab5eSchristos # define TARGET_HAS_SAR 0 87af12ab5eSchristos #endif 88af12ab5eSchristos 89cdfa2a7eSchristos #if !defined(HAVE_64BITREGS) && defined(UINT64_MAX) && (SIZE_MAX >= UINT64_MAX) 90cdfa2a7eSchristos # define HAVE_64BITREGS 91cdfa2a7eSchristos #endif 92cdfa2a7eSchristos 938585484eSchristos /* 948585484eSchristos *--------------------------------------------------------------------- 958585484eSchristos * replacing the 'time()' function 9603cfe0ffSchristos *--------------------------------------------------------------------- 978585484eSchristos */ 988585484eSchristos 998585484eSchristos static systime_func_ptr systime_func = &time; 1008585484eSchristos static inline time_t now(void); 1018585484eSchristos 1028585484eSchristos 1038585484eSchristos systime_func_ptr 1048585484eSchristos ntpcal_set_timefunc( 1058585484eSchristos systime_func_ptr nfunc 1068585484eSchristos ) 1078585484eSchristos { 1088585484eSchristos systime_func_ptr res; 1098585484eSchristos 1108585484eSchristos res = systime_func; 1118585484eSchristos if (NULL == nfunc) 1128585484eSchristos nfunc = &time; 1138585484eSchristos systime_func = nfunc; 1148585484eSchristos 1158585484eSchristos return res; 1168585484eSchristos } 1178585484eSchristos 1188585484eSchristos 1198585484eSchristos static inline time_t 1208585484eSchristos now(void) 1218585484eSchristos { 1228585484eSchristos return (*systime_func)(NULL); 1238585484eSchristos } 1248585484eSchristos 1258585484eSchristos /* 1268585484eSchristos *--------------------------------------------------------------------- 127af12ab5eSchristos * Get sign extension mask and unsigned 2cpl rep for a signed integer 128af12ab5eSchristos *--------------------------------------------------------------------- 129af12ab5eSchristos */ 130af12ab5eSchristos 131af12ab5eSchristos static inline uint32_t 132af12ab5eSchristos int32_sflag( 133af12ab5eSchristos const int32_t v) 134af12ab5eSchristos { 135af12ab5eSchristos # if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4 136af12ab5eSchristos 137af12ab5eSchristos /* Let's assume that shift is the fastest way to get the sign 138af12ab5eSchristos * extension of of a signed integer. This might not always be 139af12ab5eSchristos * true, though -- On 8bit CPUs or machines without barrel 140af12ab5eSchristos * shifter this will kill the performance. So we make sure 141af12ab5eSchristos * we do this only if 'int' has at least 4 bytes. 142af12ab5eSchristos */ 143af12ab5eSchristos return (uint32_t)(v >> 31); 144af12ab5eSchristos 145af12ab5eSchristos # else 146af12ab5eSchristos 147af12ab5eSchristos /* This should be a rather generic approach for getting a sign 148af12ab5eSchristos * extension mask... 149af12ab5eSchristos */ 150af12ab5eSchristos return UINT32_C(0) - (uint32_t)(v < 0); 151af12ab5eSchristos 152af12ab5eSchristos # endif 153af12ab5eSchristos } 154af12ab5eSchristos 155af12ab5eSchristos static inline int32_t 156af12ab5eSchristos uint32_2cpl_to_int32( 157af12ab5eSchristos const uint32_t vu) 158af12ab5eSchristos { 159af12ab5eSchristos int32_t v; 160af12ab5eSchristos 161af12ab5eSchristos # if TARGET_HAS_2CPL 162af12ab5eSchristos 163af12ab5eSchristos /* Just copy through the 32 bits from the unsigned value if 164af12ab5eSchristos * we're on a two's complement target. 165af12ab5eSchristos */ 166af12ab5eSchristos v = (int32_t)vu; 167af12ab5eSchristos 168af12ab5eSchristos # else 169af12ab5eSchristos 170af12ab5eSchristos /* Convert to signed integer, making sure signed integer 171af12ab5eSchristos * overflow cannot happen. Again, the optimiser might or might 172af12ab5eSchristos * not find out that this is just a copy of 32 bits on a target 173af12ab5eSchristos * with two's complement representation for signed integers. 174af12ab5eSchristos */ 175af12ab5eSchristos if (vu > INT32_MAX) 176af12ab5eSchristos v = -(int32_t)(~vu) - 1; 177af12ab5eSchristos else 178af12ab5eSchristos v = (int32_t)vu; 179af12ab5eSchristos 180af12ab5eSchristos # endif 181af12ab5eSchristos 182af12ab5eSchristos return v; 183af12ab5eSchristos } 184af12ab5eSchristos 185af12ab5eSchristos /* 186af12ab5eSchristos *--------------------------------------------------------------------- 1878585484eSchristos * Convert between 'time_t' and 'vint64' 1888585484eSchristos *--------------------------------------------------------------------- 1898585484eSchristos */ 1908585484eSchristos vint64 1918585484eSchristos time_to_vint64( 1928585484eSchristos const time_t * ptt 1938585484eSchristos ) 1948585484eSchristos { 1958585484eSchristos vint64 res; 1968585484eSchristos time_t tt; 1978585484eSchristos 1988585484eSchristos tt = *ptt; 1998585484eSchristos 2008585484eSchristos # if SIZEOF_TIME_T <= 4 2018585484eSchristos 2028585484eSchristos res.D_s.hi = 0; 2038585484eSchristos if (tt < 0) { 2048585484eSchristos res.D_s.lo = (uint32_t)-tt; 2058585484eSchristos M_NEG(res.D_s.hi, res.D_s.lo); 2068585484eSchristos } else { 2078585484eSchristos res.D_s.lo = (uint32_t)tt; 2088585484eSchristos } 2098585484eSchristos 2108585484eSchristos # elif defined(HAVE_INT64) 2118585484eSchristos 2128585484eSchristos res.q_s = tt; 2138585484eSchristos 2148585484eSchristos # else 2158585484eSchristos /* 2168585484eSchristos * shifting negative signed quantities is compiler-dependent, so 2178585484eSchristos * we better avoid it and do it all manually. And shifting more 2188585484eSchristos * than the width of a quantity is undefined. Also a don't do! 2198585484eSchristos */ 2208585484eSchristos if (tt < 0) { 2218585484eSchristos tt = -tt; 2228585484eSchristos res.D_s.lo = (uint32_t)tt; 2238585484eSchristos res.D_s.hi = (uint32_t)(tt >> 32); 2248585484eSchristos M_NEG(res.D_s.hi, res.D_s.lo); 2258585484eSchristos } else { 2268585484eSchristos res.D_s.lo = (uint32_t)tt; 2278585484eSchristos res.D_s.hi = (uint32_t)(tt >> 32); 2288585484eSchristos } 2298585484eSchristos 2308585484eSchristos # endif 2318585484eSchristos 2328585484eSchristos return res; 2338585484eSchristos } 2348585484eSchristos 2358585484eSchristos 2368585484eSchristos time_t 2378585484eSchristos vint64_to_time( 2388585484eSchristos const vint64 *tv 2398585484eSchristos ) 2408585484eSchristos { 2418585484eSchristos time_t res; 2428585484eSchristos 2438585484eSchristos # if SIZEOF_TIME_T <= 4 2448585484eSchristos 2458585484eSchristos res = (time_t)tv->D_s.lo; 2468585484eSchristos 2478585484eSchristos # elif defined(HAVE_INT64) 2488585484eSchristos 2498585484eSchristos res = (time_t)tv->q_s; 2508585484eSchristos 2518585484eSchristos # else 2528585484eSchristos 2538585484eSchristos res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; 2548585484eSchristos 2558585484eSchristos # endif 2568585484eSchristos 2578585484eSchristos return res; 2588585484eSchristos } 2598585484eSchristos 2608585484eSchristos /* 2618585484eSchristos *--------------------------------------------------------------------- 2628585484eSchristos * Get the build date & time 2638585484eSchristos *--------------------------------------------------------------------- 2648585484eSchristos */ 2658585484eSchristos int 2668585484eSchristos ntpcal_get_build_date( 2678585484eSchristos struct calendar * jd 2688585484eSchristos ) 2698585484eSchristos { 2708585484eSchristos /* The C standard tells us the format of '__DATE__': 2718585484eSchristos * 2728585484eSchristos * __DATE__ The date of translation of the preprocessing 2738585484eSchristos * translation unit: a character string literal of the form "Mmm 2748585484eSchristos * dd yyyy", where the names of the months are the same as those 2758585484eSchristos * generated by the asctime function, and the first character of 2768585484eSchristos * dd is a space character if the value is less than 10. If the 2778585484eSchristos * date of translation is not available, an 2788585484eSchristos * implementation-defined valid date shall be supplied. 2798585484eSchristos * 2808585484eSchristos * __TIME__ The time of translation of the preprocessing 2818585484eSchristos * translation unit: a character string literal of the form 2828585484eSchristos * "hh:mm:ss" as in the time generated by the asctime 2838585484eSchristos * function. If the time of translation is not available, an 2848585484eSchristos * implementation-defined valid time shall be supplied. 2858585484eSchristos * 2868585484eSchristos * Note that MSVC declares DATE and TIME to be in the local time 2878585484eSchristos * zone, while neither the C standard nor the GCC docs make any 2888585484eSchristos * statement about this. As a result, we may be +/-12hrs off 2898585484eSchristos * UTC. But for practical purposes, this should not be a 2908585484eSchristos * problem. 2918585484eSchristos * 2928585484eSchristos */ 293bebb9d5cSapb # ifdef MKREPRO_DATE 294bebb9d5cSapb static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; 295bebb9d5cSapb # else 2968585484eSchristos static const char build[] = __TIME__ "/" __DATE__; 297bebb9d5cSapb # endif 2988585484eSchristos static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 299ea66d795Schristos 3008585484eSchristos char monstr[4]; 3018585484eSchristos const char * cp; 3028585484eSchristos unsigned short hour, minute, second, day, year; 3038585484eSchristos /* Note: The above quantities are used for sscanf 'hu' format, 3048585484eSchristos * so using 'uint16_t' is contra-indicated! 3058585484eSchristos */ 3068585484eSchristos 307ea66d795Schristos # ifdef DEBUG 308ea66d795Schristos static int ignore = 0; 309ea66d795Schristos # endif 310ea66d795Schristos 3118585484eSchristos ZERO(*jd); 3128585484eSchristos jd->year = 1970; 3138585484eSchristos jd->month = 1; 3148585484eSchristos jd->monthday = 1; 3158585484eSchristos 316ea66d795Schristos # ifdef DEBUG 317ea66d795Schristos /* check environment if build date should be ignored */ 318ea66d795Schristos if (0 == ignore) { 319ea66d795Schristos const char * envstr; 320ea66d795Schristos envstr = getenv("NTPD_IGNORE_BUILD_DATE"); 321ea66d795Schristos ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes"))); 322ea66d795Schristos } 323ea66d795Schristos if (ignore > 1) 324ea66d795Schristos return FALSE; 325ea66d795Schristos # endif 326ea66d795Schristos 3278585484eSchristos if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu", 3288585484eSchristos &hour, &minute, &second, monstr, &day, &year)) { 3298585484eSchristos cp = strstr(mlist, monstr); 3308585484eSchristos if (NULL != cp) { 3318585484eSchristos jd->year = year; 3328585484eSchristos jd->month = (uint8_t)((cp - mlist) / 3 + 1); 3338585484eSchristos jd->monthday = (uint8_t)day; 3348585484eSchristos jd->hour = (uint8_t)hour; 3358585484eSchristos jd->minute = (uint8_t)minute; 3368585484eSchristos jd->second = (uint8_t)second; 3378585484eSchristos 3388585484eSchristos return TRUE; 3398585484eSchristos } 3408585484eSchristos } 3418585484eSchristos 3428585484eSchristos return FALSE; 3438585484eSchristos } 3448585484eSchristos 3458585484eSchristos 3468585484eSchristos /* 3478585484eSchristos *--------------------------------------------------------------------- 3488585484eSchristos * basic calendar stuff 34903cfe0ffSchristos *--------------------------------------------------------------------- 3508585484eSchristos */ 3518585484eSchristos 3528585484eSchristos /* 3538585484eSchristos * Some notes on the terminology: 3548585484eSchristos * 3558585484eSchristos * We use the proleptic Gregorian calendar, which is the Gregorian 3568585484eSchristos * calendar extended in both directions ad infinitum. This totally 3578585484eSchristos * disregards the fact that this calendar was invented in 1582, and 3588585484eSchristos * was adopted at various dates over the world; sometimes even after 3598585484eSchristos * the start of the NTP epoch. 3608585484eSchristos * 3618585484eSchristos * Normally date parts are given as current cycles, while time parts 3628585484eSchristos * are given as elapsed cycles: 3638585484eSchristos * 3648585484eSchristos * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, 3658585484eSchristos * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. 3668585484eSchristos * 3678585484eSchristos * The basic calculations for this calendar implementation deal with 3688585484eSchristos * ELAPSED date units, which is the number of full years, full months 3698585484eSchristos * and full days before a date: 1970-01-01 would be (1969, 0, 0) in 3708585484eSchristos * that notation. 3718585484eSchristos * 3728585484eSchristos * To ease the numeric computations, month and day values outside the 3738585484eSchristos * normal range are acceptable: 2001-03-00 will be treated as the day 3748585484eSchristos * before 2001-03-01, 2000-13-32 will give the same result as 3758585484eSchristos * 2001-02-01 and so on. 3768585484eSchristos * 3778585484eSchristos * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' 3788585484eSchristos * (day number). This is the number of days elapsed since 0000-12-31 3798585484eSchristos * in the proleptic Gregorian calendar. The begin of the Christian Era 3808585484eSchristos * (0001-01-01) is RD(1). 3818585484eSchristos */ 3828585484eSchristos 3838585484eSchristos /* 38403cfe0ffSchristos * ==================================================================== 3858585484eSchristos * 3868585484eSchristos * General algorithmic stuff 3878585484eSchristos * 38803cfe0ffSchristos * ==================================================================== 3898585484eSchristos */ 3908585484eSchristos 3918585484eSchristos /* 3928585484eSchristos *--------------------------------------------------------------------- 393cdfa2a7eSchristos * fast modulo 7 operations (floor/mathematical convention) 394cdfa2a7eSchristos *--------------------------------------------------------------------- 395cdfa2a7eSchristos */ 396cdfa2a7eSchristos int 397cdfa2a7eSchristos u32mod7( 398cdfa2a7eSchristos uint32_t x 399cdfa2a7eSchristos ) 400cdfa2a7eSchristos { 401cdfa2a7eSchristos /* This is a combination of tricks from "Hacker's Delight" with 402cdfa2a7eSchristos * some modifications, like a multiplication that rounds up to 403cdfa2a7eSchristos * drop the final adjustment stage. 404cdfa2a7eSchristos * 405cdfa2a7eSchristos * Do a partial reduction by digit sum to keep the value in the 406cdfa2a7eSchristos * range permitted for the mul/shift stage. There are several 407cdfa2a7eSchristos * possible and absolutely equivalent shift/mask combinations; 408cdfa2a7eSchristos * this one is ARM-friendly because of a mask that fits into 16 409cdfa2a7eSchristos * bit. 410cdfa2a7eSchristos */ 411cdfa2a7eSchristos x = (x >> 15) + (x & UINT32_C(0x7FFF)); 412cdfa2a7eSchristos /* Take reminder as (mod 8) by mul/shift. Since the multiplier 413cdfa2a7eSchristos * was calculated using ceil() instead of floor(), it skips the 414cdfa2a7eSchristos * value '7' properly. 415cdfa2a7eSchristos * M <- ceil(ldexp(8/7, 29)) 416cdfa2a7eSchristos */ 417cdfa2a7eSchristos return (int)((x * UINT32_C(0x24924925)) >> 29); 418cdfa2a7eSchristos } 419cdfa2a7eSchristos 420cdfa2a7eSchristos int 421cdfa2a7eSchristos i32mod7( 422cdfa2a7eSchristos int32_t x 423cdfa2a7eSchristos ) 424cdfa2a7eSchristos { 425cdfa2a7eSchristos /* We add (2**32 - 2**32 % 7), which is (2**32 - 4), to negative 426cdfa2a7eSchristos * numbers to map them into the postive range. Only the term '-4' 427cdfa2a7eSchristos * survives, obviously. 428cdfa2a7eSchristos */ 429cdfa2a7eSchristos uint32_t ux = (uint32_t)x; 430cdfa2a7eSchristos return u32mod7((x < 0) ? (ux - 4u) : ux); 431cdfa2a7eSchristos } 432cdfa2a7eSchristos 433cdfa2a7eSchristos uint32_t 434cdfa2a7eSchristos i32fmod( 435cdfa2a7eSchristos int32_t x, 436cdfa2a7eSchristos uint32_t d 437cdfa2a7eSchristos ) 438cdfa2a7eSchristos { 439cdfa2a7eSchristos uint32_t ux = (uint32_t)x; 440cdfa2a7eSchristos uint32_t sf = UINT32_C(0) - (x < 0); 441cdfa2a7eSchristos ux = (sf ^ ux ) % d; 442cdfa2a7eSchristos return (d & sf) + (sf ^ ux); 443cdfa2a7eSchristos } 444cdfa2a7eSchristos 445cdfa2a7eSchristos /* 446cdfa2a7eSchristos *--------------------------------------------------------------------- 4478585484eSchristos * Do a periodic extension of 'value' around 'pivot' with a period of 4488585484eSchristos * 'cycle'. 4498585484eSchristos * 4508585484eSchristos * The result 'res' is a number that holds to the following properties: 4518585484eSchristos * 4528585484eSchristos * 1) res MOD cycle == value MOD cycle 4538585484eSchristos * 2) pivot <= res < pivot + cycle 4548585484eSchristos * (replace </<= with >/>= for negative cycles) 4558585484eSchristos * 4568585484eSchristos * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which 4578585484eSchristos * is not the same as the '%' operator in C: C requires division to be 4588585484eSchristos * a truncated division, where remainder and dividend have the same 4598585484eSchristos * sign if the remainder is not zero, whereas floor division requires 4608585484eSchristos * divider and modulus to have the same sign for a non-zero modulus. 4618585484eSchristos * 4628585484eSchristos * This function has some useful applications: 4638585484eSchristos * 4648585484eSchristos * + let Y be a calendar year and V a truncated 2-digit year: then 4658585484eSchristos * periodic_extend(Y-50, V, 100) 4668585484eSchristos * is the closest expansion of the truncated year with respect to 4678585484eSchristos * the full year, that is a 4-digit year with a difference of less 4688585484eSchristos * than 50 years to the year Y. ("century unfolding") 4698585484eSchristos * 4708585484eSchristos * + let T be a UN*X time stamp and V be seconds-of-day: then 4718585484eSchristos * perodic_extend(T-43200, V, 86400) 4728585484eSchristos * is a time stamp that has the same seconds-of-day as the input 4738585484eSchristos * value, with an absolute difference to T of <= 12hrs. ("day 4748585484eSchristos * unfolding") 4758585484eSchristos * 4768585484eSchristos * + Wherever you have a truncated periodic value and a non-truncated 4778585484eSchristos * base value and you want to match them somehow... 4788585484eSchristos * 4798585484eSchristos * Basically, the function delivers 'pivot + (value - pivot) % cycle', 4808585484eSchristos * but the implementation takes some pains to avoid internal signed 4818585484eSchristos * integer overflows in the '(value - pivot) % cycle' part and adheres 4828585484eSchristos * to the floor division convention. 4838585484eSchristos * 4848585484eSchristos * If 64bit scalars where available on all intended platforms, writing a 4858585484eSchristos * version that uses 64 bit ops would be easy; writing a general 4868585484eSchristos * division routine for 64bit ops on a platform that can only do 4878585484eSchristos * 32/16bit divisions and is still performant is a bit more 4888585484eSchristos * difficult. Since most usecases can be coded in a way that does only 489cdfa2a7eSchristos * require the 32bit version a 64bit version is NOT provided here. 4908585484eSchristos *--------------------------------------------------------------------- 4918585484eSchristos */ 4928585484eSchristos int32_t 4938585484eSchristos ntpcal_periodic_extend( 4948585484eSchristos int32_t pivot, 4958585484eSchristos int32_t value, 4968585484eSchristos int32_t cycle 4978585484eSchristos ) 4988585484eSchristos { 499cdfa2a7eSchristos /* Implement a 4-quadrant modulus calculation by 2 2-quadrant 500cdfa2a7eSchristos * branches, one for positive and one for negative dividers. 501cdfa2a7eSchristos * Everything else can be handled by bit level logic and 502cdfa2a7eSchristos * conditional one's complement arithmetic. By convention, we 503cdfa2a7eSchristos * assume 504cdfa2a7eSchristos * 505cdfa2a7eSchristos * x % b == 0 if |b| < 2 506cdfa2a7eSchristos * 507cdfa2a7eSchristos * that is, we don't actually divide for cycles of -1,0,1 and 508cdfa2a7eSchristos * return the pivot value in that case. 5098585484eSchristos */ 510cdfa2a7eSchristos uint32_t uv = (uint32_t)value; 511cdfa2a7eSchristos uint32_t up = (uint32_t)pivot; 512cdfa2a7eSchristos uint32_t uc, sf; 513cdfa2a7eSchristos 514cdfa2a7eSchristos if (cycle > 1) 515cdfa2a7eSchristos { 516cdfa2a7eSchristos uc = (uint32_t)cycle; 517cdfa2a7eSchristos sf = UINT32_C(0) - (value < pivot); 518cdfa2a7eSchristos 519cdfa2a7eSchristos uv = sf ^ (uv - up); 520cdfa2a7eSchristos uv %= uc; 521cdfa2a7eSchristos pivot += (uc & sf) + (sf ^ uv); 5228585484eSchristos } 523cdfa2a7eSchristos else if (cycle < -1) 524cdfa2a7eSchristos { 525cdfa2a7eSchristos uc = ~(uint32_t)cycle + 1; 526cdfa2a7eSchristos sf = UINT32_C(0) - (value > pivot); 527cdfa2a7eSchristos 528cdfa2a7eSchristos uv = sf ^ (up - uv); 529cdfa2a7eSchristos uv %= uc; 530cdfa2a7eSchristos pivot -= (uc & sf) + (sf ^ uv); 5318585484eSchristos } 5328585484eSchristos return pivot; 5338585484eSchristos } 5348585484eSchristos 53503cfe0ffSchristos /*--------------------------------------------------------------------- 53603cfe0ffSchristos * Note to the casual reader 53703cfe0ffSchristos * 53803cfe0ffSchristos * In the next two functions you will find (or would have found...) 53903cfe0ffSchristos * the expression 54003cfe0ffSchristos * 54103cfe0ffSchristos * res.Q_s -= 0x80000000; 54203cfe0ffSchristos * 54303cfe0ffSchristos * There was some ruckus about a possible programming error due to 54403cfe0ffSchristos * integer overflow and sign propagation. 54503cfe0ffSchristos * 54603cfe0ffSchristos * This assumption is based on a lack of understanding of the C 54703cfe0ffSchristos * standard. (Though this is admittedly not one of the most 'natural' 54803cfe0ffSchristos * aspects of the 'C' language and easily to get wrong.) 54903cfe0ffSchristos * 55003cfe0ffSchristos * see 55103cfe0ffSchristos * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 55203cfe0ffSchristos * "ISO/IEC 9899:201x Committee Draft — April 12, 2011" 55303cfe0ffSchristos * 6.4.4.1 Integer constants, clause 5 55403cfe0ffSchristos * 55503cfe0ffSchristos * why there is no sign extension/overflow problem here. 55603cfe0ffSchristos * 55703cfe0ffSchristos * But to ease the minds of the doubtful, I added back the 'u' qualifiers 55803cfe0ffSchristos * that somehow got lost over the last years. 55903cfe0ffSchristos */ 56003cfe0ffSchristos 56103cfe0ffSchristos 5628585484eSchristos /* 56303cfe0ffSchristos *--------------------------------------------------------------------- 5648585484eSchristos * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X 5658585484eSchristos * scale with proper epoch unfolding around a given pivot or the current 5668585484eSchristos * system time. This function happily accepts negative pivot values as 567cdfa2a7eSchristos * timestamps before 1970-01-01, so be aware of possible trouble on 5688585484eSchristos * platforms with 32bit 'time_t'! 5698585484eSchristos * 5708585484eSchristos * This is also a periodic extension, but since the cycle is 2^32 and 5718585484eSchristos * the shift is 2^31, we can do some *very* fast math without explicit 5728585484eSchristos * divisions. 57303cfe0ffSchristos *--------------------------------------------------------------------- 5748585484eSchristos */ 5758585484eSchristos vint64 5768585484eSchristos ntpcal_ntp_to_time( 5778585484eSchristos uint32_t ntp, 5788585484eSchristos const time_t * pivot 5798585484eSchristos ) 5808585484eSchristos { 5818585484eSchristos vint64 res; 5828585484eSchristos 583af12ab5eSchristos # if defined(HAVE_INT64) 5848585484eSchristos 5858585484eSchristos res.q_s = (pivot != NULL) 5868585484eSchristos ? *pivot 5878585484eSchristos : now(); 58803cfe0ffSchristos res.Q_s -= 0x80000000u; /* unshift of half range */ 5898585484eSchristos ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 5908585484eSchristos ntp -= res.D_s.lo; /* cycle difference */ 5918585484eSchristos res.Q_s += (uint64_t)ntp; /* get expanded time */ 5928585484eSchristos 5938585484eSchristos # else /* no 64bit scalars */ 5948585484eSchristos 5958585484eSchristos time_t tmp; 5968585484eSchristos 5978585484eSchristos tmp = (pivot != NULL) 5988585484eSchristos ? *pivot 5998585484eSchristos : now(); 6008585484eSchristos res = time_to_vint64(&tmp); 60103cfe0ffSchristos M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 6028585484eSchristos ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 6038585484eSchristos ntp -= res.D_s.lo; /* cycle difference */ 6048585484eSchristos M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 6058585484eSchristos 6068585484eSchristos # endif /* no 64bit scalars */ 6078585484eSchristos 6088585484eSchristos return res; 6098585484eSchristos } 6108585484eSchristos 6118585484eSchristos /* 61203cfe0ffSchristos *--------------------------------------------------------------------- 6138585484eSchristos * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP 6148585484eSchristos * scale with proper epoch unfolding around a given pivot or the current 6158585484eSchristos * system time. 6168585484eSchristos * 6178585484eSchristos * Note: The pivot must be given in the UN*X time domain! 6188585484eSchristos * 6198585484eSchristos * This is also a periodic extension, but since the cycle is 2^32 and 6208585484eSchristos * the shift is 2^31, we can do some *very* fast math without explicit 6218585484eSchristos * divisions. 62203cfe0ffSchristos *--------------------------------------------------------------------- 6238585484eSchristos */ 6248585484eSchristos vint64 6258585484eSchristos ntpcal_ntp_to_ntp( 6268585484eSchristos uint32_t ntp, 6278585484eSchristos const time_t *pivot 6288585484eSchristos ) 6298585484eSchristos { 6308585484eSchristos vint64 res; 6318585484eSchristos 632af12ab5eSchristos # if defined(HAVE_INT64) 6338585484eSchristos 6348585484eSchristos res.q_s = (pivot) 6358585484eSchristos ? *pivot 6368585484eSchristos : now(); 63703cfe0ffSchristos res.Q_s -= 0x80000000u; /* unshift of half range */ 6388585484eSchristos res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ 6398585484eSchristos ntp -= res.D_s.lo; /* cycle difference */ 6408585484eSchristos res.Q_s += (uint64_t)ntp; /* get expanded time */ 6418585484eSchristos 6428585484eSchristos # else /* no 64bit scalars */ 6438585484eSchristos 6448585484eSchristos time_t tmp; 6458585484eSchristos 6468585484eSchristos tmp = (pivot) 6478585484eSchristos ? *pivot 6488585484eSchristos : now(); 6498585484eSchristos res = time_to_vint64(&tmp); 6508585484eSchristos M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 6518585484eSchristos M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */ 6528585484eSchristos ntp -= res.D_s.lo; /* cycle difference */ 6538585484eSchristos M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 6548585484eSchristos 6558585484eSchristos # endif /* no 64bit scalars */ 6568585484eSchristos 6578585484eSchristos return res; 6588585484eSchristos } 6598585484eSchristos 6608585484eSchristos 6618585484eSchristos /* 66203cfe0ffSchristos * ==================================================================== 6638585484eSchristos * 6648585484eSchristos * Splitting values to composite entities 6658585484eSchristos * 66603cfe0ffSchristos * ==================================================================== 6678585484eSchristos */ 6688585484eSchristos 6698585484eSchristos /* 67003cfe0ffSchristos *--------------------------------------------------------------------- 6718585484eSchristos * Split a 64bit seconds value into elapsed days in 'res.hi' and 6728585484eSchristos * elapsed seconds since midnight in 'res.lo' using explicit floor 6738585484eSchristos * division. This function happily accepts negative time values as 6748585484eSchristos * timestamps before the respective epoch start. 67503cfe0ffSchristos *--------------------------------------------------------------------- 6768585484eSchristos */ 6778585484eSchristos ntpcal_split 6788585484eSchristos ntpcal_daysplit( 6798585484eSchristos const vint64 *ts 6808585484eSchristos ) 6818585484eSchristos { 6828585484eSchristos ntpcal_split res; 683cdfa2a7eSchristos uint32_t Q, R; 6848585484eSchristos 685cdfa2a7eSchristos # if defined(HAVE_64BITREGS) 6868585484eSchristos 687cdfa2a7eSchristos /* Assume we have 64bit registers an can do a divison by 688cdfa2a7eSchristos * constant reasonably fast using the one's complement trick.. 689cdfa2a7eSchristos */ 690cdfa2a7eSchristos uint64_t sf64 = (uint64_t)-(ts->q_s < 0); 691cdfa2a7eSchristos Q = (uint32_t)(sf64 ^ ((sf64 ^ ts->Q_s) / SECSPERDAY)); 692cdfa2a7eSchristos R = (uint32_t)(ts->Q_s - Q * SECSPERDAY); 693cdfa2a7eSchristos 694cdfa2a7eSchristos # elif defined(UINT64_MAX) && !defined(__arm__) 695cdfa2a7eSchristos 696cdfa2a7eSchristos /* We rely on the compiler to do efficient 64bit divisions as 697cdfa2a7eSchristos * good as possible. Which might or might not be true. At least 698cdfa2a7eSchristos * for ARM CPUs, the sum-by-digit code in the next section is 699cdfa2a7eSchristos * faster for many compilers. (This might change over time, but 700cdfa2a7eSchristos * the 64bit-by-32bit division will never outperform the exact 701cdfa2a7eSchristos * division by a substantial factor....) 702af12ab5eSchristos */ 703af12ab5eSchristos if (ts->q_s < 0) 704af12ab5eSchristos Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY); 705af12ab5eSchristos else 706af12ab5eSchristos Q = (uint32_t)( ts->Q_s / SECSPERDAY); 707cdfa2a7eSchristos R = ts->D_s.lo - Q * SECSPERDAY; 7088585484eSchristos 7098585484eSchristos # else 7108585484eSchristos 711cdfa2a7eSchristos /* We don't have 64bit regs. That hurts a bit. 712af12ab5eSchristos * 713cdfa2a7eSchristos * Here we use a mean trick to get away with just one explicit 714cdfa2a7eSchristos * modulo operation and pure 32bit ops. 715cdfa2a7eSchristos * 716cdfa2a7eSchristos * Remember: 86400 <--> 128 * 675 717cdfa2a7eSchristos * 718cdfa2a7eSchristos * So we discard the lowest 7 bit and do an exact division by 719cdfa2a7eSchristos * 675, modulo 2**32. 720cdfa2a7eSchristos * 721cdfa2a7eSchristos * First we shift out the lower 7 bits. 722cdfa2a7eSchristos * 723cdfa2a7eSchristos * Then we use a digit-wise pseudo-reduction, where a 'digit' is 724cdfa2a7eSchristos * actually a 16-bit group. This is followed by a full reduction 725cdfa2a7eSchristos * with a 'true' division step. This yields the modulus of the 726cdfa2a7eSchristos * full 64bit value. The sign bit gets some extra treatment. 727cdfa2a7eSchristos * 728cdfa2a7eSchristos * Then we decrement the lower limb by that modulus, so it is 729cdfa2a7eSchristos * exactly divisible by 675. [*] 730cdfa2a7eSchristos * 731cdfa2a7eSchristos * Then we multiply with the modular inverse of 675 (mod 2**32) 732cdfa2a7eSchristos * and voila, we have the result. 733cdfa2a7eSchristos * 734cdfa2a7eSchristos * Special Thanks to Henry S. Warren and his "Hacker's delight" 735cdfa2a7eSchristos * for giving that idea. 736cdfa2a7eSchristos * 737cdfa2a7eSchristos * (Note[*]: that's not the full truth. We would have to 738cdfa2a7eSchristos * subtract the modulus from the full 64 bit number to get a 739cdfa2a7eSchristos * number that is divisible by 675. But since we use the 740cdfa2a7eSchristos * multiplicative inverse (mod 2**32) there's no reason to carry 741cdfa2a7eSchristos * the subtraction into the upper bits!) 7428585484eSchristos */ 743cdfa2a7eSchristos uint32_t al = ts->D_s.lo; 744cdfa2a7eSchristos uint32_t ah = ts->D_s.hi; 7458585484eSchristos 746cdfa2a7eSchristos /* shift out the lower 7 bits, smash sign bit */ 747cdfa2a7eSchristos al = (al >> 7) | (ah << 25); 748cdfa2a7eSchristos ah = (ah >> 7) & 0x00FFFFFFu; 7498585484eSchristos 750cdfa2a7eSchristos R = (ts->d_s.hi < 0) ? 239 : 0;/* sign bit value */ 751cdfa2a7eSchristos R += (al & 0xFFFF); 752cdfa2a7eSchristos R += (al >> 16 ) * 61u; /* 2**16 % 675 */ 753cdfa2a7eSchristos R += (ah & 0xFFFF) * 346u; /* 2**32 % 675 */ 754cdfa2a7eSchristos R += (ah >> 16 ) * 181u; /* 2**48 % 675 */ 755cdfa2a7eSchristos R %= 675u; /* final reduction */ 756cdfa2a7eSchristos Q = (al - R) * 0x2D21C10Bu; /* modinv(675, 2**32) */ 757cdfa2a7eSchristos R = (R << 7) | (ts->d_s.lo & 0x07F); 7588585484eSchristos 7598585484eSchristos # endif 760af12ab5eSchristos 761af12ab5eSchristos res.hi = uint32_2cpl_to_int32(Q); 762cdfa2a7eSchristos res.lo = R; 763cdfa2a7eSchristos 764cdfa2a7eSchristos return res; 765cdfa2a7eSchristos } 766cdfa2a7eSchristos 767cdfa2a7eSchristos /* 768cdfa2a7eSchristos *--------------------------------------------------------------------- 769cdfa2a7eSchristos * Split a 64bit seconds value into elapsed weeks in 'res.hi' and 770cdfa2a7eSchristos * elapsed seconds since week start in 'res.lo' using explicit floor 771cdfa2a7eSchristos * division. This function happily accepts negative time values as 772cdfa2a7eSchristos * timestamps before the respective epoch start. 773cdfa2a7eSchristos *--------------------------------------------------------------------- 774cdfa2a7eSchristos */ 775cdfa2a7eSchristos ntpcal_split 776cdfa2a7eSchristos ntpcal_weeksplit( 777cdfa2a7eSchristos const vint64 *ts 778cdfa2a7eSchristos ) 779cdfa2a7eSchristos { 780cdfa2a7eSchristos ntpcal_split res; 781cdfa2a7eSchristos uint32_t Q, R; 782cdfa2a7eSchristos 783cdfa2a7eSchristos /* This is a very close relative to the day split function; for 784cdfa2a7eSchristos * details, see there! 785cdfa2a7eSchristos */ 786cdfa2a7eSchristos 787cdfa2a7eSchristos # if defined(HAVE_64BITREGS) 788cdfa2a7eSchristos 789cdfa2a7eSchristos uint64_t sf64 = (uint64_t)-(ts->q_s < 0); 790cdfa2a7eSchristos Q = (uint32_t)(sf64 ^ ((sf64 ^ ts->Q_s) / SECSPERWEEK)); 791cdfa2a7eSchristos R = (uint32_t)(ts->Q_s - Q * SECSPERWEEK); 792cdfa2a7eSchristos 793cdfa2a7eSchristos # elif defined(UINT64_MAX) && !defined(__arm__) 794cdfa2a7eSchristos 795cdfa2a7eSchristos if (ts->q_s < 0) 796cdfa2a7eSchristos Q = ~(uint32_t)(~ts->Q_s / SECSPERWEEK); 797cdfa2a7eSchristos else 798cdfa2a7eSchristos Q = (uint32_t)( ts->Q_s / SECSPERWEEK); 799cdfa2a7eSchristos R = ts->D_s.lo - Q * SECSPERWEEK; 800cdfa2a7eSchristos 801cdfa2a7eSchristos # else 802cdfa2a7eSchristos 803cdfa2a7eSchristos /* Remember: 7*86400 <--> 604800 <--> 128 * 4725 */ 804cdfa2a7eSchristos uint32_t al = ts->D_s.lo; 805cdfa2a7eSchristos uint32_t ah = ts->D_s.hi; 806cdfa2a7eSchristos 807cdfa2a7eSchristos al = (al >> 7) | (ah << 25); 808cdfa2a7eSchristos ah = (ah >> 7) & 0x00FFFFFF; 809cdfa2a7eSchristos 810cdfa2a7eSchristos R = (ts->d_s.hi < 0) ? 2264 : 0;/* sign bit value */ 811cdfa2a7eSchristos R += (al & 0xFFFF); 812cdfa2a7eSchristos R += (al >> 16 ) * 4111u; /* 2**16 % 4725 */ 813cdfa2a7eSchristos R += (ah & 0xFFFF) * 3721u; /* 2**32 % 4725 */ 814cdfa2a7eSchristos R += (ah >> 16 ) * 2206u; /* 2**48 % 4725 */ 815cdfa2a7eSchristos R %= 4725u; /* final reduction */ 816cdfa2a7eSchristos Q = (al - R) * 0x98BBADDDu; /* modinv(4725, 2**32) */ 817cdfa2a7eSchristos R = (R << 7) | (ts->d_s.lo & 0x07F); 818cdfa2a7eSchristos 819cdfa2a7eSchristos # endif 820cdfa2a7eSchristos 821cdfa2a7eSchristos res.hi = uint32_2cpl_to_int32(Q); 822cdfa2a7eSchristos res.lo = R; 823af12ab5eSchristos 8248585484eSchristos return res; 8258585484eSchristos } 8268585484eSchristos 8278585484eSchristos /* 82803cfe0ffSchristos *--------------------------------------------------------------------- 8298585484eSchristos * Split a 32bit seconds value into h/m/s and excessive days. This 8308585484eSchristos * function happily accepts negative time values as timestamps before 8318585484eSchristos * midnight. 83203cfe0ffSchristos *--------------------------------------------------------------------- 8338585484eSchristos */ 8348585484eSchristos static int32_t 8358585484eSchristos priv_timesplit( 8368585484eSchristos int32_t split[3], 8378585484eSchristos int32_t ts 8388585484eSchristos ) 8398585484eSchristos { 840af12ab5eSchristos /* Do 3 chained floor divisions by positive constants, using the 841af12ab5eSchristos * one's complement trick and factoring out the intermediate XOR 842af12ab5eSchristos * ops to reduce the number of operations. 843af12ab5eSchristos */ 844cdfa2a7eSchristos uint32_t us, um, uh, ud, sf32; 8458585484eSchristos 846cdfa2a7eSchristos sf32 = int32_sflag(ts); 8478585484eSchristos 848cdfa2a7eSchristos us = (uint32_t)ts; 849cdfa2a7eSchristos um = (sf32 ^ us) / SECSPERMIN; 850af12ab5eSchristos uh = um / MINSPERHR; 851af12ab5eSchristos ud = uh / HRSPERDAY; 8528585484eSchristos 853cdfa2a7eSchristos um ^= sf32; 854cdfa2a7eSchristos uh ^= sf32; 855cdfa2a7eSchristos ud ^= sf32; 856af12ab5eSchristos 857af12ab5eSchristos split[0] = (int32_t)(uh - ud * HRSPERDAY ); 858af12ab5eSchristos split[1] = (int32_t)(um - uh * MINSPERHR ); 859af12ab5eSchristos split[2] = (int32_t)(us - um * SECSPERMIN); 860af12ab5eSchristos 861af12ab5eSchristos return uint32_2cpl_to_int32(ud); 8628585484eSchristos } 8638585484eSchristos 8648585484eSchristos /* 8658585484eSchristos *--------------------------------------------------------------------- 8668585484eSchristos * Given the number of elapsed days in the calendar era, split this 8678585484eSchristos * number into the number of elapsed years in 'res.hi' and the number 8688585484eSchristos * of elapsed days of that year in 'res.lo'. 8698585484eSchristos * 8708585484eSchristos * if 'isleapyear' is not NULL, it will receive an integer that is 0 for 8718585484eSchristos * regular years and a non-zero value for leap years. 8728585484eSchristos *--------------------------------------------------------------------- 8738585484eSchristos */ 8748585484eSchristos ntpcal_split 8758585484eSchristos ntpcal_split_eradays( 8768585484eSchristos int32_t days, 8778585484eSchristos int *isleapyear 8788585484eSchristos ) 8798585484eSchristos { 880af12ab5eSchristos /* Use the fast cycle split algorithm here, to calculate the 881af12ab5eSchristos * centuries and years in a century with one division each. This 882af12ab5eSchristos * reduces the number of division operations to two, but is 883cdfa2a7eSchristos * susceptible to internal range overflow. We take some extra 884cdfa2a7eSchristos * steps to avoid the gap. 885af12ab5eSchristos */ 8868585484eSchristos ntpcal_split res; 887af12ab5eSchristos int32_t n100, n001; /* calendar year cycles */ 888cdfa2a7eSchristos uint32_t uday, Q; 8898585484eSchristos 890cdfa2a7eSchristos /* split off centuries first 891cdfa2a7eSchristos * 892cdfa2a7eSchristos * We want to execute '(days * 4 + 3) /% 146097' under floor 893cdfa2a7eSchristos * division rules in the first step. Well, actually we want to 894cdfa2a7eSchristos * calculate 'floor((days + 0.75) / 36524.25)', but we want to 895cdfa2a7eSchristos * do it in scaled integer calculation. 896cdfa2a7eSchristos */ 897cdfa2a7eSchristos # if defined(HAVE_64BITREGS) 898cdfa2a7eSchristos 899cdfa2a7eSchristos /* not too complicated with an intermediate 64bit value */ 900cdfa2a7eSchristos uint64_t ud64, sf64; 901cdfa2a7eSchristos ud64 = ((uint64_t)days << 2) | 3u; 902cdfa2a7eSchristos sf64 = (uint64_t)-(days < 0); 903cdfa2a7eSchristos Q = (uint32_t)(sf64 ^ ((sf64 ^ ud64) / GREGORIAN_CYCLE_DAYS)); 904cdfa2a7eSchristos uday = (uint32_t)(ud64 - Q * GREGORIAN_CYCLE_DAYS); 905af12ab5eSchristos n100 = uint32_2cpl_to_int32(Q); 9068585484eSchristos 907cdfa2a7eSchristos # else 908cdfa2a7eSchristos 909cdfa2a7eSchristos /* '4*days+3' suffers from range overflow when going to the 910cdfa2a7eSchristos * limits. We solve this by doing an exact division (mod 2^32) 911cdfa2a7eSchristos * after caclulating the remainder first. 912cdfa2a7eSchristos * 913cdfa2a7eSchristos * We start with a partial reduction by digit sums, extracting 914cdfa2a7eSchristos * the upper bits from the original value before they get lost 915cdfa2a7eSchristos * by scaling, and do one full division step to get the true 916cdfa2a7eSchristos * remainder. Then a final multiplication with the 917cdfa2a7eSchristos * multiplicative inverse of 146097 (mod 2^32) gives us the full 918cdfa2a7eSchristos * quotient. 919cdfa2a7eSchristos * 920cdfa2a7eSchristos * (-2^33) % 146097 --> 130717 : the sign bit value 921cdfa2a7eSchristos * ( 2^20) % 146097 --> 25897 : the upper digit value 922cdfa2a7eSchristos * modinv(146097, 2^32) --> 660721233 : the inverse 923cdfa2a7eSchristos */ 924cdfa2a7eSchristos uint32_t ux = ((uint32_t)days << 2) | 3; 925cdfa2a7eSchristos uday = (days < 0) ? 130717u : 0u; /* sign dgt */ 926cdfa2a7eSchristos uday += ((days >> 18) & 0x01FFFu) * 25897u; /* hi dgt (src!) */ 927cdfa2a7eSchristos uday += (ux & 0xFFFFFu); /* lo dgt */ 928cdfa2a7eSchristos uday %= GREGORIAN_CYCLE_DAYS; /* full reduction */ 929cdfa2a7eSchristos Q = (ux - uday) * 660721233u; /* exact div */ 930cdfa2a7eSchristos n100 = uint32_2cpl_to_int32(Q); 931cdfa2a7eSchristos 932cdfa2a7eSchristos # endif 933cdfa2a7eSchristos 934af12ab5eSchristos /* Split off years in century -- days >= 0 here, and we're far 935af12ab5eSchristos * away from integer overflow trouble now. */ 936af12ab5eSchristos uday |= 3; 937af12ab5eSchristos n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 938cdfa2a7eSchristos uday -= n001 * GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 939af12ab5eSchristos 940af12ab5eSchristos /* Assemble the year and day in year */ 941af12ab5eSchristos res.hi = n100 * 100 + n001; 942af12ab5eSchristos res.lo = uday / 4u; 943af12ab5eSchristos 944cdfa2a7eSchristos /* Possibly set the leap year flag */ 945cdfa2a7eSchristos if (isleapyear) { 946cdfa2a7eSchristos uint32_t tc = (uint32_t)n100 + 1; 947cdfa2a7eSchristos uint32_t ty = (uint32_t)n001 + 1; 948cdfa2a7eSchristos *isleapyear = !(ty & 3) 949cdfa2a7eSchristos && ((ty != 100) || !(tc & 3)); 950cdfa2a7eSchristos } 9518585484eSchristos return res; 9528585484eSchristos } 9538585484eSchristos 9548585484eSchristos /* 9558585484eSchristos *--------------------------------------------------------------------- 9568585484eSchristos * Given a number of elapsed days in a year and a leap year indicator, 9578585484eSchristos * split the number of elapsed days into the number of elapsed months in 9588585484eSchristos * 'res.hi' and the number of elapsed days of that month in 'res.lo'. 9598585484eSchristos * 9608585484eSchristos * This function will fail and return {-1,-1} if the number of elapsed 9618585484eSchristos * days is not in the valid range! 9628585484eSchristos *--------------------------------------------------------------------- 9638585484eSchristos */ 9648585484eSchristos ntpcal_split 9658585484eSchristos ntpcal_split_yeardays( 9668585484eSchristos int32_t eyd, 967cdfa2a7eSchristos int isleap 9688585484eSchristos ) 9698585484eSchristos { 970cdfa2a7eSchristos /* Use the unshifted-year, February-with-30-days approach here. 971cdfa2a7eSchristos * Fractional interpolations are used in both directions, with 972cdfa2a7eSchristos * the smallest power-of-two divider to avoid any true division. 973cdfa2a7eSchristos */ 974cdfa2a7eSchristos ntpcal_split res = {-1, -1}; 9758585484eSchristos 976cdfa2a7eSchristos /* convert 'isleap' to number of defective days */ 977cdfa2a7eSchristos isleap = 1 + !isleap; 978cdfa2a7eSchristos /* adjust for February of 30 nominal days */ 979cdfa2a7eSchristos if (eyd >= 61 - isleap) 980cdfa2a7eSchristos eyd += isleap; 981cdfa2a7eSchristos /* if in range, convert to months and days in month */ 982cdfa2a7eSchristos if (eyd >= 0 && eyd < 367) { 983cdfa2a7eSchristos res.hi = (eyd * 67 + 32) >> 11; 984cdfa2a7eSchristos res.lo = eyd - ((489 * res.hi + 8) >> 4); 9858585484eSchristos } 9868585484eSchristos 9878585484eSchristos return res; 9888585484eSchristos } 9898585484eSchristos 9908585484eSchristos /* 9918585484eSchristos *--------------------------------------------------------------------- 9928585484eSchristos * Convert a RD into the date part of a 'struct calendar'. 9938585484eSchristos *--------------------------------------------------------------------- 9948585484eSchristos */ 9958585484eSchristos int 9968585484eSchristos ntpcal_rd_to_date( 9978585484eSchristos struct calendar *jd, 9988585484eSchristos int32_t rd 9998585484eSchristos ) 10008585484eSchristos { 10018585484eSchristos ntpcal_split split; 1002af12ab5eSchristos int leapy; 1003af12ab5eSchristos u_int ymask; 10048585484eSchristos 1005cdfa2a7eSchristos /* Get day-of-week first. It's simply the RD (mod 7)... */ 1006cdfa2a7eSchristos jd->weekday = i32mod7(rd); 10078585484eSchristos 1008af12ab5eSchristos split = ntpcal_split_eradays(rd - 1, &leapy); 1009af12ab5eSchristos /* Get year and day-of-year, with overflow check. If any of the 1010af12ab5eSchristos * upper 16 bits is set after shifting to unity-based years, we 1011af12ab5eSchristos * will have an overflow when converting to an unsigned 16bit 1012af12ab5eSchristos * year. Shifting to the right is OK here, since it does not 1013af12ab5eSchristos * matter if the shift is logic or arithmetic. 1014af12ab5eSchristos */ 1015af12ab5eSchristos split.hi += 1; 1016af12ab5eSchristos ymask = 0u - ((split.hi >> 16) == 0); 1017af12ab5eSchristos jd->year = (uint16_t)(split.hi & ymask); 10188585484eSchristos jd->yearday = (uint16_t)split.lo + 1; 10198585484eSchristos 10208585484eSchristos /* convert to month and mday */ 1021af12ab5eSchristos split = ntpcal_split_yeardays(split.lo, leapy); 10228585484eSchristos jd->month = (uint8_t)split.hi + 1; 10238585484eSchristos jd->monthday = (uint8_t)split.lo + 1; 10248585484eSchristos 1025af12ab5eSchristos return ymask ? leapy : -1; 10268585484eSchristos } 10278585484eSchristos 10288585484eSchristos /* 10298585484eSchristos *--------------------------------------------------------------------- 10308585484eSchristos * Convert a RD into the date part of a 'struct tm'. 10318585484eSchristos *--------------------------------------------------------------------- 10328585484eSchristos */ 10338585484eSchristos int 10348585484eSchristos ntpcal_rd_to_tm( 10358585484eSchristos struct tm *utm, 10368585484eSchristos int32_t rd 10378585484eSchristos ) 10388585484eSchristos { 10398585484eSchristos ntpcal_split split; 1040af12ab5eSchristos int leapy; 10418585484eSchristos 10428585484eSchristos /* get day-of-week first */ 1043cdfa2a7eSchristos utm->tm_wday = i32mod7(rd); 10448585484eSchristos 10458585484eSchristos /* get year and day-of-year */ 1046af12ab5eSchristos split = ntpcal_split_eradays(rd - 1, &leapy); 10478585484eSchristos utm->tm_year = split.hi - 1899; 10488585484eSchristos utm->tm_yday = split.lo; /* 0-based */ 10498585484eSchristos 10508585484eSchristos /* convert to month and mday */ 1051af12ab5eSchristos split = ntpcal_split_yeardays(split.lo, leapy); 10528585484eSchristos utm->tm_mon = split.hi; /* 0-based */ 10538585484eSchristos utm->tm_mday = split.lo + 1; /* 1-based */ 10548585484eSchristos 1055af12ab5eSchristos return leapy; 10568585484eSchristos } 10578585484eSchristos 10588585484eSchristos /* 10598585484eSchristos *--------------------------------------------------------------------- 10608585484eSchristos * Take a value of seconds since midnight and split it into hhmmss in a 10618585484eSchristos * 'struct calendar'. 10628585484eSchristos *--------------------------------------------------------------------- 10638585484eSchristos */ 10648585484eSchristos int32_t 10658585484eSchristos ntpcal_daysec_to_date( 10668585484eSchristos struct calendar *jd, 10678585484eSchristos int32_t sec 10688585484eSchristos ) 10698585484eSchristos { 10708585484eSchristos int32_t days; 10718585484eSchristos int ts[3]; 10728585484eSchristos 10738585484eSchristos days = priv_timesplit(ts, sec); 10748585484eSchristos jd->hour = (uint8_t)ts[0]; 10758585484eSchristos jd->minute = (uint8_t)ts[1]; 10768585484eSchristos jd->second = (uint8_t)ts[2]; 10778585484eSchristos 10788585484eSchristos return days; 10798585484eSchristos } 10808585484eSchristos 10818585484eSchristos /* 10828585484eSchristos *--------------------------------------------------------------------- 10838585484eSchristos * Take a value of seconds since midnight and split it into hhmmss in a 10848585484eSchristos * 'struct tm'. 10858585484eSchristos *--------------------------------------------------------------------- 10868585484eSchristos */ 10878585484eSchristos int32_t 10888585484eSchristos ntpcal_daysec_to_tm( 10898585484eSchristos struct tm *utm, 10908585484eSchristos int32_t sec 10918585484eSchristos ) 10928585484eSchristos { 10938585484eSchristos int32_t days; 10948585484eSchristos int32_t ts[3]; 10958585484eSchristos 10968585484eSchristos days = priv_timesplit(ts, sec); 10978585484eSchristos utm->tm_hour = ts[0]; 10988585484eSchristos utm->tm_min = ts[1]; 10998585484eSchristos utm->tm_sec = ts[2]; 11008585484eSchristos 11018585484eSchristos return days; 11028585484eSchristos } 11038585484eSchristos 11048585484eSchristos /* 11058585484eSchristos *--------------------------------------------------------------------- 11068585484eSchristos * take a split representation for day/second-of-day and day offset 11078585484eSchristos * and convert it to a 'struct calendar'. The seconds will be normalised 11088585484eSchristos * into the range of a day, and the day will be adjusted accordingly. 11098585484eSchristos * 11108585484eSchristos * returns >0 if the result is in a leap year, 0 if in a regular 11118585484eSchristos * year and <0 if the result did not fit into the calendar struct. 11128585484eSchristos *--------------------------------------------------------------------- 11138585484eSchristos */ 11148585484eSchristos int 11158585484eSchristos ntpcal_daysplit_to_date( 11168585484eSchristos struct calendar *jd, 11178585484eSchristos const ntpcal_split *ds, 11188585484eSchristos int32_t dof 11198585484eSchristos ) 11208585484eSchristos { 11218585484eSchristos dof += ntpcal_daysec_to_date(jd, ds->lo); 11228585484eSchristos return ntpcal_rd_to_date(jd, ds->hi + dof); 11238585484eSchristos } 11248585484eSchristos 11258585484eSchristos /* 11268585484eSchristos *--------------------------------------------------------------------- 11278585484eSchristos * take a split representation for day/second-of-day and day offset 11288585484eSchristos * and convert it to a 'struct tm'. The seconds will be normalised 11298585484eSchristos * into the range of a day, and the day will be adjusted accordingly. 11308585484eSchristos * 11318585484eSchristos * returns 1 if the result is in a leap year and zero if in a regular 11328585484eSchristos * year. 11338585484eSchristos *--------------------------------------------------------------------- 11348585484eSchristos */ 11358585484eSchristos int 11368585484eSchristos ntpcal_daysplit_to_tm( 11378585484eSchristos struct tm *utm, 11388585484eSchristos const ntpcal_split *ds , 11398585484eSchristos int32_t dof 11408585484eSchristos ) 11418585484eSchristos { 11428585484eSchristos dof += ntpcal_daysec_to_tm(utm, ds->lo); 11438585484eSchristos 11448585484eSchristos return ntpcal_rd_to_tm(utm, ds->hi + dof); 11458585484eSchristos } 11468585484eSchristos 11478585484eSchristos /* 11488585484eSchristos *--------------------------------------------------------------------- 11498585484eSchristos * Take a UN*X time and convert to a calendar structure. 11508585484eSchristos *--------------------------------------------------------------------- 11518585484eSchristos */ 11528585484eSchristos int 11538585484eSchristos ntpcal_time_to_date( 11548585484eSchristos struct calendar *jd, 11558585484eSchristos const vint64 *ts 11568585484eSchristos ) 11578585484eSchristos { 11588585484eSchristos ntpcal_split ds; 11598585484eSchristos 11608585484eSchristos ds = ntpcal_daysplit(ts); 11618585484eSchristos ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 11628585484eSchristos ds.hi += DAY_UNIX_STARTS; 11638585484eSchristos 11648585484eSchristos return ntpcal_rd_to_date(jd, ds.hi); 11658585484eSchristos } 11668585484eSchristos 11678585484eSchristos 11688585484eSchristos /* 116903cfe0ffSchristos * ==================================================================== 11708585484eSchristos * 11718585484eSchristos * merging composite entities 11728585484eSchristos * 117303cfe0ffSchristos * ==================================================================== 11748585484eSchristos */ 11758585484eSchristos 1176cdfa2a7eSchristos #if !defined(HAVE_INT64) 1177cdfa2a7eSchristos /* multiplication helper. Seconds in days and weeks are multiples of 128, 1178cdfa2a7eSchristos * and without that factor fit well into 16 bit. So a multiplication 1179cdfa2a7eSchristos * of 32bit by 16bit and some shifting can be used on pure 32bit machines 1180cdfa2a7eSchristos * with compilers that do not support 64bit integers. 1181cdfa2a7eSchristos * 1182cdfa2a7eSchristos * Calculate ( hi * mul * 128 ) + lo 1183cdfa2a7eSchristos */ 1184cdfa2a7eSchristos static vint64 1185cdfa2a7eSchristos _dwjoin( 1186cdfa2a7eSchristos uint16_t mul, 1187cdfa2a7eSchristos int32_t hi, 1188cdfa2a7eSchristos int32_t lo 1189cdfa2a7eSchristos ) 1190cdfa2a7eSchristos { 1191cdfa2a7eSchristos vint64 res; 1192cdfa2a7eSchristos uint32_t p1, p2, sf; 1193cdfa2a7eSchristos 1194cdfa2a7eSchristos /* get sign flag and absolute value of 'hi' in p1 */ 1195cdfa2a7eSchristos sf = (uint32_t)-(hi < 0); 1196cdfa2a7eSchristos p1 = ((uint32_t)hi + sf) ^ sf; 1197cdfa2a7eSchristos 1198cdfa2a7eSchristos /* assemble major units: res <- |hi| * mul */ 1199cdfa2a7eSchristos res.D_s.lo = (p1 & 0xFFFF) * mul; 1200cdfa2a7eSchristos res.D_s.hi = 0; 1201cdfa2a7eSchristos p1 = (p1 >> 16) * mul; 1202cdfa2a7eSchristos p2 = p1 >> 16; 1203cdfa2a7eSchristos p1 = p1 << 16; 1204cdfa2a7eSchristos M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1205cdfa2a7eSchristos 1206cdfa2a7eSchristos /* mul by 128, using shift: res <-- res << 7 */ 1207cdfa2a7eSchristos res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25); 1208cdfa2a7eSchristos res.D_s.lo = (res.D_s.lo << 7); 1209cdfa2a7eSchristos 1210cdfa2a7eSchristos /* fix up sign: res <-- (res + [sf|sf]) ^ [sf|sf] */ 1211cdfa2a7eSchristos M_ADD(res.D_s.hi, res.D_s.lo, sf, sf); 1212cdfa2a7eSchristos res.D_s.lo ^= sf; 1213cdfa2a7eSchristos res.D_s.hi ^= sf; 1214cdfa2a7eSchristos 1215cdfa2a7eSchristos /* properly add seconds: res <-- res + [sx(lo)|lo] */ 1216cdfa2a7eSchristos p2 = (uint32_t)-(lo < 0); 1217cdfa2a7eSchristos p1 = (uint32_t)lo; 1218cdfa2a7eSchristos M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1219cdfa2a7eSchristos return res; 1220cdfa2a7eSchristos } 1221cdfa2a7eSchristos #endif 1222cdfa2a7eSchristos 12238585484eSchristos /* 12248585484eSchristos *--------------------------------------------------------------------- 12258585484eSchristos * Merge a number of days and a number of seconds into seconds, 12268585484eSchristos * expressed in 64 bits to avoid overflow. 12278585484eSchristos *--------------------------------------------------------------------- 12288585484eSchristos */ 12298585484eSchristos vint64 12308585484eSchristos ntpcal_dayjoin( 12318585484eSchristos int32_t days, 12328585484eSchristos int32_t secs 12338585484eSchristos ) 12348585484eSchristos { 12358585484eSchristos vint64 res; 12368585484eSchristos 1237af12ab5eSchristos # if defined(HAVE_INT64) 12388585484eSchristos 12398585484eSchristos res.q_s = days; 12408585484eSchristos res.q_s *= SECSPERDAY; 12418585484eSchristos res.q_s += secs; 12428585484eSchristos 12438585484eSchristos # else 12448585484eSchristos 1245cdfa2a7eSchristos res = _dwjoin(675, days, secs); 1246cdfa2a7eSchristos 1247cdfa2a7eSchristos # endif 1248cdfa2a7eSchristos 1249cdfa2a7eSchristos return res; 1250cdfa2a7eSchristos } 12518585484eSchristos 12528585484eSchristos /* 1253cdfa2a7eSchristos *--------------------------------------------------------------------- 1254cdfa2a7eSchristos * Merge a number of weeks and a number of seconds into seconds, 1255cdfa2a7eSchristos * expressed in 64 bits to avoid overflow. 1256cdfa2a7eSchristos *--------------------------------------------------------------------- 12578585484eSchristos */ 1258cdfa2a7eSchristos vint64 1259cdfa2a7eSchristos ntpcal_weekjoin( 1260cdfa2a7eSchristos int32_t week, 1261cdfa2a7eSchristos int32_t secs 1262cdfa2a7eSchristos ) 1263cdfa2a7eSchristos { 1264cdfa2a7eSchristos vint64 res; 12658585484eSchristos 1266cdfa2a7eSchristos # if defined(HAVE_INT64) 12678585484eSchristos 1268cdfa2a7eSchristos res.q_s = week; 1269cdfa2a7eSchristos res.q_s *= SECSPERWEEK; 1270cdfa2a7eSchristos res.q_s += secs; 12718585484eSchristos 1272cdfa2a7eSchristos # else 12738585484eSchristos 1274cdfa2a7eSchristos res = _dwjoin(4725, week, secs); 12758585484eSchristos 12768585484eSchristos # endif 12778585484eSchristos 12788585484eSchristos return res; 12798585484eSchristos } 12808585484eSchristos 12818585484eSchristos /* 12828585484eSchristos *--------------------------------------------------------------------- 1283af12ab5eSchristos * get leap years since epoch in elapsed years 1284af12ab5eSchristos *--------------------------------------------------------------------- 1285af12ab5eSchristos */ 1286af12ab5eSchristos int32_t 1287af12ab5eSchristos ntpcal_leapyears_in_years( 1288af12ab5eSchristos int32_t years 1289af12ab5eSchristos ) 1290af12ab5eSchristos { 1291af12ab5eSchristos /* We use the in-out-in algorithm here, using the one's 1292af12ab5eSchristos * complement division trick for negative numbers. The chained 1293af12ab5eSchristos * division sequence by 4/25/4 gives the compiler the chance to 1294af12ab5eSchristos * get away with only one true division and doing shifts otherwise. 1295af12ab5eSchristos */ 1296af12ab5eSchristos 1297cdfa2a7eSchristos uint32_t sf32, sum, uyear; 1298af12ab5eSchristos 1299cdfa2a7eSchristos sf32 = int32_sflag(years); 1300cdfa2a7eSchristos uyear = (uint32_t)years; 1301cdfa2a7eSchristos uyear ^= sf32; 1302af12ab5eSchristos 1303af12ab5eSchristos sum = (uyear /= 4u); /* 4yr rule --> IN */ 1304af12ab5eSchristos sum -= (uyear /= 25u); /* 100yr rule --> OUT */ 1305af12ab5eSchristos sum += (uyear /= 4u); /* 400yr rule --> IN */ 1306af12ab5eSchristos 1307af12ab5eSchristos /* Thanks to the alternation of IN/OUT/IN we can do the sum 1308af12ab5eSchristos * directly and have a single one's complement operation 1309af12ab5eSchristos * here. (Only if the years are negative, of course.) Otherwise 1310af12ab5eSchristos * the one's complement would have to be done when 1311af12ab5eSchristos * adding/subtracting the terms. 1312af12ab5eSchristos */ 1313cdfa2a7eSchristos return uint32_2cpl_to_int32(sf32 ^ sum); 1314af12ab5eSchristos } 1315af12ab5eSchristos 1316af12ab5eSchristos /* 1317af12ab5eSchristos *--------------------------------------------------------------------- 13188585484eSchristos * Convert elapsed years in Era into elapsed days in Era. 13198585484eSchristos *--------------------------------------------------------------------- 13208585484eSchristos */ 13218585484eSchristos int32_t 13228585484eSchristos ntpcal_days_in_years( 13238585484eSchristos int32_t years 13248585484eSchristos ) 13258585484eSchristos { 1326af12ab5eSchristos return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years); 13278585484eSchristos } 13288585484eSchristos 13298585484eSchristos /* 13308585484eSchristos *--------------------------------------------------------------------- 13318585484eSchristos * Convert a number of elapsed month in a year into elapsed days in year. 13328585484eSchristos * 13338585484eSchristos * The month will be normalized, and 'res.hi' will contain the 13348585484eSchristos * excessive years that must be considered when converting the years, 13358585484eSchristos * while 'res.lo' will contain the number of elapsed days since start 13368585484eSchristos * of the year. 13378585484eSchristos * 13388585484eSchristos * This code uses the shifted-month-approach to convert month to days, 13398585484eSchristos * because then there is no need to have explicit leap year 13408585484eSchristos * information. The slight disadvantage is that for most month values 13418585484eSchristos * the result is a negative value, and the year excess is one; the 13428585484eSchristos * conversion is then simply based on the start of the following year. 13438585484eSchristos *--------------------------------------------------------------------- 13448585484eSchristos */ 13458585484eSchristos ntpcal_split 13468585484eSchristos ntpcal_days_in_months( 13478585484eSchristos int32_t m 13488585484eSchristos ) 13498585484eSchristos { 13508585484eSchristos ntpcal_split res; 13518585484eSchristos 1352cdfa2a7eSchristos /* Add ten months with proper year adjustment. */ 1353cdfa2a7eSchristos if (m < 2) { 1354af12ab5eSchristos res.lo = m + 10; 1355cdfa2a7eSchristos res.hi = 0; 1356cdfa2a7eSchristos } else { 1357cdfa2a7eSchristos res.lo = m - 2; 1358cdfa2a7eSchristos res.hi = 1; 1359cdfa2a7eSchristos } 13608585484eSchristos 1361cdfa2a7eSchristos /* Possibly normalise by floor division. This does not hapen for 1362cdfa2a7eSchristos * input in normal range. */ 1363af12ab5eSchristos if (res.lo < 0 || res.lo >= 12) { 1364cdfa2a7eSchristos uint32_t mu, Q, sf32; 1365cdfa2a7eSchristos sf32 = int32_sflag(res.lo); 1366cdfa2a7eSchristos mu = (uint32_t)res.lo; 1367cdfa2a7eSchristos Q = sf32 ^ ((sf32 ^ mu) / 12u); 1368cdfa2a7eSchristos 1369af12ab5eSchristos res.hi += uint32_2cpl_to_int32(Q); 1370af12ab5eSchristos res.lo = mu - Q * 12u; 13718585484eSchristos } 13728585484eSchristos 1373cdfa2a7eSchristos /* Get cummulated days in year with unshift. Use the fractional 1374cdfa2a7eSchristos * interpolation with smallest possible power of two in the 1375cdfa2a7eSchristos * divider. 1376cdfa2a7eSchristos */ 1377cdfa2a7eSchristos res.lo = ((res.lo * 979 + 16) >> 5) - 306; 13788585484eSchristos 13798585484eSchristos return res; 13808585484eSchristos } 13818585484eSchristos 13828585484eSchristos /* 13838585484eSchristos *--------------------------------------------------------------------- 13848585484eSchristos * Convert ELAPSED years/months/days of gregorian calendar to elapsed 13858585484eSchristos * days in Gregorian epoch. 13868585484eSchristos * 13878585484eSchristos * If you want to convert years and days-of-year, just give a month of 13888585484eSchristos * zero. 13898585484eSchristos *--------------------------------------------------------------------- 13908585484eSchristos */ 13918585484eSchristos int32_t 13928585484eSchristos ntpcal_edate_to_eradays( 13938585484eSchristos int32_t years, 13948585484eSchristos int32_t mons, 13958585484eSchristos int32_t mdays 13968585484eSchristos ) 13978585484eSchristos { 13988585484eSchristos ntpcal_split tmp; 13998585484eSchristos int32_t res; 14008585484eSchristos 14018585484eSchristos if (mons) { 14028585484eSchristos tmp = ntpcal_days_in_months(mons); 14038585484eSchristos res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; 14048585484eSchristos } else 14058585484eSchristos res = ntpcal_days_in_years(years); 14068585484eSchristos res += mdays; 14078585484eSchristos 14088585484eSchristos return res; 14098585484eSchristos } 14108585484eSchristos 14118585484eSchristos /* 14128585484eSchristos *--------------------------------------------------------------------- 14138585484eSchristos * Convert ELAPSED years/months/days of gregorian calendar to elapsed 14148585484eSchristos * days in year. 14158585484eSchristos * 141603cfe0ffSchristos * Note: This will give the true difference to the start of the given 141703cfe0ffSchristos * year, even if months & days are off-scale. 14188585484eSchristos *--------------------------------------------------------------------- 14198585484eSchristos */ 14208585484eSchristos int32_t 14218585484eSchristos ntpcal_edate_to_yeardays( 14228585484eSchristos int32_t years, 14238585484eSchristos int32_t mons, 14248585484eSchristos int32_t mdays 14258585484eSchristos ) 14268585484eSchristos { 14278585484eSchristos ntpcal_split tmp; 14288585484eSchristos 14298585484eSchristos if (0 <= mons && mons < 12) { 1430cdfa2a7eSchristos if (mons >= 2) 1431cdfa2a7eSchristos mdays -= 2 - is_leapyear(years+1); 1432cdfa2a7eSchristos mdays += (489 * mons + 8) >> 4; 14338585484eSchristos } else { 14348585484eSchristos tmp = ntpcal_days_in_months(mons); 14358585484eSchristos mdays += tmp.lo 14368585484eSchristos + ntpcal_days_in_years(years + tmp.hi) 14378585484eSchristos - ntpcal_days_in_years(years); 14388585484eSchristos } 14398585484eSchristos 14408585484eSchristos return mdays; 14418585484eSchristos } 14428585484eSchristos 14438585484eSchristos /* 14448585484eSchristos *--------------------------------------------------------------------- 14458585484eSchristos * Convert elapsed days and the hour/minute/second information into 14468585484eSchristos * total seconds. 14478585484eSchristos * 14488585484eSchristos * If 'isvalid' is not NULL, do a range check on the time specification 14498585484eSchristos * and tell if the time input is in the normal range, permitting for a 14508585484eSchristos * single leapsecond. 14518585484eSchristos *--------------------------------------------------------------------- 14528585484eSchristos */ 14538585484eSchristos int32_t 14548585484eSchristos ntpcal_etime_to_seconds( 14558585484eSchristos int32_t hours, 14568585484eSchristos int32_t minutes, 14578585484eSchristos int32_t seconds 14588585484eSchristos ) 14598585484eSchristos { 14608585484eSchristos int32_t res; 14618585484eSchristos 14628585484eSchristos res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; 14638585484eSchristos 14648585484eSchristos return res; 14658585484eSchristos } 14668585484eSchristos 14678585484eSchristos /* 14688585484eSchristos *--------------------------------------------------------------------- 14698585484eSchristos * Convert the date part of a 'struct tm' (that is, year, month, 14708585484eSchristos * day-of-month) into the RD of that day. 14718585484eSchristos *--------------------------------------------------------------------- 14728585484eSchristos */ 14738585484eSchristos int32_t 14748585484eSchristos ntpcal_tm_to_rd( 14758585484eSchristos const struct tm *utm 14768585484eSchristos ) 14778585484eSchristos { 14788585484eSchristos return ntpcal_edate_to_eradays(utm->tm_year + 1899, 14798585484eSchristos utm->tm_mon, 14808585484eSchristos utm->tm_mday - 1) + 1; 14818585484eSchristos } 14828585484eSchristos 14838585484eSchristos /* 14848585484eSchristos *--------------------------------------------------------------------- 14858585484eSchristos * Convert the date part of a 'struct calendar' (that is, year, month, 14868585484eSchristos * day-of-month) into the RD of that day. 14878585484eSchristos *--------------------------------------------------------------------- 14888585484eSchristos */ 14898585484eSchristos int32_t 14908585484eSchristos ntpcal_date_to_rd( 14918585484eSchristos const struct calendar *jd 14928585484eSchristos ) 14938585484eSchristos { 14948585484eSchristos return ntpcal_edate_to_eradays((int32_t)jd->year - 1, 14958585484eSchristos (int32_t)jd->month - 1, 14968585484eSchristos (int32_t)jd->monthday - 1) + 1; 14978585484eSchristos } 14988585484eSchristos 14998585484eSchristos /* 15008585484eSchristos *--------------------------------------------------------------------- 15018585484eSchristos * convert a year number to rata die of year start 15028585484eSchristos *--------------------------------------------------------------------- 15038585484eSchristos */ 15048585484eSchristos int32_t 15058585484eSchristos ntpcal_year_to_ystart( 15068585484eSchristos int32_t year 15078585484eSchristos ) 15088585484eSchristos { 15098585484eSchristos return ntpcal_days_in_years(year - 1) + 1; 15108585484eSchristos } 15118585484eSchristos 15128585484eSchristos /* 15138585484eSchristos *--------------------------------------------------------------------- 15148585484eSchristos * For a given RD, get the RD of the associated year start, 15158585484eSchristos * that is, the RD of the last January,1st on or before that day. 15168585484eSchristos *--------------------------------------------------------------------- 15178585484eSchristos */ 15188585484eSchristos int32_t 15198585484eSchristos ntpcal_rd_to_ystart( 15208585484eSchristos int32_t rd 15218585484eSchristos ) 15228585484eSchristos { 15238585484eSchristos /* 15248585484eSchristos * Rather simple exercise: split the day number into elapsed 15258585484eSchristos * years and elapsed days, then remove the elapsed days from the 15268585484eSchristos * input value. Nice'n sweet... 15278585484eSchristos */ 15288585484eSchristos return rd - ntpcal_split_eradays(rd - 1, NULL).lo; 15298585484eSchristos } 15308585484eSchristos 15318585484eSchristos /* 15328585484eSchristos *--------------------------------------------------------------------- 15338585484eSchristos * For a given RD, get the RD of the associated month start. 15348585484eSchristos *--------------------------------------------------------------------- 15358585484eSchristos */ 15368585484eSchristos int32_t 15378585484eSchristos ntpcal_rd_to_mstart( 15388585484eSchristos int32_t rd 15398585484eSchristos ) 15408585484eSchristos { 15418585484eSchristos ntpcal_split split; 15428585484eSchristos int leaps; 15438585484eSchristos 15448585484eSchristos split = ntpcal_split_eradays(rd - 1, &leaps); 15458585484eSchristos split = ntpcal_split_yeardays(split.lo, leaps); 15468585484eSchristos 15478585484eSchristos return rd - split.lo; 15488585484eSchristos } 15498585484eSchristos 15508585484eSchristos /* 15518585484eSchristos *--------------------------------------------------------------------- 15528585484eSchristos * take a 'struct calendar' and get the seconds-of-day from it. 15538585484eSchristos *--------------------------------------------------------------------- 15548585484eSchristos */ 15558585484eSchristos int32_t 15568585484eSchristos ntpcal_date_to_daysec( 15578585484eSchristos const struct calendar *jd 15588585484eSchristos ) 15598585484eSchristos { 15608585484eSchristos return ntpcal_etime_to_seconds(jd->hour, jd->minute, 15618585484eSchristos jd->second); 15628585484eSchristos } 15638585484eSchristos 15648585484eSchristos /* 15658585484eSchristos *--------------------------------------------------------------------- 15668585484eSchristos * take a 'struct tm' and get the seconds-of-day from it. 15678585484eSchristos *--------------------------------------------------------------------- 15688585484eSchristos */ 15698585484eSchristos int32_t 15708585484eSchristos ntpcal_tm_to_daysec( 15718585484eSchristos const struct tm *utm 15728585484eSchristos ) 15738585484eSchristos { 15748585484eSchristos return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, 15758585484eSchristos utm->tm_sec); 15768585484eSchristos } 15778585484eSchristos 15788585484eSchristos /* 15798585484eSchristos *--------------------------------------------------------------------- 15808585484eSchristos * take a 'struct calendar' and convert it to a 'time_t' 15818585484eSchristos *--------------------------------------------------------------------- 15828585484eSchristos */ 15838585484eSchristos time_t 15848585484eSchristos ntpcal_date_to_time( 15858585484eSchristos const struct calendar *jd 15868585484eSchristos ) 15878585484eSchristos { 15888585484eSchristos vint64 join; 15898585484eSchristos int32_t days, secs; 15908585484eSchristos 15918585484eSchristos days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; 15928585484eSchristos secs = ntpcal_date_to_daysec(jd); 15938585484eSchristos join = ntpcal_dayjoin(days, secs); 15948585484eSchristos 15958585484eSchristos return vint64_to_time(&join); 15968585484eSchristos } 15978585484eSchristos 15988585484eSchristos 15998585484eSchristos /* 160003cfe0ffSchristos * ==================================================================== 16018585484eSchristos * 16028585484eSchristos * extended and unchecked variants of caljulian/caltontp 16038585484eSchristos * 160403cfe0ffSchristos * ==================================================================== 16058585484eSchristos */ 16068585484eSchristos int 1607ea66d795Schristos ntpcal_ntp64_to_date( 1608ea66d795Schristos struct calendar *jd, 1609ea66d795Schristos const vint64 *ntp 1610ea66d795Schristos ) 1611ea66d795Schristos { 1612ea66d795Schristos ntpcal_split ds; 1613ea66d795Schristos 1614ea66d795Schristos ds = ntpcal_daysplit(ntp); 1615ea66d795Schristos ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1616ea66d795Schristos 1617ea66d795Schristos return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); 1618ea66d795Schristos } 1619ea66d795Schristos 1620ea66d795Schristos int 16218585484eSchristos ntpcal_ntp_to_date( 16228585484eSchristos struct calendar *jd, 16238585484eSchristos uint32_t ntp, 16248585484eSchristos const time_t *piv 16258585484eSchristos ) 16268585484eSchristos { 1627ea66d795Schristos vint64 ntp64; 16288585484eSchristos 16298585484eSchristos /* 16308585484eSchristos * Unfold ntp time around current time into NTP domain. Split 16318585484eSchristos * into days and seconds, shift days into CE domain and 16328585484eSchristos * process the parts. 16338585484eSchristos */ 1634ea66d795Schristos ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1635ea66d795Schristos return ntpcal_ntp64_to_date(jd, &ntp64); 1636ea66d795Schristos } 16378585484eSchristos 1638ea66d795Schristos 1639ea66d795Schristos vint64 1640ea66d795Schristos ntpcal_date_to_ntp64( 1641ea66d795Schristos const struct calendar *jd 1642ea66d795Schristos ) 1643ea66d795Schristos { 1644ea66d795Schristos /* 1645ea66d795Schristos * Convert date to NTP. Ignore yearday, use d/m/y only. 1646ea66d795Schristos */ 1647ea66d795Schristos return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, 1648ea66d795Schristos ntpcal_date_to_daysec(jd)); 16498585484eSchristos } 16508585484eSchristos 16518585484eSchristos 16528585484eSchristos uint32_t 16538585484eSchristos ntpcal_date_to_ntp( 16548585484eSchristos const struct calendar *jd 16558585484eSchristos ) 16568585484eSchristos { 16578585484eSchristos /* 1658cdfa2a7eSchristos * Get lower half of 64bit NTP timestamp from date/time. 16598585484eSchristos */ 1660ea66d795Schristos return ntpcal_date_to_ntp64(jd).d_s.lo; 16618585484eSchristos } 16628585484eSchristos 1663ea66d795Schristos 1664ea66d795Schristos 16658585484eSchristos /* 166603cfe0ffSchristos * ==================================================================== 16678585484eSchristos * 16688585484eSchristos * day-of-week calculations 16698585484eSchristos * 167003cfe0ffSchristos * ==================================================================== 16718585484eSchristos */ 16728585484eSchristos /* 16738585484eSchristos * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, 16748585484eSchristos * greater-or equal, closest, less-or-equal or less-than the given RDN 16758585484eSchristos * and denotes the given day-of-week 16768585484eSchristos */ 16778585484eSchristos int32_t 16788585484eSchristos ntpcal_weekday_gt( 16798585484eSchristos int32_t rdn, 16808585484eSchristos int32_t dow 16818585484eSchristos ) 16828585484eSchristos { 16838585484eSchristos return ntpcal_periodic_extend(rdn+1, dow, 7); 16848585484eSchristos } 16858585484eSchristos 16868585484eSchristos int32_t 16878585484eSchristos ntpcal_weekday_ge( 16888585484eSchristos int32_t rdn, 16898585484eSchristos int32_t dow 16908585484eSchristos ) 16918585484eSchristos { 16928585484eSchristos return ntpcal_periodic_extend(rdn, dow, 7); 16938585484eSchristos } 16948585484eSchristos 16958585484eSchristos int32_t 16968585484eSchristos ntpcal_weekday_close( 16978585484eSchristos int32_t rdn, 16988585484eSchristos int32_t dow 16998585484eSchristos ) 17008585484eSchristos { 17018585484eSchristos return ntpcal_periodic_extend(rdn-3, dow, 7); 17028585484eSchristos } 17038585484eSchristos 17048585484eSchristos int32_t 17058585484eSchristos ntpcal_weekday_le( 17068585484eSchristos int32_t rdn, 17078585484eSchristos int32_t dow 17088585484eSchristos ) 17098585484eSchristos { 17108585484eSchristos return ntpcal_periodic_extend(rdn, dow, -7); 17118585484eSchristos } 17128585484eSchristos 17138585484eSchristos int32_t 17148585484eSchristos ntpcal_weekday_lt( 17158585484eSchristos int32_t rdn, 17168585484eSchristos int32_t dow 17178585484eSchristos ) 17188585484eSchristos { 17198585484eSchristos return ntpcal_periodic_extend(rdn-1, dow, -7); 17208585484eSchristos } 17218585484eSchristos 17228585484eSchristos /* 172303cfe0ffSchristos * ==================================================================== 17248585484eSchristos * 17258585484eSchristos * ISO week-calendar conversions 17268585484eSchristos * 17278585484eSchristos * The ISO8601 calendar defines a calendar of years, weeks and weekdays. 17288585484eSchristos * It is related to the Gregorian calendar, and a ISO year starts at the 17298585484eSchristos * Monday closest to Jan,1st of the corresponding Gregorian year. A ISO 17308585484eSchristos * calendar year has always 52 or 53 weeks, and like the Grogrian 17318585484eSchristos * calendar the ISO8601 calendar repeats itself every 400 years, or 17328585484eSchristos * 146097 days, or 20871 weeks. 17338585484eSchristos * 17348585484eSchristos * While it is possible to write ISO calendar functions based on the 17358585484eSchristos * Gregorian calendar functions, the following implementation takes a 17368585484eSchristos * different approach, based directly on years and weeks. 17378585484eSchristos * 17388585484eSchristos * Analysis of the tabulated data shows that it is not possible to 17398585484eSchristos * interpolate from years to weeks over a full 400 year range; cyclic 17408585484eSchristos * shifts over 400 years do not provide a solution here. But it *is* 17418585484eSchristos * possible to interpolate over every single century of the 400-year 17428585484eSchristos * cycle. (The centennial leap year rule seems to be the culprit here.) 17438585484eSchristos * 17448585484eSchristos * It can be shown that a conversion from years to weeks can be done 17458585484eSchristos * using a linear transformation of the form 17468585484eSchristos * 17478585484eSchristos * w = floor( y * a + b ) 17488585484eSchristos * 17498585484eSchristos * where the slope a must hold to 17508585484eSchristos * 17518585484eSchristos * 52.1780821918 <= a < 52.1791044776 17528585484eSchristos * 17538585484eSchristos * and b must be chosen according to the selected slope and the number 17548585484eSchristos * of the century in a 400-year period. 17558585484eSchristos * 17568585484eSchristos * The inverse calculation can also be done in this way. Careful scaling 17578585484eSchristos * provides an unlimited set of integer coefficients a,k,b that enable 17588585484eSchristos * us to write the calulation in the form 17598585484eSchristos * 17608585484eSchristos * w = (y * a + b ) / k 17618585484eSchristos * y = (w * a' + b') / k' 17628585484eSchristos * 1763cdfa2a7eSchristos * In this implementation the values of k and k' are chosen to be the 17648585484eSchristos * smallest possible powers of two, so the division can be implemented 17658585484eSchristos * as shifts if the optimiser chooses to do so. 17668585484eSchristos * 176703cfe0ffSchristos * ==================================================================== 17688585484eSchristos */ 17698585484eSchristos 17708585484eSchristos /* 17718585484eSchristos * Given a number of elapsed (ISO-)years since the begin of the 17728585484eSchristos * christian era, return the number of elapsed weeks corresponding to 17738585484eSchristos * the number of years. 17748585484eSchristos */ 17758585484eSchristos int32_t 17768585484eSchristos isocal_weeks_in_years( 17778585484eSchristos int32_t years 17788585484eSchristos ) 17798585484eSchristos { 17808585484eSchristos /* 17818585484eSchristos * use: w = (y * 53431 + b[c]) / 1024 as interpolation 17828585484eSchristos */ 1783af12ab5eSchristos static const uint16_t bctab[4] = { 157, 449, 597, 889 }; 17848585484eSchristos 1785af12ab5eSchristos int32_t cs, cw; 1786cdfa2a7eSchristos uint32_t cc, ci, yu, sf32; 17878585484eSchristos 1788cdfa2a7eSchristos sf32 = int32_sflag(years); 1789cdfa2a7eSchristos yu = (uint32_t)years; 17908585484eSchristos 1791af12ab5eSchristos /* split off centuries, using floor division */ 1792cdfa2a7eSchristos cc = sf32 ^ ((sf32 ^ yu) / 100u); 1793af12ab5eSchristos yu -= cc * 100u; 1794af12ab5eSchristos 1795af12ab5eSchristos /* calculate century cycles shift and cycle index: 1796af12ab5eSchristos * Assuming a century is 5217 weeks, we have to add a cycle 1797af12ab5eSchristos * shift that is 3 for every 4 centuries, because 3 of the four 1798af12ab5eSchristos * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual 1799af12ab5eSchristos * correction, and the second century is the defective one. 1800af12ab5eSchristos * 1801af12ab5eSchristos * Needs floor division by 4, which is done with masking and 1802af12ab5eSchristos * shifting. 18038585484eSchristos */ 1804af12ab5eSchristos ci = cc * 3u + 1; 1805cdfa2a7eSchristos cs = uint32_2cpl_to_int32(sf32 ^ ((sf32 ^ ci) >> 2)); 1806cdfa2a7eSchristos ci = ci & 3u; 18078585484eSchristos 1808af12ab5eSchristos /* Get weeks in century. Can use plain division here as all ops 1809af12ab5eSchristos * are >= 0, and let the compiler sort out the possible 1810af12ab5eSchristos * optimisations. 1811af12ab5eSchristos */ 1812af12ab5eSchristos cw = (yu * 53431u + bctab[ci]) / 1024u; 1813af12ab5eSchristos 1814af12ab5eSchristos return uint32_2cpl_to_int32(cc) * 5217 + cs + cw; 18158585484eSchristos } 18168585484eSchristos 18178585484eSchristos /* 18188585484eSchristos * Given a number of elapsed weeks since the begin of the christian 18198585484eSchristos * era, split this number into the number of elapsed years in res.hi 18208585484eSchristos * and the excessive number of weeks in res.lo. (That is, res.lo is 18218585484eSchristos * the number of elapsed weeks in the remaining partial year.) 18228585484eSchristos */ 18238585484eSchristos ntpcal_split 18248585484eSchristos isocal_split_eraweeks( 18258585484eSchristos int32_t weeks 18268585484eSchristos ) 18278585484eSchristos { 18288585484eSchristos /* 18298585484eSchristos * use: y = (w * 157 + b[c]) / 8192 as interpolation 18308585484eSchristos */ 1831af12ab5eSchristos 1832af12ab5eSchristos static const uint16_t bctab[4] = { 85, 130, 17, 62 }; 1833af12ab5eSchristos 18348585484eSchristos ntpcal_split res; 1835af12ab5eSchristos int32_t cc, ci; 1836cdfa2a7eSchristos uint32_t sw, cy, Q; 18378585484eSchristos 1838cdfa2a7eSchristos /* Use two fast cycle-split divisions again. Herew e want to 1839cdfa2a7eSchristos * execute '(weeks * 4 + 2) /% 20871' under floor division rules 1840cdfa2a7eSchristos * in the first step. 1841af12ab5eSchristos * 1842cdfa2a7eSchristos * This is of course (again) susceptible to internal overflow if 1843cdfa2a7eSchristos * coded directly in 32bit. And again we use 64bit division on 1844cdfa2a7eSchristos * a 64bit target and exact division after calculating the 1845cdfa2a7eSchristos * remainder first on a 32bit target. With the smaller divider, 1846cdfa2a7eSchristos * that's even a bit neater. 18478585484eSchristos */ 1848cdfa2a7eSchristos # if defined(HAVE_64BITREGS) 1849cdfa2a7eSchristos 1850cdfa2a7eSchristos /* Full floor division with 64bit values. */ 1851cdfa2a7eSchristos uint64_t sf64, sw64; 1852cdfa2a7eSchristos sf64 = (uint64_t)-(weeks < 0); 1853cdfa2a7eSchristos sw64 = ((uint64_t)weeks << 2) | 2u; 1854cdfa2a7eSchristos Q = (uint32_t)(sf64 ^ ((sf64 ^ sw64) / GREGORIAN_CYCLE_WEEKS)); 1855cdfa2a7eSchristos sw = (uint32_t)(sw64 - Q * GREGORIAN_CYCLE_WEEKS); 1856cdfa2a7eSchristos 1857cdfa2a7eSchristos # else 1858cdfa2a7eSchristos 1859cdfa2a7eSchristos /* Exact division after calculating the remainder via partial 1860cdfa2a7eSchristos * reduction by digit sum. 1861cdfa2a7eSchristos * (-2^33) % 20871 --> 5491 : the sign bit value 1862cdfa2a7eSchristos * ( 2^20) % 20871 --> 5026 : the upper digit value 1863cdfa2a7eSchristos * modinv(20871, 2^32) --> 330081335 : the inverse 1864cdfa2a7eSchristos */ 1865cdfa2a7eSchristos uint32_t ux = ((uint32_t)weeks << 2) | 2; 1866cdfa2a7eSchristos sw = (weeks < 0) ? 5491u : 0u; /* sign dgt */ 1867cdfa2a7eSchristos sw += ((weeks >> 18) & 0x01FFFu) * 5026u; /* hi dgt (src!) */ 1868cdfa2a7eSchristos sw += (ux & 0xFFFFFu); /* lo dgt */ 1869cdfa2a7eSchristos sw %= GREGORIAN_CYCLE_WEEKS; /* full reduction */ 1870cdfa2a7eSchristos Q = (ux - sw) * 330081335u; /* exact div */ 1871cdfa2a7eSchristos 1872cdfa2a7eSchristos # endif 1873cdfa2a7eSchristos 1874cdfa2a7eSchristos ci = Q & 3u; 1875af12ab5eSchristos cc = uint32_2cpl_to_int32(Q); 18768585484eSchristos 1877af12ab5eSchristos /* Split off years; sw >= 0 here! The scaled weeks in the years 1878af12ab5eSchristos * are scaled up by 157 afterwards. 18798585484eSchristos */ 1880af12ab5eSchristos sw = (sw / 4u) * 157u + bctab[ci]; 1881cdfa2a7eSchristos cy = sw / 8192u; /* sw >> 13 , let the compiler sort it out */ 1882cdfa2a7eSchristos sw = sw % 8192u; /* sw & 8191, let the compiler sort it out */ 18838585484eSchristos 1884af12ab5eSchristos /* assemble elapsed years and downscale the elapsed weeks in 1885af12ab5eSchristos * the year. 1886af12ab5eSchristos */ 1887af12ab5eSchristos res.hi = 100*cc + cy; 1888af12ab5eSchristos res.lo = sw / 157u; 18898585484eSchristos 18908585484eSchristos return res; 18918585484eSchristos } 18928585484eSchristos 18938585484eSchristos /* 18948585484eSchristos * Given a second in the NTP time scale and a pivot, expand the NTP 18958585484eSchristos * time stamp around the pivot and convert into an ISO calendar time 18968585484eSchristos * stamp. 18978585484eSchristos */ 18988585484eSchristos int 1899ea66d795Schristos isocal_ntp64_to_date( 19008585484eSchristos struct isodate *id, 1901ea66d795Schristos const vint64 *ntp 19028585484eSchristos ) 19038585484eSchristos { 19048585484eSchristos ntpcal_split ds; 19058585484eSchristos int32_t ts[3]; 1906cdfa2a7eSchristos uint32_t uw, ud, sf32; 19078585484eSchristos 19088585484eSchristos /* 1909ea66d795Schristos * Split NTP time into days and seconds, shift days into CE 1910ea66d795Schristos * domain and process the parts. 19118585484eSchristos */ 1912ea66d795Schristos ds = ntpcal_daysplit(ntp); 19138585484eSchristos 19148585484eSchristos /* split time part */ 19158585484eSchristos ds.hi += priv_timesplit(ts, ds.lo); 19168585484eSchristos id->hour = (uint8_t)ts[0]; 19178585484eSchristos id->minute = (uint8_t)ts[1]; 19188585484eSchristos id->second = (uint8_t)ts[2]; 19198585484eSchristos 1920af12ab5eSchristos /* split days into days and weeks, using floor division in unsigned */ 1921af12ab5eSchristos ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */ 1922cdfa2a7eSchristos sf32 = int32_sflag(ds.hi); 1923cdfa2a7eSchristos ud = (uint32_t)ds.hi; 1924cdfa2a7eSchristos uw = sf32 ^ ((sf32 ^ ud) / DAYSPERWEEK); 1925af12ab5eSchristos ud -= uw * DAYSPERWEEK; 1926cdfa2a7eSchristos 1927af12ab5eSchristos ds.hi = uint32_2cpl_to_int32(uw); 1928af12ab5eSchristos ds.lo = ud; 1929af12ab5eSchristos 19308585484eSchristos id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ 19318585484eSchristos 1932af12ab5eSchristos /* get year and week in year */ 19338585484eSchristos ds = isocal_split_eraweeks(ds.hi); /* elapsed years&week*/ 19348585484eSchristos id->year = (uint16_t)ds.hi + 1; /* shift to current */ 19358585484eSchristos id->week = (uint8_t )ds.lo + 1; 19368585484eSchristos 19372950cc38Schristos return (ds.hi >= 0 && ds.hi < 0x0000FFFF); 19388585484eSchristos } 19398585484eSchristos 1940ea66d795Schristos int 1941ea66d795Schristos isocal_ntp_to_date( 1942ea66d795Schristos struct isodate *id, 1943ea66d795Schristos uint32_t ntp, 1944ea66d795Schristos const time_t *piv 1945ea66d795Schristos ) 1946ea66d795Schristos { 1947ea66d795Schristos vint64 ntp64; 1948ea66d795Schristos 1949ea66d795Schristos /* 1950ea66d795Schristos * Unfold ntp time around current time into NTP domain, then 1951ea66d795Schristos * convert the full time stamp. 1952ea66d795Schristos */ 1953ea66d795Schristos ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1954ea66d795Schristos return isocal_ntp64_to_date(id, &ntp64); 1955ea66d795Schristos } 1956ea66d795Schristos 19578585484eSchristos /* 19588585484eSchristos * Convert a ISO date spec into a second in the NTP time scale, 19598585484eSchristos * properly truncated to 32 bit. 19608585484eSchristos */ 1961ea66d795Schristos vint64 1962ea66d795Schristos isocal_date_to_ntp64( 19638585484eSchristos const struct isodate *id 19648585484eSchristos ) 19658585484eSchristos { 19668585484eSchristos int32_t weeks, days, secs; 19678585484eSchristos 19688585484eSchristos weeks = isocal_weeks_in_years((int32_t)id->year - 1) 19698585484eSchristos + (int32_t)id->week - 1; 19708585484eSchristos days = weeks * 7 + (int32_t)id->weekday; 19718585484eSchristos /* days is RDN of ISO date now */ 19728585484eSchristos secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second); 19738585484eSchristos 1974ea66d795Schristos return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs); 1975ea66d795Schristos } 1976ea66d795Schristos 1977ea66d795Schristos uint32_t 1978ea66d795Schristos isocal_date_to_ntp( 1979ea66d795Schristos const struct isodate *id 1980ea66d795Schristos ) 1981ea66d795Schristos { 1982ea66d795Schristos /* 1983cdfa2a7eSchristos * Get lower half of 64bit NTP timestamp from date/time. 1984ea66d795Schristos */ 1985ea66d795Schristos return isocal_date_to_ntp64(id).d_s.lo; 19868585484eSchristos } 19878585484eSchristos 19884eea345dSchristos /* 19894eea345dSchristos * ==================================================================== 19904eea345dSchristos * 'basedate' support functions 19914eea345dSchristos * ==================================================================== 19924eea345dSchristos */ 19934eea345dSchristos 19944eea345dSchristos static int32_t s_baseday = NTP_TO_UNIX_DAYS; 1995cdfa2a7eSchristos static int32_t s_gpsweek = 0; 19964eea345dSchristos 19974eea345dSchristos int32_t 19984eea345dSchristos basedate_eval_buildstamp(void) 19994eea345dSchristos { 20004eea345dSchristos struct calendar jd; 20014eea345dSchristos int32_t ed; 20024eea345dSchristos 20034eea345dSchristos if (!ntpcal_get_build_date(&jd)) 20044eea345dSchristos return NTP_TO_UNIX_DAYS; 20054eea345dSchristos 20064eea345dSchristos /* The time zone of the build stamp is unspecified; we remove 20074eea345dSchristos * one day to provide a certain slack. And in case somebody 20084eea345dSchristos * fiddled with the system clock, we make sure we do not go 20094eea345dSchristos * before the UNIX epoch (1970-01-01). It's probably not possible 20104eea345dSchristos * to do this to the clock on most systems, but there are other 20114eea345dSchristos * ways to tweak the build stamp. 20124eea345dSchristos */ 20134eea345dSchristos jd.monthday -= 1; 20144eea345dSchristos ed = ntpcal_date_to_rd(&jd) - DAY_NTP_STARTS; 20154eea345dSchristos return (ed < NTP_TO_UNIX_DAYS) ? NTP_TO_UNIX_DAYS : ed; 20164eea345dSchristos } 20174eea345dSchristos 20184eea345dSchristos int32_t 20194eea345dSchristos basedate_eval_string( 20204eea345dSchristos const char * str 20214eea345dSchristos ) 20224eea345dSchristos { 20234eea345dSchristos u_short y,m,d; 20244eea345dSchristos u_long ned; 20254eea345dSchristos int rc, nc; 20264eea345dSchristos size_t sl; 20274eea345dSchristos 20284eea345dSchristos sl = strlen(str); 20294eea345dSchristos rc = sscanf(str, "%4hu-%2hu-%2hu%n", &y, &m, &d, &nc); 20304eea345dSchristos if (rc == 3 && (size_t)nc == sl) { 20314eea345dSchristos if (m >= 1 && m <= 12 && d >= 1 && d <= 31) 20324eea345dSchristos return ntpcal_edate_to_eradays(y-1, m-1, d) 20334eea345dSchristos - DAY_NTP_STARTS; 20344eea345dSchristos goto buildstamp; 20354eea345dSchristos } 20364eea345dSchristos 20374eea345dSchristos rc = sscanf(str, "%lu%n", &ned, &nc); 20384eea345dSchristos if (rc == 1 && (size_t)nc == sl) { 20394eea345dSchristos if (ned <= INT32_MAX) 20404eea345dSchristos return (int32_t)ned; 20414eea345dSchristos goto buildstamp; 20424eea345dSchristos } 20434eea345dSchristos 20444eea345dSchristos buildstamp: 20454eea345dSchristos msyslog(LOG_WARNING, 20464eea345dSchristos "basedate string \"%s\" invalid, build date substituted!", 20474eea345dSchristos str); 20484eea345dSchristos return basedate_eval_buildstamp(); 20494eea345dSchristos } 20504eea345dSchristos 20514eea345dSchristos uint32_t 20524eea345dSchristos basedate_get_day(void) 20534eea345dSchristos { 20544eea345dSchristos return s_baseday; 20554eea345dSchristos } 20564eea345dSchristos 20574eea345dSchristos int32_t 20584eea345dSchristos basedate_set_day( 20594eea345dSchristos int32_t day 20604eea345dSchristos ) 20614eea345dSchristos { 20624eea345dSchristos struct calendar jd; 20634eea345dSchristos int32_t retv; 20644eea345dSchristos 2065cdfa2a7eSchristos /* set NTP base date for NTP era unfolding */ 20664eea345dSchristos if (day < NTP_TO_UNIX_DAYS) { 20674eea345dSchristos msyslog(LOG_WARNING, 20684eea345dSchristos "baseday_set_day: invalid day (%lu), UNIX epoch substituted", 20694eea345dSchristos (unsigned long)day); 20704eea345dSchristos day = NTP_TO_UNIX_DAYS; 20714eea345dSchristos } 20724eea345dSchristos retv = s_baseday; 20734eea345dSchristos s_baseday = day; 20744eea345dSchristos ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); 20754eea345dSchristos msyslog(LOG_INFO, "basedate set to %04hu-%02hu-%02hu", 20764eea345dSchristos jd.year, (u_short)jd.month, (u_short)jd.monthday); 2077cdfa2a7eSchristos 2078cdfa2a7eSchristos /* set GPS base week for GPS week unfolding */ 2079cdfa2a7eSchristos day = ntpcal_weekday_ge(day + DAY_NTP_STARTS, CAL_SUNDAY) 2080cdfa2a7eSchristos - DAY_NTP_STARTS; 2081cdfa2a7eSchristos if (day < NTP_TO_GPS_DAYS) 2082cdfa2a7eSchristos day = NTP_TO_GPS_DAYS; 2083cdfa2a7eSchristos s_gpsweek = (day - NTP_TO_GPS_DAYS) / DAYSPERWEEK; 2084cdfa2a7eSchristos ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); 2085cdfa2a7eSchristos msyslog(LOG_INFO, "gps base set to %04hu-%02hu-%02hu (week %d)", 2086cdfa2a7eSchristos jd.year, (u_short)jd.month, (u_short)jd.monthday, s_gpsweek); 2087cdfa2a7eSchristos 20884eea345dSchristos return retv; 20894eea345dSchristos } 20904eea345dSchristos 20914eea345dSchristos time_t 20924eea345dSchristos basedate_get_eracenter(void) 20934eea345dSchristos { 20944eea345dSchristos time_t retv; 20954eea345dSchristos retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); 20964eea345dSchristos retv *= SECSPERDAY; 20974eea345dSchristos retv += (UINT32_C(1) << 31); 20984eea345dSchristos return retv; 20994eea345dSchristos } 21004eea345dSchristos 21014eea345dSchristos time_t 21024eea345dSchristos basedate_get_erabase(void) 21034eea345dSchristos { 21044eea345dSchristos time_t retv; 21054eea345dSchristos retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); 21064eea345dSchristos retv *= SECSPERDAY; 21074eea345dSchristos return retv; 21084eea345dSchristos } 21094eea345dSchristos 2110cdfa2a7eSchristos uint32_t 2111cdfa2a7eSchristos basedate_get_gpsweek(void) 2112cdfa2a7eSchristos { 2113cdfa2a7eSchristos return s_gpsweek; 2114cdfa2a7eSchristos } 2115cdfa2a7eSchristos 2116cdfa2a7eSchristos uint32_t 2117cdfa2a7eSchristos basedate_expand_gpsweek( 2118cdfa2a7eSchristos unsigned short weekno 2119cdfa2a7eSchristos ) 2120cdfa2a7eSchristos { 2121cdfa2a7eSchristos /* We do a fast modulus expansion here. Since all quantities are 2122cdfa2a7eSchristos * unsigned and we cannot go before the start of the GPS epoch 2123cdfa2a7eSchristos * anyway, and since the truncated GPS week number is 10 bit, the 2124cdfa2a7eSchristos * expansion becomes a simple sub/and/add sequence. 2125cdfa2a7eSchristos */ 2126cdfa2a7eSchristos #if GPSWEEKS != 1024 2127cdfa2a7eSchristos # error GPSWEEKS defined wrong -- should be 1024! 2128cdfa2a7eSchristos #endif 2129cdfa2a7eSchristos 2130cdfa2a7eSchristos uint32_t diff; 2131cdfa2a7eSchristos diff = ((uint32_t)weekno - s_gpsweek) & (GPSWEEKS - 1); 2132cdfa2a7eSchristos return s_gpsweek + diff; 2133cdfa2a7eSchristos } 2134cdfa2a7eSchristos 2135cdfa2a7eSchristos /* 2136cdfa2a7eSchristos * ==================================================================== 2137cdfa2a7eSchristos * misc. helpers 2138cdfa2a7eSchristos * ==================================================================== 2139cdfa2a7eSchristos */ 2140cdfa2a7eSchristos 2141cdfa2a7eSchristos /* -------------------------------------------------------------------- 2142cdfa2a7eSchristos * reconstruct the centrury from a truncated date and a day-of-week 2143cdfa2a7eSchristos * 2144cdfa2a7eSchristos * Given a date with truncated year (2-digit, 0..99) and a day-of-week 2145cdfa2a7eSchristos * from 1(Mon) to 7(Sun), recover the full year between 1900AD and 2300AD. 2146cdfa2a7eSchristos */ 2147cdfa2a7eSchristos int32_t 2148cdfa2a7eSchristos ntpcal_expand_century( 2149cdfa2a7eSchristos uint32_t y, 2150cdfa2a7eSchristos uint32_t m, 2151cdfa2a7eSchristos uint32_t d, 2152cdfa2a7eSchristos uint32_t wd) 2153cdfa2a7eSchristos { 2154cdfa2a7eSchristos /* This algorithm is short but tricky... It's related to 2155cdfa2a7eSchristos * Zeller's congruence, partially done backwards. 2156cdfa2a7eSchristos * 2157cdfa2a7eSchristos * A few facts to remember: 2158cdfa2a7eSchristos * 1) The Gregorian calendar has a cycle of 400 years. 2159cdfa2a7eSchristos * 2) The weekday of the 1st day of a century shifts by 5 days 2160cdfa2a7eSchristos * during a great cycle. 2161cdfa2a7eSchristos * 3) For calendar math, a century starts with the 1st year, 2162cdfa2a7eSchristos * which is year 1, !not! zero. 2163cdfa2a7eSchristos * 2164cdfa2a7eSchristos * So we start with taking the weekday difference (mod 7) 2165cdfa2a7eSchristos * between the truncated date (which is taken as an absolute 2166cdfa2a7eSchristos * date in the 1st century in the proleptic calendar) and the 2167cdfa2a7eSchristos * weekday given. 2168cdfa2a7eSchristos * 2169cdfa2a7eSchristos * When dividing this residual by 5, we obtain the number of 2170cdfa2a7eSchristos * centuries to add to the base. But since the residual is (mod 2171cdfa2a7eSchristos * 7), we have to make this an exact division by multiplication 2172cdfa2a7eSchristos * with the modular inverse of 5 (mod 7), which is 3: 2173cdfa2a7eSchristos * 3*5 === 1 (mod 7). 2174cdfa2a7eSchristos * 2175cdfa2a7eSchristos * If this yields a result of 4/5/6, the given date/day-of-week 2176cdfa2a7eSchristos * combination is impossible, and we return zero as resulting 2177cdfa2a7eSchristos * year to indicate failure. 2178cdfa2a7eSchristos * 2179cdfa2a7eSchristos * Then we remap the century to the range starting with year 2180cdfa2a7eSchristos * 1900. 2181cdfa2a7eSchristos */ 2182cdfa2a7eSchristos 2183cdfa2a7eSchristos uint32_t c; 2184cdfa2a7eSchristos 2185cdfa2a7eSchristos /* check basic constraints */ 2186cdfa2a7eSchristos if ((y >= 100u) || (--m >= 12u) || (--d >= 31u)) 2187cdfa2a7eSchristos return 0; 2188cdfa2a7eSchristos 2189cdfa2a7eSchristos if ((m += 10u) >= 12u) /* shift base to prev. March,1st */ 2190cdfa2a7eSchristos m -= 12u; 2191cdfa2a7eSchristos else if (--y >= 100u) 2192cdfa2a7eSchristos y += 100u; 2193cdfa2a7eSchristos d += y + (y >> 2) + 2u; /* year share */ 2194cdfa2a7eSchristos d += (m * 83u + 16u) >> 5; /* month share */ 2195cdfa2a7eSchristos 2196cdfa2a7eSchristos /* get (wd - d), shifted to positive value, and multiply with 2197cdfa2a7eSchristos * 3(mod 7). (Exact division, see to comment) 2198cdfa2a7eSchristos * Note: 1) d <= 184 at this point. 2199cdfa2a7eSchristos * 2) 252 % 7 == 0, but 'wd' is off by one since we did 2200cdfa2a7eSchristos * '--d' above, so we add just 251 here! 2201cdfa2a7eSchristos */ 2202cdfa2a7eSchristos c = u32mod7(3 * (251u + wd - d)); 2203cdfa2a7eSchristos if (c > 3u) 2204cdfa2a7eSchristos return 0; 2205cdfa2a7eSchristos 2206cdfa2a7eSchristos if ((m > 9u) && (++y >= 100u)) {/* undo base shift */ 2207cdfa2a7eSchristos y -= 100u; 2208cdfa2a7eSchristos c = (c + 1) & 3u; 2209cdfa2a7eSchristos } 2210cdfa2a7eSchristos y += (c * 100u); /* combine into 1st cycle */ 2211cdfa2a7eSchristos y += (y < 300u) ? 2000 : 1600; /* map to destination era */ 2212cdfa2a7eSchristos return (int)y; 2213cdfa2a7eSchristos } 2214cdfa2a7eSchristos 2215cdfa2a7eSchristos char * 2216cdfa2a7eSchristos ntpcal_iso8601std( 2217cdfa2a7eSchristos char * buf, 2218cdfa2a7eSchristos size_t len, 2219cdfa2a7eSchristos TcCivilDate * cdp 2220cdfa2a7eSchristos ) 2221cdfa2a7eSchristos { 2222cdfa2a7eSchristos if (!buf) { 2223cdfa2a7eSchristos LIB_GETBUF(buf); 2224cdfa2a7eSchristos len = LIB_BUFLENGTH; 2225cdfa2a7eSchristos } 2226cdfa2a7eSchristos if (len) { 2227cdfa2a7eSchristos int slen = snprintf(buf, len, "%04u-%02u-%02uT%02u:%02u:%02u", 2228cdfa2a7eSchristos cdp->year, cdp->month, cdp->monthday, 2229cdfa2a7eSchristos cdp->hour, cdp->minute, cdp->second); 2230cdfa2a7eSchristos if (slen < 0) 2231cdfa2a7eSchristos *buf = '\0'; 2232cdfa2a7eSchristos } 2233cdfa2a7eSchristos return buf; 2234cdfa2a7eSchristos } 2235cdfa2a7eSchristos 22368585484eSchristos /* -*-EOF-*- */ 2237