ntp_calendar.c revision 309007
1275970Scy/* 2275970Scy * ntp_calendar.c - calendar and helper functions 3275970Scy * 4275970Scy * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5275970Scy * The contents of 'html/copyright.html' apply. 6289764Sglebius * 7289764Sglebius * -------------------------------------------------------------------- 8289764Sglebius * Some notes on the implementation: 9289764Sglebius * 10289764Sglebius * Calendar algorithms thrive on the division operation, which is one of 11289764Sglebius * the slowest numerical operations in any CPU. What saves us here from 12289764Sglebius * abysmal performance is the fact that all divisions are divisions by 13289764Sglebius * constant numbers, and most compilers can do this by a multiplication 14289764Sglebius * operation. But this might not work when using the div/ldiv/lldiv 15289764Sglebius * function family, because many compilers are not able to do inline 16289764Sglebius * expansion of the code with following optimisation for the 17289764Sglebius * constant-divider case. 18289764Sglebius * 19289764Sglebius * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which 20289764Sglebius * are inherently target dependent. Nothing that could not be cured with 21289764Sglebius * autoconf, but still a mess... 22289764Sglebius * 23289764Sglebius * Furthermore, we need floor division in many places. C either leaves 24289764Sglebius * the division behaviour undefined (< C99) or demands truncation to 25289764Sglebius * zero (>= C99), so additional steps are required to make sure the 26289764Sglebius * algorithms work. The {l,ll}div function family is requested to 27289764Sglebius * truncate towards zero, which is also the wrong direction for our 28289764Sglebius * purpose. 29289764Sglebius * 30289764Sglebius * For all this, all divisions by constant are coded manually, even when 31289764Sglebius * there is a joined div/mod operation: The optimiser should sort that 32289764Sglebius * out, if possible. Most of the calculations are done with unsigned 33289764Sglebius * types, explicitely using two's complement arithmetics where 34289764Sglebius * necessary. This minimises the dependecies to compiler and target, 35289764Sglebius * while still giving reasonable to good performance. 36289764Sglebius * 37289764Sglebius * The implementation uses a few tricks that exploit properties of the 38289764Sglebius * two's complement: Floor division on negative dividents can be 39289764Sglebius * executed by using the one's complement of the divident. One's 40289764Sglebius * complement can be easily created using XOR and a mask. 41289764Sglebius * 42289764Sglebius * Finally, check for overflow conditions is minimal. There are only two 43289764Sglebius * calculation steps in the whole calendar that suffer from an internal 44289764Sglebius * overflow, and these conditions are checked: errno is set to EDOM and 45289764Sglebius * the results are clamped/saturated in this case. All other functions 46289764Sglebius * do not suffer from internal overflow and simply return the result 47289764Sglebius * truncated to 32 bits. 48289764Sglebius * 49289764Sglebius * This is a sacrifice made for execution speed. Since a 32-bit day 50289764Sglebius * counter covers +/- 5,879,610 years and the clamp limits the effective 51289764Sglebius * range to +/-2.9 million years, this should not pose a problem here. 52289764Sglebius * 53275970Scy */ 54289764Sglebius 55275970Scy#include <config.h> 56275970Scy#include <sys/types.h> 57275970Scy 58275970Scy#include "ntp_types.h" 59275970Scy#include "ntp_calendar.h" 60275970Scy#include "ntp_stdlib.h" 61275970Scy#include "ntp_fp.h" 62275970Scy#include "ntp_unixtime.h" 63275970Scy 64289764Sglebius/* For now, let's take the conservative approach: if the target property 65289764Sglebius * macros are not defined, check a few well-known compiler/architecture 66289764Sglebius * settings. Default is to assume that the representation of signed 67289764Sglebius * integers is unknown and shift-arithmetic-right is not available. 68289764Sglebius */ 69289764Sglebius#ifndef TARGET_HAS_2CPL 70289764Sglebius# if defined(__GNUC__) 71289764Sglebius# if defined(__i386__) || defined(__x86_64__) || defined(__arm__) 72289764Sglebius# define TARGET_HAS_2CPL 1 73289764Sglebius# else 74289764Sglebius# define TARGET_HAS_2CPL 0 75289764Sglebius# endif 76289764Sglebius# elif defined(_MSC_VER) 77289764Sglebius# if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) 78289764Sglebius# define TARGET_HAS_2CPL 1 79289764Sglebius# else 80289764Sglebius# define TARGET_HAS_2CPL 0 81289764Sglebius# endif 82289764Sglebius# else 83289764Sglebius# define TARGET_HAS_2CPL 0 84289764Sglebius# endif 85289764Sglebius#endif 86289764Sglebius 87289764Sglebius#ifndef TARGET_HAS_SAR 88289764Sglebius# define TARGET_HAS_SAR 0 89289764Sglebius#endif 90289764Sglebius 91275970Scy/* 92275970Scy *--------------------------------------------------------------------- 93275970Scy * replacing the 'time()' function 94309007Sdelphij *--------------------------------------------------------------------- 95275970Scy */ 96275970Scy 97275970Scystatic systime_func_ptr systime_func = &time; 98275970Scystatic inline time_t now(void); 99275970Scy 100275970Scy 101275970Scysystime_func_ptr 102275970Scyntpcal_set_timefunc( 103275970Scy systime_func_ptr nfunc 104275970Scy ) 105275970Scy{ 106275970Scy systime_func_ptr res; 107282408Scy 108275970Scy res = systime_func; 109275970Scy if (NULL == nfunc) 110275970Scy nfunc = &time; 111275970Scy systime_func = nfunc; 112275970Scy 113275970Scy return res; 114275970Scy} 115275970Scy 116275970Scy 117275970Scystatic inline time_t 118275970Scynow(void) 119275970Scy{ 120275970Scy return (*systime_func)(NULL); 121275970Scy} 122275970Scy 123275970Scy/* 124275970Scy *--------------------------------------------------------------------- 125289764Sglebius * Get sign extension mask and unsigned 2cpl rep for a signed integer 126289764Sglebius *--------------------------------------------------------------------- 127289764Sglebius */ 128289764Sglebius 129289764Sglebiusstatic inline uint32_t 130289764Sglebiusint32_sflag( 131289764Sglebius const int32_t v) 132289764Sglebius{ 133289764Sglebius# if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4 134289764Sglebius 135289764Sglebius /* Let's assume that shift is the fastest way to get the sign 136289764Sglebius * extension of of a signed integer. This might not always be 137289764Sglebius * true, though -- On 8bit CPUs or machines without barrel 138289764Sglebius * shifter this will kill the performance. So we make sure 139289764Sglebius * we do this only if 'int' has at least 4 bytes. 140289764Sglebius */ 141289764Sglebius return (uint32_t)(v >> 31); 142289764Sglebius 143289764Sglebius# else 144289764Sglebius 145289764Sglebius /* This should be a rather generic approach for getting a sign 146289764Sglebius * extension mask... 147289764Sglebius */ 148289764Sglebius return UINT32_C(0) - (uint32_t)(v < 0); 149289764Sglebius 150289764Sglebius# endif 151289764Sglebius} 152289764Sglebius 153289764Sglebiusstatic inline uint32_t 154289764Sglebiusint32_to_uint32_2cpl( 155289764Sglebius const int32_t v) 156289764Sglebius{ 157289764Sglebius uint32_t vu; 158289764Sglebius 159289764Sglebius# if TARGET_HAS_2CPL 160289764Sglebius 161289764Sglebius /* Just copy through the 32 bits from the signed value if we're 162289764Sglebius * on a two's complement target. 163289764Sglebius */ 164289764Sglebius vu = (uint32_t)v; 165289764Sglebius 166289764Sglebius# else 167289764Sglebius 168289764Sglebius /* Convert from signed int to unsigned int two's complement. Do 169289764Sglebius * not make any assumptions about the representation of signed 170289764Sglebius * integers, but make sure signed integer overflow cannot happen 171289764Sglebius * here. A compiler on a two's complement target *might* find 172289764Sglebius * out that this is just a complicated cast (as above), but your 173289764Sglebius * mileage might vary. 174289764Sglebius */ 175289764Sglebius if (v < 0) 176289764Sglebius vu = ~(uint32_t)(-(v + 1)); 177289764Sglebius else 178289764Sglebius vu = (uint32_t)v; 179289764Sglebius 180289764Sglebius# endif 181289764Sglebius 182289764Sglebius return vu; 183289764Sglebius} 184289764Sglebius 185289764Sglebiusstatic inline int32_t 186289764Sglebiusuint32_2cpl_to_int32( 187289764Sglebius const uint32_t vu) 188289764Sglebius{ 189289764Sglebius int32_t v; 190289764Sglebius 191289764Sglebius# if TARGET_HAS_2CPL 192289764Sglebius 193289764Sglebius /* Just copy through the 32 bits from the unsigned value if 194289764Sglebius * we're on a two's complement target. 195289764Sglebius */ 196289764Sglebius v = (int32_t)vu; 197289764Sglebius 198289764Sglebius# else 199289764Sglebius 200289764Sglebius /* Convert to signed integer, making sure signed integer 201289764Sglebius * overflow cannot happen. Again, the optimiser might or might 202289764Sglebius * not find out that this is just a copy of 32 bits on a target 203289764Sglebius * with two's complement representation for signed integers. 204289764Sglebius */ 205289764Sglebius if (vu > INT32_MAX) 206289764Sglebius v = -(int32_t)(~vu) - 1; 207289764Sglebius else 208289764Sglebius v = (int32_t)vu; 209289764Sglebius 210289764Sglebius# endif 211289764Sglebius 212289764Sglebius return v; 213289764Sglebius} 214289764Sglebius 215289764Sglebius/* Some of the calculations need to multiply the input by 4 before doing 216289764Sglebius * a division. This can cause overflow and strange results. Therefore we 217289764Sglebius * clamp / saturate the input operand. And since we do the calculations 218289764Sglebius * in unsigned int with an extra sign flag/mask, we only loose one bit 219289764Sglebius * of the input value range. 220289764Sglebius */ 221289764Sglebiusstatic inline uint32_t 222289764Sglebiusuint32_saturate( 223289764Sglebius uint32_t vu, 224289764Sglebius uint32_t mu) 225289764Sglebius{ 226289764Sglebius static const uint32_t limit = UINT32_MAX/4u; 227289764Sglebius if ((mu ^ vu) > limit) { 228289764Sglebius vu = mu ^ limit; 229289764Sglebius errno = EDOM; 230289764Sglebius } 231289764Sglebius return vu; 232289764Sglebius} 233289764Sglebius 234289764Sglebius/* 235289764Sglebius *--------------------------------------------------------------------- 236275970Scy * Convert between 'time_t' and 'vint64' 237275970Scy *--------------------------------------------------------------------- 238275970Scy */ 239275970Scyvint64 240275970Scytime_to_vint64( 241275970Scy const time_t * ptt 242275970Scy ) 243275970Scy{ 244275970Scy vint64 res; 245275970Scy time_t tt; 246275970Scy 247275970Scy tt = *ptt; 248275970Scy 249289764Sglebius# if SIZEOF_TIME_T <= 4 250275970Scy 251275970Scy res.D_s.hi = 0; 252275970Scy if (tt < 0) { 253275970Scy res.D_s.lo = (uint32_t)-tt; 254275970Scy M_NEG(res.D_s.hi, res.D_s.lo); 255275970Scy } else { 256275970Scy res.D_s.lo = (uint32_t)tt; 257275970Scy } 258275970Scy 259289764Sglebius# elif defined(HAVE_INT64) 260275970Scy 261275970Scy res.q_s = tt; 262275970Scy 263289764Sglebius# else 264275970Scy /* 265275970Scy * shifting negative signed quantities is compiler-dependent, so 266275970Scy * we better avoid it and do it all manually. And shifting more 267275970Scy * than the width of a quantity is undefined. Also a don't do! 268275970Scy */ 269275970Scy if (tt < 0) { 270275970Scy tt = -tt; 271275970Scy res.D_s.lo = (uint32_t)tt; 272275970Scy res.D_s.hi = (uint32_t)(tt >> 32); 273275970Scy M_NEG(res.D_s.hi, res.D_s.lo); 274275970Scy } else { 275275970Scy res.D_s.lo = (uint32_t)tt; 276275970Scy res.D_s.hi = (uint32_t)(tt >> 32); 277275970Scy } 278275970Scy 279289764Sglebius# endif 280275970Scy 281275970Scy return res; 282275970Scy} 283275970Scy 284275970Scy 285275970Scytime_t 286275970Scyvint64_to_time( 287275970Scy const vint64 *tv 288275970Scy ) 289275970Scy{ 290275970Scy time_t res; 291275970Scy 292289764Sglebius# if SIZEOF_TIME_T <= 4 293275970Scy 294275970Scy res = (time_t)tv->D_s.lo; 295275970Scy 296289764Sglebius# elif defined(HAVE_INT64) 297275970Scy 298275970Scy res = (time_t)tv->q_s; 299275970Scy 300289764Sglebius# else 301275970Scy 302275970Scy res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; 303275970Scy 304289764Sglebius# endif 305275970Scy 306275970Scy return res; 307282408Scy} 308275970Scy 309275970Scy/* 310275970Scy *--------------------------------------------------------------------- 311275970Scy * Get the build date & time 312275970Scy *--------------------------------------------------------------------- 313275970Scy */ 314275970Scyint 315275970Scyntpcal_get_build_date( 316275970Scy struct calendar * jd 317275970Scy ) 318275970Scy{ 319275970Scy /* The C standard tells us the format of '__DATE__': 320275970Scy * 321275970Scy * __DATE__ The date of translation of the preprocessing 322275970Scy * translation unit: a character string literal of the form "Mmm 323275970Scy * dd yyyy", where the names of the months are the same as those 324275970Scy * generated by the asctime function, and the first character of 325275970Scy * dd is a space character if the value is less than 10. If the 326275970Scy * date of translation is not available, an 327275970Scy * implementation-defined valid date shall be supplied. 328275970Scy * 329275970Scy * __TIME__ The time of translation of the preprocessing 330275970Scy * translation unit: a character string literal of the form 331275970Scy * "hh:mm:ss" as in the time generated by the asctime 332275970Scy * function. If the time of translation is not available, an 333275970Scy * implementation-defined valid time shall be supplied. 334275970Scy * 335275970Scy * Note that MSVC declares DATE and TIME to be in the local time 336275970Scy * zone, while neither the C standard nor the GCC docs make any 337275970Scy * statement about this. As a result, we may be +/-12hrs off 338275970Scy * UTC. But for practical purposes, this should not be a 339275970Scy * problem. 340275970Scy * 341275970Scy */ 342289764Sglebius# ifdef MKREPRO_DATE 343280849Scy static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; 344289764Sglebius# else 345275970Scy static const char build[] = __TIME__ "/" __DATE__; 346289764Sglebius# endif 347275970Scy static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 348275970Scy 349275970Scy char monstr[4]; 350275970Scy const char * cp; 351275970Scy unsigned short hour, minute, second, day, year; 352275970Scy /* Note: The above quantities are used for sscanf 'hu' format, 353275970Scy * so using 'uint16_t' is contra-indicated! 354275970Scy */ 355275970Scy 356289764Sglebius# ifdef DEBUG 357275970Scy static int ignore = 0; 358289764Sglebius# endif 359282408Scy 360275970Scy ZERO(*jd); 361275970Scy jd->year = 1970; 362275970Scy jd->month = 1; 363275970Scy jd->monthday = 1; 364275970Scy 365289764Sglebius# ifdef DEBUG 366275970Scy /* check environment if build date should be ignored */ 367275970Scy if (0 == ignore) { 368275970Scy const char * envstr; 369275970Scy envstr = getenv("NTPD_IGNORE_BUILD_DATE"); 370275970Scy ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes"))); 371275970Scy } 372275970Scy if (ignore > 1) 373275970Scy return FALSE; 374289764Sglebius# endif 375275970Scy 376275970Scy if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu", 377275970Scy &hour, &minute, &second, monstr, &day, &year)) { 378275970Scy cp = strstr(mlist, monstr); 379275970Scy if (NULL != cp) { 380275970Scy jd->year = year; 381275970Scy jd->month = (uint8_t)((cp - mlist) / 3 + 1); 382275970Scy jd->monthday = (uint8_t)day; 383275970Scy jd->hour = (uint8_t)hour; 384275970Scy jd->minute = (uint8_t)minute; 385275970Scy jd->second = (uint8_t)second; 386275970Scy 387275970Scy return TRUE; 388275970Scy } 389275970Scy } 390275970Scy 391275970Scy return FALSE; 392275970Scy} 393275970Scy 394275970Scy 395275970Scy/* 396275970Scy *--------------------------------------------------------------------- 397275970Scy * basic calendar stuff 398309007Sdelphij *--------------------------------------------------------------------- 399275970Scy */ 400275970Scy 401275970Scy/* month table for a year starting with March,1st */ 402275970Scystatic const uint16_t shift_month_table[13] = { 403275970Scy 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 404275970Scy}; 405275970Scy 406275970Scy/* month tables for years starting with January,1st; regular & leap */ 407275970Scystatic const uint16_t real_month_table[2][13] = { 408275970Scy /* -*- table for regular years -*- */ 409275970Scy { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 410275970Scy /* -*- table for leap years -*- */ 411275970Scy { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 412275970Scy}; 413275970Scy 414275970Scy/* 415275970Scy * Some notes on the terminology: 416275970Scy * 417275970Scy * We use the proleptic Gregorian calendar, which is the Gregorian 418275970Scy * calendar extended in both directions ad infinitum. This totally 419275970Scy * disregards the fact that this calendar was invented in 1582, and 420275970Scy * was adopted at various dates over the world; sometimes even after 421275970Scy * the start of the NTP epoch. 422275970Scy * 423275970Scy * Normally date parts are given as current cycles, while time parts 424275970Scy * are given as elapsed cycles: 425275970Scy * 426275970Scy * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, 427275970Scy * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. 428275970Scy * 429275970Scy * The basic calculations for this calendar implementation deal with 430275970Scy * ELAPSED date units, which is the number of full years, full months 431275970Scy * and full days before a date: 1970-01-01 would be (1969, 0, 0) in 432275970Scy * that notation. 433275970Scy * 434275970Scy * To ease the numeric computations, month and day values outside the 435275970Scy * normal range are acceptable: 2001-03-00 will be treated as the day 436275970Scy * before 2001-03-01, 2000-13-32 will give the same result as 437275970Scy * 2001-02-01 and so on. 438275970Scy * 439275970Scy * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' 440275970Scy * (day number). This is the number of days elapsed since 0000-12-31 441275970Scy * in the proleptic Gregorian calendar. The begin of the Christian Era 442275970Scy * (0001-01-01) is RD(1). 443275970Scy */ 444275970Scy 445275970Scy/* 446309007Sdelphij * ==================================================================== 447275970Scy * 448275970Scy * General algorithmic stuff 449275970Scy * 450309007Sdelphij * ==================================================================== 451275970Scy */ 452275970Scy 453275970Scy/* 454275970Scy *--------------------------------------------------------------------- 455275970Scy * Do a periodic extension of 'value' around 'pivot' with a period of 456275970Scy * 'cycle'. 457275970Scy * 458275970Scy * The result 'res' is a number that holds to the following properties: 459275970Scy * 460275970Scy * 1) res MOD cycle == value MOD cycle 461275970Scy * 2) pivot <= res < pivot + cycle 462275970Scy * (replace </<= with >/>= for negative cycles) 463275970Scy * 464275970Scy * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which 465275970Scy * is not the same as the '%' operator in C: C requires division to be 466275970Scy * a truncated division, where remainder and dividend have the same 467275970Scy * sign if the remainder is not zero, whereas floor division requires 468275970Scy * divider and modulus to have the same sign for a non-zero modulus. 469275970Scy * 470275970Scy * This function has some useful applications: 471275970Scy * 472275970Scy * + let Y be a calendar year and V a truncated 2-digit year: then 473275970Scy * periodic_extend(Y-50, V, 100) 474275970Scy * is the closest expansion of the truncated year with respect to 475275970Scy * the full year, that is a 4-digit year with a difference of less 476275970Scy * than 50 years to the year Y. ("century unfolding") 477275970Scy * 478275970Scy * + let T be a UN*X time stamp and V be seconds-of-day: then 479275970Scy * perodic_extend(T-43200, V, 86400) 480275970Scy * is a time stamp that has the same seconds-of-day as the input 481275970Scy * value, with an absolute difference to T of <= 12hrs. ("day 482275970Scy * unfolding") 483275970Scy * 484275970Scy * + Wherever you have a truncated periodic value and a non-truncated 485275970Scy * base value and you want to match them somehow... 486275970Scy * 487275970Scy * Basically, the function delivers 'pivot + (value - pivot) % cycle', 488275970Scy * but the implementation takes some pains to avoid internal signed 489275970Scy * integer overflows in the '(value - pivot) % cycle' part and adheres 490275970Scy * to the floor division convention. 491275970Scy * 492275970Scy * If 64bit scalars where available on all intended platforms, writing a 493275970Scy * version that uses 64 bit ops would be easy; writing a general 494275970Scy * division routine for 64bit ops on a platform that can only do 495275970Scy * 32/16bit divisions and is still performant is a bit more 496275970Scy * difficult. Since most usecases can be coded in a way that does only 497275970Scy * require the 32-bit version a 64bit version is NOT provided here. 498309007Sdelphij *--------------------------------------------------------------------- 499275970Scy */ 500275970Scyint32_t 501275970Scyntpcal_periodic_extend( 502275970Scy int32_t pivot, 503275970Scy int32_t value, 504275970Scy int32_t cycle 505275970Scy ) 506275970Scy{ 507275970Scy uint32_t diff; 508275970Scy char cpl = 0; /* modulo complement flag */ 509275970Scy char neg = 0; /* sign change flag */ 510275970Scy 511282408Scy /* make the cycle positive and adjust the flags */ 512275970Scy if (cycle < 0) { 513275970Scy cycle = - cycle; 514275970Scy neg ^= 1; 515275970Scy cpl ^= 1; 516275970Scy } 517275970Scy /* guard against div by zero or one */ 518275970Scy if (cycle > 1) { 519275970Scy /* 520275970Scy * Get absolute difference as unsigned quantity and 521275970Scy * the complement flag. This is done by always 522275970Scy * subtracting the smaller value from the bigger 523289764Sglebius * one. 524275970Scy */ 525275970Scy if (value >= pivot) { 526289764Sglebius diff = int32_to_uint32_2cpl(value) 527289764Sglebius - int32_to_uint32_2cpl(pivot); 528275970Scy } else { 529289764Sglebius diff = int32_to_uint32_2cpl(pivot) 530289764Sglebius - int32_to_uint32_2cpl(value); 531275970Scy cpl ^= 1; 532275970Scy } 533275970Scy diff %= (uint32_t)cycle; 534275970Scy if (diff) { 535275970Scy if (cpl) 536289764Sglebius diff = (uint32_t)cycle - diff; 537275970Scy if (neg) 538275970Scy diff = ~diff + 1; 539289764Sglebius pivot += uint32_2cpl_to_int32(diff); 540275970Scy } 541275970Scy } 542275970Scy return pivot; 543275970Scy} 544275970Scy 545309007Sdelphij/*--------------------------------------------------------------------- 546309007Sdelphij * Note to the casual reader 547309007Sdelphij * 548309007Sdelphij * In the next two functions you will find (or would have found...) 549309007Sdelphij * the expression 550309007Sdelphij * 551309007Sdelphij * res.Q_s -= 0x80000000; 552309007Sdelphij * 553309007Sdelphij * There was some ruckus about a possible programming error due to 554309007Sdelphij * integer overflow and sign propagation. 555309007Sdelphij * 556309007Sdelphij * This assumption is based on a lack of understanding of the C 557309007Sdelphij * standard. (Though this is admittedly not one of the most 'natural' 558309007Sdelphij * aspects of the 'C' language and easily to get wrong.) 559309007Sdelphij * 560309007Sdelphij * see 561309007Sdelphij * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 562309007Sdelphij * "ISO/IEC 9899:201x Committee Draft ��� April 12, 2011" 563309007Sdelphij * 6.4.4.1 Integer constants, clause 5 564309007Sdelphij * 565309007Sdelphij * why there is no sign extension/overflow problem here. 566309007Sdelphij * 567309007Sdelphij * But to ease the minds of the doubtful, I added back the 'u' qualifiers 568309007Sdelphij * that somehow got lost over the last years. 569309007Sdelphij */ 570309007Sdelphij 571309007Sdelphij 572275970Scy/* 573309007Sdelphij *--------------------------------------------------------------------- 574275970Scy * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X 575275970Scy * scale with proper epoch unfolding around a given pivot or the current 576275970Scy * system time. This function happily accepts negative pivot values as 577275970Scy * timestamps befor 1970-01-01, so be aware of possible trouble on 578275970Scy * platforms with 32bit 'time_t'! 579275970Scy * 580275970Scy * This is also a periodic extension, but since the cycle is 2^32 and 581275970Scy * the shift is 2^31, we can do some *very* fast math without explicit 582275970Scy * divisions. 583309007Sdelphij *--------------------------------------------------------------------- 584275970Scy */ 585275970Scyvint64 586275970Scyntpcal_ntp_to_time( 587275970Scy uint32_t ntp, 588275970Scy const time_t * pivot 589275970Scy ) 590275970Scy{ 591275970Scy vint64 res; 592275970Scy 593289764Sglebius# if defined(HAVE_INT64) 594275970Scy 595282408Scy res.q_s = (pivot != NULL) 596275970Scy ? *pivot 597282408Scy : now(); 598309007Sdelphij res.Q_s -= 0x80000000u; /* unshift of half range */ 599275970Scy ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 600275970Scy ntp -= res.D_s.lo; /* cycle difference */ 601275970Scy res.Q_s += (uint64_t)ntp; /* get expanded time */ 602275970Scy 603289764Sglebius# else /* no 64bit scalars */ 604282408Scy 605275970Scy time_t tmp; 606282408Scy 607282408Scy tmp = (pivot != NULL) 608275970Scy ? *pivot 609282408Scy : now(); 610275970Scy res = time_to_vint64(&tmp); 611309007Sdelphij M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 612275970Scy ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 613275970Scy ntp -= res.D_s.lo; /* cycle difference */ 614275970Scy M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 615275970Scy 616289764Sglebius# endif /* no 64bit scalars */ 617275970Scy 618275970Scy return res; 619275970Scy} 620275970Scy 621275970Scy/* 622309007Sdelphij *--------------------------------------------------------------------- 623275970Scy * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP 624275970Scy * scale with proper epoch unfolding around a given pivot or the current 625275970Scy * system time. 626275970Scy * 627275970Scy * Note: The pivot must be given in the UN*X time domain! 628275970Scy * 629275970Scy * This is also a periodic extension, but since the cycle is 2^32 and 630275970Scy * the shift is 2^31, we can do some *very* fast math without explicit 631275970Scy * divisions. 632309007Sdelphij *--------------------------------------------------------------------- 633275970Scy */ 634275970Scyvint64 635275970Scyntpcal_ntp_to_ntp( 636275970Scy uint32_t ntp, 637275970Scy const time_t *pivot 638275970Scy ) 639275970Scy{ 640275970Scy vint64 res; 641275970Scy 642289764Sglebius# if defined(HAVE_INT64) 643275970Scy 644275970Scy res.q_s = (pivot) 645275970Scy ? *pivot 646275970Scy : now(); 647309007Sdelphij res.Q_s -= 0x80000000u; /* unshift of half range */ 648275970Scy res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ 649275970Scy ntp -= res.D_s.lo; /* cycle difference */ 650275970Scy res.Q_s += (uint64_t)ntp; /* get expanded time */ 651275970Scy 652289764Sglebius# else /* no 64bit scalars */ 653282408Scy 654275970Scy time_t tmp; 655282408Scy 656275970Scy tmp = (pivot) 657275970Scy ? *pivot 658275970Scy : now(); 659275970Scy res = time_to_vint64(&tmp); 660275970Scy M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 661275970Scy M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */ 662275970Scy ntp -= res.D_s.lo; /* cycle difference */ 663275970Scy M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 664275970Scy 665289764Sglebius# endif /* no 64bit scalars */ 666275970Scy 667275970Scy return res; 668275970Scy} 669275970Scy 670275970Scy 671275970Scy/* 672309007Sdelphij * ==================================================================== 673275970Scy * 674275970Scy * Splitting values to composite entities 675275970Scy * 676309007Sdelphij * ==================================================================== 677275970Scy */ 678275970Scy 679275970Scy/* 680309007Sdelphij *--------------------------------------------------------------------- 681275970Scy * Split a 64bit seconds value into elapsed days in 'res.hi' and 682275970Scy * elapsed seconds since midnight in 'res.lo' using explicit floor 683275970Scy * division. This function happily accepts negative time values as 684275970Scy * timestamps before the respective epoch start. 685309007Sdelphij *--------------------------------------------------------------------- 686275970Scy */ 687275970Scyntpcal_split 688275970Scyntpcal_daysplit( 689275970Scy const vint64 *ts 690275970Scy ) 691275970Scy{ 692275970Scy ntpcal_split res; 693289764Sglebius uint32_t Q; 694275970Scy 695289764Sglebius# if defined(HAVE_INT64) 696289764Sglebius 697289764Sglebius /* Manual floor division by SECSPERDAY. This uses the one's 698289764Sglebius * complement trick, too, but without an extra flag value: The 699289764Sglebius * flag would be 64bit, and that's a bit of overkill on a 32bit 700289764Sglebius * target that has to use a register pair for a 64bit number. 701289764Sglebius */ 702289764Sglebius if (ts->q_s < 0) 703289764Sglebius Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY); 704289764Sglebius else 705289764Sglebius Q = (uint32_t)(ts->Q_s / SECSPERDAY); 706275970Scy 707289764Sglebius# else 708275970Scy 709289764Sglebius uint32_t ah, al, sflag, A; 710275970Scy 711289764Sglebius /* get operand into ah/al (either ts or ts' one's complement, 712289764Sglebius * for later floor division) 713275970Scy */ 714289764Sglebius sflag = int32_sflag(ts->d_s.hi); 715289764Sglebius ah = sflag ^ ts->D_s.hi; 716289764Sglebius al = sflag ^ ts->D_s.lo; 717275970Scy 718289764Sglebius /* Since 86400 == 128*675 we can drop the least 7 bits and 719289764Sglebius * divide by 675 instead of 86400. Then the maximum remainder 720289764Sglebius * after each devision step is 674, and we need 10 bits for 721289764Sglebius * that. So in the next step we can shift in 22 bits from the 722289764Sglebius * numerator. 723289764Sglebius * 724289764Sglebius * Therefore we load the accu with the top 13 bits (51..63) in 725289764Sglebius * the first shot. We don't have to remember the quotient -- it 726289764Sglebius * would be shifted out anyway. 727289764Sglebius */ 728289764Sglebius A = ah >> 19; 729289764Sglebius if (A >= 675) 730289764Sglebius A = (A % 675u); 731282408Scy 732289764Sglebius /* Now assemble the remainder with bits 29..50 from the 733289764Sglebius * numerator and divide. This creates the upper ten bits of the 734289764Sglebius * quotient. (Well, the top 22 bits of a 44bit result. But that 735289764Sglebius * will be truncated to 32 bits anyway.) 736289764Sglebius */ 737289764Sglebius A = (A << 19) | (ah & 0x0007FFFFu); 738289764Sglebius A = (A << 3) | (al >> 29); 739289764Sglebius Q = A / 675u; 740289764Sglebius A = A % 675u; 741275970Scy 742289764Sglebius /* Now assemble the remainder with bits 7..28 from the numerator 743289764Sglebius * and do a final division step. 744275970Scy */ 745289764Sglebius A = (A << 22) | ((al >> 7) & 0x003FFFFFu); 746289764Sglebius Q = (Q << 22) | (A / 675u); 747275970Scy 748289764Sglebius /* The last 7 bits get simply dropped, as they have no affect on 749289764Sglebius * the quotient when dividing by 86400. 750289764Sglebius */ 751275970Scy 752289764Sglebius /* apply sign correction and calculate the true floor 753289764Sglebius * remainder. 754289764Sglebius */ 755289764Sglebius Q ^= sflag; 756289764Sglebius 757289764Sglebius# endif 758289764Sglebius 759289764Sglebius res.hi = uint32_2cpl_to_int32(Q); 760289764Sglebius res.lo = ts->D_s.lo - Q * SECSPERDAY; 761275970Scy 762275970Scy return res; 763275970Scy} 764275970Scy 765275970Scy/* 766309007Sdelphij *--------------------------------------------------------------------- 767275970Scy * Split a 32bit seconds value into h/m/s and excessive days. This 768275970Scy * function happily accepts negative time values as timestamps before 769275970Scy * midnight. 770309007Sdelphij *--------------------------------------------------------------------- 771275970Scy */ 772275970Scystatic int32_t 773275970Scypriv_timesplit( 774275970Scy int32_t split[3], 775275970Scy int32_t ts 776275970Scy ) 777275970Scy{ 778289764Sglebius /* Do 3 chained floor divisions by positive constants, using the 779289764Sglebius * one's complement trick and factoring out the intermediate XOR 780289764Sglebius * ops to reduce the number of operations. 781289764Sglebius */ 782289764Sglebius uint32_t us, um, uh, ud, sflag; 783275970Scy 784289764Sglebius sflag = int32_sflag(ts); 785289764Sglebius us = int32_to_uint32_2cpl(ts); 786275970Scy 787289764Sglebius um = (sflag ^ us) / SECSPERMIN; 788289764Sglebius uh = um / MINSPERHR; 789289764Sglebius ud = uh / HRSPERDAY; 790275970Scy 791289764Sglebius um ^= sflag; 792289764Sglebius uh ^= sflag; 793289764Sglebius ud ^= sflag; 794289764Sglebius 795289764Sglebius split[0] = (int32_t)(uh - ud * HRSPERDAY ); 796289764Sglebius split[1] = (int32_t)(um - uh * MINSPERHR ); 797289764Sglebius split[2] = (int32_t)(us - um * SECSPERMIN); 798289764Sglebius 799289764Sglebius return uint32_2cpl_to_int32(ud); 800275970Scy} 801275970Scy 802275970Scy/* 803309007Sdelphij *--------------------------------------------------------------------- 804275970Scy * Given the number of elapsed days in the calendar era, split this 805275970Scy * number into the number of elapsed years in 'res.hi' and the number 806275970Scy * of elapsed days of that year in 'res.lo'. 807275970Scy * 808275970Scy * if 'isleapyear' is not NULL, it will receive an integer that is 0 for 809275970Scy * regular years and a non-zero value for leap years. 810275970Scy *--------------------------------------------------------------------- 811275970Scy */ 812275970Scyntpcal_split 813275970Scyntpcal_split_eradays( 814275970Scy int32_t days, 815275970Scy int *isleapyear 816275970Scy ) 817275970Scy{ 818289764Sglebius /* Use the fast cyclesplit algorithm here, to calculate the 819289764Sglebius * centuries and years in a century with one division each. This 820289764Sglebius * reduces the number of division operations to two, but is 821289764Sglebius * susceptible to internal range overflow. We make sure the 822289764Sglebius * input operands are in the safe range; this still gives us 823289764Sglebius * approx +/-2.9 million years. 824289764Sglebius */ 825275970Scy ntpcal_split res; 826289764Sglebius int32_t n100, n001; /* calendar year cycles */ 827289764Sglebius uint32_t uday, Q, sflag; 828282408Scy 829289764Sglebius /* split off centuries first */ 830289764Sglebius sflag = int32_sflag(days); 831289764Sglebius uday = uint32_saturate(int32_to_uint32_2cpl(days), sflag); 832289764Sglebius uday = (4u * uday) | 3u; 833289764Sglebius Q = sflag ^ ((sflag ^ uday) / GREGORIAN_CYCLE_DAYS); 834289764Sglebius uday = uday - Q * GREGORIAN_CYCLE_DAYS; 835289764Sglebius n100 = uint32_2cpl_to_int32(Q); 836289764Sglebius 837289764Sglebius /* Split off years in century -- days >= 0 here, and we're far 838289764Sglebius * away from integer overflow trouble now. */ 839289764Sglebius uday |= 3; 840289764Sglebius n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 841289764Sglebius uday = uday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 842282408Scy 843289764Sglebius /* Assemble the year and day in year */ 844289764Sglebius res.hi = n100 * 100 + n001; 845289764Sglebius res.lo = uday / 4u; 846289764Sglebius 847289764Sglebius /* Eventually set the leap year flag. Note: 0 <= n001 <= 99 and 848289764Sglebius * Q is still the two's complement representation of the 849289764Sglebius * centuries: The modulo 4 ops can be done with masking here. 850289764Sglebius * We also shift the year and the century by one, so the tests 851289764Sglebius * can be done against zero instead of 3. 852275970Scy */ 853289764Sglebius if (isleapyear) 854289764Sglebius *isleapyear = !((n001+1) & 3) 855289764Sglebius && ((n001 != 99) || !((Q+1) & 3)); 856289764Sglebius 857275970Scy return res; 858275970Scy} 859275970Scy 860275970Scy/* 861275970Scy *--------------------------------------------------------------------- 862275970Scy * Given a number of elapsed days in a year and a leap year indicator, 863275970Scy * split the number of elapsed days into the number of elapsed months in 864275970Scy * 'res.hi' and the number of elapsed days of that month in 'res.lo'. 865275970Scy * 866275970Scy * This function will fail and return {-1,-1} if the number of elapsed 867275970Scy * days is not in the valid range! 868275970Scy *--------------------------------------------------------------------- 869275970Scy */ 870275970Scyntpcal_split 871275970Scyntpcal_split_yeardays( 872275970Scy int32_t eyd, 873275970Scy int isleapyear 874275970Scy ) 875275970Scy{ 876275970Scy ntpcal_split res; 877275970Scy const uint16_t *lt; /* month length table */ 878275970Scy 879275970Scy /* check leap year flag and select proper table */ 880275970Scy lt = real_month_table[(isleapyear != 0)]; 881275970Scy if (0 <= eyd && eyd < lt[12]) { 882275970Scy /* get zero-based month by approximation & correction step */ 883275970Scy res.hi = eyd >> 5; /* approx month; might be 1 too low */ 884275970Scy if (lt[res.hi + 1] <= eyd) /* fixup approximative month value */ 885275970Scy res.hi += 1; 886275970Scy res.lo = eyd - lt[res.hi]; 887275970Scy } else { 888275970Scy res.lo = res.hi = -1; 889275970Scy } 890275970Scy 891275970Scy return res; 892275970Scy} 893275970Scy 894275970Scy/* 895275970Scy *--------------------------------------------------------------------- 896275970Scy * Convert a RD into the date part of a 'struct calendar'. 897275970Scy *--------------------------------------------------------------------- 898275970Scy */ 899275970Scyint 900275970Scyntpcal_rd_to_date( 901275970Scy struct calendar *jd, 902275970Scy int32_t rd 903275970Scy ) 904275970Scy{ 905275970Scy ntpcal_split split; 906289764Sglebius int leapy; 907289764Sglebius u_int ymask; 908275970Scy 909282408Scy /* Get day-of-week first. Since rd is signed, the remainder can 910282408Scy * be in the range [-6..+6], but the assignment to an unsigned 911282408Scy * variable maps the negative values to positive values >=7. 912282408Scy * This makes the sign correction look strange, but adding 7 913282408Scy * causes the needed wrap-around into the desired value range of 914282408Scy * zero to six, both inclusive. 915282408Scy */ 916289764Sglebius jd->weekday = rd % DAYSPERWEEK; 917289764Sglebius if (jd->weekday >= DAYSPERWEEK) /* weekday is unsigned! */ 918289764Sglebius jd->weekday += DAYSPERWEEK; 919275970Scy 920289764Sglebius split = ntpcal_split_eradays(rd - 1, &leapy); 921289764Sglebius /* Get year and day-of-year, with overflow check. If any of the 922289764Sglebius * upper 16 bits is set after shifting to unity-based years, we 923289764Sglebius * will have an overflow when converting to an unsigned 16bit 924289764Sglebius * year. Shifting to the right is OK here, since it does not 925289764Sglebius * matter if the shift is logic or arithmetic. 926289764Sglebius */ 927289764Sglebius split.hi += 1; 928289764Sglebius ymask = 0u - ((split.hi >> 16) == 0); 929289764Sglebius jd->year = (uint16_t)(split.hi & ymask); 930275970Scy jd->yearday = (uint16_t)split.lo + 1; 931275970Scy 932275970Scy /* convert to month and mday */ 933289764Sglebius split = ntpcal_split_yeardays(split.lo, leapy); 934275970Scy jd->month = (uint8_t)split.hi + 1; 935275970Scy jd->monthday = (uint8_t)split.lo + 1; 936275970Scy 937289764Sglebius return ymask ? leapy : -1; 938275970Scy} 939275970Scy 940275970Scy/* 941275970Scy *--------------------------------------------------------------------- 942275970Scy * Convert a RD into the date part of a 'struct tm'. 943275970Scy *--------------------------------------------------------------------- 944275970Scy */ 945275970Scyint 946275970Scyntpcal_rd_to_tm( 947275970Scy struct tm *utm, 948275970Scy int32_t rd 949275970Scy ) 950275970Scy{ 951275970Scy ntpcal_split split; 952289764Sglebius int leapy; 953275970Scy 954275970Scy /* get day-of-week first */ 955289764Sglebius utm->tm_wday = rd % DAYSPERWEEK; 956275970Scy if (utm->tm_wday < 0) 957289764Sglebius utm->tm_wday += DAYSPERWEEK; 958275970Scy 959275970Scy /* get year and day-of-year */ 960289764Sglebius split = ntpcal_split_eradays(rd - 1, &leapy); 961275970Scy utm->tm_year = split.hi - 1899; 962275970Scy utm->tm_yday = split.lo; /* 0-based */ 963275970Scy 964275970Scy /* convert to month and mday */ 965289764Sglebius split = ntpcal_split_yeardays(split.lo, leapy); 966275970Scy utm->tm_mon = split.hi; /* 0-based */ 967275970Scy utm->tm_mday = split.lo + 1; /* 1-based */ 968275970Scy 969289764Sglebius return leapy; 970275970Scy} 971275970Scy 972275970Scy/* 973275970Scy *--------------------------------------------------------------------- 974275970Scy * Take a value of seconds since midnight and split it into hhmmss in a 975275970Scy * 'struct calendar'. 976275970Scy *--------------------------------------------------------------------- 977275970Scy */ 978275970Scyint32_t 979275970Scyntpcal_daysec_to_date( 980275970Scy struct calendar *jd, 981275970Scy int32_t sec 982275970Scy ) 983275970Scy{ 984275970Scy int32_t days; 985275970Scy int ts[3]; 986282408Scy 987275970Scy days = priv_timesplit(ts, sec); 988275970Scy jd->hour = (uint8_t)ts[0]; 989275970Scy jd->minute = (uint8_t)ts[1]; 990275970Scy jd->second = (uint8_t)ts[2]; 991275970Scy 992275970Scy return days; 993275970Scy} 994275970Scy 995275970Scy/* 996275970Scy *--------------------------------------------------------------------- 997275970Scy * Take a value of seconds since midnight and split it into hhmmss in a 998275970Scy * 'struct tm'. 999275970Scy *--------------------------------------------------------------------- 1000275970Scy */ 1001275970Scyint32_t 1002275970Scyntpcal_daysec_to_tm( 1003275970Scy struct tm *utm, 1004275970Scy int32_t sec 1005275970Scy ) 1006275970Scy{ 1007275970Scy int32_t days; 1008275970Scy int32_t ts[3]; 1009282408Scy 1010275970Scy days = priv_timesplit(ts, sec); 1011275970Scy utm->tm_hour = ts[0]; 1012275970Scy utm->tm_min = ts[1]; 1013275970Scy utm->tm_sec = ts[2]; 1014275970Scy 1015275970Scy return days; 1016275970Scy} 1017275970Scy 1018275970Scy/* 1019275970Scy *--------------------------------------------------------------------- 1020275970Scy * take a split representation for day/second-of-day and day offset 1021275970Scy * and convert it to a 'struct calendar'. The seconds will be normalised 1022275970Scy * into the range of a day, and the day will be adjusted accordingly. 1023275970Scy * 1024275970Scy * returns >0 if the result is in a leap year, 0 if in a regular 1025275970Scy * year and <0 if the result did not fit into the calendar struct. 1026275970Scy *--------------------------------------------------------------------- 1027275970Scy */ 1028275970Scyint 1029275970Scyntpcal_daysplit_to_date( 1030275970Scy struct calendar *jd, 1031275970Scy const ntpcal_split *ds, 1032275970Scy int32_t dof 1033275970Scy ) 1034275970Scy{ 1035275970Scy dof += ntpcal_daysec_to_date(jd, ds->lo); 1036275970Scy return ntpcal_rd_to_date(jd, ds->hi + dof); 1037275970Scy} 1038275970Scy 1039275970Scy/* 1040275970Scy *--------------------------------------------------------------------- 1041275970Scy * take a split representation for day/second-of-day and day offset 1042275970Scy * and convert it to a 'struct tm'. The seconds will be normalised 1043275970Scy * into the range of a day, and the day will be adjusted accordingly. 1044275970Scy * 1045275970Scy * returns 1 if the result is in a leap year and zero if in a regular 1046275970Scy * year. 1047275970Scy *--------------------------------------------------------------------- 1048275970Scy */ 1049275970Scyint 1050275970Scyntpcal_daysplit_to_tm( 1051275970Scy struct tm *utm, 1052275970Scy const ntpcal_split *ds , 1053275970Scy int32_t dof 1054275970Scy ) 1055275970Scy{ 1056275970Scy dof += ntpcal_daysec_to_tm(utm, ds->lo); 1057275970Scy 1058275970Scy return ntpcal_rd_to_tm(utm, ds->hi + dof); 1059275970Scy} 1060275970Scy 1061275970Scy/* 1062275970Scy *--------------------------------------------------------------------- 1063275970Scy * Take a UN*X time and convert to a calendar structure. 1064275970Scy *--------------------------------------------------------------------- 1065275970Scy */ 1066275970Scyint 1067275970Scyntpcal_time_to_date( 1068275970Scy struct calendar *jd, 1069275970Scy const vint64 *ts 1070275970Scy ) 1071275970Scy{ 1072275970Scy ntpcal_split ds; 1073275970Scy 1074275970Scy ds = ntpcal_daysplit(ts); 1075275970Scy ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1076275970Scy ds.hi += DAY_UNIX_STARTS; 1077275970Scy 1078275970Scy return ntpcal_rd_to_date(jd, ds.hi); 1079275970Scy} 1080275970Scy 1081275970Scy 1082275970Scy/* 1083309007Sdelphij * ==================================================================== 1084275970Scy * 1085275970Scy * merging composite entities 1086275970Scy * 1087309007Sdelphij * ==================================================================== 1088275970Scy */ 1089275970Scy 1090275970Scy/* 1091275970Scy *--------------------------------------------------------------------- 1092275970Scy * Merge a number of days and a number of seconds into seconds, 1093275970Scy * expressed in 64 bits to avoid overflow. 1094275970Scy *--------------------------------------------------------------------- 1095275970Scy */ 1096275970Scyvint64 1097275970Scyntpcal_dayjoin( 1098275970Scy int32_t days, 1099275970Scy int32_t secs 1100275970Scy ) 1101275970Scy{ 1102275970Scy vint64 res; 1103275970Scy 1104289764Sglebius# if defined(HAVE_INT64) 1105275970Scy 1106275970Scy res.q_s = days; 1107275970Scy res.q_s *= SECSPERDAY; 1108275970Scy res.q_s += secs; 1109275970Scy 1110289764Sglebius# else 1111275970Scy 1112275970Scy uint32_t p1, p2; 1113275970Scy int isneg; 1114275970Scy 1115275970Scy /* 1116275970Scy * res = days *86400 + secs, using manual 16/32 bit 1117275970Scy * multiplications and shifts. 1118275970Scy */ 1119275970Scy isneg = (days < 0); 1120275970Scy if (isneg) 1121275970Scy days = -days; 1122275970Scy 1123275970Scy /* assemble days * 675 */ 1124275970Scy res.D_s.lo = (days & 0xFFFF) * 675u; 1125275970Scy res.D_s.hi = 0; 1126275970Scy p1 = (days >> 16) * 675u; 1127275970Scy p2 = p1 >> 16; 1128275970Scy p1 = p1 << 16; 1129275970Scy M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1130275970Scy 1131275970Scy /* mul by 128, using shift */ 1132275970Scy res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25); 1133275970Scy res.D_s.lo = (res.D_s.lo << 7); 1134275970Scy 1135275970Scy /* fix sign */ 1136275970Scy if (isneg) 1137275970Scy M_NEG(res.D_s.hi, res.D_s.lo); 1138282408Scy 1139275970Scy /* properly add seconds */ 1140275970Scy p2 = 0; 1141275970Scy if (secs < 0) { 1142275970Scy p1 = (uint32_t)-secs; 1143275970Scy M_NEG(p2, p1); 1144275970Scy } else { 1145275970Scy p1 = (uint32_t)secs; 1146275970Scy } 1147275970Scy M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1148275970Scy 1149289764Sglebius# endif 1150275970Scy 1151275970Scy return res; 1152275970Scy} 1153275970Scy 1154275970Scy/* 1155275970Scy *--------------------------------------------------------------------- 1156289764Sglebius * get leap years since epoch in elapsed years 1157275970Scy *--------------------------------------------------------------------- 1158275970Scy */ 1159275970Scyint32_t 1160289764Sglebiusntpcal_leapyears_in_years( 1161275970Scy int32_t years 1162275970Scy ) 1163275970Scy{ 1164289764Sglebius /* We use the in-out-in algorithm here, using the one's 1165289764Sglebius * complement division trick for negative numbers. The chained 1166289764Sglebius * division sequence by 4/25/4 gives the compiler the chance to 1167289764Sglebius * get away with only one true division and doing shifts otherwise. 1168289764Sglebius */ 1169275970Scy 1170289764Sglebius uint32_t sflag, sum, uyear; 1171275970Scy 1172289764Sglebius sflag = int32_sflag(years); 1173289764Sglebius uyear = int32_to_uint32_2cpl(years); 1174289764Sglebius uyear ^= sflag; 1175289764Sglebius 1176289764Sglebius sum = (uyear /= 4u); /* 4yr rule --> IN */ 1177289764Sglebius sum -= (uyear /= 25u); /* 100yr rule --> OUT */ 1178289764Sglebius sum += (uyear /= 4u); /* 400yr rule --> IN */ 1179289764Sglebius 1180289764Sglebius /* Thanks to the alternation of IN/OUT/IN we can do the sum 1181289764Sglebius * directly and have a single one's complement operation 1182289764Sglebius * here. (Only if the years are negative, of course.) Otherwise 1183289764Sglebius * the one's complement would have to be done when 1184289764Sglebius * adding/subtracting the terms. 1185275970Scy */ 1186289764Sglebius return uint32_2cpl_to_int32(sflag ^ sum); 1187275970Scy} 1188275970Scy 1189275970Scy/* 1190275970Scy *--------------------------------------------------------------------- 1191289764Sglebius * Convert elapsed years in Era into elapsed days in Era. 1192289764Sglebius *--------------------------------------------------------------------- 1193289764Sglebius */ 1194289764Sglebiusint32_t 1195289764Sglebiusntpcal_days_in_years( 1196289764Sglebius int32_t years 1197289764Sglebius ) 1198289764Sglebius{ 1199289764Sglebius return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years); 1200289764Sglebius} 1201289764Sglebius 1202289764Sglebius/* 1203289764Sglebius *--------------------------------------------------------------------- 1204275970Scy * Convert a number of elapsed month in a year into elapsed days in year. 1205275970Scy * 1206275970Scy * The month will be normalized, and 'res.hi' will contain the 1207275970Scy * excessive years that must be considered when converting the years, 1208275970Scy * while 'res.lo' will contain the number of elapsed days since start 1209275970Scy * of the year. 1210275970Scy * 1211275970Scy * This code uses the shifted-month-approach to convert month to days, 1212275970Scy * because then there is no need to have explicit leap year 1213275970Scy * information. The slight disadvantage is that for most month values 1214275970Scy * the result is a negative value, and the year excess is one; the 1215275970Scy * conversion is then simply based on the start of the following year. 1216275970Scy *--------------------------------------------------------------------- 1217275970Scy */ 1218275970Scyntpcal_split 1219275970Scyntpcal_days_in_months( 1220275970Scy int32_t m 1221275970Scy ) 1222275970Scy{ 1223275970Scy ntpcal_split res; 1224275970Scy 1225289764Sglebius /* Add ten months and correct if needed. (It likely is...) */ 1226289764Sglebius res.lo = m + 10; 1227289764Sglebius res.hi = (res.lo >= 12); 1228289764Sglebius if (res.hi) 1229289764Sglebius res.lo -= 12; 1230289764Sglebius 1231289764Sglebius /* if still out of range, normalise by floor division ... */ 1232275970Scy if (res.lo < 0 || res.lo >= 12) { 1233289764Sglebius uint32_t mu, Q, sflag; 1234289764Sglebius sflag = int32_sflag(res.lo); 1235289764Sglebius mu = int32_to_uint32_2cpl(res.lo); 1236289764Sglebius Q = sflag ^ ((sflag ^ mu) / 12u); 1237289764Sglebius res.hi += uint32_2cpl_to_int32(Q); 1238289764Sglebius res.lo = mu - Q * 12u; 1239275970Scy } 1240289764Sglebius 1241275970Scy /* get cummulated days in year with unshift */ 1242275970Scy res.lo = shift_month_table[res.lo] - 306; 1243275970Scy 1244275970Scy return res; 1245275970Scy} 1246275970Scy 1247275970Scy/* 1248275970Scy *--------------------------------------------------------------------- 1249275970Scy * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1250275970Scy * days in Gregorian epoch. 1251275970Scy * 1252275970Scy * If you want to convert years and days-of-year, just give a month of 1253275970Scy * zero. 1254275970Scy *--------------------------------------------------------------------- 1255275970Scy */ 1256275970Scyint32_t 1257275970Scyntpcal_edate_to_eradays( 1258275970Scy int32_t years, 1259275970Scy int32_t mons, 1260275970Scy int32_t mdays 1261275970Scy ) 1262275970Scy{ 1263275970Scy ntpcal_split tmp; 1264275970Scy int32_t res; 1265275970Scy 1266275970Scy if (mons) { 1267275970Scy tmp = ntpcal_days_in_months(mons); 1268275970Scy res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; 1269275970Scy } else 1270275970Scy res = ntpcal_days_in_years(years); 1271275970Scy res += mdays; 1272275970Scy 1273275970Scy return res; 1274275970Scy} 1275275970Scy 1276275970Scy/* 1277275970Scy *--------------------------------------------------------------------- 1278275970Scy * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1279275970Scy * days in year. 1280275970Scy * 1281309007Sdelphij * Note: This will give the true difference to the start of the given 1282309007Sdelphij * year, even if months & days are off-scale. 1283275970Scy *--------------------------------------------------------------------- 1284275970Scy */ 1285275970Scyint32_t 1286275970Scyntpcal_edate_to_yeardays( 1287275970Scy int32_t years, 1288275970Scy int32_t mons, 1289275970Scy int32_t mdays 1290275970Scy ) 1291275970Scy{ 1292275970Scy ntpcal_split tmp; 1293275970Scy 1294275970Scy if (0 <= mons && mons < 12) { 1295275970Scy years += 1; 1296275970Scy mdays += real_month_table[is_leapyear(years)][mons]; 1297275970Scy } else { 1298275970Scy tmp = ntpcal_days_in_months(mons); 1299275970Scy mdays += tmp.lo 1300275970Scy + ntpcal_days_in_years(years + tmp.hi) 1301275970Scy - ntpcal_days_in_years(years); 1302275970Scy } 1303275970Scy 1304275970Scy return mdays; 1305275970Scy} 1306275970Scy 1307275970Scy/* 1308275970Scy *--------------------------------------------------------------------- 1309275970Scy * Convert elapsed days and the hour/minute/second information into 1310275970Scy * total seconds. 1311275970Scy * 1312275970Scy * If 'isvalid' is not NULL, do a range check on the time specification 1313275970Scy * and tell if the time input is in the normal range, permitting for a 1314275970Scy * single leapsecond. 1315275970Scy *--------------------------------------------------------------------- 1316275970Scy */ 1317275970Scyint32_t 1318275970Scyntpcal_etime_to_seconds( 1319275970Scy int32_t hours, 1320275970Scy int32_t minutes, 1321275970Scy int32_t seconds 1322275970Scy ) 1323275970Scy{ 1324275970Scy int32_t res; 1325275970Scy 1326275970Scy res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; 1327275970Scy 1328275970Scy return res; 1329275970Scy} 1330275970Scy 1331275970Scy/* 1332275970Scy *--------------------------------------------------------------------- 1333275970Scy * Convert the date part of a 'struct tm' (that is, year, month, 1334275970Scy * day-of-month) into the RD of that day. 1335275970Scy *--------------------------------------------------------------------- 1336275970Scy */ 1337275970Scyint32_t 1338275970Scyntpcal_tm_to_rd( 1339275970Scy const struct tm *utm 1340275970Scy ) 1341275970Scy{ 1342275970Scy return ntpcal_edate_to_eradays(utm->tm_year + 1899, 1343275970Scy utm->tm_mon, 1344275970Scy utm->tm_mday - 1) + 1; 1345275970Scy} 1346275970Scy 1347275970Scy/* 1348275970Scy *--------------------------------------------------------------------- 1349275970Scy * Convert the date part of a 'struct calendar' (that is, year, month, 1350275970Scy * day-of-month) into the RD of that day. 1351275970Scy *--------------------------------------------------------------------- 1352275970Scy */ 1353275970Scyint32_t 1354275970Scyntpcal_date_to_rd( 1355275970Scy const struct calendar *jd 1356275970Scy ) 1357275970Scy{ 1358275970Scy return ntpcal_edate_to_eradays((int32_t)jd->year - 1, 1359275970Scy (int32_t)jd->month - 1, 1360275970Scy (int32_t)jd->monthday - 1) + 1; 1361275970Scy} 1362275970Scy 1363275970Scy/* 1364275970Scy *--------------------------------------------------------------------- 1365275970Scy * convert a year number to rata die of year start 1366275970Scy *--------------------------------------------------------------------- 1367275970Scy */ 1368275970Scyint32_t 1369275970Scyntpcal_year_to_ystart( 1370275970Scy int32_t year 1371275970Scy ) 1372275970Scy{ 1373275970Scy return ntpcal_days_in_years(year - 1) + 1; 1374275970Scy} 1375275970Scy 1376275970Scy/* 1377275970Scy *--------------------------------------------------------------------- 1378275970Scy * For a given RD, get the RD of the associated year start, 1379275970Scy * that is, the RD of the last January,1st on or before that day. 1380275970Scy *--------------------------------------------------------------------- 1381275970Scy */ 1382275970Scyint32_t 1383275970Scyntpcal_rd_to_ystart( 1384275970Scy int32_t rd 1385275970Scy ) 1386275970Scy{ 1387275970Scy /* 1388275970Scy * Rather simple exercise: split the day number into elapsed 1389275970Scy * years and elapsed days, then remove the elapsed days from the 1390275970Scy * input value. Nice'n sweet... 1391275970Scy */ 1392275970Scy return rd - ntpcal_split_eradays(rd - 1, NULL).lo; 1393275970Scy} 1394275970Scy 1395275970Scy/* 1396275970Scy *--------------------------------------------------------------------- 1397275970Scy * For a given RD, get the RD of the associated month start. 1398275970Scy *--------------------------------------------------------------------- 1399275970Scy */ 1400275970Scyint32_t 1401275970Scyntpcal_rd_to_mstart( 1402275970Scy int32_t rd 1403275970Scy ) 1404275970Scy{ 1405275970Scy ntpcal_split split; 1406275970Scy int leaps; 1407275970Scy 1408275970Scy split = ntpcal_split_eradays(rd - 1, &leaps); 1409275970Scy split = ntpcal_split_yeardays(split.lo, leaps); 1410275970Scy 1411275970Scy return rd - split.lo; 1412275970Scy} 1413275970Scy 1414275970Scy/* 1415275970Scy *--------------------------------------------------------------------- 1416275970Scy * take a 'struct calendar' and get the seconds-of-day from it. 1417275970Scy *--------------------------------------------------------------------- 1418275970Scy */ 1419275970Scyint32_t 1420275970Scyntpcal_date_to_daysec( 1421275970Scy const struct calendar *jd 1422275970Scy ) 1423275970Scy{ 1424275970Scy return ntpcal_etime_to_seconds(jd->hour, jd->minute, 1425275970Scy jd->second); 1426275970Scy} 1427275970Scy 1428275970Scy/* 1429275970Scy *--------------------------------------------------------------------- 1430275970Scy * take a 'struct tm' and get the seconds-of-day from it. 1431275970Scy *--------------------------------------------------------------------- 1432275970Scy */ 1433275970Scyint32_t 1434275970Scyntpcal_tm_to_daysec( 1435275970Scy const struct tm *utm 1436275970Scy ) 1437275970Scy{ 1438275970Scy return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, 1439275970Scy utm->tm_sec); 1440275970Scy} 1441275970Scy 1442275970Scy/* 1443275970Scy *--------------------------------------------------------------------- 1444275970Scy * take a 'struct calendar' and convert it to a 'time_t' 1445275970Scy *--------------------------------------------------------------------- 1446275970Scy */ 1447275970Scytime_t 1448275970Scyntpcal_date_to_time( 1449275970Scy const struct calendar *jd 1450275970Scy ) 1451275970Scy{ 1452275970Scy vint64 join; 1453275970Scy int32_t days, secs; 1454275970Scy 1455275970Scy days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; 1456275970Scy secs = ntpcal_date_to_daysec(jd); 1457275970Scy join = ntpcal_dayjoin(days, secs); 1458275970Scy 1459275970Scy return vint64_to_time(&join); 1460275970Scy} 1461275970Scy 1462275970Scy 1463275970Scy/* 1464309007Sdelphij * ==================================================================== 1465275970Scy * 1466275970Scy * extended and unchecked variants of caljulian/caltontp 1467275970Scy * 1468309007Sdelphij * ==================================================================== 1469275970Scy */ 1470275970Scyint 1471275970Scyntpcal_ntp64_to_date( 1472275970Scy struct calendar *jd, 1473275970Scy const vint64 *ntp 1474275970Scy ) 1475275970Scy{ 1476275970Scy ntpcal_split ds; 1477282408Scy 1478275970Scy ds = ntpcal_daysplit(ntp); 1479275970Scy ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1480275970Scy 1481275970Scy return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); 1482275970Scy} 1483275970Scy 1484275970Scyint 1485275970Scyntpcal_ntp_to_date( 1486275970Scy struct calendar *jd, 1487275970Scy uint32_t ntp, 1488275970Scy const time_t *piv 1489275970Scy ) 1490275970Scy{ 1491275970Scy vint64 ntp64; 1492282408Scy 1493275970Scy /* 1494275970Scy * Unfold ntp time around current time into NTP domain. Split 1495275970Scy * into days and seconds, shift days into CE domain and 1496275970Scy * process the parts. 1497275970Scy */ 1498275970Scy ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1499275970Scy return ntpcal_ntp64_to_date(jd, &ntp64); 1500275970Scy} 1501275970Scy 1502275970Scy 1503275970Scyvint64 1504275970Scyntpcal_date_to_ntp64( 1505275970Scy const struct calendar *jd 1506275970Scy ) 1507275970Scy{ 1508275970Scy /* 1509275970Scy * Convert date to NTP. Ignore yearday, use d/m/y only. 1510275970Scy */ 1511275970Scy return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, 1512275970Scy ntpcal_date_to_daysec(jd)); 1513275970Scy} 1514275970Scy 1515275970Scy 1516275970Scyuint32_t 1517275970Scyntpcal_date_to_ntp( 1518275970Scy const struct calendar *jd 1519275970Scy ) 1520275970Scy{ 1521275970Scy /* 1522275970Scy * Get lower half of 64-bit NTP timestamp from date/time. 1523275970Scy */ 1524275970Scy return ntpcal_date_to_ntp64(jd).d_s.lo; 1525275970Scy} 1526275970Scy 1527275970Scy 1528275970Scy 1529275970Scy/* 1530309007Sdelphij * ==================================================================== 1531275970Scy * 1532275970Scy * day-of-week calculations 1533275970Scy * 1534309007Sdelphij * ==================================================================== 1535275970Scy */ 1536275970Scy/* 1537275970Scy * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, 1538275970Scy * greater-or equal, closest, less-or-equal or less-than the given RDN 1539275970Scy * and denotes the given day-of-week 1540275970Scy */ 1541275970Scyint32_t 1542275970Scyntpcal_weekday_gt( 1543275970Scy int32_t rdn, 1544275970Scy int32_t dow 1545275970Scy ) 1546275970Scy{ 1547275970Scy return ntpcal_periodic_extend(rdn+1, dow, 7); 1548275970Scy} 1549275970Scy 1550275970Scyint32_t 1551275970Scyntpcal_weekday_ge( 1552275970Scy int32_t rdn, 1553275970Scy int32_t dow 1554275970Scy ) 1555275970Scy{ 1556275970Scy return ntpcal_periodic_extend(rdn, dow, 7); 1557275970Scy} 1558275970Scy 1559275970Scyint32_t 1560275970Scyntpcal_weekday_close( 1561275970Scy int32_t rdn, 1562275970Scy int32_t dow 1563275970Scy ) 1564275970Scy{ 1565275970Scy return ntpcal_periodic_extend(rdn-3, dow, 7); 1566275970Scy} 1567275970Scy 1568275970Scyint32_t 1569275970Scyntpcal_weekday_le( 1570275970Scy int32_t rdn, 1571275970Scy int32_t dow 1572275970Scy ) 1573275970Scy{ 1574275970Scy return ntpcal_periodic_extend(rdn, dow, -7); 1575275970Scy} 1576275970Scy 1577275970Scyint32_t 1578275970Scyntpcal_weekday_lt( 1579275970Scy int32_t rdn, 1580275970Scy int32_t dow 1581275970Scy ) 1582275970Scy{ 1583275970Scy return ntpcal_periodic_extend(rdn-1, dow, -7); 1584275970Scy} 1585275970Scy 1586275970Scy/* 1587309007Sdelphij * ==================================================================== 1588275970Scy * 1589275970Scy * ISO week-calendar conversions 1590275970Scy * 1591275970Scy * The ISO8601 calendar defines a calendar of years, weeks and weekdays. 1592275970Scy * It is related to the Gregorian calendar, and a ISO year starts at the 1593275970Scy * Monday closest to Jan,1st of the corresponding Gregorian year. A ISO 1594275970Scy * calendar year has always 52 or 53 weeks, and like the Grogrian 1595275970Scy * calendar the ISO8601 calendar repeats itself every 400 years, or 1596275970Scy * 146097 days, or 20871 weeks. 1597275970Scy * 1598275970Scy * While it is possible to write ISO calendar functions based on the 1599275970Scy * Gregorian calendar functions, the following implementation takes a 1600275970Scy * different approach, based directly on years and weeks. 1601275970Scy * 1602275970Scy * Analysis of the tabulated data shows that it is not possible to 1603275970Scy * interpolate from years to weeks over a full 400 year range; cyclic 1604275970Scy * shifts over 400 years do not provide a solution here. But it *is* 1605275970Scy * possible to interpolate over every single century of the 400-year 1606275970Scy * cycle. (The centennial leap year rule seems to be the culprit here.) 1607275970Scy * 1608275970Scy * It can be shown that a conversion from years to weeks can be done 1609275970Scy * using a linear transformation of the form 1610275970Scy * 1611275970Scy * w = floor( y * a + b ) 1612275970Scy * 1613275970Scy * where the slope a must hold to 1614275970Scy * 1615275970Scy * 52.1780821918 <= a < 52.1791044776 1616275970Scy * 1617275970Scy * and b must be chosen according to the selected slope and the number 1618275970Scy * of the century in a 400-year period. 1619275970Scy * 1620275970Scy * The inverse calculation can also be done in this way. Careful scaling 1621275970Scy * provides an unlimited set of integer coefficients a,k,b that enable 1622275970Scy * us to write the calulation in the form 1623275970Scy * 1624275970Scy * w = (y * a + b ) / k 1625275970Scy * y = (w * a' + b') / k' 1626275970Scy * 1627275970Scy * In this implementation the values of k and k' are chosen to be 1628275970Scy * smallest possible powers of two, so the division can be implemented 1629275970Scy * as shifts if the optimiser chooses to do so. 1630275970Scy * 1631309007Sdelphij * ==================================================================== 1632275970Scy */ 1633275970Scy 1634275970Scy/* 1635275970Scy * Given a number of elapsed (ISO-)years since the begin of the 1636275970Scy * christian era, return the number of elapsed weeks corresponding to 1637275970Scy * the number of years. 1638275970Scy */ 1639275970Scyint32_t 1640275970Scyisocal_weeks_in_years( 1641275970Scy int32_t years 1642275970Scy ) 1643289764Sglebius{ 1644275970Scy /* 1645275970Scy * use: w = (y * 53431 + b[c]) / 1024 as interpolation 1646275970Scy */ 1647289764Sglebius static const uint16_t bctab[4] = { 157, 449, 597, 889 }; 1648275970Scy 1649289764Sglebius int32_t cs, cw; 1650289764Sglebius uint32_t cc, ci, yu, sflag; 1651275970Scy 1652289764Sglebius sflag = int32_sflag(years); 1653289764Sglebius yu = int32_to_uint32_2cpl(years); 1654289764Sglebius 1655289764Sglebius /* split off centuries, using floor division */ 1656289764Sglebius cc = sflag ^ ((sflag ^ yu) / 100u); 1657289764Sglebius yu -= cc * 100u; 1658275970Scy 1659289764Sglebius /* calculate century cycles shift and cycle index: 1660289764Sglebius * Assuming a century is 5217 weeks, we have to add a cycle 1661289764Sglebius * shift that is 3 for every 4 centuries, because 3 of the four 1662289764Sglebius * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual 1663289764Sglebius * correction, and the second century is the defective one. 1664289764Sglebius * 1665289764Sglebius * Needs floor division by 4, which is done with masking and 1666289764Sglebius * shifting. 1667275970Scy */ 1668289764Sglebius ci = cc * 3u + 1; 1669289764Sglebius cs = uint32_2cpl_to_int32(sflag ^ ((sflag ^ ci) / 4u)); 1670289764Sglebius ci = ci % 4u; 1671289764Sglebius 1672289764Sglebius /* Get weeks in century. Can use plain division here as all ops 1673289764Sglebius * are >= 0, and let the compiler sort out the possible 1674289764Sglebius * optimisations. 1675289764Sglebius */ 1676289764Sglebius cw = (yu * 53431u + bctab[ci]) / 1024u; 1677275970Scy 1678289764Sglebius return uint32_2cpl_to_int32(cc) * 5217 + cs + cw; 1679275970Scy} 1680275970Scy 1681275970Scy/* 1682275970Scy * Given a number of elapsed weeks since the begin of the christian 1683275970Scy * era, split this number into the number of elapsed years in res.hi 1684275970Scy * and the excessive number of weeks in res.lo. (That is, res.lo is 1685275970Scy * the number of elapsed weeks in the remaining partial year.) 1686275970Scy */ 1687275970Scyntpcal_split 1688275970Scyisocal_split_eraweeks( 1689275970Scy int32_t weeks 1690275970Scy ) 1691275970Scy{ 1692275970Scy /* 1693275970Scy * use: y = (w * 157 + b[c]) / 8192 as interpolation 1694275970Scy */ 1695289764Sglebius 1696289764Sglebius static const uint16_t bctab[4] = { 85, 130, 17, 62 }; 1697289764Sglebius 1698275970Scy ntpcal_split res; 1699289764Sglebius int32_t cc, ci; 1700289764Sglebius uint32_t sw, cy, Q, sflag; 1701275970Scy 1702289764Sglebius /* Use two fast cycle-split divisions here. This is again 1703289764Sglebius * susceptible to internal overflow, so we check the range. This 1704289764Sglebius * still permits more than +/-20 million years, so this is 1705289764Sglebius * likely a pure academical problem. 1706289764Sglebius * 1707289764Sglebius * We want to execute '(weeks * 4 + 2) /% 20871' under floor 1708289764Sglebius * division rules in the first step. 1709275970Scy */ 1710289764Sglebius sflag = int32_sflag(weeks); 1711289764Sglebius sw = uint32_saturate(int32_to_uint32_2cpl(weeks), sflag); 1712289764Sglebius sw = 4u * sw + 2; 1713289764Sglebius Q = sflag ^ ((sflag ^ sw) / GREGORIAN_CYCLE_WEEKS); 1714289764Sglebius sw -= Q * GREGORIAN_CYCLE_WEEKS; 1715289764Sglebius ci = Q % 4u; 1716289764Sglebius cc = uint32_2cpl_to_int32(Q); 1717275970Scy 1718289764Sglebius /* Split off years; sw >= 0 here! The scaled weeks in the years 1719289764Sglebius * are scaled up by 157 afterwards. 1720289764Sglebius */ 1721289764Sglebius sw = (sw / 4u) * 157u + bctab[ci]; 1722289764Sglebius cy = sw / 8192u; /* ws >> 13 , let the compiler sort it out */ 1723289764Sglebius sw = sw % 8192u; /* ws & 8191, let the compiler sort it out */ 1724289764Sglebius 1725289764Sglebius /* assemble elapsed years and downscale the elapsed weeks in 1726289764Sglebius * the year. 1727275970Scy */ 1728289764Sglebius res.hi = 100*cc + cy; 1729289764Sglebius res.lo = sw / 157u; 1730282408Scy 1731275970Scy return res; 1732275970Scy} 1733275970Scy 1734275970Scy/* 1735275970Scy * Given a second in the NTP time scale and a pivot, expand the NTP 1736275970Scy * time stamp around the pivot and convert into an ISO calendar time 1737275970Scy * stamp. 1738275970Scy */ 1739275970Scyint 1740275970Scyisocal_ntp64_to_date( 1741275970Scy struct isodate *id, 1742275970Scy const vint64 *ntp 1743275970Scy ) 1744275970Scy{ 1745275970Scy ntpcal_split ds; 1746275970Scy int32_t ts[3]; 1747289764Sglebius uint32_t uw, ud, sflag; 1748282408Scy 1749275970Scy /* 1750275970Scy * Split NTP time into days and seconds, shift days into CE 1751275970Scy * domain and process the parts. 1752275970Scy */ 1753275970Scy ds = ntpcal_daysplit(ntp); 1754275970Scy 1755275970Scy /* split time part */ 1756275970Scy ds.hi += priv_timesplit(ts, ds.lo); 1757275970Scy id->hour = (uint8_t)ts[0]; 1758275970Scy id->minute = (uint8_t)ts[1]; 1759275970Scy id->second = (uint8_t)ts[2]; 1760275970Scy 1761289764Sglebius /* split days into days and weeks, using floor division in unsigned */ 1762289764Sglebius ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */ 1763289764Sglebius sflag = int32_sflag(ds.hi); 1764289764Sglebius ud = int32_to_uint32_2cpl(ds.hi); 1765289764Sglebius uw = sflag ^ ((sflag ^ ud) / DAYSPERWEEK); 1766289764Sglebius ud -= uw * DAYSPERWEEK; 1767289764Sglebius ds.hi = uint32_2cpl_to_int32(uw); 1768289764Sglebius ds.lo = ud; 1769289764Sglebius 1770275970Scy id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ 1771275970Scy 1772289764Sglebius /* get year and week in year */ 1773275970Scy ds = isocal_split_eraweeks(ds.hi); /* elapsed years&week*/ 1774275970Scy id->year = (uint16_t)ds.hi + 1; /* shift to current */ 1775275970Scy id->week = (uint8_t )ds.lo + 1; 1776275970Scy 1777280849Scy return (ds.hi >= 0 && ds.hi < 0x0000FFFF); 1778275970Scy} 1779275970Scy 1780275970Scyint 1781275970Scyisocal_ntp_to_date( 1782275970Scy struct isodate *id, 1783275970Scy uint32_t ntp, 1784275970Scy const time_t *piv 1785275970Scy ) 1786275970Scy{ 1787275970Scy vint64 ntp64; 1788282408Scy 1789275970Scy /* 1790275970Scy * Unfold ntp time around current time into NTP domain, then 1791275970Scy * convert the full time stamp. 1792275970Scy */ 1793275970Scy ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1794275970Scy return isocal_ntp64_to_date(id, &ntp64); 1795275970Scy} 1796275970Scy 1797275970Scy/* 1798275970Scy * Convert a ISO date spec into a second in the NTP time scale, 1799275970Scy * properly truncated to 32 bit. 1800275970Scy */ 1801275970Scyvint64 1802275970Scyisocal_date_to_ntp64( 1803275970Scy const struct isodate *id 1804275970Scy ) 1805275970Scy{ 1806275970Scy int32_t weeks, days, secs; 1807275970Scy 1808275970Scy weeks = isocal_weeks_in_years((int32_t)id->year - 1) 1809275970Scy + (int32_t)id->week - 1; 1810275970Scy days = weeks * 7 + (int32_t)id->weekday; 1811275970Scy /* days is RDN of ISO date now */ 1812275970Scy secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second); 1813275970Scy 1814275970Scy return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs); 1815275970Scy} 1816275970Scy 1817275970Scyuint32_t 1818275970Scyisocal_date_to_ntp( 1819275970Scy const struct isodate *id 1820275970Scy ) 1821275970Scy{ 1822275970Scy /* 1823275970Scy * Get lower half of 64-bit NTP timestamp from date/time. 1824275970Scy */ 1825275970Scy return isocal_date_to_ntp64(id).d_s.lo; 1826275970Scy} 1827275970Scy 1828275970Scy/* -*-EOF-*- */ 1829