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 43358659Scy * calculation steps in the whole calendar that potentially suffer from 44358659Scy * an internal overflow, and these are coded in a way that avoids 45358659Scy * it. All other functions do not suffer from internal overflow and 46358659Scy * simply return the result truncated to 32 bits. 47275970Scy */ 48289764Sglebius 49275970Scy#include <config.h> 50275970Scy#include <sys/types.h> 51275970Scy 52275970Scy#include "ntp_types.h" 53275970Scy#include "ntp_calendar.h" 54275970Scy#include "ntp_stdlib.h" 55275970Scy#include "ntp_fp.h" 56275970Scy#include "ntp_unixtime.h" 57275970Scy 58358659Scy#include "ntpd.h" 59358659Scy#include "lib_strbuf.h" 60358659Scy 61289764Sglebius/* For now, let's take the conservative approach: if the target property 62289764Sglebius * macros are not defined, check a few well-known compiler/architecture 63289764Sglebius * settings. Default is to assume that the representation of signed 64289764Sglebius * integers is unknown and shift-arithmetic-right is not available. 65289764Sglebius */ 66289764Sglebius#ifndef TARGET_HAS_2CPL 67289764Sglebius# if defined(__GNUC__) 68289764Sglebius# if defined(__i386__) || defined(__x86_64__) || defined(__arm__) 69289764Sglebius# define TARGET_HAS_2CPL 1 70289764Sglebius# else 71289764Sglebius# define TARGET_HAS_2CPL 0 72289764Sglebius# endif 73289764Sglebius# elif defined(_MSC_VER) 74289764Sglebius# if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) 75289764Sglebius# define TARGET_HAS_2CPL 1 76289764Sglebius# else 77289764Sglebius# define TARGET_HAS_2CPL 0 78289764Sglebius# endif 79289764Sglebius# else 80289764Sglebius# define TARGET_HAS_2CPL 0 81289764Sglebius# endif 82289764Sglebius#endif 83289764Sglebius 84289764Sglebius#ifndef TARGET_HAS_SAR 85289764Sglebius# define TARGET_HAS_SAR 0 86289764Sglebius#endif 87289764Sglebius 88358659Scy#if !defined(HAVE_64BITREGS) && defined(UINT64_MAX) && (SIZE_MAX >= UINT64_MAX) 89358659Scy# define HAVE_64BITREGS 90358659Scy#endif 91358659Scy 92275970Scy/* 93275970Scy *--------------------------------------------------------------------- 94275970Scy * replacing the 'time()' function 95309007Sdelphij *--------------------------------------------------------------------- 96275970Scy */ 97275970Scy 98275970Scystatic systime_func_ptr systime_func = &time; 99275970Scystatic inline time_t now(void); 100275970Scy 101275970Scy 102275970Scysystime_func_ptr 103275970Scyntpcal_set_timefunc( 104275970Scy systime_func_ptr nfunc 105275970Scy ) 106275970Scy{ 107275970Scy systime_func_ptr res; 108282408Scy 109275970Scy res = systime_func; 110275970Scy if (NULL == nfunc) 111275970Scy nfunc = &time; 112275970Scy systime_func = nfunc; 113275970Scy 114275970Scy return res; 115275970Scy} 116275970Scy 117275970Scy 118275970Scystatic inline time_t 119275970Scynow(void) 120275970Scy{ 121275970Scy return (*systime_func)(NULL); 122275970Scy} 123275970Scy 124275970Scy/* 125275970Scy *--------------------------------------------------------------------- 126289764Sglebius * Get sign extension mask and unsigned 2cpl rep for a signed integer 127289764Sglebius *--------------------------------------------------------------------- 128289764Sglebius */ 129289764Sglebius 130289764Sglebiusstatic inline uint32_t 131289764Sglebiusint32_sflag( 132289764Sglebius const int32_t v) 133289764Sglebius{ 134289764Sglebius# if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4 135289764Sglebius 136289764Sglebius /* Let's assume that shift is the fastest way to get the sign 137289764Sglebius * extension of of a signed integer. This might not always be 138289764Sglebius * true, though -- On 8bit CPUs or machines without barrel 139289764Sglebius * shifter this will kill the performance. So we make sure 140289764Sglebius * we do this only if 'int' has at least 4 bytes. 141289764Sglebius */ 142289764Sglebius return (uint32_t)(v >> 31); 143358659Scy 144289764Sglebius# else 145289764Sglebius 146289764Sglebius /* This should be a rather generic approach for getting a sign 147289764Sglebius * extension mask... 148289764Sglebius */ 149289764Sglebius return UINT32_C(0) - (uint32_t)(v < 0); 150289764Sglebius 151289764Sglebius# endif 152289764Sglebius} 153289764Sglebius 154289764Sglebiusstatic inline int32_t 155289764Sglebiusuint32_2cpl_to_int32( 156289764Sglebius const uint32_t vu) 157289764Sglebius{ 158289764Sglebius int32_t v; 159358659Scy 160289764Sglebius# if TARGET_HAS_2CPL 161289764Sglebius 162289764Sglebius /* Just copy through the 32 bits from the unsigned value if 163289764Sglebius * we're on a two's complement target. 164289764Sglebius */ 165289764Sglebius v = (int32_t)vu; 166289764Sglebius 167289764Sglebius# else 168289764Sglebius 169289764Sglebius /* Convert to signed integer, making sure signed integer 170289764Sglebius * overflow cannot happen. Again, the optimiser might or might 171289764Sglebius * not find out that this is just a copy of 32 bits on a target 172289764Sglebius * with two's complement representation for signed integers. 173289764Sglebius */ 174289764Sglebius if (vu > INT32_MAX) 175289764Sglebius v = -(int32_t)(~vu) - 1; 176289764Sglebius else 177289764Sglebius v = (int32_t)vu; 178358659Scy 179289764Sglebius# endif 180358659Scy 181289764Sglebius return v; 182289764Sglebius} 183289764Sglebius 184289764Sglebius/* 185289764Sglebius *--------------------------------------------------------------------- 186275970Scy * Convert between 'time_t' and 'vint64' 187275970Scy *--------------------------------------------------------------------- 188275970Scy */ 189275970Scyvint64 190275970Scytime_to_vint64( 191275970Scy const time_t * ptt 192275970Scy ) 193275970Scy{ 194275970Scy vint64 res; 195275970Scy time_t tt; 196275970Scy 197275970Scy tt = *ptt; 198275970Scy 199289764Sglebius# if SIZEOF_TIME_T <= 4 200275970Scy 201275970Scy res.D_s.hi = 0; 202275970Scy if (tt < 0) { 203275970Scy res.D_s.lo = (uint32_t)-tt; 204275970Scy M_NEG(res.D_s.hi, res.D_s.lo); 205275970Scy } else { 206275970Scy res.D_s.lo = (uint32_t)tt; 207275970Scy } 208275970Scy 209289764Sglebius# elif defined(HAVE_INT64) 210275970Scy 211275970Scy res.q_s = tt; 212275970Scy 213289764Sglebius# else 214275970Scy /* 215275970Scy * shifting negative signed quantities is compiler-dependent, so 216275970Scy * we better avoid it and do it all manually. And shifting more 217275970Scy * than the width of a quantity is undefined. Also a don't do! 218275970Scy */ 219275970Scy if (tt < 0) { 220275970Scy tt = -tt; 221275970Scy res.D_s.lo = (uint32_t)tt; 222275970Scy res.D_s.hi = (uint32_t)(tt >> 32); 223275970Scy M_NEG(res.D_s.hi, res.D_s.lo); 224275970Scy } else { 225275970Scy res.D_s.lo = (uint32_t)tt; 226275970Scy res.D_s.hi = (uint32_t)(tt >> 32); 227275970Scy } 228275970Scy 229289764Sglebius# endif 230275970Scy 231275970Scy return res; 232275970Scy} 233275970Scy 234275970Scy 235275970Scytime_t 236275970Scyvint64_to_time( 237275970Scy const vint64 *tv 238275970Scy ) 239275970Scy{ 240275970Scy time_t res; 241275970Scy 242289764Sglebius# if SIZEOF_TIME_T <= 4 243275970Scy 244275970Scy res = (time_t)tv->D_s.lo; 245275970Scy 246289764Sglebius# elif defined(HAVE_INT64) 247275970Scy 248275970Scy res = (time_t)tv->q_s; 249275970Scy 250289764Sglebius# else 251275970Scy 252275970Scy res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; 253275970Scy 254289764Sglebius# endif 255275970Scy 256275970Scy return res; 257282408Scy} 258275970Scy 259275970Scy/* 260275970Scy *--------------------------------------------------------------------- 261275970Scy * Get the build date & time 262275970Scy *--------------------------------------------------------------------- 263275970Scy */ 264275970Scyint 265275970Scyntpcal_get_build_date( 266275970Scy struct calendar * jd 267275970Scy ) 268275970Scy{ 269275970Scy /* The C standard tells us the format of '__DATE__': 270275970Scy * 271275970Scy * __DATE__ The date of translation of the preprocessing 272275970Scy * translation unit: a character string literal of the form "Mmm 273275970Scy * dd yyyy", where the names of the months are the same as those 274275970Scy * generated by the asctime function, and the first character of 275275970Scy * dd is a space character if the value is less than 10. If the 276275970Scy * date of translation is not available, an 277275970Scy * implementation-defined valid date shall be supplied. 278275970Scy * 279275970Scy * __TIME__ The time of translation of the preprocessing 280275970Scy * translation unit: a character string literal of the form 281275970Scy * "hh:mm:ss" as in the time generated by the asctime 282275970Scy * function. If the time of translation is not available, an 283275970Scy * implementation-defined valid time shall be supplied. 284275970Scy * 285275970Scy * Note that MSVC declares DATE and TIME to be in the local time 286275970Scy * zone, while neither the C standard nor the GCC docs make any 287275970Scy * statement about this. As a result, we may be +/-12hrs off 288358659Scy * UTC. But for practical purposes, this should not be a 289275970Scy * problem. 290275970Scy * 291275970Scy */ 292289764Sglebius# ifdef MKREPRO_DATE 293280849Scy static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; 294289764Sglebius# else 295275970Scy static const char build[] = __TIME__ "/" __DATE__; 296289764Sglebius# endif 297275970Scy static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 298275970Scy 299275970Scy char monstr[4]; 300275970Scy const char * cp; 301275970Scy unsigned short hour, minute, second, day, year; 302358659Scy /* Note: The above quantities are used for sscanf 'hu' format, 303275970Scy * so using 'uint16_t' is contra-indicated! 304275970Scy */ 305275970Scy 306289764Sglebius# ifdef DEBUG 307358659Scy static int ignore = 0; 308289764Sglebius# endif 309282408Scy 310275970Scy ZERO(*jd); 311275970Scy jd->year = 1970; 312275970Scy jd->month = 1; 313275970Scy jd->monthday = 1; 314275970Scy 315289764Sglebius# ifdef DEBUG 316275970Scy /* check environment if build date should be ignored */ 317275970Scy if (0 == ignore) { 318275970Scy const char * envstr; 319275970Scy envstr = getenv("NTPD_IGNORE_BUILD_DATE"); 320275970Scy ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes"))); 321275970Scy } 322275970Scy if (ignore > 1) 323275970Scy return FALSE; 324289764Sglebius# endif 325275970Scy 326275970Scy if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu", 327275970Scy &hour, &minute, &second, monstr, &day, &year)) { 328275970Scy cp = strstr(mlist, monstr); 329275970Scy if (NULL != cp) { 330275970Scy jd->year = year; 331275970Scy jd->month = (uint8_t)((cp - mlist) / 3 + 1); 332275970Scy jd->monthday = (uint8_t)day; 333275970Scy jd->hour = (uint8_t)hour; 334275970Scy jd->minute = (uint8_t)minute; 335275970Scy jd->second = (uint8_t)second; 336275970Scy 337275970Scy return TRUE; 338275970Scy } 339275970Scy } 340275970Scy 341275970Scy return FALSE; 342275970Scy} 343275970Scy 344275970Scy 345275970Scy/* 346275970Scy *--------------------------------------------------------------------- 347275970Scy * basic calendar stuff 348309007Sdelphij *--------------------------------------------------------------------- 349275970Scy */ 350275970Scy 351275970Scy/* 352275970Scy * Some notes on the terminology: 353275970Scy * 354275970Scy * We use the proleptic Gregorian calendar, which is the Gregorian 355275970Scy * calendar extended in both directions ad infinitum. This totally 356275970Scy * disregards the fact that this calendar was invented in 1582, and 357275970Scy * was adopted at various dates over the world; sometimes even after 358275970Scy * the start of the NTP epoch. 359275970Scy * 360275970Scy * Normally date parts are given as current cycles, while time parts 361275970Scy * are given as elapsed cycles: 362275970Scy * 363275970Scy * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, 364275970Scy * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. 365275970Scy * 366275970Scy * The basic calculations for this calendar implementation deal with 367275970Scy * ELAPSED date units, which is the number of full years, full months 368275970Scy * and full days before a date: 1970-01-01 would be (1969, 0, 0) in 369275970Scy * that notation. 370275970Scy * 371275970Scy * To ease the numeric computations, month and day values outside the 372275970Scy * normal range are acceptable: 2001-03-00 will be treated as the day 373275970Scy * before 2001-03-01, 2000-13-32 will give the same result as 374275970Scy * 2001-02-01 and so on. 375275970Scy * 376275970Scy * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' 377275970Scy * (day number). This is the number of days elapsed since 0000-12-31 378275970Scy * in the proleptic Gregorian calendar. The begin of the Christian Era 379275970Scy * (0001-01-01) is RD(1). 380275970Scy */ 381275970Scy 382275970Scy/* 383309007Sdelphij * ==================================================================== 384275970Scy * 385275970Scy * General algorithmic stuff 386275970Scy * 387309007Sdelphij * ==================================================================== 388275970Scy */ 389275970Scy 390275970Scy/* 391275970Scy *--------------------------------------------------------------------- 392358659Scy * fast modulo 7 operations (floor/mathematical convention) 393358659Scy *--------------------------------------------------------------------- 394358659Scy */ 395358659Scyint 396358659Scyu32mod7( 397358659Scy uint32_t x 398358659Scy ) 399358659Scy{ 400358659Scy /* This is a combination of tricks from "Hacker's Delight" with 401358659Scy * some modifications, like a multiplication that rounds up to 402358659Scy * drop the final adjustment stage. 403358659Scy * 404358659Scy * Do a partial reduction by digit sum to keep the value in the 405358659Scy * range permitted for the mul/shift stage. There are several 406358659Scy * possible and absolutely equivalent shift/mask combinations; 407358659Scy * this one is ARM-friendly because of a mask that fits into 16 408358659Scy * bit. 409358659Scy */ 410358659Scy x = (x >> 15) + (x & UINT32_C(0x7FFF)); 411358659Scy /* Take reminder as (mod 8) by mul/shift. Since the multiplier 412358659Scy * was calculated using ceil() instead of floor(), it skips the 413358659Scy * value '7' properly. 414358659Scy * M <- ceil(ldexp(8/7, 29)) 415358659Scy */ 416358659Scy return (int)((x * UINT32_C(0x24924925)) >> 29); 417358659Scy} 418358659Scy 419358659Scyint 420358659Scyi32mod7( 421358659Scy int32_t x 422358659Scy ) 423358659Scy{ 424358659Scy /* We add (2**32 - 2**32 % 7), which is (2**32 - 4), to negative 425358659Scy * numbers to map them into the postive range. Only the term '-4' 426358659Scy * survives, obviously. 427358659Scy */ 428358659Scy uint32_t ux = (uint32_t)x; 429358659Scy return u32mod7((x < 0) ? (ux - 4u) : ux); 430358659Scy} 431358659Scy 432358659Scyuint32_t 433358659Scyi32fmod( 434358659Scy int32_t x, 435358659Scy uint32_t d 436358659Scy ) 437358659Scy{ 438358659Scy uint32_t ux = (uint32_t)x; 439358659Scy uint32_t sf = UINT32_C(0) - (x < 0); 440358659Scy ux = (sf ^ ux ) % d; 441358659Scy return (d & sf) + (sf ^ ux); 442358659Scy} 443358659Scy 444358659Scy/* 445358659Scy *--------------------------------------------------------------------- 446275970Scy * Do a periodic extension of 'value' around 'pivot' with a period of 447275970Scy * 'cycle'. 448275970Scy * 449275970Scy * The result 'res' is a number that holds to the following properties: 450275970Scy * 451275970Scy * 1) res MOD cycle == value MOD cycle 452275970Scy * 2) pivot <= res < pivot + cycle 453275970Scy * (replace </<= with >/>= for negative cycles) 454275970Scy * 455275970Scy * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which 456275970Scy * is not the same as the '%' operator in C: C requires division to be 457275970Scy * a truncated division, where remainder and dividend have the same 458275970Scy * sign if the remainder is not zero, whereas floor division requires 459275970Scy * divider and modulus to have the same sign for a non-zero modulus. 460275970Scy * 461275970Scy * This function has some useful applications: 462275970Scy * 463275970Scy * + let Y be a calendar year and V a truncated 2-digit year: then 464275970Scy * periodic_extend(Y-50, V, 100) 465275970Scy * is the closest expansion of the truncated year with respect to 466275970Scy * the full year, that is a 4-digit year with a difference of less 467275970Scy * than 50 years to the year Y. ("century unfolding") 468275970Scy * 469275970Scy * + let T be a UN*X time stamp and V be seconds-of-day: then 470275970Scy * perodic_extend(T-43200, V, 86400) 471275970Scy * is a time stamp that has the same seconds-of-day as the input 472275970Scy * value, with an absolute difference to T of <= 12hrs. ("day 473275970Scy * unfolding") 474275970Scy * 475275970Scy * + Wherever you have a truncated periodic value and a non-truncated 476275970Scy * base value and you want to match them somehow... 477275970Scy * 478275970Scy * Basically, the function delivers 'pivot + (value - pivot) % cycle', 479275970Scy * but the implementation takes some pains to avoid internal signed 480275970Scy * integer overflows in the '(value - pivot) % cycle' part and adheres 481275970Scy * to the floor division convention. 482275970Scy * 483275970Scy * If 64bit scalars where available on all intended platforms, writing a 484275970Scy * version that uses 64 bit ops would be easy; writing a general 485275970Scy * division routine for 64bit ops on a platform that can only do 486275970Scy * 32/16bit divisions and is still performant is a bit more 487275970Scy * difficult. Since most usecases can be coded in a way that does only 488358659Scy * require the 32bit version a 64bit version is NOT provided here. 489309007Sdelphij *--------------------------------------------------------------------- 490275970Scy */ 491275970Scyint32_t 492275970Scyntpcal_periodic_extend( 493275970Scy int32_t pivot, 494275970Scy int32_t value, 495275970Scy int32_t cycle 496275970Scy ) 497275970Scy{ 498358659Scy /* Implement a 4-quadrant modulus calculation by 2 2-quadrant 499358659Scy * branches, one for positive and one for negative dividers. 500358659Scy * Everything else can be handled by bit level logic and 501358659Scy * conditional one's complement arithmetic. By convention, we 502358659Scy * assume 503358659Scy * 504358659Scy * x % b == 0 if |b| < 2 505358659Scy * 506358659Scy * that is, we don't actually divide for cycles of -1,0,1 and 507358659Scy * return the pivot value in that case. 508358659Scy */ 509358659Scy uint32_t uv = (uint32_t)value; 510358659Scy uint32_t up = (uint32_t)pivot; 511358659Scy uint32_t uc, sf; 512275970Scy 513358659Scy if (cycle > 1) 514358659Scy { 515358659Scy uc = (uint32_t)cycle; 516358659Scy sf = UINT32_C(0) - (value < pivot); 517358659Scy 518358659Scy uv = sf ^ (uv - up); 519358659Scy uv %= uc; 520358659Scy pivot += (uc & sf) + (sf ^ uv); 521275970Scy } 522358659Scy else if (cycle < -1) 523358659Scy { 524358659Scy uc = ~(uint32_t)cycle + 1; 525358659Scy sf = UINT32_C(0) - (value > pivot); 526358659Scy 527358659Scy uv = sf ^ (up - uv); 528358659Scy uv %= uc; 529358659Scy pivot -= (uc & sf) + (sf ^ uv); 530275970Scy } 531275970Scy return pivot; 532275970Scy} 533275970Scy 534309007Sdelphij/*--------------------------------------------------------------------- 535309007Sdelphij * Note to the casual reader 536309007Sdelphij * 537309007Sdelphij * In the next two functions you will find (or would have found...) 538309007Sdelphij * the expression 539309007Sdelphij * 540309007Sdelphij * res.Q_s -= 0x80000000; 541309007Sdelphij * 542309007Sdelphij * There was some ruckus about a possible programming error due to 543309007Sdelphij * integer overflow and sign propagation. 544309007Sdelphij * 545309007Sdelphij * This assumption is based on a lack of understanding of the C 546309007Sdelphij * standard. (Though this is admittedly not one of the most 'natural' 547309007Sdelphij * aspects of the 'C' language and easily to get wrong.) 548309007Sdelphij * 549358659Scy * see 550309007Sdelphij * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf 551309007Sdelphij * "ISO/IEC 9899:201x Committee Draft ��� April 12, 2011" 552309007Sdelphij * 6.4.4.1 Integer constants, clause 5 553309007Sdelphij * 554309007Sdelphij * why there is no sign extension/overflow problem here. 555309007Sdelphij * 556309007Sdelphij * But to ease the minds of the doubtful, I added back the 'u' qualifiers 557358659Scy * that somehow got lost over the last years. 558309007Sdelphij */ 559309007Sdelphij 560309007Sdelphij 561275970Scy/* 562309007Sdelphij *--------------------------------------------------------------------- 563275970Scy * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X 564275970Scy * scale with proper epoch unfolding around a given pivot or the current 565275970Scy * system time. This function happily accepts negative pivot values as 566358659Scy * timestamps before 1970-01-01, so be aware of possible trouble on 567275970Scy * platforms with 32bit 'time_t'! 568275970Scy * 569275970Scy * This is also a periodic extension, but since the cycle is 2^32 and 570275970Scy * the shift is 2^31, we can do some *very* fast math without explicit 571275970Scy * divisions. 572309007Sdelphij *--------------------------------------------------------------------- 573275970Scy */ 574275970Scyvint64 575275970Scyntpcal_ntp_to_time( 576275970Scy uint32_t ntp, 577275970Scy const time_t * pivot 578275970Scy ) 579275970Scy{ 580275970Scy vint64 res; 581275970Scy 582289764Sglebius# if defined(HAVE_INT64) 583275970Scy 584282408Scy res.q_s = (pivot != NULL) 585275970Scy ? *pivot 586282408Scy : now(); 587309007Sdelphij res.Q_s -= 0x80000000u; /* unshift of half range */ 588275970Scy ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 589275970Scy ntp -= res.D_s.lo; /* cycle difference */ 590275970Scy res.Q_s += (uint64_t)ntp; /* get expanded time */ 591275970Scy 592289764Sglebius# else /* no 64bit scalars */ 593282408Scy 594275970Scy time_t tmp; 595282408Scy 596282408Scy tmp = (pivot != NULL) 597275970Scy ? *pivot 598282408Scy : now(); 599275970Scy res = time_to_vint64(&tmp); 600309007Sdelphij M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 601275970Scy ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 602275970Scy ntp -= res.D_s.lo; /* cycle difference */ 603275970Scy M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 604275970Scy 605289764Sglebius# endif /* no 64bit scalars */ 606275970Scy 607275970Scy return res; 608275970Scy} 609275970Scy 610275970Scy/* 611309007Sdelphij *--------------------------------------------------------------------- 612275970Scy * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP 613275970Scy * scale with proper epoch unfolding around a given pivot or the current 614275970Scy * system time. 615275970Scy * 616275970Scy * Note: The pivot must be given in the UN*X time domain! 617275970Scy * 618275970Scy * This is also a periodic extension, but since the cycle is 2^32 and 619275970Scy * the shift is 2^31, we can do some *very* fast math without explicit 620275970Scy * divisions. 621309007Sdelphij *--------------------------------------------------------------------- 622275970Scy */ 623275970Scyvint64 624275970Scyntpcal_ntp_to_ntp( 625275970Scy uint32_t ntp, 626275970Scy const time_t *pivot 627275970Scy ) 628275970Scy{ 629275970Scy vint64 res; 630275970Scy 631289764Sglebius# if defined(HAVE_INT64) 632275970Scy 633275970Scy res.q_s = (pivot) 634275970Scy ? *pivot 635275970Scy : now(); 636309007Sdelphij res.Q_s -= 0x80000000u; /* unshift of half range */ 637275970Scy res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ 638275970Scy ntp -= res.D_s.lo; /* cycle difference */ 639275970Scy res.Q_s += (uint64_t)ntp; /* get expanded time */ 640275970Scy 641289764Sglebius# else /* no 64bit scalars */ 642282408Scy 643275970Scy time_t tmp; 644282408Scy 645275970Scy tmp = (pivot) 646275970Scy ? *pivot 647275970Scy : now(); 648275970Scy res = time_to_vint64(&tmp); 649275970Scy M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 650275970Scy M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */ 651275970Scy ntp -= res.D_s.lo; /* cycle difference */ 652275970Scy M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 653275970Scy 654289764Sglebius# endif /* no 64bit scalars */ 655275970Scy 656275970Scy return res; 657275970Scy} 658275970Scy 659275970Scy 660275970Scy/* 661309007Sdelphij * ==================================================================== 662275970Scy * 663275970Scy * Splitting values to composite entities 664275970Scy * 665309007Sdelphij * ==================================================================== 666275970Scy */ 667275970Scy 668275970Scy/* 669309007Sdelphij *--------------------------------------------------------------------- 670275970Scy * Split a 64bit seconds value into elapsed days in 'res.hi' and 671275970Scy * elapsed seconds since midnight in 'res.lo' using explicit floor 672275970Scy * division. This function happily accepts negative time values as 673275970Scy * timestamps before the respective epoch start. 674309007Sdelphij *--------------------------------------------------------------------- 675275970Scy */ 676275970Scyntpcal_split 677275970Scyntpcal_daysplit( 678275970Scy const vint64 *ts 679275970Scy ) 680275970Scy{ 681275970Scy ntpcal_split res; 682358659Scy uint32_t Q, R; 683275970Scy 684358659Scy# if defined(HAVE_64BITREGS) 685358659Scy 686358659Scy /* Assume we have 64bit registers an can do a divison by 687358659Scy * constant reasonably fast using the one's complement trick.. 688289764Sglebius */ 689358659Scy uint64_t sf64 = (uint64_t)-(ts->q_s < 0); 690358659Scy Q = (uint32_t)(sf64 ^ ((sf64 ^ ts->Q_s) / SECSPERDAY)); 691358659Scy R = (uint32_t)(ts->Q_s - Q * SECSPERDAY); 692358659Scy 693358659Scy# elif defined(UINT64_MAX) && !defined(__arm__) 694358659Scy 695358659Scy /* We rely on the compiler to do efficient 64bit divisions as 696358659Scy * good as possible. Which might or might not be true. At least 697358659Scy * for ARM CPUs, the sum-by-digit code in the next section is 698358659Scy * faster for many compilers. (This might change over time, but 699358659Scy * the 64bit-by-32bit division will never outperform the exact 700358659Scy * division by a substantial factor....) 701358659Scy */ 702289764Sglebius if (ts->q_s < 0) 703289764Sglebius Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY); 704289764Sglebius else 705358659Scy Q = (uint32_t)( ts->Q_s / SECSPERDAY); 706358659Scy R = ts->D_s.lo - Q * SECSPERDAY; 707275970Scy 708289764Sglebius# else 709275970Scy 710358659Scy /* We don't have 64bit regs. That hurts a bit. 711289764Sglebius * 712358659Scy * Here we use a mean trick to get away with just one explicit 713358659Scy * modulo operation and pure 32bit ops. 714358659Scy * 715358659Scy * Remember: 86400 <--> 128 * 675 716358659Scy * 717358659Scy * So we discard the lowest 7 bit and do an exact division by 718358659Scy * 675, modulo 2**32. 719358659Scy * 720358659Scy * First we shift out the lower 7 bits. 721358659Scy * 722358659Scy * Then we use a digit-wise pseudo-reduction, where a 'digit' is 723358659Scy * actually a 16-bit group. This is followed by a full reduction 724358659Scy * with a 'true' division step. This yields the modulus of the 725358659Scy * full 64bit value. The sign bit gets some extra treatment. 726358659Scy * 727358659Scy * Then we decrement the lower limb by that modulus, so it is 728358659Scy * exactly divisible by 675. [*] 729358659Scy * 730358659Scy * Then we multiply with the modular inverse of 675 (mod 2**32) 731358659Scy * and voila, we have the result. 732358659Scy * 733358659Scy * Special Thanks to Henry S. Warren and his "Hacker's delight" 734358659Scy * for giving that idea. 735358659Scy * 736358659Scy * (Note[*]: that's not the full truth. We would have to 737358659Scy * subtract the modulus from the full 64 bit number to get a 738358659Scy * number that is divisible by 675. But since we use the 739358659Scy * multiplicative inverse (mod 2**32) there's no reason to carry 740358659Scy * the subtraction into the upper bits!) 741289764Sglebius */ 742358659Scy uint32_t al = ts->D_s.lo; 743358659Scy uint32_t ah = ts->D_s.hi; 744282408Scy 745358659Scy /* shift out the lower 7 bits, smash sign bit */ 746358659Scy al = (al >> 7) | (ah << 25); 747358659Scy ah = (ah >> 7) & 0x00FFFFFFu; 748275970Scy 749358659Scy R = (ts->d_s.hi < 0) ? 239 : 0;/* sign bit value */ 750358659Scy R += (al & 0xFFFF); 751358659Scy R += (al >> 16 ) * 61u; /* 2**16 % 675 */ 752358659Scy R += (ah & 0xFFFF) * 346u; /* 2**32 % 675 */ 753358659Scy R += (ah >> 16 ) * 181u; /* 2**48 % 675 */ 754358659Scy R %= 675u; /* final reduction */ 755358659Scy Q = (al - R) * 0x2D21C10Bu; /* modinv(675, 2**32) */ 756358659Scy R = (R << 7) | (ts->d_s.lo & 0x07F); 757275970Scy 758358659Scy# endif 759358659Scy 760358659Scy res.hi = uint32_2cpl_to_int32(Q); 761358659Scy res.lo = R; 762358659Scy 763358659Scy return res; 764358659Scy} 765358659Scy 766358659Scy/* 767358659Scy *--------------------------------------------------------------------- 768358659Scy * Split a 64bit seconds value into elapsed weeks in 'res.hi' and 769358659Scy * elapsed seconds since week start in 'res.lo' using explicit floor 770358659Scy * division. This function happily accepts negative time values as 771358659Scy * timestamps before the respective epoch start. 772358659Scy *--------------------------------------------------------------------- 773358659Scy */ 774358659Scyntpcal_split 775358659Scyntpcal_weeksplit( 776358659Scy const vint64 *ts 777358659Scy ) 778358659Scy{ 779358659Scy ntpcal_split res; 780358659Scy uint32_t Q, R; 781358659Scy 782358659Scy /* This is a very close relative to the day split function; for 783358659Scy * details, see there! 784289764Sglebius */ 785275970Scy 786358659Scy# if defined(HAVE_64BITREGS) 787358659Scy 788358659Scy uint64_t sf64 = (uint64_t)-(ts->q_s < 0); 789358659Scy Q = (uint32_t)(sf64 ^ ((sf64 ^ ts->Q_s) / SECSPERWEEK)); 790358659Scy R = (uint32_t)(ts->Q_s - Q * SECSPERWEEK); 791358659Scy 792358659Scy# elif defined(UINT64_MAX) && !defined(__arm__) 793358659Scy 794358659Scy if (ts->q_s < 0) 795358659Scy Q = ~(uint32_t)(~ts->Q_s / SECSPERWEEK); 796358659Scy else 797358659Scy Q = (uint32_t)( ts->Q_s / SECSPERWEEK); 798358659Scy R = ts->D_s.lo - Q * SECSPERWEEK; 799358659Scy 800358659Scy# else 801358659Scy 802358659Scy /* Remember: 7*86400 <--> 604800 <--> 128 * 4725 */ 803358659Scy uint32_t al = ts->D_s.lo; 804358659Scy uint32_t ah = ts->D_s.hi; 805358659Scy 806358659Scy al = (al >> 7) | (ah << 25); 807358659Scy ah = (ah >> 7) & 0x00FFFFFF; 808358659Scy 809358659Scy R = (ts->d_s.hi < 0) ? 2264 : 0;/* sign bit value */ 810358659Scy R += (al & 0xFFFF); 811358659Scy R += (al >> 16 ) * 4111u; /* 2**16 % 4725 */ 812358659Scy R += (ah & 0xFFFF) * 3721u; /* 2**32 % 4725 */ 813358659Scy R += (ah >> 16 ) * 2206u; /* 2**48 % 4725 */ 814358659Scy R %= 4725u; /* final reduction */ 815358659Scy Q = (al - R) * 0x98BBADDDu; /* modinv(4725, 2**32) */ 816358659Scy R = (R << 7) | (ts->d_s.lo & 0x07F); 817358659Scy 818289764Sglebius# endif 819358659Scy 820289764Sglebius res.hi = uint32_2cpl_to_int32(Q); 821358659Scy res.lo = R; 822275970Scy 823275970Scy return res; 824275970Scy} 825275970Scy 826275970Scy/* 827309007Sdelphij *--------------------------------------------------------------------- 828275970Scy * Split a 32bit seconds value into h/m/s and excessive days. This 829275970Scy * function happily accepts negative time values as timestamps before 830275970Scy * midnight. 831309007Sdelphij *--------------------------------------------------------------------- 832275970Scy */ 833275970Scystatic int32_t 834275970Scypriv_timesplit( 835275970Scy int32_t split[3], 836275970Scy int32_t ts 837275970Scy ) 838275970Scy{ 839289764Sglebius /* Do 3 chained floor divisions by positive constants, using the 840289764Sglebius * one's complement trick and factoring out the intermediate XOR 841289764Sglebius * ops to reduce the number of operations. 842289764Sglebius */ 843358659Scy uint32_t us, um, uh, ud, sf32; 844275970Scy 845358659Scy sf32 = int32_sflag(ts); 846275970Scy 847358659Scy us = (uint32_t)ts; 848358659Scy um = (sf32 ^ us) / SECSPERMIN; 849289764Sglebius uh = um / MINSPERHR; 850289764Sglebius ud = uh / HRSPERDAY; 851275970Scy 852358659Scy um ^= sf32; 853358659Scy uh ^= sf32; 854358659Scy ud ^= sf32; 855289764Sglebius 856289764Sglebius split[0] = (int32_t)(uh - ud * HRSPERDAY ); 857289764Sglebius split[1] = (int32_t)(um - uh * MINSPERHR ); 858289764Sglebius split[2] = (int32_t)(us - um * SECSPERMIN); 859358659Scy 860289764Sglebius return uint32_2cpl_to_int32(ud); 861275970Scy} 862275970Scy 863275970Scy/* 864309007Sdelphij *--------------------------------------------------------------------- 865275970Scy * Given the number of elapsed days in the calendar era, split this 866275970Scy * number into the number of elapsed years in 'res.hi' and the number 867275970Scy * of elapsed days of that year in 'res.lo'. 868275970Scy * 869275970Scy * if 'isleapyear' is not NULL, it will receive an integer that is 0 for 870275970Scy * regular years and a non-zero value for leap years. 871275970Scy *--------------------------------------------------------------------- 872275970Scy */ 873275970Scyntpcal_split 874275970Scyntpcal_split_eradays( 875275970Scy int32_t days, 876275970Scy int *isleapyear 877275970Scy ) 878275970Scy{ 879358659Scy /* Use the fast cycle split algorithm here, to calculate the 880289764Sglebius * centuries and years in a century with one division each. This 881289764Sglebius * reduces the number of division operations to two, but is 882358659Scy * susceptible to internal range overflow. We take some extra 883358659Scy * steps to avoid the gap. 884289764Sglebius */ 885275970Scy ntpcal_split res; 886289764Sglebius int32_t n100, n001; /* calendar year cycles */ 887358659Scy uint32_t uday, Q; 888282408Scy 889358659Scy /* split off centuries first 890358659Scy * 891358659Scy * We want to execute '(days * 4 + 3) /% 146097' under floor 892358659Scy * division rules in the first step. Well, actually we want to 893358659Scy * calculate 'floor((days + 0.75) / 36524.25)', but we want to 894358659Scy * do it in scaled integer calculation. 895358659Scy */ 896358659Scy# if defined(HAVE_64BITREGS) 897358659Scy 898358659Scy /* not too complicated with an intermediate 64bit value */ 899358659Scy uint64_t ud64, sf64; 900358659Scy ud64 = ((uint64_t)days << 2) | 3u; 901358659Scy sf64 = (uint64_t)-(days < 0); 902358659Scy Q = (uint32_t)(sf64 ^ ((sf64 ^ ud64) / GREGORIAN_CYCLE_DAYS)); 903358659Scy uday = (uint32_t)(ud64 - Q * GREGORIAN_CYCLE_DAYS); 904289764Sglebius n100 = uint32_2cpl_to_int32(Q); 905358659Scy 906358659Scy# else 907358659Scy 908358659Scy /* '4*days+3' suffers from range overflow when going to the 909358659Scy * limits. We solve this by doing an exact division (mod 2^32) 910358659Scy * after caclulating the remainder first. 911358659Scy * 912358659Scy * We start with a partial reduction by digit sums, extracting 913358659Scy * the upper bits from the original value before they get lost 914358659Scy * by scaling, and do one full division step to get the true 915358659Scy * remainder. Then a final multiplication with the 916358659Scy * multiplicative inverse of 146097 (mod 2^32) gives us the full 917358659Scy * quotient. 918358659Scy * 919358659Scy * (-2^33) % 146097 --> 130717 : the sign bit value 920358659Scy * ( 2^20) % 146097 --> 25897 : the upper digit value 921358659Scy * modinv(146097, 2^32) --> 660721233 : the inverse 922358659Scy */ 923358659Scy uint32_t ux = ((uint32_t)days << 2) | 3; 924358659Scy uday = (days < 0) ? 130717u : 0u; /* sign dgt */ 925358659Scy uday += ((days >> 18) & 0x01FFFu) * 25897u; /* hi dgt (src!) */ 926358659Scy uday += (ux & 0xFFFFFu); /* lo dgt */ 927358659Scy uday %= GREGORIAN_CYCLE_DAYS; /* full reduction */ 928358659Scy Q = (ux - uday) * 660721233u; /* exact div */ 929358659Scy n100 = uint32_2cpl_to_int32(Q); 930358659Scy 931358659Scy# endif 932358659Scy 933289764Sglebius /* Split off years in century -- days >= 0 here, and we're far 934289764Sglebius * away from integer overflow trouble now. */ 935289764Sglebius uday |= 3; 936358659Scy n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 937358659Scy uday -= n001 * GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 938282408Scy 939289764Sglebius /* Assemble the year and day in year */ 940289764Sglebius res.hi = n100 * 100 + n001; 941289764Sglebius res.lo = uday / 4u; 942289764Sglebius 943358659Scy /* Possibly set the leap year flag */ 944358659Scy if (isleapyear) { 945358659Scy uint32_t tc = (uint32_t)n100 + 1; 946358659Scy uint32_t ty = (uint32_t)n001 + 1; 947358659Scy *isleapyear = !(ty & 3) 948358659Scy && ((ty != 100) || !(tc & 3)); 949358659Scy } 950275970Scy return res; 951275970Scy} 952275970Scy 953275970Scy/* 954275970Scy *--------------------------------------------------------------------- 955275970Scy * Given a number of elapsed days in a year and a leap year indicator, 956275970Scy * split the number of elapsed days into the number of elapsed months in 957275970Scy * 'res.hi' and the number of elapsed days of that month in 'res.lo'. 958275970Scy * 959275970Scy * This function will fail and return {-1,-1} if the number of elapsed 960275970Scy * days is not in the valid range! 961275970Scy *--------------------------------------------------------------------- 962275970Scy */ 963275970Scyntpcal_split 964275970Scyntpcal_split_yeardays( 965275970Scy int32_t eyd, 966358659Scy int isleap 967275970Scy ) 968275970Scy{ 969358659Scy /* Use the unshifted-year, February-with-30-days approach here. 970358659Scy * Fractional interpolations are used in both directions, with 971358659Scy * the smallest power-of-two divider to avoid any true division. 972358659Scy */ 973358659Scy ntpcal_split res = {-1, -1}; 974275970Scy 975358659Scy /* convert 'isleap' to number of defective days */ 976358659Scy isleap = 1 + !isleap; 977358659Scy /* adjust for February of 30 nominal days */ 978358659Scy if (eyd >= 61 - isleap) 979358659Scy eyd += isleap; 980358659Scy /* if in range, convert to months and days in month */ 981358659Scy if (eyd >= 0 && eyd < 367) { 982358659Scy res.hi = (eyd * 67 + 32) >> 11; 983358659Scy res.lo = eyd - ((489 * res.hi + 8) >> 4); 984275970Scy } 985275970Scy 986275970Scy return res; 987275970Scy} 988275970Scy 989275970Scy/* 990275970Scy *--------------------------------------------------------------------- 991275970Scy * Convert a RD into the date part of a 'struct calendar'. 992275970Scy *--------------------------------------------------------------------- 993275970Scy */ 994275970Scyint 995275970Scyntpcal_rd_to_date( 996275970Scy struct calendar *jd, 997275970Scy int32_t rd 998275970Scy ) 999275970Scy{ 1000275970Scy ntpcal_split split; 1001289764Sglebius int leapy; 1002289764Sglebius u_int ymask; 1003275970Scy 1004358659Scy /* Get day-of-week first. It's simply the RD (mod 7)... */ 1005358659Scy jd->weekday = i32mod7(rd); 1006275970Scy 1007289764Sglebius split = ntpcal_split_eradays(rd - 1, &leapy); 1008289764Sglebius /* Get year and day-of-year, with overflow check. If any of the 1009289764Sglebius * upper 16 bits is set after shifting to unity-based years, we 1010289764Sglebius * will have an overflow when converting to an unsigned 16bit 1011289764Sglebius * year. Shifting to the right is OK here, since it does not 1012289764Sglebius * matter if the shift is logic or arithmetic. 1013289764Sglebius */ 1014289764Sglebius split.hi += 1; 1015289764Sglebius ymask = 0u - ((split.hi >> 16) == 0); 1016289764Sglebius jd->year = (uint16_t)(split.hi & ymask); 1017275970Scy jd->yearday = (uint16_t)split.lo + 1; 1018275970Scy 1019275970Scy /* convert to month and mday */ 1020289764Sglebius split = ntpcal_split_yeardays(split.lo, leapy); 1021275970Scy jd->month = (uint8_t)split.hi + 1; 1022275970Scy jd->monthday = (uint8_t)split.lo + 1; 1023275970Scy 1024289764Sglebius return ymask ? leapy : -1; 1025275970Scy} 1026275970Scy 1027275970Scy/* 1028275970Scy *--------------------------------------------------------------------- 1029275970Scy * Convert a RD into the date part of a 'struct tm'. 1030275970Scy *--------------------------------------------------------------------- 1031275970Scy */ 1032275970Scyint 1033275970Scyntpcal_rd_to_tm( 1034275970Scy struct tm *utm, 1035275970Scy int32_t rd 1036275970Scy ) 1037275970Scy{ 1038275970Scy ntpcal_split split; 1039289764Sglebius int leapy; 1040275970Scy 1041275970Scy /* get day-of-week first */ 1042358659Scy utm->tm_wday = i32mod7(rd); 1043275970Scy 1044275970Scy /* get year and day-of-year */ 1045289764Sglebius split = ntpcal_split_eradays(rd - 1, &leapy); 1046275970Scy utm->tm_year = split.hi - 1899; 1047275970Scy utm->tm_yday = split.lo; /* 0-based */ 1048275970Scy 1049275970Scy /* convert to month and mday */ 1050289764Sglebius split = ntpcal_split_yeardays(split.lo, leapy); 1051275970Scy utm->tm_mon = split.hi; /* 0-based */ 1052275970Scy utm->tm_mday = split.lo + 1; /* 1-based */ 1053275970Scy 1054289764Sglebius return leapy; 1055275970Scy} 1056275970Scy 1057275970Scy/* 1058275970Scy *--------------------------------------------------------------------- 1059275970Scy * Take a value of seconds since midnight and split it into hhmmss in a 1060275970Scy * 'struct calendar'. 1061275970Scy *--------------------------------------------------------------------- 1062275970Scy */ 1063275970Scyint32_t 1064275970Scyntpcal_daysec_to_date( 1065275970Scy struct calendar *jd, 1066275970Scy int32_t sec 1067275970Scy ) 1068275970Scy{ 1069275970Scy int32_t days; 1070275970Scy int ts[3]; 1071282408Scy 1072275970Scy days = priv_timesplit(ts, sec); 1073275970Scy jd->hour = (uint8_t)ts[0]; 1074275970Scy jd->minute = (uint8_t)ts[1]; 1075275970Scy jd->second = (uint8_t)ts[2]; 1076275970Scy 1077275970Scy return days; 1078275970Scy} 1079275970Scy 1080275970Scy/* 1081275970Scy *--------------------------------------------------------------------- 1082275970Scy * Take a value of seconds since midnight and split it into hhmmss in a 1083275970Scy * 'struct tm'. 1084275970Scy *--------------------------------------------------------------------- 1085275970Scy */ 1086275970Scyint32_t 1087275970Scyntpcal_daysec_to_tm( 1088275970Scy struct tm *utm, 1089275970Scy int32_t sec 1090275970Scy ) 1091275970Scy{ 1092275970Scy int32_t days; 1093275970Scy int32_t ts[3]; 1094282408Scy 1095275970Scy days = priv_timesplit(ts, sec); 1096275970Scy utm->tm_hour = ts[0]; 1097275970Scy utm->tm_min = ts[1]; 1098275970Scy utm->tm_sec = ts[2]; 1099275970Scy 1100275970Scy return days; 1101275970Scy} 1102275970Scy 1103275970Scy/* 1104275970Scy *--------------------------------------------------------------------- 1105275970Scy * take a split representation for day/second-of-day and day offset 1106275970Scy * and convert it to a 'struct calendar'. The seconds will be normalised 1107275970Scy * into the range of a day, and the day will be adjusted accordingly. 1108275970Scy * 1109275970Scy * returns >0 if the result is in a leap year, 0 if in a regular 1110275970Scy * year and <0 if the result did not fit into the calendar struct. 1111275970Scy *--------------------------------------------------------------------- 1112275970Scy */ 1113275970Scyint 1114275970Scyntpcal_daysplit_to_date( 1115275970Scy struct calendar *jd, 1116275970Scy const ntpcal_split *ds, 1117275970Scy int32_t dof 1118275970Scy ) 1119275970Scy{ 1120275970Scy dof += ntpcal_daysec_to_date(jd, ds->lo); 1121275970Scy return ntpcal_rd_to_date(jd, ds->hi + dof); 1122275970Scy} 1123275970Scy 1124275970Scy/* 1125275970Scy *--------------------------------------------------------------------- 1126275970Scy * take a split representation for day/second-of-day and day offset 1127275970Scy * and convert it to a 'struct tm'. The seconds will be normalised 1128275970Scy * into the range of a day, and the day will be adjusted accordingly. 1129275970Scy * 1130275970Scy * returns 1 if the result is in a leap year and zero if in a regular 1131275970Scy * year. 1132275970Scy *--------------------------------------------------------------------- 1133275970Scy */ 1134275970Scyint 1135275970Scyntpcal_daysplit_to_tm( 1136275970Scy struct tm *utm, 1137275970Scy const ntpcal_split *ds , 1138275970Scy int32_t dof 1139275970Scy ) 1140275970Scy{ 1141275970Scy dof += ntpcal_daysec_to_tm(utm, ds->lo); 1142275970Scy 1143275970Scy return ntpcal_rd_to_tm(utm, ds->hi + dof); 1144275970Scy} 1145275970Scy 1146275970Scy/* 1147275970Scy *--------------------------------------------------------------------- 1148275970Scy * Take a UN*X time and convert to a calendar structure. 1149275970Scy *--------------------------------------------------------------------- 1150275970Scy */ 1151275970Scyint 1152275970Scyntpcal_time_to_date( 1153275970Scy struct calendar *jd, 1154275970Scy const vint64 *ts 1155275970Scy ) 1156275970Scy{ 1157275970Scy ntpcal_split ds; 1158275970Scy 1159275970Scy ds = ntpcal_daysplit(ts); 1160275970Scy ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1161275970Scy ds.hi += DAY_UNIX_STARTS; 1162275970Scy 1163275970Scy return ntpcal_rd_to_date(jd, ds.hi); 1164275970Scy} 1165275970Scy 1166275970Scy 1167275970Scy/* 1168309007Sdelphij * ==================================================================== 1169275970Scy * 1170275970Scy * merging composite entities 1171275970Scy * 1172309007Sdelphij * ==================================================================== 1173275970Scy */ 1174275970Scy 1175358659Scy#if !defined(HAVE_INT64) 1176358659Scy/* multiplication helper. Seconds in days and weeks are multiples of 128, 1177358659Scy * and without that factor fit well into 16 bit. So a multiplication 1178358659Scy * of 32bit by 16bit and some shifting can be used on pure 32bit machines 1179358659Scy * with compilers that do not support 64bit integers. 1180358659Scy * 1181358659Scy * Calculate ( hi * mul * 128 ) + lo 1182358659Scy */ 1183358659Scystatic vint64 1184358659Scy_dwjoin( 1185358659Scy uint16_t mul, 1186358659Scy int32_t hi, 1187358659Scy int32_t lo 1188358659Scy ) 1189358659Scy{ 1190358659Scy vint64 res; 1191358659Scy uint32_t p1, p2, sf; 1192358659Scy 1193358659Scy /* get sign flag and absolute value of 'hi' in p1 */ 1194358659Scy sf = (uint32_t)-(hi < 0); 1195358659Scy p1 = ((uint32_t)hi + sf) ^ sf; 1196358659Scy 1197358659Scy /* assemble major units: res <- |hi| * mul */ 1198358659Scy res.D_s.lo = (p1 & 0xFFFF) * mul; 1199358659Scy res.D_s.hi = 0; 1200358659Scy p1 = (p1 >> 16) * mul; 1201358659Scy p2 = p1 >> 16; 1202358659Scy p1 = p1 << 16; 1203358659Scy M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1204358659Scy 1205358659Scy /* mul by 128, using shift: res <-- res << 7 */ 1206358659Scy res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25); 1207358659Scy res.D_s.lo = (res.D_s.lo << 7); 1208358659Scy 1209358659Scy /* fix up sign: res <-- (res + [sf|sf]) ^ [sf|sf] */ 1210358659Scy M_ADD(res.D_s.hi, res.D_s.lo, sf, sf); 1211358659Scy res.D_s.lo ^= sf; 1212358659Scy res.D_s.hi ^= sf; 1213358659Scy 1214358659Scy /* properly add seconds: res <-- res + [sx(lo)|lo] */ 1215358659Scy p2 = (uint32_t)-(lo < 0); 1216358659Scy p1 = (uint32_t)lo; 1217358659Scy M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1218358659Scy return res; 1219358659Scy} 1220358659Scy#endif 1221358659Scy 1222275970Scy/* 1223275970Scy *--------------------------------------------------------------------- 1224275970Scy * Merge a number of days and a number of seconds into seconds, 1225275970Scy * expressed in 64 bits to avoid overflow. 1226275970Scy *--------------------------------------------------------------------- 1227275970Scy */ 1228275970Scyvint64 1229275970Scyntpcal_dayjoin( 1230275970Scy int32_t days, 1231275970Scy int32_t secs 1232275970Scy ) 1233275970Scy{ 1234275970Scy vint64 res; 1235275970Scy 1236289764Sglebius# if defined(HAVE_INT64) 1237275970Scy 1238275970Scy res.q_s = days; 1239275970Scy res.q_s *= SECSPERDAY; 1240275970Scy res.q_s += secs; 1241275970Scy 1242289764Sglebius# else 1243275970Scy 1244358659Scy res = _dwjoin(675, days, secs); 1245275970Scy 1246358659Scy# endif 1247275970Scy 1248358659Scy return res; 1249358659Scy} 1250275970Scy 1251358659Scy/* 1252358659Scy *--------------------------------------------------------------------- 1253358659Scy * Merge a number of weeks and a number of seconds into seconds, 1254358659Scy * expressed in 64 bits to avoid overflow. 1255358659Scy *--------------------------------------------------------------------- 1256358659Scy */ 1257358659Scyvint64 1258358659Scyntpcal_weekjoin( 1259358659Scy int32_t week, 1260358659Scy int32_t secs 1261358659Scy ) 1262358659Scy{ 1263358659Scy vint64 res; 1264275970Scy 1265358659Scy# if defined(HAVE_INT64) 1266282408Scy 1267358659Scy res.q_s = week; 1268358659Scy res.q_s *= SECSPERWEEK; 1269358659Scy res.q_s += secs; 1270275970Scy 1271358659Scy# else 1272358659Scy 1273358659Scy res = _dwjoin(4725, week, secs); 1274358659Scy 1275289764Sglebius# endif 1276275970Scy 1277275970Scy return res; 1278275970Scy} 1279275970Scy 1280275970Scy/* 1281275970Scy *--------------------------------------------------------------------- 1282289764Sglebius * get leap years since epoch in elapsed years 1283275970Scy *--------------------------------------------------------------------- 1284275970Scy */ 1285275970Scyint32_t 1286289764Sglebiusntpcal_leapyears_in_years( 1287275970Scy int32_t years 1288275970Scy ) 1289275970Scy{ 1290289764Sglebius /* We use the in-out-in algorithm here, using the one's 1291289764Sglebius * complement division trick for negative numbers. The chained 1292289764Sglebius * division sequence by 4/25/4 gives the compiler the chance to 1293289764Sglebius * get away with only one true division and doing shifts otherwise. 1294289764Sglebius */ 1295275970Scy 1296358659Scy uint32_t sf32, sum, uyear; 1297275970Scy 1298358659Scy sf32 = int32_sflag(years); 1299358659Scy uyear = (uint32_t)years; 1300358659Scy uyear ^= sf32; 1301289764Sglebius 1302289764Sglebius sum = (uyear /= 4u); /* 4yr rule --> IN */ 1303289764Sglebius sum -= (uyear /= 25u); /* 100yr rule --> OUT */ 1304289764Sglebius sum += (uyear /= 4u); /* 400yr rule --> IN */ 1305289764Sglebius 1306289764Sglebius /* Thanks to the alternation of IN/OUT/IN we can do the sum 1307289764Sglebius * directly and have a single one's complement operation 1308289764Sglebius * here. (Only if the years are negative, of course.) Otherwise 1309289764Sglebius * the one's complement would have to be done when 1310289764Sglebius * adding/subtracting the terms. 1311275970Scy */ 1312358659Scy return uint32_2cpl_to_int32(sf32 ^ sum); 1313275970Scy} 1314275970Scy 1315275970Scy/* 1316275970Scy *--------------------------------------------------------------------- 1317289764Sglebius * Convert elapsed years in Era into elapsed days in Era. 1318289764Sglebius *--------------------------------------------------------------------- 1319289764Sglebius */ 1320289764Sglebiusint32_t 1321289764Sglebiusntpcal_days_in_years( 1322289764Sglebius int32_t years 1323289764Sglebius ) 1324289764Sglebius{ 1325289764Sglebius return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years); 1326289764Sglebius} 1327289764Sglebius 1328289764Sglebius/* 1329289764Sglebius *--------------------------------------------------------------------- 1330275970Scy * Convert a number of elapsed month in a year into elapsed days in year. 1331275970Scy * 1332275970Scy * The month will be normalized, and 'res.hi' will contain the 1333275970Scy * excessive years that must be considered when converting the years, 1334275970Scy * while 'res.lo' will contain the number of elapsed days since start 1335275970Scy * of the year. 1336275970Scy * 1337275970Scy * This code uses the shifted-month-approach to convert month to days, 1338275970Scy * because then there is no need to have explicit leap year 1339275970Scy * information. The slight disadvantage is that for most month values 1340275970Scy * the result is a negative value, and the year excess is one; the 1341275970Scy * conversion is then simply based on the start of the following year. 1342275970Scy *--------------------------------------------------------------------- 1343275970Scy */ 1344275970Scyntpcal_split 1345275970Scyntpcal_days_in_months( 1346275970Scy int32_t m 1347275970Scy ) 1348275970Scy{ 1349275970Scy ntpcal_split res; 1350275970Scy 1351358659Scy /* Add ten months with proper year adjustment. */ 1352358659Scy if (m < 2) { 1353358659Scy res.lo = m + 10; 1354358659Scy res.hi = 0; 1355358659Scy } else { 1356358659Scy res.lo = m - 2; 1357358659Scy res.hi = 1; 1358358659Scy } 1359289764Sglebius 1360358659Scy /* Possibly normalise by floor division. This does not hapen for 1361358659Scy * input in normal range. */ 1362275970Scy if (res.lo < 0 || res.lo >= 12) { 1363358659Scy uint32_t mu, Q, sf32; 1364358659Scy sf32 = int32_sflag(res.lo); 1365358659Scy mu = (uint32_t)res.lo; 1366358659Scy Q = sf32 ^ ((sf32 ^ mu) / 12u); 1367358659Scy 1368289764Sglebius res.hi += uint32_2cpl_to_int32(Q); 1369358659Scy res.lo = mu - Q * 12u; 1370275970Scy } 1371275970Scy 1372358659Scy /* Get cummulated days in year with unshift. Use the fractional 1373358659Scy * interpolation with smallest possible power of two in the 1374358659Scy * divider. 1375358659Scy */ 1376358659Scy res.lo = ((res.lo * 979 + 16) >> 5) - 306; 1377358659Scy 1378275970Scy return res; 1379275970Scy} 1380275970Scy 1381275970Scy/* 1382275970Scy *--------------------------------------------------------------------- 1383275970Scy * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1384275970Scy * days in Gregorian epoch. 1385275970Scy * 1386275970Scy * If you want to convert years and days-of-year, just give a month of 1387275970Scy * zero. 1388275970Scy *--------------------------------------------------------------------- 1389275970Scy */ 1390275970Scyint32_t 1391275970Scyntpcal_edate_to_eradays( 1392275970Scy int32_t years, 1393275970Scy int32_t mons, 1394275970Scy int32_t mdays 1395275970Scy ) 1396275970Scy{ 1397275970Scy ntpcal_split tmp; 1398275970Scy int32_t res; 1399275970Scy 1400275970Scy if (mons) { 1401275970Scy tmp = ntpcal_days_in_months(mons); 1402275970Scy res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; 1403275970Scy } else 1404275970Scy res = ntpcal_days_in_years(years); 1405275970Scy res += mdays; 1406275970Scy 1407275970Scy return res; 1408275970Scy} 1409275970Scy 1410275970Scy/* 1411275970Scy *--------------------------------------------------------------------- 1412275970Scy * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1413275970Scy * days in year. 1414275970Scy * 1415309007Sdelphij * Note: This will give the true difference to the start of the given 1416309007Sdelphij * year, even if months & days are off-scale. 1417275970Scy *--------------------------------------------------------------------- 1418275970Scy */ 1419275970Scyint32_t 1420275970Scyntpcal_edate_to_yeardays( 1421275970Scy int32_t years, 1422275970Scy int32_t mons, 1423275970Scy int32_t mdays 1424275970Scy ) 1425275970Scy{ 1426275970Scy ntpcal_split tmp; 1427275970Scy 1428275970Scy if (0 <= mons && mons < 12) { 1429358659Scy if (mons >= 2) 1430358659Scy mdays -= 2 - is_leapyear(years+1); 1431358659Scy mdays += (489 * mons + 8) >> 4; 1432275970Scy } else { 1433275970Scy tmp = ntpcal_days_in_months(mons); 1434275970Scy mdays += tmp.lo 1435275970Scy + ntpcal_days_in_years(years + tmp.hi) 1436275970Scy - ntpcal_days_in_years(years); 1437275970Scy } 1438275970Scy 1439275970Scy return mdays; 1440275970Scy} 1441275970Scy 1442275970Scy/* 1443275970Scy *--------------------------------------------------------------------- 1444275970Scy * Convert elapsed days and the hour/minute/second information into 1445275970Scy * total seconds. 1446275970Scy * 1447275970Scy * If 'isvalid' is not NULL, do a range check on the time specification 1448275970Scy * and tell if the time input is in the normal range, permitting for a 1449275970Scy * single leapsecond. 1450275970Scy *--------------------------------------------------------------------- 1451275970Scy */ 1452275970Scyint32_t 1453275970Scyntpcal_etime_to_seconds( 1454275970Scy int32_t hours, 1455275970Scy int32_t minutes, 1456275970Scy int32_t seconds 1457275970Scy ) 1458275970Scy{ 1459275970Scy int32_t res; 1460275970Scy 1461275970Scy res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; 1462275970Scy 1463275970Scy return res; 1464275970Scy} 1465275970Scy 1466275970Scy/* 1467275970Scy *--------------------------------------------------------------------- 1468275970Scy * Convert the date part of a 'struct tm' (that is, year, month, 1469275970Scy * day-of-month) into the RD of that day. 1470275970Scy *--------------------------------------------------------------------- 1471275970Scy */ 1472275970Scyint32_t 1473275970Scyntpcal_tm_to_rd( 1474275970Scy const struct tm *utm 1475275970Scy ) 1476275970Scy{ 1477275970Scy return ntpcal_edate_to_eradays(utm->tm_year + 1899, 1478275970Scy utm->tm_mon, 1479275970Scy utm->tm_mday - 1) + 1; 1480275970Scy} 1481275970Scy 1482275970Scy/* 1483275970Scy *--------------------------------------------------------------------- 1484275970Scy * Convert the date part of a 'struct calendar' (that is, year, month, 1485275970Scy * day-of-month) into the RD of that day. 1486275970Scy *--------------------------------------------------------------------- 1487275970Scy */ 1488275970Scyint32_t 1489275970Scyntpcal_date_to_rd( 1490275970Scy const struct calendar *jd 1491275970Scy ) 1492275970Scy{ 1493275970Scy return ntpcal_edate_to_eradays((int32_t)jd->year - 1, 1494275970Scy (int32_t)jd->month - 1, 1495275970Scy (int32_t)jd->monthday - 1) + 1; 1496275970Scy} 1497275970Scy 1498275970Scy/* 1499275970Scy *--------------------------------------------------------------------- 1500275970Scy * convert a year number to rata die of year start 1501275970Scy *--------------------------------------------------------------------- 1502275970Scy */ 1503275970Scyint32_t 1504275970Scyntpcal_year_to_ystart( 1505275970Scy int32_t year 1506275970Scy ) 1507275970Scy{ 1508275970Scy return ntpcal_days_in_years(year - 1) + 1; 1509275970Scy} 1510275970Scy 1511275970Scy/* 1512275970Scy *--------------------------------------------------------------------- 1513275970Scy * For a given RD, get the RD of the associated year start, 1514275970Scy * that is, the RD of the last January,1st on or before that day. 1515275970Scy *--------------------------------------------------------------------- 1516275970Scy */ 1517275970Scyint32_t 1518275970Scyntpcal_rd_to_ystart( 1519275970Scy int32_t rd 1520275970Scy ) 1521275970Scy{ 1522275970Scy /* 1523275970Scy * Rather simple exercise: split the day number into elapsed 1524275970Scy * years and elapsed days, then remove the elapsed days from the 1525275970Scy * input value. Nice'n sweet... 1526275970Scy */ 1527275970Scy return rd - ntpcal_split_eradays(rd - 1, NULL).lo; 1528275970Scy} 1529275970Scy 1530275970Scy/* 1531275970Scy *--------------------------------------------------------------------- 1532275970Scy * For a given RD, get the RD of the associated month start. 1533275970Scy *--------------------------------------------------------------------- 1534275970Scy */ 1535275970Scyint32_t 1536275970Scyntpcal_rd_to_mstart( 1537275970Scy int32_t rd 1538275970Scy ) 1539275970Scy{ 1540275970Scy ntpcal_split split; 1541275970Scy int leaps; 1542275970Scy 1543275970Scy split = ntpcal_split_eradays(rd - 1, &leaps); 1544275970Scy split = ntpcal_split_yeardays(split.lo, leaps); 1545275970Scy 1546275970Scy return rd - split.lo; 1547275970Scy} 1548275970Scy 1549275970Scy/* 1550275970Scy *--------------------------------------------------------------------- 1551275970Scy * take a 'struct calendar' and get the seconds-of-day from it. 1552275970Scy *--------------------------------------------------------------------- 1553275970Scy */ 1554275970Scyint32_t 1555275970Scyntpcal_date_to_daysec( 1556275970Scy const struct calendar *jd 1557275970Scy ) 1558275970Scy{ 1559275970Scy return ntpcal_etime_to_seconds(jd->hour, jd->minute, 1560275970Scy jd->second); 1561275970Scy} 1562275970Scy 1563275970Scy/* 1564275970Scy *--------------------------------------------------------------------- 1565275970Scy * take a 'struct tm' and get the seconds-of-day from it. 1566275970Scy *--------------------------------------------------------------------- 1567275970Scy */ 1568275970Scyint32_t 1569275970Scyntpcal_tm_to_daysec( 1570275970Scy const struct tm *utm 1571275970Scy ) 1572275970Scy{ 1573275970Scy return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, 1574275970Scy utm->tm_sec); 1575275970Scy} 1576275970Scy 1577275970Scy/* 1578275970Scy *--------------------------------------------------------------------- 1579275970Scy * take a 'struct calendar' and convert it to a 'time_t' 1580275970Scy *--------------------------------------------------------------------- 1581275970Scy */ 1582275970Scytime_t 1583275970Scyntpcal_date_to_time( 1584275970Scy const struct calendar *jd 1585275970Scy ) 1586275970Scy{ 1587358659Scy vint64 join; 1588275970Scy int32_t days, secs; 1589275970Scy 1590275970Scy days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; 1591275970Scy secs = ntpcal_date_to_daysec(jd); 1592275970Scy join = ntpcal_dayjoin(days, secs); 1593275970Scy 1594275970Scy return vint64_to_time(&join); 1595275970Scy} 1596275970Scy 1597275970Scy 1598275970Scy/* 1599309007Sdelphij * ==================================================================== 1600275970Scy * 1601275970Scy * extended and unchecked variants of caljulian/caltontp 1602275970Scy * 1603309007Sdelphij * ==================================================================== 1604275970Scy */ 1605275970Scyint 1606275970Scyntpcal_ntp64_to_date( 1607275970Scy struct calendar *jd, 1608358659Scy const vint64 *ntp 1609275970Scy ) 1610275970Scy{ 1611275970Scy ntpcal_split ds; 1612282408Scy 1613275970Scy ds = ntpcal_daysplit(ntp); 1614275970Scy ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1615275970Scy 1616275970Scy return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); 1617275970Scy} 1618275970Scy 1619275970Scyint 1620275970Scyntpcal_ntp_to_date( 1621275970Scy struct calendar *jd, 1622275970Scy uint32_t ntp, 1623275970Scy const time_t *piv 1624275970Scy ) 1625275970Scy{ 1626275970Scy vint64 ntp64; 1627282408Scy 1628275970Scy /* 1629275970Scy * Unfold ntp time around current time into NTP domain. Split 1630275970Scy * into days and seconds, shift days into CE domain and 1631275970Scy * process the parts. 1632275970Scy */ 1633275970Scy ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1634275970Scy return ntpcal_ntp64_to_date(jd, &ntp64); 1635275970Scy} 1636275970Scy 1637275970Scy 1638275970Scyvint64 1639275970Scyntpcal_date_to_ntp64( 1640275970Scy const struct calendar *jd 1641275970Scy ) 1642275970Scy{ 1643275970Scy /* 1644275970Scy * Convert date to NTP. Ignore yearday, use d/m/y only. 1645275970Scy */ 1646275970Scy return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, 1647275970Scy ntpcal_date_to_daysec(jd)); 1648275970Scy} 1649275970Scy 1650275970Scy 1651275970Scyuint32_t 1652275970Scyntpcal_date_to_ntp( 1653275970Scy const struct calendar *jd 1654275970Scy ) 1655275970Scy{ 1656275970Scy /* 1657358659Scy * Get lower half of 64bit NTP timestamp from date/time. 1658275970Scy */ 1659275970Scy return ntpcal_date_to_ntp64(jd).d_s.lo; 1660275970Scy} 1661275970Scy 1662275970Scy 1663275970Scy 1664275970Scy/* 1665309007Sdelphij * ==================================================================== 1666275970Scy * 1667275970Scy * day-of-week calculations 1668275970Scy * 1669309007Sdelphij * ==================================================================== 1670275970Scy */ 1671275970Scy/* 1672275970Scy * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, 1673275970Scy * greater-or equal, closest, less-or-equal or less-than the given RDN 1674275970Scy * and denotes the given day-of-week 1675275970Scy */ 1676275970Scyint32_t 1677275970Scyntpcal_weekday_gt( 1678275970Scy int32_t rdn, 1679275970Scy int32_t dow 1680275970Scy ) 1681275970Scy{ 1682275970Scy return ntpcal_periodic_extend(rdn+1, dow, 7); 1683275970Scy} 1684275970Scy 1685275970Scyint32_t 1686275970Scyntpcal_weekday_ge( 1687275970Scy int32_t rdn, 1688275970Scy int32_t dow 1689275970Scy ) 1690275970Scy{ 1691275970Scy return ntpcal_periodic_extend(rdn, dow, 7); 1692275970Scy} 1693275970Scy 1694275970Scyint32_t 1695275970Scyntpcal_weekday_close( 1696275970Scy int32_t rdn, 1697275970Scy int32_t dow 1698275970Scy ) 1699275970Scy{ 1700275970Scy return ntpcal_periodic_extend(rdn-3, dow, 7); 1701275970Scy} 1702275970Scy 1703275970Scyint32_t 1704275970Scyntpcal_weekday_le( 1705275970Scy int32_t rdn, 1706275970Scy int32_t dow 1707275970Scy ) 1708275970Scy{ 1709275970Scy return ntpcal_periodic_extend(rdn, dow, -7); 1710275970Scy} 1711275970Scy 1712275970Scyint32_t 1713275970Scyntpcal_weekday_lt( 1714275970Scy int32_t rdn, 1715275970Scy int32_t dow 1716275970Scy ) 1717275970Scy{ 1718275970Scy return ntpcal_periodic_extend(rdn-1, dow, -7); 1719275970Scy} 1720275970Scy 1721275970Scy/* 1722309007Sdelphij * ==================================================================== 1723275970Scy * 1724275970Scy * ISO week-calendar conversions 1725275970Scy * 1726275970Scy * The ISO8601 calendar defines a calendar of years, weeks and weekdays. 1727275970Scy * It is related to the Gregorian calendar, and a ISO year starts at the 1728275970Scy * Monday closest to Jan,1st of the corresponding Gregorian year. A ISO 1729275970Scy * calendar year has always 52 or 53 weeks, and like the Grogrian 1730275970Scy * calendar the ISO8601 calendar repeats itself every 400 years, or 1731275970Scy * 146097 days, or 20871 weeks. 1732275970Scy * 1733275970Scy * While it is possible to write ISO calendar functions based on the 1734275970Scy * Gregorian calendar functions, the following implementation takes a 1735275970Scy * different approach, based directly on years and weeks. 1736275970Scy * 1737275970Scy * Analysis of the tabulated data shows that it is not possible to 1738275970Scy * interpolate from years to weeks over a full 400 year range; cyclic 1739275970Scy * shifts over 400 years do not provide a solution here. But it *is* 1740275970Scy * possible to interpolate over every single century of the 400-year 1741275970Scy * cycle. (The centennial leap year rule seems to be the culprit here.) 1742275970Scy * 1743275970Scy * It can be shown that a conversion from years to weeks can be done 1744275970Scy * using a linear transformation of the form 1745275970Scy * 1746275970Scy * w = floor( y * a + b ) 1747275970Scy * 1748275970Scy * where the slope a must hold to 1749275970Scy * 1750275970Scy * 52.1780821918 <= a < 52.1791044776 1751275970Scy * 1752275970Scy * and b must be chosen according to the selected slope and the number 1753275970Scy * of the century in a 400-year period. 1754275970Scy * 1755275970Scy * The inverse calculation can also be done in this way. Careful scaling 1756275970Scy * provides an unlimited set of integer coefficients a,k,b that enable 1757275970Scy * us to write the calulation in the form 1758275970Scy * 1759275970Scy * w = (y * a + b ) / k 1760275970Scy * y = (w * a' + b') / k' 1761275970Scy * 1762358659Scy * In this implementation the values of k and k' are chosen to be the 1763275970Scy * smallest possible powers of two, so the division can be implemented 1764275970Scy * as shifts if the optimiser chooses to do so. 1765275970Scy * 1766309007Sdelphij * ==================================================================== 1767275970Scy */ 1768275970Scy 1769275970Scy/* 1770275970Scy * Given a number of elapsed (ISO-)years since the begin of the 1771275970Scy * christian era, return the number of elapsed weeks corresponding to 1772275970Scy * the number of years. 1773275970Scy */ 1774275970Scyint32_t 1775275970Scyisocal_weeks_in_years( 1776275970Scy int32_t years 1777275970Scy ) 1778358659Scy{ 1779275970Scy /* 1780275970Scy * use: w = (y * 53431 + b[c]) / 1024 as interpolation 1781275970Scy */ 1782289764Sglebius static const uint16_t bctab[4] = { 157, 449, 597, 889 }; 1783275970Scy 1784358659Scy int32_t cs, cw; 1785358659Scy uint32_t cc, ci, yu, sf32; 1786275970Scy 1787358659Scy sf32 = int32_sflag(years); 1788358659Scy yu = (uint32_t)years; 1789358659Scy 1790289764Sglebius /* split off centuries, using floor division */ 1791358659Scy cc = sf32 ^ ((sf32 ^ yu) / 100u); 1792289764Sglebius yu -= cc * 100u; 1793275970Scy 1794289764Sglebius /* calculate century cycles shift and cycle index: 1795289764Sglebius * Assuming a century is 5217 weeks, we have to add a cycle 1796289764Sglebius * shift that is 3 for every 4 centuries, because 3 of the four 1797289764Sglebius * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual 1798289764Sglebius * correction, and the second century is the defective one. 1799289764Sglebius * 1800289764Sglebius * Needs floor division by 4, which is done with masking and 1801289764Sglebius * shifting. 1802275970Scy */ 1803289764Sglebius ci = cc * 3u + 1; 1804358659Scy cs = uint32_2cpl_to_int32(sf32 ^ ((sf32 ^ ci) >> 2)); 1805358659Scy ci = ci & 3u; 1806358659Scy 1807289764Sglebius /* Get weeks in century. Can use plain division here as all ops 1808289764Sglebius * are >= 0, and let the compiler sort out the possible 1809289764Sglebius * optimisations. 1810289764Sglebius */ 1811289764Sglebius cw = (yu * 53431u + bctab[ci]) / 1024u; 1812275970Scy 1813289764Sglebius return uint32_2cpl_to_int32(cc) * 5217 + cs + cw; 1814275970Scy} 1815275970Scy 1816275970Scy/* 1817275970Scy * Given a number of elapsed weeks since the begin of the christian 1818275970Scy * era, split this number into the number of elapsed years in res.hi 1819275970Scy * and the excessive number of weeks in res.lo. (That is, res.lo is 1820275970Scy * the number of elapsed weeks in the remaining partial year.) 1821275970Scy */ 1822275970Scyntpcal_split 1823275970Scyisocal_split_eraweeks( 1824275970Scy int32_t weeks 1825275970Scy ) 1826275970Scy{ 1827275970Scy /* 1828275970Scy * use: y = (w * 157 + b[c]) / 8192 as interpolation 1829275970Scy */ 1830289764Sglebius 1831289764Sglebius static const uint16_t bctab[4] = { 85, 130, 17, 62 }; 1832289764Sglebius 1833275970Scy ntpcal_split res; 1834358659Scy int32_t cc, ci; 1835358659Scy uint32_t sw, cy, Q; 1836275970Scy 1837358659Scy /* Use two fast cycle-split divisions again. Herew e want to 1838358659Scy * execute '(weeks * 4 + 2) /% 20871' under floor division rules 1839358659Scy * in the first step. 1840289764Sglebius * 1841358659Scy * This is of course (again) susceptible to internal overflow if 1842358659Scy * coded directly in 32bit. And again we use 64bit division on 1843358659Scy * a 64bit target and exact division after calculating the 1844358659Scy * remainder first on a 32bit target. With the smaller divider, 1845358659Scy * that's even a bit neater. 1846275970Scy */ 1847358659Scy# if defined(HAVE_64BITREGS) 1848358659Scy 1849358659Scy /* Full floor division with 64bit values. */ 1850358659Scy uint64_t sf64, sw64; 1851358659Scy sf64 = (uint64_t)-(weeks < 0); 1852358659Scy sw64 = ((uint64_t)weeks << 2) | 2u; 1853358659Scy Q = (uint32_t)(sf64 ^ ((sf64 ^ sw64) / GREGORIAN_CYCLE_WEEKS)); 1854358659Scy sw = (uint32_t)(sw64 - Q * GREGORIAN_CYCLE_WEEKS); 1855358659Scy 1856358659Scy# else 1857358659Scy 1858358659Scy /* Exact division after calculating the remainder via partial 1859358659Scy * reduction by digit sum. 1860358659Scy * (-2^33) % 20871 --> 5491 : the sign bit value 1861358659Scy * ( 2^20) % 20871 --> 5026 : the upper digit value 1862358659Scy * modinv(20871, 2^32) --> 330081335 : the inverse 1863358659Scy */ 1864358659Scy uint32_t ux = ((uint32_t)weeks << 2) | 2; 1865358659Scy sw = (weeks < 0) ? 5491u : 0u; /* sign dgt */ 1866358659Scy sw += ((weeks >> 18) & 0x01FFFu) * 5026u; /* hi dgt (src!) */ 1867358659Scy sw += (ux & 0xFFFFFu); /* lo dgt */ 1868358659Scy sw %= GREGORIAN_CYCLE_WEEKS; /* full reduction */ 1869358659Scy Q = (ux - sw) * 330081335u; /* exact div */ 1870358659Scy 1871358659Scy# endif 1872358659Scy 1873358659Scy ci = Q & 3u; 1874289764Sglebius cc = uint32_2cpl_to_int32(Q); 1875275970Scy 1876289764Sglebius /* Split off years; sw >= 0 here! The scaled weeks in the years 1877289764Sglebius * are scaled up by 157 afterwards. 1878358659Scy */ 1879289764Sglebius sw = (sw / 4u) * 157u + bctab[ci]; 1880358659Scy cy = sw / 8192u; /* sw >> 13 , let the compiler sort it out */ 1881358659Scy sw = sw % 8192u; /* sw & 8191, let the compiler sort it out */ 1882289764Sglebius 1883289764Sglebius /* assemble elapsed years and downscale the elapsed weeks in 1884289764Sglebius * the year. 1885275970Scy */ 1886289764Sglebius res.hi = 100*cc + cy; 1887289764Sglebius res.lo = sw / 157u; 1888282408Scy 1889275970Scy return res; 1890275970Scy} 1891275970Scy 1892275970Scy/* 1893275970Scy * Given a second in the NTP time scale and a pivot, expand the NTP 1894275970Scy * time stamp around the pivot and convert into an ISO calendar time 1895275970Scy * stamp. 1896275970Scy */ 1897275970Scyint 1898275970Scyisocal_ntp64_to_date( 1899275970Scy struct isodate *id, 1900275970Scy const vint64 *ntp 1901275970Scy ) 1902275970Scy{ 1903275970Scy ntpcal_split ds; 1904358659Scy int32_t ts[3]; 1905358659Scy uint32_t uw, ud, sf32; 1906282408Scy 1907275970Scy /* 1908275970Scy * Split NTP time into days and seconds, shift days into CE 1909275970Scy * domain and process the parts. 1910275970Scy */ 1911275970Scy ds = ntpcal_daysplit(ntp); 1912275970Scy 1913275970Scy /* split time part */ 1914275970Scy ds.hi += priv_timesplit(ts, ds.lo); 1915275970Scy id->hour = (uint8_t)ts[0]; 1916275970Scy id->minute = (uint8_t)ts[1]; 1917275970Scy id->second = (uint8_t)ts[2]; 1918275970Scy 1919289764Sglebius /* split days into days and weeks, using floor division in unsigned */ 1920289764Sglebius ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */ 1921358659Scy sf32 = int32_sflag(ds.hi); 1922358659Scy ud = (uint32_t)ds.hi; 1923358659Scy uw = sf32 ^ ((sf32 ^ ud) / DAYSPERWEEK); 1924358659Scy ud -= uw * DAYSPERWEEK; 1925358659Scy 1926289764Sglebius ds.hi = uint32_2cpl_to_int32(uw); 1927289764Sglebius ds.lo = ud; 1928289764Sglebius 1929275970Scy id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ 1930275970Scy 1931289764Sglebius /* get year and week in year */ 1932275970Scy ds = isocal_split_eraweeks(ds.hi); /* elapsed years&week*/ 1933275970Scy id->year = (uint16_t)ds.hi + 1; /* shift to current */ 1934275970Scy id->week = (uint8_t )ds.lo + 1; 1935275970Scy 1936280849Scy return (ds.hi >= 0 && ds.hi < 0x0000FFFF); 1937275970Scy} 1938275970Scy 1939275970Scyint 1940275970Scyisocal_ntp_to_date( 1941275970Scy struct isodate *id, 1942275970Scy uint32_t ntp, 1943275970Scy const time_t *piv 1944275970Scy ) 1945275970Scy{ 1946275970Scy vint64 ntp64; 1947282408Scy 1948275970Scy /* 1949275970Scy * Unfold ntp time around current time into NTP domain, then 1950275970Scy * convert the full time stamp. 1951275970Scy */ 1952275970Scy ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1953275970Scy return isocal_ntp64_to_date(id, &ntp64); 1954275970Scy} 1955275970Scy 1956275970Scy/* 1957275970Scy * Convert a ISO date spec into a second in the NTP time scale, 1958275970Scy * properly truncated to 32 bit. 1959275970Scy */ 1960275970Scyvint64 1961275970Scyisocal_date_to_ntp64( 1962275970Scy const struct isodate *id 1963275970Scy ) 1964275970Scy{ 1965275970Scy int32_t weeks, days, secs; 1966275970Scy 1967275970Scy weeks = isocal_weeks_in_years((int32_t)id->year - 1) 1968275970Scy + (int32_t)id->week - 1; 1969275970Scy days = weeks * 7 + (int32_t)id->weekday; 1970275970Scy /* days is RDN of ISO date now */ 1971275970Scy secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second); 1972275970Scy 1973275970Scy return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs); 1974275970Scy} 1975275970Scy 1976275970Scyuint32_t 1977275970Scyisocal_date_to_ntp( 1978275970Scy const struct isodate *id 1979275970Scy ) 1980275970Scy{ 1981275970Scy /* 1982358659Scy * Get lower half of 64bit NTP timestamp from date/time. 1983275970Scy */ 1984275970Scy return isocal_date_to_ntp64(id).d_s.lo; 1985275970Scy} 1986275970Scy 1987330106Sdelphij/* 1988330106Sdelphij * ==================================================================== 1989330106Sdelphij * 'basedate' support functions 1990330106Sdelphij * ==================================================================== 1991330106Sdelphij */ 1992330106Sdelphij 1993330106Sdelphijstatic int32_t s_baseday = NTP_TO_UNIX_DAYS; 1994344884Scystatic int32_t s_gpsweek = 0; 1995330106Sdelphij 1996330106Sdelphijint32_t 1997330106Sdelphijbasedate_eval_buildstamp(void) 1998330106Sdelphij{ 1999330106Sdelphij struct calendar jd; 2000330106Sdelphij int32_t ed; 2001358659Scy 2002330106Sdelphij if (!ntpcal_get_build_date(&jd)) 2003330106Sdelphij return NTP_TO_UNIX_DAYS; 2004330106Sdelphij 2005330106Sdelphij /* The time zone of the build stamp is unspecified; we remove 2006330106Sdelphij * one day to provide a certain slack. And in case somebody 2007330106Sdelphij * fiddled with the system clock, we make sure we do not go 2008330106Sdelphij * before the UNIX epoch (1970-01-01). It's probably not possible 2009330106Sdelphij * to do this to the clock on most systems, but there are other 2010330106Sdelphij * ways to tweak the build stamp. 2011330106Sdelphij */ 2012330106Sdelphij jd.monthday -= 1; 2013330106Sdelphij ed = ntpcal_date_to_rd(&jd) - DAY_NTP_STARTS; 2014330106Sdelphij return (ed < NTP_TO_UNIX_DAYS) ? NTP_TO_UNIX_DAYS : ed; 2015330106Sdelphij} 2016330106Sdelphij 2017330106Sdelphijint32_t 2018330106Sdelphijbasedate_eval_string( 2019330106Sdelphij const char * str 2020330106Sdelphij ) 2021330106Sdelphij{ 2022330106Sdelphij u_short y,m,d; 2023330106Sdelphij u_long ned; 2024330106Sdelphij int rc, nc; 2025330106Sdelphij size_t sl; 2026330106Sdelphij 2027358659Scy sl = strlen(str); 2028330106Sdelphij rc = sscanf(str, "%4hu-%2hu-%2hu%n", &y, &m, &d, &nc); 2029330106Sdelphij if (rc == 3 && (size_t)nc == sl) { 2030330106Sdelphij if (m >= 1 && m <= 12 && d >= 1 && d <= 31) 2031330106Sdelphij return ntpcal_edate_to_eradays(y-1, m-1, d) 2032330106Sdelphij - DAY_NTP_STARTS; 2033330106Sdelphij goto buildstamp; 2034330106Sdelphij } 2035330106Sdelphij 2036338530Sdelphij rc = sscanf(str, "%lu%n", &ned, &nc); 2037330106Sdelphij if (rc == 1 && (size_t)nc == sl) { 2038330106Sdelphij if (ned <= INT32_MAX) 2039330106Sdelphij return (int32_t)ned; 2040330106Sdelphij goto buildstamp; 2041330106Sdelphij } 2042330106Sdelphij 2043330106Sdelphij buildstamp: 2044330106Sdelphij msyslog(LOG_WARNING, 2045330106Sdelphij "basedate string \"%s\" invalid, build date substituted!", 2046330106Sdelphij str); 2047330106Sdelphij return basedate_eval_buildstamp(); 2048330106Sdelphij} 2049330106Sdelphij 2050330106Sdelphijuint32_t 2051330106Sdelphijbasedate_get_day(void) 2052330106Sdelphij{ 2053330106Sdelphij return s_baseday; 2054330106Sdelphij} 2055330106Sdelphij 2056330106Sdelphijint32_t 2057330106Sdelphijbasedate_set_day( 2058330106Sdelphij int32_t day 2059330106Sdelphij ) 2060330106Sdelphij{ 2061330106Sdelphij struct calendar jd; 2062330106Sdelphij int32_t retv; 2063330106Sdelphij 2064344884Scy /* set NTP base date for NTP era unfolding */ 2065330106Sdelphij if (day < NTP_TO_UNIX_DAYS) { 2066330106Sdelphij msyslog(LOG_WARNING, 2067330106Sdelphij "baseday_set_day: invalid day (%lu), UNIX epoch substituted", 2068330106Sdelphij (unsigned long)day); 2069330106Sdelphij day = NTP_TO_UNIX_DAYS; 2070330106Sdelphij } 2071358659Scy retv = s_baseday; 2072330106Sdelphij s_baseday = day; 2073330106Sdelphij ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); 2074330106Sdelphij msyslog(LOG_INFO, "basedate set to %04hu-%02hu-%02hu", 2075330106Sdelphij jd.year, (u_short)jd.month, (u_short)jd.monthday); 2076344884Scy 2077344884Scy /* set GPS base week for GPS week unfolding */ 2078344884Scy day = ntpcal_weekday_ge(day + DAY_NTP_STARTS, CAL_SUNDAY) 2079344884Scy - DAY_NTP_STARTS; 2080344884Scy if (day < NTP_TO_GPS_DAYS) 2081344884Scy day = NTP_TO_GPS_DAYS; 2082344884Scy s_gpsweek = (day - NTP_TO_GPS_DAYS) / DAYSPERWEEK; 2083344884Scy ntpcal_rd_to_date(&jd, day + DAY_NTP_STARTS); 2084344884Scy msyslog(LOG_INFO, "gps base set to %04hu-%02hu-%02hu (week %d)", 2085344884Scy jd.year, (u_short)jd.month, (u_short)jd.monthday, s_gpsweek); 2086358659Scy 2087330106Sdelphij return retv; 2088330106Sdelphij} 2089330106Sdelphij 2090330106Sdelphijtime_t 2091330106Sdelphijbasedate_get_eracenter(void) 2092330106Sdelphij{ 2093330106Sdelphij time_t retv; 2094330106Sdelphij retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); 2095330106Sdelphij retv *= SECSPERDAY; 2096330106Sdelphij retv += (UINT32_C(1) << 31); 2097330106Sdelphij return retv; 2098330106Sdelphij} 2099330106Sdelphij 2100330106Sdelphijtime_t 2101330106Sdelphijbasedate_get_erabase(void) 2102330106Sdelphij{ 2103330106Sdelphij time_t retv; 2104330106Sdelphij retv = (time_t)(s_baseday - NTP_TO_UNIX_DAYS); 2105330106Sdelphij retv *= SECSPERDAY; 2106330106Sdelphij return retv; 2107330106Sdelphij} 2108330106Sdelphij 2109344884Scyuint32_t 2110344884Scybasedate_get_gpsweek(void) 2111344884Scy{ 2112344884Scy return s_gpsweek; 2113344884Scy} 2114344884Scy 2115344884Scyuint32_t 2116344884Scybasedate_expand_gpsweek( 2117344884Scy unsigned short weekno 2118344884Scy ) 2119344884Scy{ 2120344884Scy /* We do a fast modulus expansion here. Since all quantities are 2121344884Scy * unsigned and we cannot go before the start of the GPS epoch 2122344884Scy * anyway, and since the truncated GPS week number is 10 bit, the 2123344884Scy * expansion becomes a simple sub/and/add sequence. 2124344884Scy */ 2125344884Scy #if GPSWEEKS != 1024 2126344884Scy # error GPSWEEKS defined wrong -- should be 1024! 2127344884Scy #endif 2128358659Scy 2129344884Scy uint32_t diff; 2130344884Scy diff = ((uint32_t)weekno - s_gpsweek) & (GPSWEEKS - 1); 2131344884Scy return s_gpsweek + diff; 2132344884Scy} 2133344884Scy 2134358659Scy/* 2135358659Scy * ==================================================================== 2136358659Scy * misc. helpers 2137358659Scy * ==================================================================== 2138358659Scy */ 2139358659Scy 2140358659Scy/* -------------------------------------------------------------------- 2141358659Scy * reconstruct the centrury from a truncated date and a day-of-week 2142358659Scy * 2143358659Scy * Given a date with truncated year (2-digit, 0..99) and a day-of-week 2144358659Scy * from 1(Mon) to 7(Sun), recover the full year between 1900AD and 2300AD. 2145358659Scy */ 2146358659Scyint32_t 2147358659Scyntpcal_expand_century( 2148358659Scy uint32_t y, 2149358659Scy uint32_t m, 2150358659Scy uint32_t d, 2151358659Scy uint32_t wd) 2152358659Scy{ 2153358659Scy /* This algorithm is short but tricky... It's related to 2154358659Scy * Zeller's congruence, partially done backwards. 2155358659Scy * 2156358659Scy * A few facts to remember: 2157358659Scy * 1) The Gregorian calendar has a cycle of 400 years. 2158358659Scy * 2) The weekday of the 1st day of a century shifts by 5 days 2159358659Scy * during a great cycle. 2160358659Scy * 3) For calendar math, a century starts with the 1st year, 2161358659Scy * which is year 1, !not! zero. 2162358659Scy * 2163358659Scy * So we start with taking the weekday difference (mod 7) 2164358659Scy * between the truncated date (which is taken as an absolute 2165358659Scy * date in the 1st century in the proleptic calendar) and the 2166358659Scy * weekday given. 2167358659Scy * 2168358659Scy * When dividing this residual by 5, we obtain the number of 2169358659Scy * centuries to add to the base. But since the residual is (mod 2170358659Scy * 7), we have to make this an exact division by multiplication 2171358659Scy * with the modular inverse of 5 (mod 7), which is 3: 2172358659Scy * 3*5 === 1 (mod 7). 2173358659Scy * 2174358659Scy * If this yields a result of 4/5/6, the given date/day-of-week 2175358659Scy * combination is impossible, and we return zero as resulting 2176358659Scy * year to indicate failure. 2177358659Scy * 2178358659Scy * Then we remap the century to the range starting with year 2179358659Scy * 1900. 2180358659Scy */ 2181358659Scy 2182358659Scy uint32_t c; 2183358659Scy 2184358659Scy /* check basic constraints */ 2185358659Scy if ((y >= 100u) || (--m >= 12u) || (--d >= 31u)) 2186358659Scy return 0; 2187358659Scy 2188358659Scy if ((m += 10u) >= 12u) /* shift base to prev. March,1st */ 2189358659Scy m -= 12u; 2190358659Scy else if (--y >= 100u) 2191358659Scy y += 100u; 2192358659Scy d += y + (y >> 2) + 2u; /* year share */ 2193358659Scy d += (m * 83u + 16u) >> 5; /* month share */ 2194358659Scy 2195358659Scy /* get (wd - d), shifted to positive value, and multiply with 2196358659Scy * 3(mod 7). (Exact division, see to comment) 2197358659Scy * Note: 1) d <= 184 at this point. 2198358659Scy * 2) 252 % 7 == 0, but 'wd' is off by one since we did 2199358659Scy * '--d' above, so we add just 251 here! 2200358659Scy */ 2201358659Scy c = u32mod7(3 * (251u + wd - d)); 2202358659Scy if (c > 3u) 2203358659Scy return 0; 2204358659Scy 2205358659Scy if ((m > 9u) && (++y >= 100u)) {/* undo base shift */ 2206358659Scy y -= 100u; 2207358659Scy c = (c + 1) & 3u; 2208358659Scy } 2209358659Scy y += (c * 100u); /* combine into 1st cycle */ 2210358659Scy y += (y < 300u) ? 2000 : 1600; /* map to destination era */ 2211358659Scy return (int)y; 2212358659Scy} 2213358659Scy 2214358659Scychar * 2215358659Scyntpcal_iso8601std( 2216358659Scy char * buf, 2217358659Scy size_t len, 2218358659Scy TcCivilDate * cdp 2219358659Scy ) 2220358659Scy{ 2221358659Scy if (!buf) { 2222358659Scy LIB_GETBUF(buf); 2223358659Scy len = LIB_BUFLENGTH; 2224358659Scy } 2225358659Scy if (len) { 2226358659Scy len = snprintf(buf, len, "%04u-%02u-%02uT%02u:%02u:%02u", 2227358659Scy cdp->year, cdp->month, cdp->monthday, 2228358659Scy cdp->hour, cdp->minute, cdp->second); 2229358659Scy if (len < 0) 2230358659Scy *buf = '\0'; 2231358659Scy } 2232358659Scy return buf; 2233358659Scy} 2234358659Scy 2235275970Scy/* -*-EOF-*- */ 2236