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. 6289997Sglebius * 7289997Sglebius * -------------------------------------------------------------------- 8289997Sglebius * Some notes on the implementation: 9289997Sglebius * 10289997Sglebius * Calendar algorithms thrive on the division operation, which is one of 11289997Sglebius * the slowest numerical operations in any CPU. What saves us here from 12289997Sglebius * abysmal performance is the fact that all divisions are divisions by 13289997Sglebius * constant numbers, and most compilers can do this by a multiplication 14289997Sglebius * operation. But this might not work when using the div/ldiv/lldiv 15289997Sglebius * function family, because many compilers are not able to do inline 16289997Sglebius * expansion of the code with following optimisation for the 17289997Sglebius * constant-divider case. 18289997Sglebius * 19289997Sglebius * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which 20289997Sglebius * are inherently target dependent. Nothing that could not be cured with 21289997Sglebius * autoconf, but still a mess... 22289997Sglebius * 23289997Sglebius * Furthermore, we need floor division in many places. C either leaves 24289997Sglebius * the division behaviour undefined (< C99) or demands truncation to 25289997Sglebius * zero (>= C99), so additional steps are required to make sure the 26289997Sglebius * algorithms work. The {l,ll}div function family is requested to 27289997Sglebius * truncate towards zero, which is also the wrong direction for our 28289997Sglebius * purpose. 29289997Sglebius * 30289997Sglebius * For all this, all divisions by constant are coded manually, even when 31289997Sglebius * there is a joined div/mod operation: The optimiser should sort that 32289997Sglebius * out, if possible. Most of the calculations are done with unsigned 33289997Sglebius * types, explicitely using two's complement arithmetics where 34289997Sglebius * necessary. This minimises the dependecies to compiler and target, 35289997Sglebius * while still giving reasonable to good performance. 36289997Sglebius * 37289997Sglebius * The implementation uses a few tricks that exploit properties of the 38289997Sglebius * two's complement: Floor division on negative dividents can be 39289997Sglebius * executed by using the one's complement of the divident. One's 40289997Sglebius * complement can be easily created using XOR and a mask. 41289997Sglebius * 42289997Sglebius * Finally, check for overflow conditions is minimal. There are only two 43289997Sglebius * calculation steps in the whole calendar that suffer from an internal 44289997Sglebius * overflow, and these conditions are checked: errno is set to EDOM and 45289997Sglebius * the results are clamped/saturated in this case. All other functions 46289997Sglebius * do not suffer from internal overflow and simply return the result 47289997Sglebius * truncated to 32 bits. 48289997Sglebius * 49289997Sglebius * This is a sacrifice made for execution speed. Since a 32-bit day 50289997Sglebius * counter covers +/- 5,879,610 years and the clamp limits the effective 51289997Sglebius * range to +/-2.9 million years, this should not pose a problem here. 52289997Sglebius * 53275970Scy */ 54289997Sglebius 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 64289997Sglebius/* For now, let's take the conservative approach: if the target property 65289997Sglebius * macros are not defined, check a few well-known compiler/architecture 66289997Sglebius * settings. Default is to assume that the representation of signed 67289997Sglebius * integers is unknown and shift-arithmetic-right is not available. 68289997Sglebius */ 69289997Sglebius#ifndef TARGET_HAS_2CPL 70289997Sglebius# if defined(__GNUC__) 71289997Sglebius# if defined(__i386__) || defined(__x86_64__) || defined(__arm__) 72289997Sglebius# define TARGET_HAS_2CPL 1 73289997Sglebius# else 74289997Sglebius# define TARGET_HAS_2CPL 0 75289997Sglebius# endif 76289997Sglebius# elif defined(_MSC_VER) 77289997Sglebius# if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) 78289997Sglebius# define TARGET_HAS_2CPL 1 79289997Sglebius# else 80289997Sglebius# define TARGET_HAS_2CPL 0 81289997Sglebius# endif 82289997Sglebius# else 83289997Sglebius# define TARGET_HAS_2CPL 0 84289997Sglebius# endif 85289997Sglebius#endif 86289997Sglebius 87289997Sglebius#ifndef TARGET_HAS_SAR 88289997Sglebius# define TARGET_HAS_SAR 0 89289997Sglebius#endif 90289997Sglebius 91275970Scy/* 92275970Scy *--------------------------------------------------------------------- 93275970Scy * replacing the 'time()' function 94309008Sdelphij *--------------------------------------------------------------------- 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; 107285612Sdelphij 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 *--------------------------------------------------------------------- 125289997Sglebius * Get sign extension mask and unsigned 2cpl rep for a signed integer 126289997Sglebius *--------------------------------------------------------------------- 127289997Sglebius */ 128289997Sglebius 129289997Sglebiusstatic inline uint32_t 130289997Sglebiusint32_sflag( 131289997Sglebius const int32_t v) 132289997Sglebius{ 133289997Sglebius# if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4 134289997Sglebius 135289997Sglebius /* Let's assume that shift is the fastest way to get the sign 136289997Sglebius * extension of of a signed integer. This might not always be 137289997Sglebius * true, though -- On 8bit CPUs or machines without barrel 138289997Sglebius * shifter this will kill the performance. So we make sure 139289997Sglebius * we do this only if 'int' has at least 4 bytes. 140289997Sglebius */ 141289997Sglebius return (uint32_t)(v >> 31); 142289997Sglebius 143289997Sglebius# else 144289997Sglebius 145289997Sglebius /* This should be a rather generic approach for getting a sign 146289997Sglebius * extension mask... 147289997Sglebius */ 148289997Sglebius return UINT32_C(0) - (uint32_t)(v < 0); 149289997Sglebius 150289997Sglebius# endif 151289997Sglebius} 152289997Sglebius 153289997Sglebiusstatic inline uint32_t 154289997Sglebiusint32_to_uint32_2cpl( 155289997Sglebius const int32_t v) 156289997Sglebius{ 157289997Sglebius uint32_t vu; 158289997Sglebius 159289997Sglebius# if TARGET_HAS_2CPL 160289997Sglebius 161289997Sglebius /* Just copy through the 32 bits from the signed value if we're 162289997Sglebius * on a two's complement target. 163289997Sglebius */ 164289997Sglebius vu = (uint32_t)v; 165289997Sglebius 166289997Sglebius# else 167289997Sglebius 168289997Sglebius /* Convert from signed int to unsigned int two's complement. Do 169289997Sglebius * not make any assumptions about the representation of signed 170289997Sglebius * integers, but make sure signed integer overflow cannot happen 171289997Sglebius * here. A compiler on a two's complement target *might* find 172289997Sglebius * out that this is just a complicated cast (as above), but your 173289997Sglebius * mileage might vary. 174289997Sglebius */ 175289997Sglebius if (v < 0) 176289997Sglebius vu = ~(uint32_t)(-(v + 1)); 177289997Sglebius else 178289997Sglebius vu = (uint32_t)v; 179289997Sglebius 180289997Sglebius# endif 181289997Sglebius 182289997Sglebius return vu; 183289997Sglebius} 184289997Sglebius 185289997Sglebiusstatic inline int32_t 186289997Sglebiusuint32_2cpl_to_int32( 187289997Sglebius const uint32_t vu) 188289997Sglebius{ 189289997Sglebius int32_t v; 190289997Sglebius 191289997Sglebius# if TARGET_HAS_2CPL 192289997Sglebius 193289997Sglebius /* Just copy through the 32 bits from the unsigned value if 194289997Sglebius * we're on a two's complement target. 195289997Sglebius */ 196289997Sglebius v = (int32_t)vu; 197289997Sglebius 198289997Sglebius# else 199289997Sglebius 200289997Sglebius /* Convert to signed integer, making sure signed integer 201289997Sglebius * overflow cannot happen. Again, the optimiser might or might 202289997Sglebius * not find out that this is just a copy of 32 bits on a target 203289997Sglebius * with two's complement representation for signed integers. 204289997Sglebius */ 205289997Sglebius if (vu > INT32_MAX) 206289997Sglebius v = -(int32_t)(~vu) - 1; 207289997Sglebius else 208289997Sglebius v = (int32_t)vu; 209289997Sglebius 210289997Sglebius# endif 211289997Sglebius 212289997Sglebius return v; 213289997Sglebius} 214289997Sglebius 215289997Sglebius/* Some of the calculations need to multiply the input by 4 before doing 216289997Sglebius * a division. This can cause overflow and strange results. Therefore we 217289997Sglebius * clamp / saturate the input operand. And since we do the calculations 218289997Sglebius * in unsigned int with an extra sign flag/mask, we only loose one bit 219289997Sglebius * of the input value range. 220289997Sglebius */ 221289997Sglebiusstatic inline uint32_t 222289997Sglebiusuint32_saturate( 223289997Sglebius uint32_t vu, 224289997Sglebius uint32_t mu) 225289997Sglebius{ 226289997Sglebius static const uint32_t limit = UINT32_MAX/4u; 227289997Sglebius if ((mu ^ vu) > limit) { 228289997Sglebius vu = mu ^ limit; 229289997Sglebius errno = EDOM; 230289997Sglebius } 231289997Sglebius return vu; 232289997Sglebius} 233289997Sglebius 234289997Sglebius/* 235289997Sglebius *--------------------------------------------------------------------- 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 249289997Sglebius# 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 259289997Sglebius# elif defined(HAVE_INT64) 260275970Scy 261275970Scy res.q_s = tt; 262275970Scy 263289997Sglebius# 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 279289997Sglebius# endif 280275970Scy 281275970Scy return res; 282275970Scy} 283275970Scy 284275970Scy 285275970Scytime_t 286275970Scyvint64_to_time( 287275970Scy const vint64 *tv 288275970Scy ) 289275970Scy{ 290275970Scy time_t res; 291275970Scy 292289997Sglebius# if SIZEOF_TIME_T <= 4 293275970Scy 294275970Scy res = (time_t)tv->D_s.lo; 295275970Scy 296289997Sglebius# elif defined(HAVE_INT64) 297275970Scy 298275970Scy res = (time_t)tv->q_s; 299275970Scy 300289997Sglebius# else 301275970Scy 302275970Scy res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; 303275970Scy 304289997Sglebius# endif 305275970Scy 306275970Scy return res; 307285612Sdelphij} 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 */ 342289997Sglebius# ifdef MKREPRO_DATE 343280849Scy static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; 344289997Sglebius# else 345275970Scy static const char build[] = __TIME__ "/" __DATE__; 346289997Sglebius# 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 356289997Sglebius# ifdef DEBUG 357275970Scy static int ignore = 0; 358289997Sglebius# endif 359285612Sdelphij 360275970Scy ZERO(*jd); 361275970Scy jd->year = 1970; 362275970Scy jd->month = 1; 363275970Scy jd->monthday = 1; 364275970Scy 365289997Sglebius# 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; 374289997Sglebius# 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 398309008Sdelphij *--------------------------------------------------------------------- 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/* 446309008Sdelphij * ==================================================================== 447275970Scy * 448275970Scy * General algorithmic stuff 449275970Scy * 450309008Sdelphij * ==================================================================== 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. 498309008Sdelphij *--------------------------------------------------------------------- 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 511285612Sdelphij /* 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 523289997Sglebius * one. 524275970Scy */ 525275970Scy if (value >= pivot) { 526289997Sglebius diff = int32_to_uint32_2cpl(value) 527289997Sglebius - int32_to_uint32_2cpl(pivot); 528275970Scy } else { 529289997Sglebius diff = int32_to_uint32_2cpl(pivot) 530289997Sglebius - int32_to_uint32_2cpl(value); 531275970Scy cpl ^= 1; 532275970Scy } 533275970Scy diff %= (uint32_t)cycle; 534275970Scy if (diff) { 535275970Scy if (cpl) 536289997Sglebius diff = (uint32_t)cycle - diff; 537275970Scy if (neg) 538275970Scy diff = ~diff + 1; 539289997Sglebius pivot += uint32_2cpl_to_int32(diff); 540275970Scy } 541275970Scy } 542275970Scy return pivot; 543275970Scy} 544275970Scy 545309008Sdelphij/*--------------------------------------------------------------------- 546309008Sdelphij * Note to the casual reader 547309008Sdelphij * 548309008Sdelphij * In the next two functions you will find (or would have found...) 549309008Sdelphij * the expression 550309008Sdelphij * 551309008Sdelphij * res.Q_s -= 0x80000000; 552309008Sdelphij * 553309008Sdelphij * There was some ruckus about a possible programming error due to 554309008Sdelphij * integer overflow and sign propagation. 555309008Sdelphij * 556309008Sdelphij * This assumption is based on a lack of understanding of the C 557309008Sdelphij * standard. (Though this is admittedly not one of the most 'natural' 558309008Sdelphij * aspects of the 'C' language and easily to get wrong.) 559309008Sdelphij * 560309008Sdelphij * see 561309008Sdelphij * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 562309008Sdelphij * "ISO/IEC 9899:201x Committee Draft ��� April 12, 2011" 563309008Sdelphij * 6.4.4.1 Integer constants, clause 5 564309008Sdelphij * 565309008Sdelphij * why there is no sign extension/overflow problem here. 566309008Sdelphij * 567309008Sdelphij * But to ease the minds of the doubtful, I added back the 'u' qualifiers 568309008Sdelphij * that somehow got lost over the last years. 569309008Sdelphij */ 570309008Sdelphij 571309008Sdelphij 572275970Scy/* 573309008Sdelphij *--------------------------------------------------------------------- 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. 583309008Sdelphij *--------------------------------------------------------------------- 584275970Scy */ 585275970Scyvint64 586275970Scyntpcal_ntp_to_time( 587275970Scy uint32_t ntp, 588275970Scy const time_t * pivot 589275970Scy ) 590275970Scy{ 591275970Scy vint64 res; 592275970Scy 593289997Sglebius# if defined(HAVE_INT64) 594275970Scy 595285612Sdelphij res.q_s = (pivot != NULL) 596275970Scy ? *pivot 597285612Sdelphij : now(); 598309008Sdelphij 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 603289997Sglebius# else /* no 64bit scalars */ 604285612Sdelphij 605275970Scy time_t tmp; 606285612Sdelphij 607285612Sdelphij tmp = (pivot != NULL) 608275970Scy ? *pivot 609285612Sdelphij : now(); 610275970Scy res = time_to_vint64(&tmp); 611309008Sdelphij 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 616289997Sglebius# endif /* no 64bit scalars */ 617275970Scy 618275970Scy return res; 619275970Scy} 620275970Scy 621275970Scy/* 622309008Sdelphij *--------------------------------------------------------------------- 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. 632309008Sdelphij *--------------------------------------------------------------------- 633275970Scy */ 634275970Scyvint64 635275970Scyntpcal_ntp_to_ntp( 636275970Scy uint32_t ntp, 637275970Scy const time_t *pivot 638275970Scy ) 639275970Scy{ 640275970Scy vint64 res; 641275970Scy 642289997Sglebius# if defined(HAVE_INT64) 643275970Scy 644275970Scy res.q_s = (pivot) 645275970Scy ? *pivot 646275970Scy : now(); 647309008Sdelphij 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 652289997Sglebius# else /* no 64bit scalars */ 653285612Sdelphij 654275970Scy time_t tmp; 655285612Sdelphij 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 665289997Sglebius# endif /* no 64bit scalars */ 666275970Scy 667275970Scy return res; 668275970Scy} 669275970Scy 670275970Scy 671275970Scy/* 672309008Sdelphij * ==================================================================== 673275970Scy * 674275970Scy * Splitting values to composite entities 675275970Scy * 676309008Sdelphij * ==================================================================== 677275970Scy */ 678275970Scy 679275970Scy/* 680309008Sdelphij *--------------------------------------------------------------------- 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. 685309008Sdelphij *--------------------------------------------------------------------- 686275970Scy */ 687275970Scyntpcal_split 688275970Scyntpcal_daysplit( 689275970Scy const vint64 *ts 690275970Scy ) 691275970Scy{ 692275970Scy ntpcal_split res; 693289997Sglebius uint32_t Q; 694275970Scy 695289997Sglebius# if defined(HAVE_INT64) 696289997Sglebius 697289997Sglebius /* Manual floor division by SECSPERDAY. This uses the one's 698289997Sglebius * complement trick, too, but without an extra flag value: The 699289997Sglebius * flag would be 64bit, and that's a bit of overkill on a 32bit 700289997Sglebius * target that has to use a register pair for a 64bit number. 701289997Sglebius */ 702289997Sglebius if (ts->q_s < 0) 703289997Sglebius Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY); 704289997Sglebius else 705289997Sglebius Q = (uint32_t)(ts->Q_s / SECSPERDAY); 706275970Scy 707289997Sglebius# else 708275970Scy 709289997Sglebius uint32_t ah, al, sflag, A; 710275970Scy 711289997Sglebius /* get operand into ah/al (either ts or ts' one's complement, 712289997Sglebius * for later floor division) 713275970Scy */ 714289997Sglebius sflag = int32_sflag(ts->d_s.hi); 715289997Sglebius ah = sflag ^ ts->D_s.hi; 716289997Sglebius al = sflag ^ ts->D_s.lo; 717275970Scy 718289997Sglebius /* Since 86400 == 128*675 we can drop the least 7 bits and 719289997Sglebius * divide by 675 instead of 86400. Then the maximum remainder 720289997Sglebius * after each devision step is 674, and we need 10 bits for 721289997Sglebius * that. So in the next step we can shift in 22 bits from the 722289997Sglebius * numerator. 723289997Sglebius * 724289997Sglebius * Therefore we load the accu with the top 13 bits (51..63) in 725289997Sglebius * the first shot. We don't have to remember the quotient -- it 726289997Sglebius * would be shifted out anyway. 727289997Sglebius */ 728289997Sglebius A = ah >> 19; 729289997Sglebius if (A >= 675) 730289997Sglebius A = (A % 675u); 731285612Sdelphij 732289997Sglebius /* Now assemble the remainder with bits 29..50 from the 733289997Sglebius * numerator and divide. This creates the upper ten bits of the 734289997Sglebius * quotient. (Well, the top 22 bits of a 44bit result. But that 735289997Sglebius * will be truncated to 32 bits anyway.) 736289997Sglebius */ 737289997Sglebius A = (A << 19) | (ah & 0x0007FFFFu); 738289997Sglebius A = (A << 3) | (al >> 29); 739289997Sglebius Q = A / 675u; 740289997Sglebius A = A % 675u; 741275970Scy 742289997Sglebius /* Now assemble the remainder with bits 7..28 from the numerator 743289997Sglebius * and do a final division step. 744275970Scy */ 745289997Sglebius A = (A << 22) | ((al >> 7) & 0x003FFFFFu); 746289997Sglebius Q = (Q << 22) | (A / 675u); 747275970Scy 748289997Sglebius /* The last 7 bits get simply dropped, as they have no affect on 749289997Sglebius * the quotient when dividing by 86400. 750289997Sglebius */ 751275970Scy 752289997Sglebius /* apply sign correction and calculate the true floor 753289997Sglebius * remainder. 754289997Sglebius */ 755289997Sglebius Q ^= sflag; 756289997Sglebius 757289997Sglebius# endif 758289997Sglebius 759289997Sglebius res.hi = uint32_2cpl_to_int32(Q); 760289997Sglebius res.lo = ts->D_s.lo - Q * SECSPERDAY; 761275970Scy 762275970Scy return res; 763275970Scy} 764275970Scy 765275970Scy/* 766309008Sdelphij *--------------------------------------------------------------------- 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. 770309008Sdelphij *--------------------------------------------------------------------- 771275970Scy */ 772275970Scystatic int32_t 773275970Scypriv_timesplit( 774275970Scy int32_t split[3], 775275970Scy int32_t ts 776275970Scy ) 777275970Scy{ 778289997Sglebius /* Do 3 chained floor divisions by positive constants, using the 779289997Sglebius * one's complement trick and factoring out the intermediate XOR 780289997Sglebius * ops to reduce the number of operations. 781289997Sglebius */ 782289997Sglebius uint32_t us, um, uh, ud, sflag; 783275970Scy 784289997Sglebius sflag = int32_sflag(ts); 785289997Sglebius us = int32_to_uint32_2cpl(ts); 786275970Scy 787289997Sglebius um = (sflag ^ us) / SECSPERMIN; 788289997Sglebius uh = um / MINSPERHR; 789289997Sglebius ud = uh / HRSPERDAY; 790275970Scy 791289997Sglebius um ^= sflag; 792289997Sglebius uh ^= sflag; 793289997Sglebius ud ^= sflag; 794289997Sglebius 795289997Sglebius split[0] = (int32_t)(uh - ud * HRSPERDAY ); 796289997Sglebius split[1] = (int32_t)(um - uh * MINSPERHR ); 797289997Sglebius split[2] = (int32_t)(us - um * SECSPERMIN); 798289997Sglebius 799289997Sglebius return uint32_2cpl_to_int32(ud); 800275970Scy} 801275970Scy 802275970Scy/* 803309008Sdelphij *--------------------------------------------------------------------- 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{ 818289997Sglebius /* Use the fast cyclesplit algorithm here, to calculate the 819289997Sglebius * centuries and years in a century with one division each. This 820289997Sglebius * reduces the number of division operations to two, but is 821289997Sglebius * susceptible to internal range overflow. We make sure the 822289997Sglebius * input operands are in the safe range; this still gives us 823289997Sglebius * approx +/-2.9 million years. 824289997Sglebius */ 825275970Scy ntpcal_split res; 826289997Sglebius int32_t n100, n001; /* calendar year cycles */ 827289997Sglebius uint32_t uday, Q, sflag; 828285612Sdelphij 829289997Sglebius /* split off centuries first */ 830289997Sglebius sflag = int32_sflag(days); 831289997Sglebius uday = uint32_saturate(int32_to_uint32_2cpl(days), sflag); 832289997Sglebius uday = (4u * uday) | 3u; 833289997Sglebius Q = sflag ^ ((sflag ^ uday) / GREGORIAN_CYCLE_DAYS); 834289997Sglebius uday = uday - Q * GREGORIAN_CYCLE_DAYS; 835289997Sglebius n100 = uint32_2cpl_to_int32(Q); 836289997Sglebius 837289997Sglebius /* Split off years in century -- days >= 0 here, and we're far 838289997Sglebius * away from integer overflow trouble now. */ 839289997Sglebius uday |= 3; 840289997Sglebius n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 841289997Sglebius uday = uday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 842285612Sdelphij 843289997Sglebius /* Assemble the year and day in year */ 844289997Sglebius res.hi = n100 * 100 + n001; 845289997Sglebius res.lo = uday / 4u; 846289997Sglebius 847289997Sglebius /* Eventually set the leap year flag. Note: 0 <= n001 <= 99 and 848289997Sglebius * Q is still the two's complement representation of the 849289997Sglebius * centuries: The modulo 4 ops can be done with masking here. 850289997Sglebius * We also shift the year and the century by one, so the tests 851289997Sglebius * can be done against zero instead of 3. 852275970Scy */ 853289997Sglebius if (isleapyear) 854289997Sglebius *isleapyear = !((n001+1) & 3) 855289997Sglebius && ((n001 != 99) || !((Q+1) & 3)); 856289997Sglebius 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; 906289997Sglebius int leapy; 907289997Sglebius u_int ymask; 908275970Scy 909285612Sdelphij /* Get day-of-week first. Since rd is signed, the remainder can 910285612Sdelphij * be in the range [-6..+6], but the assignment to an unsigned 911285612Sdelphij * variable maps the negative values to positive values >=7. 912285612Sdelphij * This makes the sign correction look strange, but adding 7 913285612Sdelphij * causes the needed wrap-around into the desired value range of 914285612Sdelphij * zero to six, both inclusive. 915285612Sdelphij */ 916289997Sglebius jd->weekday = rd % DAYSPERWEEK; 917289997Sglebius if (jd->weekday >= DAYSPERWEEK) /* weekday is unsigned! */ 918289997Sglebius jd->weekday += DAYSPERWEEK; 919275970Scy 920289997Sglebius split = ntpcal_split_eradays(rd - 1, &leapy); 921289997Sglebius /* Get year and day-of-year, with overflow check. If any of the 922289997Sglebius * upper 16 bits is set after shifting to unity-based years, we 923289997Sglebius * will have an overflow when converting to an unsigned 16bit 924289997Sglebius * year. Shifting to the right is OK here, since it does not 925289997Sglebius * matter if the shift is logic or arithmetic. 926289997Sglebius */ 927289997Sglebius split.hi += 1; 928289997Sglebius ymask = 0u - ((split.hi >> 16) == 0); 929289997Sglebius jd->year = (uint16_t)(split.hi & ymask); 930275970Scy jd->yearday = (uint16_t)split.lo + 1; 931275970Scy 932275970Scy /* convert to month and mday */ 933289997Sglebius split = ntpcal_split_yeardays(split.lo, leapy); 934275970Scy jd->month = (uint8_t)split.hi + 1; 935275970Scy jd->monthday = (uint8_t)split.lo + 1; 936275970Scy 937289997Sglebius 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; 952289997Sglebius int leapy; 953275970Scy 954275970Scy /* get day-of-week first */ 955289997Sglebius utm->tm_wday = rd % DAYSPERWEEK; 956275970Scy if (utm->tm_wday < 0) 957289997Sglebius utm->tm_wday += DAYSPERWEEK; 958275970Scy 959275970Scy /* get year and day-of-year */ 960289997Sglebius 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 */ 965289997Sglebius 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 969289997Sglebius 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]; 986285612Sdelphij 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]; 1009285612Sdelphij 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/* 1083309008Sdelphij * ==================================================================== 1084275970Scy * 1085275970Scy * merging composite entities 1086275970Scy * 1087309008Sdelphij * ==================================================================== 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 1104289997Sglebius# if defined(HAVE_INT64) 1105275970Scy 1106275970Scy res.q_s = days; 1107275970Scy res.q_s *= SECSPERDAY; 1108275970Scy res.q_s += secs; 1109275970Scy 1110289997Sglebius# 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); 1138285612Sdelphij 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 1149289997Sglebius# endif 1150275970Scy 1151275970Scy return res; 1152275970Scy} 1153275970Scy 1154275970Scy/* 1155275970Scy *--------------------------------------------------------------------- 1156289997Sglebius * get leap years since epoch in elapsed years 1157275970Scy *--------------------------------------------------------------------- 1158275970Scy */ 1159275970Scyint32_t 1160289997Sglebiusntpcal_leapyears_in_years( 1161275970Scy int32_t years 1162275970Scy ) 1163275970Scy{ 1164289997Sglebius /* We use the in-out-in algorithm here, using the one's 1165289997Sglebius * complement division trick for negative numbers. The chained 1166289997Sglebius * division sequence by 4/25/4 gives the compiler the chance to 1167289997Sglebius * get away with only one true division and doing shifts otherwise. 1168289997Sglebius */ 1169275970Scy 1170289997Sglebius uint32_t sflag, sum, uyear; 1171275970Scy 1172289997Sglebius sflag = int32_sflag(years); 1173289997Sglebius uyear = int32_to_uint32_2cpl(years); 1174289997Sglebius uyear ^= sflag; 1175289997Sglebius 1176289997Sglebius sum = (uyear /= 4u); /* 4yr rule --> IN */ 1177289997Sglebius sum -= (uyear /= 25u); /* 100yr rule --> OUT */ 1178289997Sglebius sum += (uyear /= 4u); /* 400yr rule --> IN */ 1179289997Sglebius 1180289997Sglebius /* Thanks to the alternation of IN/OUT/IN we can do the sum 1181289997Sglebius * directly and have a single one's complement operation 1182289997Sglebius * here. (Only if the years are negative, of course.) Otherwise 1183289997Sglebius * the one's complement would have to be done when 1184289997Sglebius * adding/subtracting the terms. 1185275970Scy */ 1186289997Sglebius return uint32_2cpl_to_int32(sflag ^ sum); 1187275970Scy} 1188275970Scy 1189275970Scy/* 1190275970Scy *--------------------------------------------------------------------- 1191289997Sglebius * Convert elapsed years in Era into elapsed days in Era. 1192289997Sglebius *--------------------------------------------------------------------- 1193289997Sglebius */ 1194289997Sglebiusint32_t 1195289997Sglebiusntpcal_days_in_years( 1196289997Sglebius int32_t years 1197289997Sglebius ) 1198289997Sglebius{ 1199289997Sglebius return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years); 1200289997Sglebius} 1201289997Sglebius 1202289997Sglebius/* 1203289997Sglebius *--------------------------------------------------------------------- 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 1225289997Sglebius /* Add ten months and correct if needed. (It likely is...) */ 1226289997Sglebius res.lo = m + 10; 1227289997Sglebius res.hi = (res.lo >= 12); 1228289997Sglebius if (res.hi) 1229289997Sglebius res.lo -= 12; 1230289997Sglebius 1231289997Sglebius /* if still out of range, normalise by floor division ... */ 1232275970Scy if (res.lo < 0 || res.lo >= 12) { 1233289997Sglebius uint32_t mu, Q, sflag; 1234289997Sglebius sflag = int32_sflag(res.lo); 1235289997Sglebius mu = int32_to_uint32_2cpl(res.lo); 1236289997Sglebius Q = sflag ^ ((sflag ^ mu) / 12u); 1237289997Sglebius res.hi += uint32_2cpl_to_int32(Q); 1238289997Sglebius res.lo = mu - Q * 12u; 1239275970Scy } 1240289997Sglebius 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 * 1281309008Sdelphij * Note: This will give the true difference to the start of the given 1282309008Sdelphij * 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/* 1464309008Sdelphij * ==================================================================== 1465275970Scy * 1466275970Scy * extended and unchecked variants of caljulian/caltontp 1467275970Scy * 1468309008Sdelphij * ==================================================================== 1469275970Scy */ 1470275970Scyint 1471275970Scyntpcal_ntp64_to_date( 1472275970Scy struct calendar *jd, 1473275970Scy const vint64 *ntp 1474275970Scy ) 1475275970Scy{ 1476275970Scy ntpcal_split ds; 1477285612Sdelphij 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; 1492285612Sdelphij 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/* 1530309008Sdelphij * ==================================================================== 1531275970Scy * 1532275970Scy * day-of-week calculations 1533275970Scy * 1534309008Sdelphij * ==================================================================== 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/* 1587309008Sdelphij * ==================================================================== 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 * 1631309008Sdelphij * ==================================================================== 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 ) 1643289997Sglebius{ 1644275970Scy /* 1645275970Scy * use: w = (y * 53431 + b[c]) / 1024 as interpolation 1646275970Scy */ 1647289997Sglebius static const uint16_t bctab[4] = { 157, 449, 597, 889 }; 1648275970Scy 1649289997Sglebius int32_t cs, cw; 1650289997Sglebius uint32_t cc, ci, yu, sflag; 1651275970Scy 1652289997Sglebius sflag = int32_sflag(years); 1653289997Sglebius yu = int32_to_uint32_2cpl(years); 1654289997Sglebius 1655289997Sglebius /* split off centuries, using floor division */ 1656289997Sglebius cc = sflag ^ ((sflag ^ yu) / 100u); 1657289997Sglebius yu -= cc * 100u; 1658275970Scy 1659289997Sglebius /* calculate century cycles shift and cycle index: 1660289997Sglebius * Assuming a century is 5217 weeks, we have to add a cycle 1661289997Sglebius * shift that is 3 for every 4 centuries, because 3 of the four 1662289997Sglebius * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual 1663289997Sglebius * correction, and the second century is the defective one. 1664289997Sglebius * 1665289997Sglebius * Needs floor division by 4, which is done with masking and 1666289997Sglebius * shifting. 1667275970Scy */ 1668289997Sglebius ci = cc * 3u + 1; 1669289997Sglebius cs = uint32_2cpl_to_int32(sflag ^ ((sflag ^ ci) / 4u)); 1670289997Sglebius ci = ci % 4u; 1671289997Sglebius 1672289997Sglebius /* Get weeks in century. Can use plain division here as all ops 1673289997Sglebius * are >= 0, and let the compiler sort out the possible 1674289997Sglebius * optimisations. 1675289997Sglebius */ 1676289997Sglebius cw = (yu * 53431u + bctab[ci]) / 1024u; 1677275970Scy 1678289997Sglebius 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 */ 1695289997Sglebius 1696289997Sglebius static const uint16_t bctab[4] = { 85, 130, 17, 62 }; 1697289997Sglebius 1698275970Scy ntpcal_split res; 1699289997Sglebius int32_t cc, ci; 1700289997Sglebius uint32_t sw, cy, Q, sflag; 1701275970Scy 1702289997Sglebius /* Use two fast cycle-split divisions here. This is again 1703289997Sglebius * susceptible to internal overflow, so we check the range. This 1704289997Sglebius * still permits more than +/-20 million years, so this is 1705289997Sglebius * likely a pure academical problem. 1706289997Sglebius * 1707289997Sglebius * We want to execute '(weeks * 4 + 2) /% 20871' under floor 1708289997Sglebius * division rules in the first step. 1709275970Scy */ 1710289997Sglebius sflag = int32_sflag(weeks); 1711289997Sglebius sw = uint32_saturate(int32_to_uint32_2cpl(weeks), sflag); 1712289997Sglebius sw = 4u * sw + 2; 1713289997Sglebius Q = sflag ^ ((sflag ^ sw) / GREGORIAN_CYCLE_WEEKS); 1714289997Sglebius sw -= Q * GREGORIAN_CYCLE_WEEKS; 1715289997Sglebius ci = Q % 4u; 1716289997Sglebius cc = uint32_2cpl_to_int32(Q); 1717275970Scy 1718289997Sglebius /* Split off years; sw >= 0 here! The scaled weeks in the years 1719289997Sglebius * are scaled up by 157 afterwards. 1720289997Sglebius */ 1721289997Sglebius sw = (sw / 4u) * 157u + bctab[ci]; 1722289997Sglebius cy = sw / 8192u; /* ws >> 13 , let the compiler sort it out */ 1723289997Sglebius sw = sw % 8192u; /* ws & 8191, let the compiler sort it out */ 1724289997Sglebius 1725289997Sglebius /* assemble elapsed years and downscale the elapsed weeks in 1726289997Sglebius * the year. 1727275970Scy */ 1728289997Sglebius res.hi = 100*cc + cy; 1729289997Sglebius res.lo = sw / 157u; 1730285612Sdelphij 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]; 1747289997Sglebius uint32_t uw, ud, sflag; 1748285612Sdelphij 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 1761289997Sglebius /* split days into days and weeks, using floor division in unsigned */ 1762289997Sglebius ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */ 1763289997Sglebius sflag = int32_sflag(ds.hi); 1764289997Sglebius ud = int32_to_uint32_2cpl(ds.hi); 1765289997Sglebius uw = sflag ^ ((sflag ^ ud) / DAYSPERWEEK); 1766289997Sglebius ud -= uw * DAYSPERWEEK; 1767289997Sglebius ds.hi = uint32_2cpl_to_int32(uw); 1768289997Sglebius ds.lo = ud; 1769289997Sglebius 1770275970Scy id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ 1771275970Scy 1772289997Sglebius /* 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; 1788285612Sdelphij 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 1828330141Sdelphij/* 1829330141Sdelphij * ==================================================================== 1830330141Sdelphij * 'basedate' support functions 1831330141Sdelphij * ==================================================================== 1832330141Sdelphij */ 1833330141Sdelphij 1834330141Sdelphijstatic int32_t s_baseday = NTP_TO_UNIX_DAYS; 1835330141Sdelphij 1836330141Sdelphijint32_t 1837330141Sdelphijbasedate_eval_buildstamp(void) 1838330141Sdelphij{ 1839330141Sdelphij struct calendar jd; 1840330141Sdelphij int32_t ed; 1841330141Sdelphij 1842330141Sdelphij if (!ntpcal_get_build_date(&jd)) 1843330141Sdelphij return NTP_TO_UNIX_DAYS; 1844330141Sdelphij 1845330141Sdelphij /* The time zone of the build stamp is unspecified; we remove 1846330141Sdelphij * one day to provide a certain slack. And in case somebody 1847330141Sdelphij * fiddled with the system clock, we make sure we do not go 1848330141Sdelphij * before the UNIX epoch (1970-01-01). It's probably not possible 1849330141Sdelphij * to do this to the clock on most systems, but there are other 1850330141Sdelphij * ways to tweak the build stamp. 1851330141Sdelphij */ 1852330141Sdelphij jd.monthday -= 1; 1853330141Sdelphij ed = ntpcal_date_to_rd(&jd) - DAY_NTP_STARTS; 1854330141Sdelphij return (ed < NTP_TO_UNIX_DAYS) ? NTP_TO_UNIX_DAYS : ed; 1855330141Sdelphij} 1856330141Sdelphij 1857330141Sdelphijint32_t 1858330141Sdelphijbasedate_eval_string( 1859330141Sdelphij const char * str 1860330141Sdelphij ) 1861330141Sdelphij{ 1862330141Sdelphij u_short y,m,d; 1863330141Sdelphij u_long ned; 1864330141Sdelphij int rc, nc; 1865330141Sdelphij size_t sl; 1866330141Sdelphij 1867330141Sdelphij sl = strlen(str); 1868330141Sdelphij rc = sscanf(str, "%4hu-%2hu-%2hu%n", &y, &m, &d, &nc); 1869330141Sdelphij if (rc == 3 && (size_t)nc == sl) { 1870330141Sdelphij if (m >= 1 && m <= 12 && d >= 1 && d <= 31) 1871330141Sdelphij return ntpcal_edate_to_eradays(y-1, m-1, d) 1872330141Sdelphij - DAY_NTP_STARTS; 1873330141Sdelphij goto buildstamp; 1874330141Sdelphij } 1875330141Sdelphij 1876338531Sdelphij rc = sscanf(str, "%lu%n", &ned, &nc); 1877330141Sdelphij if (rc == 1 && (size_t)nc == sl) { 1878330141Sdelphij if (ned <= INT32_MAX) 1879330141Sdelphij return (int32_t)ned; 1880330141Sdelphij goto buildstamp; 1881330141Sdelphij } 1882330141Sdelphij 1883330141Sdelphij buildstamp: 1884330141Sdelphij msyslog(LOG_WARNING, 1885330141Sdelphij "basedate string \"%s\" invalid, build date substituted!", 1886330141Sdelphij str); 1887330141Sdelphij return basedate_eval_buildstamp(); 1888330141Sdelphij} 1889330141Sdelphij 1890330141Sdelphijuint32_t 1891330141Sdelphijbasedate_get_day(void) 1892330141Sdelphij{ 1893330141Sdelphij return s_baseday; 1894330141Sdelphij} 1895330141Sdelphij 1896330141Sdelphijint32_t 1897330141Sdelphijbasedate_set_day( 1898330141Sdelphij int32_t day 1899330141Sdelphij ) 1900330141Sdelphij{ 1901330141Sdelphij struct calendar jd; 1902330141Sdelphij int32_t retv; 1903330141Sdelphij 1904330141Sdelphij if (day < NTP_TO_UNIX_DAYS) { 1905330141Sdelphij msyslog(LOG_WARNING, 1906330141Sdelphij "baseday_set_day: invalid day (%lu), UNIX epoch substituted", 1907330141Sdelphij (unsigned long)day); 1908330141Sdelphij day = NTP_TO_UNIX_DAYS; 1909330141Sdelphij } 1910330141Sdelphij retv = s_baseday; 1911330141Sdelphij s_baseday = day; 1912330141Sdelphij ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); 1913330141Sdelphij msyslog(LOG_INFO, "basedate set to %04hu-%02hu-%02hu", 1914330141Sdelphij jd.year, (u_short)jd.month, (u_short)jd.monthday); 1915330141Sdelphij return retv; 1916330141Sdelphij} 1917330141Sdelphij 1918330141Sdelphijtime_t 1919330141Sdelphijbasedate_get_eracenter(void) 1920330141Sdelphij{ 1921330141Sdelphij time_t retv; 1922330141Sdelphij retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); 1923330141Sdelphij retv *= SECSPERDAY; 1924330141Sdelphij retv += (UINT32_C(1) << 31); 1925330141Sdelphij return retv; 1926330141Sdelphij} 1927330141Sdelphij 1928330141Sdelphijtime_t 1929330141Sdelphijbasedate_get_erabase(void) 1930330141Sdelphij{ 1931330141Sdelphij time_t retv; 1932330141Sdelphij retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); 1933330141Sdelphij retv *= SECSPERDAY; 1934330141Sdelphij return retv; 1935330141Sdelphij} 1936330141Sdelphij 1937275970Scy/* -*-EOF-*- */ 1938