ntp_calendar.c revision 290001
1195618Srpaulo/* 2195618Srpaulo * ntp_calendar.c - calendar and helper functions 3195618Srpaulo * 4195618Srpaulo * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 5195618Srpaulo * The contents of 'html/copyright.html' apply. 6195618Srpaulo * 7195618Srpaulo * -------------------------------------------------------------------- 8195618Srpaulo * Some notes on the implementation: 9195618Srpaulo * 10195618Srpaulo * Calendar algorithms thrive on the division operation, which is one of 11195618Srpaulo * the slowest numerical operations in any CPU. What saves us here from 12195618Srpaulo * abysmal performance is the fact that all divisions are divisions by 13195618Srpaulo * constant numbers, and most compilers can do this by a multiplication 14195618Srpaulo * operation. But this might not work when using the div/ldiv/lldiv 15195618Srpaulo * function family, because many compilers are not able to do inline 16195618Srpaulo * expansion of the code with following optimisation for the 17195618Srpaulo * constant-divider case. 18195618Srpaulo * 19195618Srpaulo * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which 20195618Srpaulo * are inherently target dependent. Nothing that could not be cured with 21195618Srpaulo * autoconf, but still a mess... 22195618Srpaulo * 23195618Srpaulo * Furthermore, we need floor division in many places. C either leaves 24195618Srpaulo * the division behaviour undefined (< C99) or demands truncation to 25195618Srpaulo * zero (>= C99), so additional steps are required to make sure the 26195618Srpaulo * algorithms work. The {l,ll}div function family is requested to 27195618Srpaulo * truncate towards zero, which is also the wrong direction for our 28195618Srpaulo * purpose. 29195618Srpaulo * 30195618Srpaulo * For all this, all divisions by constant are coded manually, even when 31195618Srpaulo * there is a joined div/mod operation: The optimiser should sort that 32195618Srpaulo * out, if possible. Most of the calculations are done with unsigned 33195618Srpaulo * types, explicitely using two's complement arithmetics where 34195618Srpaulo * necessary. This minimises the dependecies to compiler and target, 35195618Srpaulo * while still giving reasonable to good performance. 36195618Srpaulo * 37195618Srpaulo * The implementation uses a few tricks that exploit properties of the 38195618Srpaulo * two's complement: Floor division on negative dividents can be 39195618Srpaulo * executed by using the one's complement of the divident. One's 40195618Srpaulo * complement can be easily created using XOR and a mask. 41195618Srpaulo * 42195618Srpaulo * Finally, check for overflow conditions is minimal. There are only two 43195618Srpaulo * calculation steps in the whole calendar that suffer from an internal 44195618Srpaulo * overflow, and these conditions are checked: errno is set to EDOM and 45195618Srpaulo * the results are clamped/saturated in this case. All other functions 46195618Srpaulo * do not suffer from internal overflow and simply return the result 47195618Srpaulo * truncated to 32 bits. 48195618Srpaulo * 49195618Srpaulo * This is a sacrifice made for execution speed. Since a 32-bit day 50195618Srpaulo * counter covers +/- 5,879,610 years and the clamp limits the effective 51195618Srpaulo * range to +/-2.9 million years, this should not pose a problem here. 52195618Srpaulo * 53195618Srpaulo */ 54195618Srpaulo 55246511Smonthadar#include <config.h> 56195618Srpaulo#include <sys/types.h> 57257176Sglebius 58195618Srpaulo#include "ntp_types.h" 59195618Srpaulo#include "ntp_calendar.h" 60195618Srpaulo#include "ntp_stdlib.h" 61195618Srpaulo#include "ntp_fp.h" 62195618Srpaulo#include "ntp_unixtime.h" 63195618Srpaulo 64246537Sadrian/* For now, let's take the conservative approach: if the target property 65246537Sadrian * macros are not defined, check a few well-known compiler/architecture 66246537Sadrian * settings. Default is to assume that the representation of signed 67195618Srpaulo * integers is unknown and shift-arithmetic-right is not available. 68195618Srpaulo */ 69195618Srpaulo#ifndef TARGET_HAS_2CPL 70195784Srpaulo# if defined(__GNUC__) 71195618Srpaulo# if defined(__i386__) || defined(__x86_64__) || defined(__arm__) 72195618Srpaulo# define TARGET_HAS_2CPL 1 73195618Srpaulo# else 74195618Srpaulo# define TARGET_HAS_2CPL 0 75195784Srpaulo# endif 76246506Smonthadar# elif defined(_MSC_VER) 77246506Smonthadar# if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) 78195661Srpaulo# define TARGET_HAS_2CPL 1 79195661Srpaulo# else 80195618Srpaulo# define TARGET_HAS_2CPL 0 81195618Srpaulo# endif 82195618Srpaulo# else 83195618Srpaulo# define TARGET_HAS_2CPL 0 84246511Smonthadar# endif 85246511Smonthadar#endif 86195618Srpaulo 87195618Srpaulo#ifndef TARGET_HAS_SAR 88283535Sadrian# define TARGET_HAS_SAR 0 89283535Sadrian#endif 90195618Srpaulo 91283535Sadrian/* 92205277Srpaulo *--------------------------------------------------------------------- 93195618Srpaulo * replacing the 'time()' function 94195618Srpaulo * -------------------------------------------------------------------- 95195618Srpaulo */ 96195618Srpaulo 97195618Srpaulostatic systime_func_ptr systime_func = &time; 98195618Srpaulostatic inline time_t now(void); 99195618Srpaulo 100197413Srpaulo 101197413Srpaulosystime_func_ptr 102195618Srpaulontpcal_set_timefunc( 103195618Srpaulo systime_func_ptr nfunc 104195618Srpaulo ) 105195618Srpaulo{ 106195618Srpaulo systime_func_ptr res; 107227309Sed 108195618Srpaulo res = systime_func; 109246506Smonthadar if (NULL == nfunc) 110246506Smonthadar nfunc = &time; 111246506Smonthadar systime_func = nfunc; 112246506Smonthadar 113195618Srpaulo return res; 114195618Srpaulo} 115195618Srpaulo 116195618Srpaulo 117195618Srpaulostatic inline time_t 118246537Sadriannow(void) 119195618Srpaulo{ 120195618Srpaulo return (*systime_func)(NULL); 121195618Srpaulo} 122195618Srpaulo 123195618Srpaulo/* 124195618Srpaulo *--------------------------------------------------------------------- 125195618Srpaulo * Get sign extension mask and unsigned 2cpl rep for a signed integer 126246497Smonthadar *--------------------------------------------------------------------- 127246497Smonthadar */ 128246497Smonthadar 129246497Smonthadarstatic inline uint32_t 130281383Seadlerint32_sflag( 131195618Srpaulo const int32_t v) 132273377Shselasky{ 133195618Srpaulo# if TARGET_HAS_2CPL && TARGET_HAS_SAR && SIZEOF_INT >= 4 134195618Srpaulo 135246497Smonthadar /* Let's assume that shift is the fastest way to get the sign 136273377Shselasky * extension of of a signed integer. This might not always be 137246497Smonthadar * true, though -- On 8bit CPUs or machines without barrel 138246497Smonthadar * shifter this will kill the performance. So we make sure 139246497Smonthadar * we do this only if 'int' has at least 4 bytes. 140195618Srpaulo */ 141195618Srpaulo return (uint32_t)(v >> 31); 142195618Srpaulo 143195618Srpaulo# else 144195618Srpaulo 145195618Srpaulo /* This should be a rather generic approach for getting a sign 146195618Srpaulo * extension mask... 147232479Sadrian */ 148246506Smonthadar return UINT32_C(0) - (uint32_t)(v < 0); 149195618Srpaulo 150195618Srpaulo# endif 151195618Srpaulo} 152195618Srpaulo 153232479Sadrianstatic inline uint32_t 154246506Smonthadarint32_to_uint32_2cpl( 155195618Srpaulo const int32_t v) 156195618Srpaulo{ 157195618Srpaulo uint32_t vu; 158197975Srpaulo 159195618Srpaulo# if TARGET_HAS_2CPL 160195618Srpaulo 161195618Srpaulo /* Just copy through the 32 bits from the signed value if we're 162195618Srpaulo * on a two's complement target. 163195618Srpaulo */ 164195618Srpaulo vu = (uint32_t)v; 165232625Sadrian 166232625Sadrian# else 167232625Sadrian 168232625Sadrian /* Convert from signed int to unsigned int two's complement. Do 169234877Smonthadar * not make any assumptions about the representation of signed 170234877Smonthadar * integers, but make sure signed integer overflow cannot happen 171234877Smonthadar * here. A compiler on a two's complement target *might* find 172246508Smonthadar * out that this is just a complicated cast (as above), but your 173246508Smonthadar * mileage might vary. 174195618Srpaulo */ 175195618Srpaulo if (v < 0) 176195618Srpaulo vu = ~(uint32_t)(-(v + 1)); 177195618Srpaulo else 178195784Srpaulo vu = (uint32_t)v; 179195784Srpaulo 180195784Srpaulo# endif 181195784Srpaulo 182195784Srpaulo return vu; 183195784Srpaulo} 184195784Srpaulo 185195784Srpaulostatic inline int32_t 186195784Srpaulouint32_2cpl_to_int32( 187195784Srpaulo const uint32_t vu) 188195784Srpaulo{ 189195784Srpaulo int32_t v; 190195784Srpaulo 191195784Srpaulo# if TARGET_HAS_2CPL 192195784Srpaulo 193195784Srpaulo /* Just copy through the 32 bits from the unsigned value if 194195784Srpaulo * we're on a two's complement target. 195234894Smonthadar */ 196195784Srpaulo v = (int32_t)vu; 197195784Srpaulo 198234894Smonthadar# else 199195784Srpaulo 200195784Srpaulo /* Convert to signed integer, making sure signed integer 201195784Srpaulo * overflow cannot happen. Again, the optimiser might or might 202195784Srpaulo * not find out that this is just a copy of 32 bits on a target 203195784Srpaulo * with two's complement representation for signed integers. 204195784Srpaulo */ 205195784Srpaulo if (vu > INT32_MAX) 206283538Sadrian v = -(int32_t)(~vu) - 1; 207283538Sadrian else 208283538Sadrian v = (int32_t)vu; 209195784Srpaulo 210234894Smonthadar# endif 211195784Srpaulo 212195784Srpaulo return v; 213283555Sadrian} 214283291Sjkim 215234877Smonthadar/* Some of the calculations need to multiply the input by 4 before doing 216195784Srpaulo * a division. This can cause overflow and strange results. Therefore we 217195784Srpaulo * clamp / saturate the input operand. And since we do the calculations 218195784Srpaulo * in unsigned int with an extra sign flag/mask, we only loose one bit 219195784Srpaulo * of the input value range. 220195784Srpaulo */ 221195618Srpaulostatic inline uint32_t 222195618Srpaulouint32_saturate( 223195618Srpaulo uint32_t vu, 224195618Srpaulo uint32_t mu) 225195618Srpaulo{ 226195618Srpaulo static const uint32_t limit = UINT32_MAX/4u; 227195618Srpaulo if ((mu ^ vu) > limit) { 228195618Srpaulo vu = mu ^ limit; 229195784Srpaulo errno = EDOM; 230195618Srpaulo } 231195784Srpaulo return vu; 232195618Srpaulo} 233195618Srpaulo 234195618Srpaulo/* 235195618Srpaulo *--------------------------------------------------------------------- 236195618Srpaulo * Convert between 'time_t' and 'vint64' 237195618Srpaulo *--------------------------------------------------------------------- 238195618Srpaulo */ 239195618Srpaulovint64 240195618Srpaulotime_to_vint64( 241195618Srpaulo const time_t * ptt 242195618Srpaulo ) 243195618Srpaulo{ 244195618Srpaulo vint64 res; 245195618Srpaulo time_t tt; 246195618Srpaulo 247234894Smonthadar tt = *ptt; 248195618Srpaulo 249195618Srpaulo# if SIZEOF_TIME_T <= 4 250195618Srpaulo 251195618Srpaulo res.D_s.hi = 0; 252195784Srpaulo if (tt < 0) { 253234877Smonthadar res.D_s.lo = (uint32_t)-tt; 254234877Smonthadar M_NEG(res.D_s.hi, res.D_s.lo); 255234877Smonthadar } else { 256234877Smonthadar res.D_s.lo = (uint32_t)tt; 257234877Smonthadar } 258234877Smonthadar 259234877Smonthadar# elif defined(HAVE_INT64) 260234877Smonthadar 261234877Smonthadar res.q_s = tt; 262234877Smonthadar 263234881Smonthadar# else 264234881Smonthadar /* 265234877Smonthadar * shifting negative signed quantities is compiler-dependent, so 266283555Sadrian * we better avoid it and do it all manually. And shifting more 267234879Smonthadar * than the width of a quantity is undefined. Also a don't do! 268234879Smonthadar */ 269234879Smonthadar if (tt < 0) { 270283555Sadrian tt = -tt; 271234879Smonthadar res.D_s.lo = (uint32_t)tt; 272234879Smonthadar res.D_s.hi = (uint32_t)(tt >> 32); 273234879Smonthadar M_NEG(res.D_s.hi, res.D_s.lo); 274234877Smonthadar } else { 275234877Smonthadar res.D_s.lo = (uint32_t)tt; 276234877Smonthadar res.D_s.hi = (uint32_t)(tt >> 32); 277234877Smonthadar } 278234877Smonthadar 279234877Smonthadar# endif 280234877Smonthadar 281234877Smonthadar return res; 282234877Smonthadar} 283234877Smonthadar 284234877Smonthadar 285234877Smonthadartime_t 286234877Smonthadarvint64_to_time( 287234877Smonthadar const vint64 *tv 288234877Smonthadar ) 289234877Smonthadar{ 290234877Smonthadar time_t res; 291283555Sadrian 292234877Smonthadar# if SIZEOF_TIME_T <= 4 293234877Smonthadar 294234877Smonthadar res = (time_t)tv->D_s.lo; 295234877Smonthadar 296234877Smonthadar# elif defined(HAVE_INT64) 297195784Srpaulo 298195784Srpaulo res = (time_t)tv->q_s; 299195618Srpaulo 300195784Srpaulo# else 301195784Srpaulo 302195784Srpaulo res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; 303195784Srpaulo 304195784Srpaulo# endif 305195784Srpaulo 306195784Srpaulo return res; 307195784Srpaulo} 308195784Srpaulo 309234894Smonthadar/* 310195784Srpaulo *--------------------------------------------------------------------- 311195784Srpaulo * Get the build date & time 312195784Srpaulo *--------------------------------------------------------------------- 313195784Srpaulo */ 314195784Srpauloint 315195908Srpaulontpcal_get_build_date( 316195908Srpaulo struct calendar * jd 317234878Smonthadar ) 318195784Srpaulo{ 319195784Srpaulo /* The C standard tells us the format of '__DATE__': 320195784Srpaulo * 321195784Srpaulo * __DATE__ The date of translation of the preprocessing 322195784Srpaulo * translation unit: a character string literal of the form "Mmm 323234878Smonthadar * dd yyyy", where the names of the months are the same as those 324234878Smonthadar * generated by the asctime function, and the first character of 325195784Srpaulo * dd is a space character if the value is less than 10. If the 326195784Srpaulo * date of translation is not available, an 327195784Srpaulo * implementation-defined valid date shall be supplied. 328195784Srpaulo * 329195784Srpaulo * __TIME__ The time of translation of the preprocessing 330195784Srpaulo * translation unit: a character string literal of the form 331195784Srpaulo * "hh:mm:ss" as in the time generated by the asctime 332195784Srpaulo * function. If the time of translation is not available, an 333195784Srpaulo * implementation-defined valid time shall be supplied. 334195784Srpaulo * 335195784Srpaulo * Note that MSVC declares DATE and TIME to be in the local time 336195784Srpaulo * zone, while neither the C standard nor the GCC docs make any 337195784Srpaulo * statement about this. As a result, we may be +/-12hrs off 338195784Srpaulo * UTC. But for practical purposes, this should not be a 339195784Srpaulo * problem. 340195784Srpaulo * 341195784Srpaulo */ 342195784Srpaulo# ifdef MKREPRO_DATE 343195784Srpaulo static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; 344195784Srpaulo# else 345195784Srpaulo static const char build[] = __TIME__ "/" __DATE__; 346195784Srpaulo# endif 347195784Srpaulo static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 348195784Srpaulo 349234877Smonthadar char monstr[4]; 350234877Smonthadar const char * cp; 351234877Smonthadar unsigned short hour, minute, second, day, year; 352234877Smonthadar /* Note: The above quantities are used for sscanf 'hu' format, 353283555Sadrian * so using 'uint16_t' is contra-indicated! 354234894Smonthadar */ 355283555Sadrian 356283538Sadrian# ifdef DEBUG 357195784Srpaulo static int ignore = 0; 358195784Srpaulo# endif 359195784Srpaulo 360195618Srpaulo ZERO(*jd); 361195618Srpaulo jd->year = 1970; 362195618Srpaulo jd->month = 1; 363195618Srpaulo jd->monthday = 1; 364195618Srpaulo 365195618Srpaulo# ifdef DEBUG 366195618Srpaulo /* check environment if build date should be ignored */ 367195618Srpaulo if (0 == ignore) { 368195618Srpaulo const char * envstr; 369234890Smonthadar envstr = getenv("NTPD_IGNORE_BUILD_DATE"); 370234890Smonthadar ignore = 1 + (envstr && (!*envstr || !strcasecmp(envstr, "yes"))); 371234890Smonthadar } 372234890Smonthadar if (ignore > 1) 373234890Smonthadar return FALSE; 374234890Smonthadar# endif 375234890Smonthadar 376195784Srpaulo if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu", 377195618Srpaulo &hour, &minute, &second, monstr, &day, &year)) { 378195618Srpaulo cp = strstr(mlist, monstr); 379195618Srpaulo if (NULL != cp) { 380195618Srpaulo jd->year = year; 381195618Srpaulo jd->month = (uint8_t)((cp - mlist) / 3 + 1); 382195618Srpaulo jd->monthday = (uint8_t)day; 383195618Srpaulo jd->hour = (uint8_t)hour; 384195618Srpaulo jd->minute = (uint8_t)minute; 385195618Srpaulo jd->second = (uint8_t)second; 386195618Srpaulo 387195618Srpaulo return TRUE; 388195618Srpaulo } 389195618Srpaulo } 390195618Srpaulo 391195618Srpaulo return FALSE; 392195618Srpaulo} 393195784Srpaulo 394195784Srpaulo 395195784Srpaulo/* 396195784Srpaulo *--------------------------------------------------------------------- 397195784Srpaulo * basic calendar stuff 398195908Srpaulo * -------------------------------------------------------------------- 399195908Srpaulo */ 400195908Srpaulo 401195908Srpaulo/* month table for a year starting with March,1st */ 402195908Srpaulostatic const uint16_t shift_month_table[13] = { 403195908Srpaulo 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 404195908Srpaulo}; 405195908Srpaulo 406195908Srpaulo/* month tables for years starting with January,1st; regular & leap */ 407195908Srpaulostatic const uint16_t real_month_table[2][13] = { 408195908Srpaulo /* -*- table for regular years -*- */ 409195908Srpaulo { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 410195908Srpaulo /* -*- table for leap years -*- */ 411195908Srpaulo { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 412195908Srpaulo}; 413195784Srpaulo 414195784Srpaulo/* 415195784Srpaulo * Some notes on the terminology: 416195784Srpaulo * 417195784Srpaulo * We use the proleptic Gregorian calendar, which is the Gregorian 418195784Srpaulo * calendar extended in both directions ad infinitum. This totally 419195784Srpaulo * disregards the fact that this calendar was invented in 1582, and 420195784Srpaulo * was adopted at various dates over the world; sometimes even after 421195784Srpaulo * the start of the NTP epoch. 422195784Srpaulo * 423195784Srpaulo * Normally date parts are given as current cycles, while time parts 424195784Srpaulo * are given as elapsed cycles: 425195784Srpaulo * 426195618Srpaulo * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, 427234894Smonthadar * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. 428234894Smonthadar * 429234894Smonthadar * The basic calculations for this calendar implementation deal with 430234877Smonthadar * ELAPSED date units, which is the number of full years, full months 431234894Smonthadar * and full days before a date: 1970-01-01 would be (1969, 0, 0) in 432195784Srpaulo * that notation. 433195618Srpaulo * 434195618Srpaulo * To ease the numeric computations, month and day values outside the 435195618Srpaulo * normal range are acceptable: 2001-03-00 will be treated as the day 436195618Srpaulo * before 2001-03-01, 2000-13-32 will give the same result as 437195618Srpaulo * 2001-02-01 and so on. 438195618Srpaulo * 439195618Srpaulo * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' 440195618Srpaulo * (day number). This is the number of days elapsed since 0000-12-31 441195618Srpaulo * in the proleptic Gregorian calendar. The begin of the Christian Era 442288086Sadrian * (0001-01-01) is RD(1). 443197975Srpaulo */ 444197975Srpaulo 445195618Srpaulo/* 446197975Srpaulo * ================================================================== 447195618Srpaulo * 448195618Srpaulo * General algorithmic stuff 449195618Srpaulo * 450195618Srpaulo * ================================================================== 451195618Srpaulo */ 452197975Srpaulo 453195618Srpaulo/* 454195618Srpaulo *--------------------------------------------------------------------- 455195618Srpaulo * Do a periodic extension of 'value' around 'pivot' with a period of 456195618Srpaulo * 'cycle'. 457195618Srpaulo * 458195618Srpaulo * The result 'res' is a number that holds to the following properties: 459195618Srpaulo * 460195618Srpaulo * 1) res MOD cycle == value MOD cycle 461195618Srpaulo * 2) pivot <= res < pivot + cycle 462288086Sadrian * (replace </<= with >/>= for negative cycles) 463197975Srpaulo * 464197975Srpaulo * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which 465195618Srpaulo * is not the same as the '%' operator in C: C requires division to be 466197975Srpaulo * a truncated division, where remainder and dividend have the same 467195618Srpaulo * sign if the remainder is not zero, whereas floor division requires 468195618Srpaulo * divider and modulus to have the same sign for a non-zero modulus. 469195618Srpaulo * 470195618Srpaulo * This function has some useful applications: 471195618Srpaulo * 472197975Srpaulo * + let Y be a calendar year and V a truncated 2-digit year: then 473195618Srpaulo * periodic_extend(Y-50, V, 100) 474195618Srpaulo * is the closest expansion of the truncated year with respect to 475195618Srpaulo * the full year, that is a 4-digit year with a difference of less 476195618Srpaulo * than 50 years to the year Y. ("century unfolding") 477195618Srpaulo * 478195618Srpaulo * + let T be a UN*X time stamp and V be seconds-of-day: then 479195618Srpaulo * perodic_extend(T-43200, V, 86400) 480195618Srpaulo * is a time stamp that has the same seconds-of-day as the input 481195618Srpaulo * value, with an absolute difference to T of <= 12hrs. ("day 482288086Sadrian * unfolding") 483195618Srpaulo * 484195618Srpaulo * + Wherever you have a truncated periodic value and a non-truncated 485195618Srpaulo * base value and you want to match them somehow... 486195618Srpaulo * 487195618Srpaulo * Basically, the function delivers 'pivot + (value - pivot) % cycle', 488195618Srpaulo * but the implementation takes some pains to avoid internal signed 489195618Srpaulo * integer overflows in the '(value - pivot) % cycle' part and adheres 490195618Srpaulo * to the floor division convention. 491195618Srpaulo * 492195618Srpaulo * If 64bit scalars where available on all intended platforms, writing a 493195618Srpaulo * version that uses 64 bit ops would be easy; writing a general 494195618Srpaulo * division routine for 64bit ops on a platform that can only do 495195618Srpaulo * 32/16bit divisions and is still performant is a bit more 496195618Srpaulo * difficult. Since most usecases can be coded in a way that does only 497288086Sadrian * require the 32-bit version a 64bit version is NOT provided here. 498195618Srpaulo * --------------------------------------------------------------------- 499195618Srpaulo */ 500195618Srpauloint32_t 501195618Srpaulontpcal_periodic_extend( 502195618Srpaulo int32_t pivot, 503195618Srpaulo int32_t value, 504195618Srpaulo int32_t cycle 505195618Srpaulo ) 506195618Srpaulo{ 507246506Smonthadar uint32_t diff; 508246506Smonthadar char cpl = 0; /* modulo complement flag */ 509246506Smonthadar char neg = 0; /* sign change flag */ 510246506Smonthadar 511246506Smonthadar /* make the cycle positive and adjust the flags */ 512246506Smonthadar if (cycle < 0) { 513246506Smonthadar cycle = - cycle; 514246506Smonthadar neg ^= 1; 515246506Smonthadar cpl ^= 1; 516246506Smonthadar } 517246506Smonthadar /* guard against div by zero or one */ 518246506Smonthadar if (cycle > 1) { 519246506Smonthadar /* 520246506Smonthadar * Get absolute difference as unsigned quantity and 521246506Smonthadar * the complement flag. This is done by always 522246506Smonthadar * subtracting the smaller value from the bigger 523246506Smonthadar * one. 524246506Smonthadar */ 525246506Smonthadar if (value >= pivot) { 526246506Smonthadar diff = int32_to_uint32_2cpl(value) 527246506Smonthadar - int32_to_uint32_2cpl(pivot); 528246506Smonthadar } else { 529246506Smonthadar diff = int32_to_uint32_2cpl(pivot) 530246506Smonthadar - int32_to_uint32_2cpl(value); 531246506Smonthadar cpl ^= 1; 532246506Smonthadar } 533246506Smonthadar diff %= (uint32_t)cycle; 534246506Smonthadar if (diff) { 535246506Smonthadar if (cpl) 536246506Smonthadar diff = (uint32_t)cycle - diff; 537246506Smonthadar if (neg) 538246506Smonthadar diff = ~diff + 1; 539246506Smonthadar pivot += uint32_2cpl_to_int32(diff); 540246520Smonthadar } 541246520Smonthadar } 542246520Smonthadar return pivot; 543246506Smonthadar} 544246506Smonthadar 545246506Smonthadar/* 546246506Smonthadar *------------------------------------------------------------------- 547246506Smonthadar * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X 548246506Smonthadar * scale with proper epoch unfolding around a given pivot or the current 549195618Srpaulo * system time. This function happily accepts negative pivot values as 550195618Srpaulo * timestamps befor 1970-01-01, so be aware of possible trouble on 551195618Srpaulo * platforms with 32bit 'time_t'! 552195618Srpaulo * 553195618Srpaulo * This is also a periodic extension, but since the cycle is 2^32 and 554195618Srpaulo * the shift is 2^31, we can do some *very* fast math without explicit 555195618Srpaulo * divisions. 556195618Srpaulo *------------------------------------------------------------------- 557195618Srpaulo */ 558246506Smonthadarvint64 559195618Srpaulontpcal_ntp_to_time( 560195618Srpaulo uint32_t ntp, 561195618Srpaulo const time_t * pivot 562246497Smonthadar ) 563195618Srpaulo{ 564195618Srpaulo vint64 res; 565195618Srpaulo 566195618Srpaulo# if defined(HAVE_INT64) 567234874Smonthadar 568195618Srpaulo res.q_s = (pivot != NULL) 569195618Srpaulo ? *pivot 570234874Smonthadar : now(); 571195618Srpaulo res.Q_s -= 0x80000000; /* unshift of half range */ 572195618Srpaulo ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 573234874Smonthadar ntp -= res.D_s.lo; /* cycle difference */ 574195618Srpaulo res.Q_s += (uint64_t)ntp; /* get expanded time */ 575195618Srpaulo 576232479Sadrian# else /* no 64bit scalars */ 577232479Sadrian 578246506Smonthadar time_t tmp; 579246506Smonthadar 580195618Srpaulo tmp = (pivot != NULL) 581234874Smonthadar ? *pivot 582195618Srpaulo : now(); 583195618Srpaulo res = time_to_vint64(&tmp); 584234874Smonthadar M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000); 585195618Srpaulo ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 586195618Srpaulo ntp -= res.D_s.lo; /* cycle difference */ 587234874Smonthadar M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 588195618Srpaulo 589195618Srpaulo# endif /* no 64bit scalars */ 590232479Sadrian 591232479Sadrian return res; 592232479Sadrian} 593246506Smonthadar 594246506Smonthadar/* 595246506Smonthadar *------------------------------------------------------------------- 596195618Srpaulo * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP 597195618Srpaulo * scale with proper epoch unfolding around a given pivot or the current 598195618Srpaulo * system time. 599195618Srpaulo * 600195618Srpaulo * Note: The pivot must be given in the UN*X time domain! 601195618Srpaulo * 602195618Srpaulo * This is also a periodic extension, but since the cycle is 2^32 and 603195618Srpaulo * the shift is 2^31, we can do some *very* fast math without explicit 604195618Srpaulo * divisions. 605195618Srpaulo *------------------------------------------------------------------- 606195618Srpaulo */ 607195618Srpaulovint64 608195618Srpaulontpcal_ntp_to_ntp( 609195618Srpaulo uint32_t ntp, 610195618Srpaulo const time_t *pivot 611195618Srpaulo ) 612195618Srpaulo{ 613195618Srpaulo vint64 res; 614195618Srpaulo 615195618Srpaulo# if defined(HAVE_INT64) 616195618Srpaulo 617195618Srpaulo res.q_s = (pivot) 618195618Srpaulo ? *pivot 619195618Srpaulo : now(); 620195618Srpaulo res.Q_s -= 0x80000000; /* unshift of half range */ 621195618Srpaulo res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ 622195618Srpaulo ntp -= res.D_s.lo; /* cycle difference */ 623195618Srpaulo res.Q_s += (uint64_t)ntp; /* get expanded time */ 624195618Srpaulo 625195618Srpaulo# else /* no 64bit scalars */ 626195618Srpaulo 627234874Smonthadar time_t tmp; 628195618Srpaulo 629195618Srpaulo tmp = (pivot) 630195618Srpaulo ? *pivot 631195784Srpaulo : now(); 632195618Srpaulo res = time_to_vint64(&tmp); 633195618Srpaulo M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 634195618Srpaulo M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */ 635195618Srpaulo ntp -= res.D_s.lo; /* cycle difference */ 636195618Srpaulo M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 637195618Srpaulo 638195618Srpaulo# endif /* no 64bit scalars */ 639195618Srpaulo 640195618Srpaulo return res; 641195618Srpaulo} 642195784Srpaulo 643195618Srpaulo 644195618Srpaulo/* 645195618Srpaulo * ================================================================== 646283555Sadrian * 647195618Srpaulo * Splitting values to composite entities 648283538Sadrian * 649195618Srpaulo * ================================================================== 650195618Srpaulo */ 651195618Srpaulo 652195618Srpaulo/* 653195618Srpaulo *------------------------------------------------------------------- 654195618Srpaulo * Split a 64bit seconds value into elapsed days in 'res.hi' and 655195618Srpaulo * elapsed seconds since midnight in 'res.lo' using explicit floor 656195618Srpaulo * division. This function happily accepts negative time values as 657195618Srpaulo * timestamps before the respective epoch start. 658195618Srpaulo * ------------------------------------------------------------------- 659195618Srpaulo */ 660205277Srpaulontpcal_split 661283538Sadrianntpcal_daysplit( 662283538Sadrian const vint64 *ts 663195618Srpaulo ) 664195618Srpaulo{ 665195784Srpaulo ntpcal_split res; 666195618Srpaulo uint32_t Q; 667195618Srpaulo 668195618Srpaulo# if defined(HAVE_INT64) 669195618Srpaulo 670195618Srpaulo /* Manual floor division by SECSPERDAY. This uses the one's 671246508Smonthadar * complement trick, too, but without an extra flag value: The 672195618Srpaulo * flag would be 64bit, and that's a bit of overkill on a 32bit 673283555Sadrian * target that has to use a register pair for a 64bit number. 674283291Sjkim */ 675283291Sjkim if (ts->q_s < 0) 676246506Smonthadar Q = ~(uint32_t)(~ts->Q_s / SECSPERDAY); 677195618Srpaulo else 678195618Srpaulo Q = (uint32_t)(ts->Q_s / SECSPERDAY); 679195618Srpaulo 680195618Srpaulo# else 681195618Srpaulo 682195618Srpaulo uint32_t ah, al, sflag, A; 683195618Srpaulo 684195618Srpaulo /* get operand into ah/al (either ts or ts' one's complement, 685195618Srpaulo * for later floor division) 686195618Srpaulo */ 687195618Srpaulo sflag = int32_sflag(ts->d_s.hi); 688195618Srpaulo ah = sflag ^ ts->D_s.hi; 689195618Srpaulo al = sflag ^ ts->D_s.lo; 690195618Srpaulo 691195618Srpaulo /* Since 86400 == 128*675 we can drop the least 7 bits and 692195618Srpaulo * divide by 675 instead of 86400. Then the maximum remainder 693195618Srpaulo * after each devision step is 674, and we need 10 bits for 694195618Srpaulo * that. So in the next step we can shift in 22 bits from the 695195618Srpaulo * numerator. 696195618Srpaulo * 697195618Srpaulo * Therefore we load the accu with the top 13 bits (51..63) in 698195618Srpaulo * the first shot. We don't have to remember the quotient -- it 699195618Srpaulo * would be shifted out anyway. 700195618Srpaulo */ 701195618Srpaulo A = ah >> 19; 702195618Srpaulo if (A >= 675) 703195618Srpaulo A = (A % 675u); 704195618Srpaulo 705246506Smonthadar /* Now assemble the remainder with bits 29..50 from the 706195784Srpaulo * numerator and divide. This creates the upper ten bits of the 707246506Smonthadar * quotient. (Well, the top 22 bits of a 44bit result. But that 708246506Smonthadar * will be truncated to 32 bits anyway.) 709195618Srpaulo */ 710195618Srpaulo A = (A << 19) | (ah & 0x0007FFFFu); 711195618Srpaulo A = (A << 3) | (al >> 29); 712195618Srpaulo Q = A / 675u; 713195618Srpaulo A = A % 675u; 714195618Srpaulo 715195618Srpaulo /* Now assemble the remainder with bits 7..28 from the numerator 716195618Srpaulo * and do a final division step. 717195618Srpaulo */ 718195618Srpaulo A = (A << 22) | ((al >> 7) & 0x003FFFFFu); 719195618Srpaulo Q = (Q << 22) | (A / 675u); 720195618Srpaulo 721195618Srpaulo /* The last 7 bits get simply dropped, as they have no affect on 722195618Srpaulo * the quotient when dividing by 86400. 723195618Srpaulo */ 724195618Srpaulo 725195618Srpaulo /* apply sign correction and calculate the true floor 726195618Srpaulo * remainder. 727195618Srpaulo */ 728195784Srpaulo Q ^= sflag; 729195618Srpaulo 730195618Srpaulo# endif 731195618Srpaulo 732195618Srpaulo res.hi = uint32_2cpl_to_int32(Q); 733195618Srpaulo res.lo = ts->D_s.lo - Q * SECSPERDAY; 734195618Srpaulo 735195618Srpaulo return res; 736195618Srpaulo} 737195618Srpaulo 738195618Srpaulo/* 739195618Srpaulo *------------------------------------------------------------------- 740195618Srpaulo * Split a 32bit seconds value into h/m/s and excessive days. This 741195618Srpaulo * function happily accepts negative time values as timestamps before 742195618Srpaulo * midnight. 743195618Srpaulo * ------------------------------------------------------------------- 744195618Srpaulo */ 745195618Srpaulostatic int32_t 746195618Srpaulopriv_timesplit( 747195618Srpaulo int32_t split[3], 748195618Srpaulo int32_t ts 749195618Srpaulo ) 750195618Srpaulo{ 751195618Srpaulo /* Do 3 chained floor divisions by positive constants, using the 752195618Srpaulo * one's complement trick and factoring out the intermediate XOR 753195618Srpaulo * ops to reduce the number of operations. 754195618Srpaulo */ 755195618Srpaulo uint32_t us, um, uh, ud, sflag; 756195618Srpaulo 757195618Srpaulo sflag = int32_sflag(ts); 758195618Srpaulo us = int32_to_uint32_2cpl(ts); 759195618Srpaulo 760195618Srpaulo um = (sflag ^ us) / SECSPERMIN; 761195618Srpaulo uh = um / MINSPERHR; 762195618Srpaulo ud = uh / HRSPERDAY; 763195618Srpaulo 764195618Srpaulo um ^= sflag; 765195618Srpaulo uh ^= sflag; 766195618Srpaulo ud ^= sflag; 767195618Srpaulo 768195618Srpaulo split[0] = (int32_t)(uh - ud * HRSPERDAY ); 769195618Srpaulo split[1] = (int32_t)(um - uh * MINSPERHR ); 770195618Srpaulo split[2] = (int32_t)(us - um * SECSPERMIN); 771195618Srpaulo 772195618Srpaulo return uint32_2cpl_to_int32(ud); 773195618Srpaulo} 774195618Srpaulo 775195618Srpaulo/* 776195618Srpaulo * --------------------------------------------------------------------- 777195618Srpaulo * Given the number of elapsed days in the calendar era, split this 778195618Srpaulo * number into the number of elapsed years in 'res.hi' and the number 779195618Srpaulo * of elapsed days of that year in 'res.lo'. 780195618Srpaulo * 781195618Srpaulo * if 'isleapyear' is not NULL, it will receive an integer that is 0 for 782195618Srpaulo * regular years and a non-zero value for leap years. 783195618Srpaulo *--------------------------------------------------------------------- 784195618Srpaulo */ 785195618Srpaulontpcal_split 786195618Srpaulontpcal_split_eradays( 787195618Srpaulo int32_t days, 788195618Srpaulo int *isleapyear 789195618Srpaulo ) 790195618Srpaulo{ 791195618Srpaulo /* Use the fast cyclesplit algorithm here, to calculate the 792195618Srpaulo * centuries and years in a century with one division each. This 793195618Srpaulo * reduces the number of division operations to two, but is 794195618Srpaulo * susceptible to internal range overflow. We make sure the 795195618Srpaulo * input operands are in the safe range; this still gives us 796195618Srpaulo * approx +/-2.9 million years. 797195618Srpaulo */ 798195618Srpaulo ntpcal_split res; 799195618Srpaulo int32_t n100, n001; /* calendar year cycles */ 800195618Srpaulo uint32_t uday, Q, sflag; 801195618Srpaulo 802195618Srpaulo /* split off centuries first */ 803195618Srpaulo sflag = int32_sflag(days); 804195618Srpaulo uday = uint32_saturate(int32_to_uint32_2cpl(days), sflag); 805195618Srpaulo uday = (4u * uday) | 3u; 806300232Savos Q = sflag ^ ((sflag ^ uday) / GREGORIAN_CYCLE_DAYS); 807195618Srpaulo uday = uday - Q * GREGORIAN_CYCLE_DAYS; 808300232Savos n100 = uint32_2cpl_to_int32(Q); 809195618Srpaulo 810195618Srpaulo /* Split off years in century -- days >= 0 here, and we're far 811195618Srpaulo * away from integer overflow trouble now. */ 812195618Srpaulo uday |= 3; 813195618Srpaulo n001 = uday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 814195618Srpaulo uday = uday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 815195618Srpaulo 816195618Srpaulo /* Assemble the year and day in year */ 817195618Srpaulo res.hi = n100 * 100 + n001; 818195618Srpaulo res.lo = uday / 4u; 819195618Srpaulo 820195618Srpaulo /* Eventually set the leap year flag. Note: 0 <= n001 <= 99 and 821195618Srpaulo * Q is still the two's complement representation of the 822195618Srpaulo * centuries: The modulo 4 ops can be done with masking here. 823195618Srpaulo * We also shift the year and the century by one, so the tests 824195618Srpaulo * can be done against zero instead of 3. 825195618Srpaulo */ 826195618Srpaulo if (isleapyear) 827195618Srpaulo *isleapyear = !((n001+1) & 3) 828195618Srpaulo && ((n001 != 99) || !((Q+1) & 3)); 829300232Savos 830195813Ssam return res; 831195784Srpaulo} 832246506Smonthadar 833195618Srpaulo/* 834195618Srpaulo *--------------------------------------------------------------------- 835195618Srpaulo * Given a number of elapsed days in a year and a leap year indicator, 836195618Srpaulo * split the number of elapsed days into the number of elapsed months in 837195618Srpaulo * 'res.hi' and the number of elapsed days of that month in 'res.lo'. 838195618Srpaulo * 839195618Srpaulo * This function will fail and return {-1,-1} if the number of elapsed 840195618Srpaulo * days is not in the valid range! 841195618Srpaulo *--------------------------------------------------------------------- 842195784Srpaulo */ 843195784Srpaulontpcal_split 844195784Srpaulontpcal_split_yeardays( 845195784Srpaulo int32_t eyd, 846195784Srpaulo int isleapyear 847195784Srpaulo ) 848195784Srpaulo{ 849195813Ssam ntpcal_split res; 850195784Srpaulo const uint16_t *lt; /* month length table */ 851195784Srpaulo 852195784Srpaulo /* check leap year flag and select proper table */ 853246509Smonthadar lt = real_month_table[(isleapyear != 0)]; 854246509Smonthadar if (0 <= eyd && eyd < lt[12]) { 855246509Smonthadar /* get zero-based month by approximation & correction step */ 856246509Smonthadar res.hi = eyd >> 5; /* approx month; might be 1 too low */ 857246509Smonthadar if (lt[res.hi + 1] <= eyd) /* fixup approximative month value */ 858246509Smonthadar res.hi += 1; 859246509Smonthadar res.lo = eyd - lt[res.hi]; 860246509Smonthadar } else { 861246509Smonthadar res.lo = res.hi = -1; 862246509Smonthadar } 863246509Smonthadar 864246509Smonthadar return res; 865195784Srpaulo} 866246509Smonthadar 867246509Smonthadar/* 868246509Smonthadar *--------------------------------------------------------------------- 869246509Smonthadar * Convert a RD into the date part of a 'struct calendar'. 870246509Smonthadar *--------------------------------------------------------------------- 871246509Smonthadar */ 872246509Smonthadarint 873246509Smonthadarntpcal_rd_to_date( 874246509Smonthadar struct calendar *jd, 875246509Smonthadar int32_t rd 876246509Smonthadar ) 877246509Smonthadar{ 878283538Sadrian ntpcal_split split; 879283538Sadrian int leapy; 880283538Sadrian u_int ymask; 881246509Smonthadar 882246509Smonthadar /* Get day-of-week first. Since rd is signed, the remainder can 883246509Smonthadar * be in the range [-6..+6], but the assignment to an unsigned 884246509Smonthadar * variable maps the negative values to positive values >=7. 885246509Smonthadar * This makes the sign correction look strange, but adding 7 886246509Smonthadar * causes the needed wrap-around into the desired value range of 887246509Smonthadar * zero to six, both inclusive. 888246509Smonthadar */ 889246509Smonthadar jd->weekday = rd % DAYSPERWEEK; 890246509Smonthadar if (jd->weekday >= DAYSPERWEEK) /* weekday is unsigned! */ 891246509Smonthadar jd->weekday += DAYSPERWEEK; 892195618Srpaulo 893195618Srpaulo split = ntpcal_split_eradays(rd - 1, &leapy); 894195618Srpaulo /* Get year and day-of-year, with overflow check. If any of the 895195618Srpaulo * upper 16 bits is set after shifting to unity-based years, we 896195618Srpaulo * will have an overflow when converting to an unsigned 16bit 897195618Srpaulo * year. Shifting to the right is OK here, since it does not 898195618Srpaulo * matter if the shift is logic or arithmetic. 899195618Srpaulo */ 900195618Srpaulo split.hi += 1; 901195618Srpaulo ymask = 0u - ((split.hi >> 16) == 0); 902195618Srpaulo jd->year = (uint16_t)(split.hi & ymask); 903195618Srpaulo jd->yearday = (uint16_t)split.lo + 1; 904195618Srpaulo 905195618Srpaulo /* convert to month and mday */ 906195618Srpaulo split = ntpcal_split_yeardays(split.lo, leapy); 907195618Srpaulo jd->month = (uint8_t)split.hi + 1; 908195618Srpaulo jd->monthday = (uint8_t)split.lo + 1; 909195618Srpaulo 910195618Srpaulo return ymask ? leapy : -1; 911195618Srpaulo} 912195618Srpaulo 913195618Srpaulo/* 914195618Srpaulo *--------------------------------------------------------------------- 915195618Srpaulo * Convert a RD into the date part of a 'struct tm'. 916195618Srpaulo *--------------------------------------------------------------------- 917195618Srpaulo */ 918195618Srpauloint 919198242Srpaulontpcal_rd_to_tm( 920195618Srpaulo struct tm *utm, 921195618Srpaulo int32_t rd 922195618Srpaulo ) 923195618Srpaulo{ 924198242Srpaulo ntpcal_split split; 925195618Srpaulo int leapy; 926195618Srpaulo 927195784Srpaulo /* get day-of-week first */ 928195784Srpaulo utm->tm_wday = rd % DAYSPERWEEK; 929195618Srpaulo if (utm->tm_wday < 0) 930195784Srpaulo utm->tm_wday += DAYSPERWEEK; 931195784Srpaulo 932195784Srpaulo /* get year and day-of-year */ 933195784Srpaulo split = ntpcal_split_eradays(rd - 1, &leapy); 934195784Srpaulo utm->tm_year = split.hi - 1899; 935195784Srpaulo utm->tm_yday = split.lo; /* 0-based */ 936195784Srpaulo 937195618Srpaulo /* convert to month and mday */ 938195618Srpaulo split = ntpcal_split_yeardays(split.lo, leapy); 939195618Srpaulo utm->tm_mon = split.hi; /* 0-based */ 940195618Srpaulo utm->tm_mday = split.lo + 1; /* 1-based */ 941195618Srpaulo 942195618Srpaulo return leapy; 943195618Srpaulo} 944195618Srpaulo 945195618Srpaulo/* 946195618Srpaulo *--------------------------------------------------------------------- 947195618Srpaulo * Take a value of seconds since midnight and split it into hhmmss in a 948195618Srpaulo * 'struct calendar'. 949195618Srpaulo *--------------------------------------------------------------------- 950195618Srpaulo */ 951195618Srpauloint32_t 952195618Srpaulontpcal_daysec_to_date( 953195618Srpaulo struct calendar *jd, 954195618Srpaulo int32_t sec 955195618Srpaulo ) 956195618Srpaulo{ 957195618Srpaulo int32_t days; 958195618Srpaulo int ts[3]; 959195618Srpaulo 960195618Srpaulo days = priv_timesplit(ts, sec); 961195618Srpaulo jd->hour = (uint8_t)ts[0]; 962195618Srpaulo jd->minute = (uint8_t)ts[1]; 963195618Srpaulo jd->second = (uint8_t)ts[2]; 964195618Srpaulo 965195618Srpaulo return days; 966195618Srpaulo} 967195618Srpaulo 968195618Srpaulo/* 969195908Srpaulo *--------------------------------------------------------------------- 970195618Srpaulo * Take a value of seconds since midnight and split it into hhmmss in a 971195618Srpaulo * 'struct tm'. 972195618Srpaulo *--------------------------------------------------------------------- 973195618Srpaulo */ 974195618Srpauloint32_t 975195618Srpaulontpcal_daysec_to_tm( 976195618Srpaulo struct tm *utm, 977195618Srpaulo int32_t sec 978195618Srpaulo ) 979195908Srpaulo{ 980195908Srpaulo int32_t days; 981195908Srpaulo int32_t ts[3]; 982195908Srpaulo 983195908Srpaulo days = priv_timesplit(ts, sec); 984195908Srpaulo utm->tm_hour = ts[0]; 985195908Srpaulo utm->tm_min = ts[1]; 986195784Srpaulo utm->tm_sec = ts[2]; 987195784Srpaulo 988195908Srpaulo return days; 989195618Srpaulo} 990195618Srpaulo 991195618Srpaulo/* 992195618Srpaulo *--------------------------------------------------------------------- 993195618Srpaulo * take a split representation for day/second-of-day and day offset 994195618Srpaulo * and convert it to a 'struct calendar'. The seconds will be normalised 995195618Srpaulo * into the range of a day, and the day will be adjusted accordingly. 996195618Srpaulo * 997195618Srpaulo * returns >0 if the result is in a leap year, 0 if in a regular 998195618Srpaulo * year and <0 if the result did not fit into the calendar struct. 999195618Srpaulo *--------------------------------------------------------------------- 1000195618Srpaulo */ 1001195618Srpauloint 1002246512Smonthadarntpcal_daysplit_to_date( 1003246512Smonthadar struct calendar *jd, 1004195618Srpaulo const ntpcal_split *ds, 1005195618Srpaulo int32_t dof 1006195618Srpaulo ) 1007195618Srpaulo{ 1008195618Srpaulo dof += ntpcal_daysec_to_date(jd, ds->lo); 1009195618Srpaulo return ntpcal_rd_to_date(jd, ds->hi + dof); 1010195618Srpaulo} 1011246511Smonthadar 1012195784Srpaulo/* 1013246511Smonthadar *--------------------------------------------------------------------- 1014195784Srpaulo * take a split representation for day/second-of-day and day offset 1015195784Srpaulo * and convert it to a 'struct tm'. The seconds will be normalised 1016195784Srpaulo * into the range of a day, and the day will be adjusted accordingly. 1017246511Smonthadar * 1018246511Smonthadar * returns 1 if the result is in a leap year and zero if in a regular 1019246511Smonthadar * year. 1020246511Smonthadar *--------------------------------------------------------------------- 1021246511Smonthadar */ 1022246511Smonthadarint 1023246511Smonthadarntpcal_daysplit_to_tm( 1024246511Smonthadar struct tm *utm, 1025246511Smonthadar const ntpcal_split *ds , 1026246511Smonthadar int32_t dof 1027246511Smonthadar ) 1028195618Srpaulo{ 1029195618Srpaulo dof += ntpcal_daysec_to_tm(utm, ds->lo); 1030195618Srpaulo 1031246511Smonthadar return ntpcal_rd_to_tm(utm, ds->hi + dof); 1032246511Smonthadar} 1033246511Smonthadar 1034246511Smonthadar/* 1035246511Smonthadar *--------------------------------------------------------------------- 1036246511Smonthadar * Take a UN*X time and convert to a calendar structure. 1037246511Smonthadar *--------------------------------------------------------------------- 1038253745Sadrian */ 1039248069Sadrianint 1040246512Smonthadarntpcal_time_to_date( 1041246511Smonthadar struct calendar *jd, 1042271861Sglebius const vint64 *ts 1043246511Smonthadar ) 1044246511Smonthadar{ 1045246511Smonthadar ntpcal_split ds; 1046246511Smonthadar 1047246511Smonthadar ds = ntpcal_daysplit(ts); 1048253727Sadrian ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1049253727Sadrian ds.hi += DAY_UNIX_STARTS; 1050253727Sadrian 1051253727Sadrian return ntpcal_rd_to_date(jd, ds.hi); 1052246511Smonthadar} 1053253727Sadrian 1054246511Smonthadar 1055246511Smonthadar/* 1056195618Srpaulo * ================================================================== 1057246510Smonthadar * 1058246510Smonthadar * merging composite entities 1059246510Smonthadar * 1060246511Smonthadar * ================================================================== 1061246511Smonthadar */ 1062246511Smonthadar 1063246510Smonthadar/* 1064246510Smonthadar *--------------------------------------------------------------------- 1065246510Smonthadar * Merge a number of days and a number of seconds into seconds, 1066246510Smonthadar * expressed in 64 bits to avoid overflow. 1067246510Smonthadar *--------------------------------------------------------------------- 1068246510Smonthadar */ 1069246510Smonthadarvint64 1070246510Smonthadarntpcal_dayjoin( 1071246510Smonthadar int32_t days, 1072246511Smonthadar int32_t secs 1073246510Smonthadar ) 1074248069Sadrian{ 1075248069Sadrian vint64 res; 1076246510Smonthadar 1077246510Smonthadar# if defined(HAVE_INT64) 1078246510Smonthadar 1079246510Smonthadar res.q_s = days; 1080246510Smonthadar res.q_s *= SECSPERDAY; 1081246511Smonthadar res.q_s += secs; 1082246511Smonthadar 1083246511Smonthadar# else 1084246511Smonthadar 1085246511Smonthadar uint32_t p1, p2; 1086246510Smonthadar int isneg; 1087246510Smonthadar 1088246510Smonthadar /* 1089246510Smonthadar * res = days *86400 + secs, using manual 16/32 bit 1090246510Smonthadar * multiplications and shifts. 1091246510Smonthadar */ 1092246510Smonthadar isneg = (days < 0); 1093246510Smonthadar if (isneg) 1094246510Smonthadar days = -days; 1095246511Smonthadar 1096246511Smonthadar /* assemble days * 675 */ 1097246511Smonthadar res.D_s.lo = (days & 0xFFFF) * 675u; 1098246511Smonthadar res.D_s.hi = 0; 1099246511Smonthadar p1 = (days >> 16) * 675u; 1100246511Smonthadar p2 = p1 >> 16; 1101246511Smonthadar p1 = p1 << 16; 1102246511Smonthadar M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1103246511Smonthadar 1104246511Smonthadar /* mul by 128, using shift */ 1105246511Smonthadar res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25); 1106246511Smonthadar res.D_s.lo = (res.D_s.lo << 7); 1107246510Smonthadar 1108246510Smonthadar /* fix sign */ 1109246510Smonthadar if (isneg) 1110246510Smonthadar M_NEG(res.D_s.hi, res.D_s.lo); 1111246510Smonthadar 1112246510Smonthadar /* properly add seconds */ 1113246510Smonthadar p2 = 0; 1114246510Smonthadar if (secs < 0) { 1115246510Smonthadar p1 = (uint32_t)-secs; 1116246511Smonthadar M_NEG(p2, p1); 1117246511Smonthadar } else { 1118246511Smonthadar p1 = (uint32_t)secs; 1119246511Smonthadar } 1120246511Smonthadar M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 1121246511Smonthadar 1122246511Smonthadar# endif 1123246511Smonthadar 1124246511Smonthadar return res; 1125246511Smonthadar} 1126246510Smonthadar 1127246510Smonthadar/* 1128246510Smonthadar *--------------------------------------------------------------------- 1129246511Smonthadar * get leap years since epoch in elapsed years 1130246511Smonthadar *--------------------------------------------------------------------- 1131246510Smonthadar */ 1132246510Smonthadarint32_t 1133246510Smonthadarntpcal_leapyears_in_years( 1134246510Smonthadar int32_t years 1135195618Srpaulo ) 1136195618Srpaulo{ 1137195618Srpaulo /* We use the in-out-in algorithm here, using the one's 1138195618Srpaulo * complement division trick for negative numbers. The chained 1139195618Srpaulo * division sequence by 4/25/4 gives the compiler the chance to 1140195618Srpaulo * get away with only one true division and doing shifts otherwise. 1141195618Srpaulo */ 1142195618Srpaulo 1143195618Srpaulo uint32_t sflag, sum, uyear; 1144195618Srpaulo 1145195618Srpaulo sflag = int32_sflag(years); 1146195618Srpaulo uyear = int32_to_uint32_2cpl(years); 1147195618Srpaulo uyear ^= sflag; 1148195618Srpaulo 1149195618Srpaulo sum = (uyear /= 4u); /* 4yr rule --> IN */ 1150195618Srpaulo sum -= (uyear /= 25u); /* 100yr rule --> OUT */ 1151195618Srpaulo sum += (uyear /= 4u); /* 400yr rule --> IN */ 1152195618Srpaulo 1153248069Sadrian /* Thanks to the alternation of IN/OUT/IN we can do the sum 1154248069Sadrian * directly and have a single one's complement operation 1155248069Sadrian * here. (Only if the years are negative, of course.) Otherwise 1156234878Smonthadar * the one's complement would have to be done when 1157298995Spfg * adding/subtracting the terms. 1158234878Smonthadar */ 1159234878Smonthadar return uint32_2cpl_to_int32(sflag ^ sum); 1160234878Smonthadar} 1161234878Smonthadar 1162195618Srpaulo/* 1163234878Smonthadar *--------------------------------------------------------------------- 1164195618Srpaulo * Convert elapsed years in Era into elapsed days in Era. 1165195618Srpaulo *--------------------------------------------------------------------- 1166195618Srpaulo */ 1167195618Srpauloint32_t 1168195618Srpaulontpcal_days_in_years( 1169195618Srpaulo int32_t years 1170195618Srpaulo ) 1171195618Srpaulo{ 1172195618Srpaulo return years * DAYSPERYEAR + ntpcal_leapyears_in_years(years); 1173243882Sglebius} 1174195618Srpaulo 1175195618Srpaulo/* 1176195618Srpaulo *--------------------------------------------------------------------- 1177195618Srpaulo * Convert a number of elapsed month in a year into elapsed days in year. 1178271861Sglebius * 1179195618Srpaulo * The month will be normalized, and 'res.hi' will contain the 1180195618Srpaulo * excessive years that must be considered when converting the years, 1181195618Srpaulo * while 'res.lo' will contain the number of elapsed days since start 1182195618Srpaulo * of the year. 1183195618Srpaulo * 1184195618Srpaulo * This code uses the shifted-month-approach to convert month to days, 1185195618Srpaulo * because then there is no need to have explicit leap year 1186195618Srpaulo * information. The slight disadvantage is that for most month values 1187271861Sglebius * the result is a negative value, and the year excess is one; the 1188195618Srpaulo * conversion is then simply based on the start of the following year. 1189195618Srpaulo *--------------------------------------------------------------------- 1190195618Srpaulo */ 1191195618Srpaulontpcal_split 1192195618Srpaulontpcal_days_in_months( 1193195618Srpaulo int32_t m 1194195618Srpaulo ) 1195195618Srpaulo{ 1196195618Srpaulo ntpcal_split res; 1197195618Srpaulo 1198195618Srpaulo /* Add ten months and correct if needed. (It likely is...) */ 1199195618Srpaulo res.lo = m + 10; 1200195618Srpaulo res.hi = (res.lo >= 12); 1201246512Smonthadar if (res.hi) 1202195618Srpaulo res.lo -= 12; 1203234878Smonthadar 1204234878Smonthadar /* if still out of range, normalise by floor division ... */ 1205234878Smonthadar if (res.lo < 0 || res.lo >= 12) { 1206234878Smonthadar uint32_t mu, Q, sflag; 1207234890Smonthadar sflag = int32_sflag(res.lo); 1208234878Smonthadar mu = int32_to_uint32_2cpl(res.lo); 1209195618Srpaulo Q = sflag ^ ((sflag ^ mu) / 12u); 1210195618Srpaulo res.hi += uint32_2cpl_to_int32(Q); 1211234890Smonthadar res.lo = mu - Q * 12u; 1212234890Smonthadar } 1213195618Srpaulo 1214195618Srpaulo /* get cummulated days in year with unshift */ 1215195618Srpaulo res.lo = shift_month_table[res.lo] - 306; 1216195618Srpaulo 1217195618Srpaulo return res; 1218195618Srpaulo} 1219195618Srpaulo 1220195618Srpaulo/* 1221195618Srpaulo *--------------------------------------------------------------------- 1222195618Srpaulo * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1223195618Srpaulo * days in Gregorian epoch. 1224195618Srpaulo * 1225195618Srpaulo * If you want to convert years and days-of-year, just give a month of 1226195618Srpaulo * zero. 1227248069Sadrian *--------------------------------------------------------------------- 1228248069Sadrian */ 1229248069Sadrianint32_t 1230248069Sadrianntpcal_edate_to_eradays( 1231248069Sadrian int32_t years, 1232248069Sadrian int32_t mons, 1233248069Sadrian int32_t mdays 1234248069Sadrian ) 1235248069Sadrian{ 1236248069Sadrian ntpcal_split tmp; 1237248069Sadrian int32_t res; 1238248069Sadrian 1239254082Sadrian if (mons) { 1240248069Sadrian tmp = ntpcal_days_in_months(mons); 1241289164Sadrian res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; 1242271861Sglebius } else 1243195618Srpaulo res = ntpcal_days_in_years(years); 1244195618Srpaulo res += mdays; 1245195784Srpaulo 1246195784Srpaulo return res; 1247195784Srpaulo} 1248234878Smonthadar 1249234878Smonthadar/* 1250195784Srpaulo *--------------------------------------------------------------------- 1251234878Smonthadar * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1252195784Srpaulo * days in year. 1253195784Srpaulo * 1254195784Srpaulo * Note: This will give the true difference to the start of the given year, 1255195784Srpaulo * even if months & days are off-scale. 1256195784Srpaulo *--------------------------------------------------------------------- 1257195784Srpaulo */ 1258195784Srpauloint32_t 1259195784Srpaulontpcal_edate_to_yeardays( 1260195784Srpaulo int32_t years, 1261195784Srpaulo int32_t mons, 1262195784Srpaulo int32_t mdays 1263195784Srpaulo ) 1264195784Srpaulo{ 1265195784Srpaulo ntpcal_split tmp; 1266195784Srpaulo 1267195784Srpaulo if (0 <= mons && mons < 12) { 1268195784Srpaulo years += 1; 1269195784Srpaulo mdays += real_month_table[is_leapyear(years)][mons]; 1270195784Srpaulo } else { 1271195784Srpaulo tmp = ntpcal_days_in_months(mons); 1272195784Srpaulo mdays += tmp.lo 1273195784Srpaulo + ntpcal_days_in_years(years + tmp.hi) 1274195784Srpaulo - ntpcal_days_in_years(years); 1275195784Srpaulo } 1276195784Srpaulo 1277195784Srpaulo return mdays; 1278195784Srpaulo} 1279195784Srpaulo 1280195784Srpaulo/* 1281195784Srpaulo *--------------------------------------------------------------------- 1282195784Srpaulo * Convert elapsed days and the hour/minute/second information into 1283195784Srpaulo * total seconds. 1284195784Srpaulo * 1285234878Smonthadar * If 'isvalid' is not NULL, do a range check on the time specification 1286195784Srpaulo * and tell if the time input is in the normal range, permitting for a 1287195784Srpaulo * single leapsecond. 1288234878Smonthadar *--------------------------------------------------------------------- 1289195784Srpaulo */ 1290234878Smonthadarint32_t 1291234878Smonthadarntpcal_etime_to_seconds( 1292234878Smonthadar int32_t hours, 1293195784Srpaulo int32_t minutes, 1294195784Srpaulo int32_t seconds 1295195784Srpaulo ) 1296195784Srpaulo{ 1297195784Srpaulo int32_t res; 1298195784Srpaulo 1299195784Srpaulo res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; 1300195784Srpaulo 1301195784Srpaulo return res; 1302234878Smonthadar} 1303195784Srpaulo 1304195784Srpaulo/* 1305234878Smonthadar *--------------------------------------------------------------------- 1306234878Smonthadar * Convert the date part of a 'struct tm' (that is, year, month, 1307234878Smonthadar * day-of-month) into the RD of that day. 1308195784Srpaulo *--------------------------------------------------------------------- 1309195784Srpaulo */ 1310195784Srpauloint32_t 1311195784Srpaulontpcal_tm_to_rd( 1312195784Srpaulo const struct tm *utm 1313195784Srpaulo ) 1314195784Srpaulo{ 1315195784Srpaulo return ntpcal_edate_to_eradays(utm->tm_year + 1899, 1316195784Srpaulo utm->tm_mon, 1317246710Sglebius utm->tm_mday - 1) + 1; 1318195784Srpaulo} 1319195784Srpaulo 1320195784Srpaulo/* 1321195784Srpaulo *--------------------------------------------------------------------- 1322195784Srpaulo * Convert the date part of a 'struct calendar' (that is, year, month, 1323246710Sglebius * day-of-month) into the RD of that day. 1324195784Srpaulo *--------------------------------------------------------------------- 1325195784Srpaulo */ 1326195784Srpauloint32_t 1327195784Srpaulontpcal_date_to_rd( 1328195784Srpaulo const struct calendar *jd 1329234878Smonthadar ) 1330234878Smonthadar{ 1331195784Srpaulo return ntpcal_edate_to_eradays((int32_t)jd->year - 1, 1332195784Srpaulo (int32_t)jd->month - 1, 1333195784Srpaulo (int32_t)jd->monthday - 1) + 1; 1334195784Srpaulo} 1335195784Srpaulo 1336195784Srpaulo/* 1337195784Srpaulo *--------------------------------------------------------------------- 1338195784Srpaulo * convert a year number to rata die of year start 1339195784Srpaulo *--------------------------------------------------------------------- 1340195784Srpaulo */ 1341195784Srpauloint32_t 1342195784Srpaulontpcal_year_to_ystart( 1343195784Srpaulo int32_t year 1344195784Srpaulo ) 1345195784Srpaulo{ 1346234878Smonthadar return ntpcal_days_in_years(year - 1) + 1; 1347234878Smonthadar} 1348234878Smonthadar 1349195784Srpaulo/* 1350195784Srpaulo *--------------------------------------------------------------------- 1351195784Srpaulo * For a given RD, get the RD of the associated year start, 1352234878Smonthadar * that is, the RD of the last January,1st on or before that day. 1353195784Srpaulo *--------------------------------------------------------------------- 1354195784Srpaulo */ 1355195784Srpauloint32_t 1356195784Srpaulontpcal_rd_to_ystart( 1357195784Srpaulo int32_t rd 1358195784Srpaulo ) 1359195784Srpaulo{ 1360234878Smonthadar /* 1361234878Smonthadar * Rather simple exercise: split the day number into elapsed 1362234878Smonthadar * years and elapsed days, then remove the elapsed days from the 1363234878Smonthadar * input value. Nice'n sweet... 1364234878Smonthadar */ 1365234878Smonthadar return rd - ntpcal_split_eradays(rd - 1, NULL).lo; 1366195618Srpaulo} 1367234878Smonthadar 1368234878Smonthadar/* 1369234878Smonthadar *--------------------------------------------------------------------- 1370234879Smonthadar * For a given RD, get the RD of the associated month start. 1371234879Smonthadar *--------------------------------------------------------------------- 1372234879Smonthadar */ 1373234878Smonthadarint32_t 1374248069Sadrianntpcal_rd_to_mstart( 1375248089Sadrian int32_t rd 1376248069Sadrian ) 1377234879Smonthadar{ 1378234879Smonthadar ntpcal_split split; 1379234878Smonthadar int leaps; 1380234878Smonthadar 1381234878Smonthadar split = ntpcal_split_eradays(rd - 1, &leaps); 1382234878Smonthadar split = ntpcal_split_yeardays(split.lo, leaps); 1383234878Smonthadar 1384234878Smonthadar return rd - split.lo; 1385234878Smonthadar} 1386234879Smonthadar 1387234879Smonthadar/* 1388234880Smonthadar *--------------------------------------------------------------------- 1389234880Smonthadar * take a 'struct calendar' and get the seconds-of-day from it. 1390234880Smonthadar *--------------------------------------------------------------------- 1391234880Smonthadar */ 1392234880Smonthadarint32_t 1393234880Smonthadarntpcal_date_to_daysec( 1394234880Smonthadar const struct calendar *jd 1395234880Smonthadar ) 1396234880Smonthadar{ 1397234880Smonthadar return ntpcal_etime_to_seconds(jd->hour, jd->minute, 1398234880Smonthadar jd->second); 1399234880Smonthadar} 1400234880Smonthadar 1401234879Smonthadar/* 1402234879Smonthadar *--------------------------------------------------------------------- 1403234879Smonthadar * take a 'struct tm' and get the seconds-of-day from it. 1404234879Smonthadar *--------------------------------------------------------------------- 1405234879Smonthadar */ 1406234879Smonthadarint32_t 1407234879Smonthadarntpcal_tm_to_daysec( 1408234879Smonthadar const struct tm *utm 1409234879Smonthadar ) 1410234878Smonthadar{ 1411234878Smonthadar return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, 1412234878Smonthadar utm->tm_sec); 1413234878Smonthadar} 1414234878Smonthadar 1415234878Smonthadar/* 1416240521Seadler *--------------------------------------------------------------------- 1417234878Smonthadar * take a 'struct calendar' and convert it to a 'time_t' 1418234878Smonthadar *--------------------------------------------------------------------- 1419234878Smonthadar */ 1420234878Smonthadartime_t 1421234878Smonthadarntpcal_date_to_time( 1422234878Smonthadar const struct calendar *jd 1423234878Smonthadar ) 1424234878Smonthadar{ 1425234878Smonthadar vint64 join; 1426234878Smonthadar int32_t days, secs; 1427234878Smonthadar 1428234878Smonthadar days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; 1429234879Smonthadar secs = ntpcal_date_to_daysec(jd); 1430234878Smonthadar join = ntpcal_dayjoin(days, secs); 1431234878Smonthadar 1432234878Smonthadar return vint64_to_time(&join); 1433248069Sadrian} 1434248089Sadrian 1435248069Sadrian 1436234878Smonthadar/* 1437234878Smonthadar * ================================================================== 1438234878Smonthadar * 1439234878Smonthadar * extended and unchecked variants of caljulian/caltontp 1440234878Smonthadar * 1441234878Smonthadar * ================================================================== 1442234878Smonthadar */ 1443234878Smonthadarint 1444234878Smonthadarntpcal_ntp64_to_date( 1445234879Smonthadar struct calendar *jd, 1446234879Smonthadar const vint64 *ntp 1447234879Smonthadar ) 1448234879Smonthadar{ 1449234879Smonthadar ntpcal_split ds; 1450234879Smonthadar 1451234878Smonthadar ds = ntpcal_daysplit(ntp); 1452234878Smonthadar ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1453234878Smonthadar 1454234878Smonthadar return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); 1455234878Smonthadar} 1456234878Smonthadar 1457234878Smonthadarint 1458234878Smonthadarntpcal_ntp_to_date( 1459234878Smonthadar struct calendar *jd, 1460234878Smonthadar uint32_t ntp, 1461234878Smonthadar const time_t *piv 1462234878Smonthadar ) 1463234878Smonthadar{ 1464234878Smonthadar vint64 ntp64; 1465234878Smonthadar 1466234878Smonthadar /* 1467234878Smonthadar * Unfold ntp time around current time into NTP domain. Split 1468234878Smonthadar * into days and seconds, shift days into CE domain and 1469234878Smonthadar * process the parts. 1470234878Smonthadar */ 1471234878Smonthadar ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1472234878Smonthadar return ntpcal_ntp64_to_date(jd, &ntp64); 1473234878Smonthadar} 1474234878Smonthadar 1475234878Smonthadar 1476246499Smonthadarvint64 1477246499Smonthadarntpcal_date_to_ntp64( 1478246499Smonthadar const struct calendar *jd 1479234878Smonthadar ) 1480234878Smonthadar{ 1481234878Smonthadar /* 1482234878Smonthadar * Convert date to NTP. Ignore yearday, use d/m/y only. 1483234878Smonthadar */ 1484234878Smonthadar return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, 1485234878Smonthadar ntpcal_date_to_daysec(jd)); 1486234878Smonthadar} 1487234878Smonthadar 1488234878Smonthadar 1489234878Smonthadaruint32_t 1490234878Smonthadarntpcal_date_to_ntp( 1491234878Smonthadar const struct calendar *jd 1492234878Smonthadar ) 1493234878Smonthadar{ 1494234878Smonthadar /* 1495234878Smonthadar * Get lower half of 64-bit NTP timestamp from date/time. 1496234878Smonthadar */ 1497234878Smonthadar return ntpcal_date_to_ntp64(jd).d_s.lo; 1498248069Sadrian} 1499248089Sadrian 1500248069Sadrian 1501234878Smonthadar 1502234878Smonthadar/* 1503234878Smonthadar * ================================================================== 1504234878Smonthadar * 1505234878Smonthadar * day-of-week calculations 1506234878Smonthadar * 1507234878Smonthadar * ================================================================== 1508234878Smonthadar */ 1509234878Smonthadar/* 1510234878Smonthadar * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, 1511234892Smonthadar * greater-or equal, closest, less-or-equal or less-than the given RDN 1512300232Savos * and denotes the given day-of-week 1513234878Smonthadar */ 1514234878Smonthadarint32_t 1515234878Smonthadarntpcal_weekday_gt( 1516300232Savos int32_t rdn, 1517234878Smonthadar int32_t dow 1518234878Smonthadar ) 1519234878Smonthadar{ 1520234878Smonthadar return ntpcal_periodic_extend(rdn+1, dow, 7); 1521234878Smonthadar} 1522234878Smonthadar 1523234878Smonthadarint32_t 1524283535Sadrianntpcal_weekday_ge( 1525283535Sadrian int32_t rdn, 1526195618Srpaulo int32_t dow 1527195618Srpaulo ) 1528234878Smonthadar{ 1529195618Srpaulo return ntpcal_periodic_extend(rdn, dow, 7); 1530195618Srpaulo} 1531195618Srpaulo 1532195618Srpauloint32_t 1533195618Srpaulontpcal_weekday_close( 1534234878Smonthadar int32_t rdn, 1535234878Smonthadar int32_t dow 1536195618Srpaulo ) 1537234878Smonthadar{ 1538234878Smonthadar return ntpcal_periodic_extend(rdn-3, dow, 7); 1539195618Srpaulo} 1540195618Srpaulo 1541195618Srpauloint32_t 1542195618Srpaulontpcal_weekday_le( 1543195618Srpaulo int32_t rdn, 1544195618Srpaulo int32_t dow 1545195618Srpaulo ) 1546248069Sadrian{ 1547248069Sadrian return ntpcal_periodic_extend(rdn, dow, -7); 1548248069Sadrian} 1549195618Srpaulo 1550195618Srpauloint32_t 1551195618Srpaulontpcal_weekday_lt( 1552195618Srpaulo int32_t rdn, 1553195618Srpaulo int32_t dow 1554195618Srpaulo ) 1555195618Srpaulo{ 1556195618Srpaulo return ntpcal_periodic_extend(rdn-1, dow, -7); 1557195618Srpaulo} 1558195618Srpaulo 1559195618Srpaulo/* 1560195618Srpaulo * ================================================================== 1561195618Srpaulo * 1562195618Srpaulo * ISO week-calendar conversions 1563195618Srpaulo * 1564195618Srpaulo * The ISO8601 calendar defines a calendar of years, weeks and weekdays. 1565195618Srpaulo * It is related to the Gregorian calendar, and a ISO year starts at the 1566195618Srpaulo * Monday closest to Jan,1st of the corresponding Gregorian year. A ISO 1567195618Srpaulo * calendar year has always 52 or 53 weeks, and like the Grogrian 1568195618Srpaulo * calendar the ISO8601 calendar repeats itself every 400 years, or 1569195618Srpaulo * 146097 days, or 20871 weeks. 1570195618Srpaulo * 1571195618Srpaulo * While it is possible to write ISO calendar functions based on the 1572195618Srpaulo * Gregorian calendar functions, the following implementation takes a 1573195618Srpaulo * different approach, based directly on years and weeks. 1574195618Srpaulo * 1575195618Srpaulo * Analysis of the tabulated data shows that it is not possible to 1576195618Srpaulo * interpolate from years to weeks over a full 400 year range; cyclic 1577195618Srpaulo * shifts over 400 years do not provide a solution here. But it *is* 1578195618Srpaulo * possible to interpolate over every single century of the 400-year 1579195618Srpaulo * cycle. (The centennial leap year rule seems to be the culprit here.) 1580195618Srpaulo * 1581195618Srpaulo * It can be shown that a conversion from years to weeks can be done 1582195618Srpaulo * using a linear transformation of the form 1583296254Savos * 1584195618Srpaulo * w = floor( y * a + b ) 1585195618Srpaulo * 1586195618Srpaulo * where the slope a must hold to 1587195618Srpaulo * 1588195618Srpaulo * 52.1780821918 <= a < 52.1791044776 1589195618Srpaulo * 1590195618Srpaulo * and b must be chosen according to the selected slope and the number 1591195618Srpaulo * of the century in a 400-year period. 1592195618Srpaulo * 1593195618Srpaulo * The inverse calculation can also be done in this way. Careful scaling 1594195618Srpaulo * provides an unlimited set of integer coefficients a,k,b that enable 1595228622Sbschmidt * us to write the calulation in the form 1596195618Srpaulo * 1597195618Srpaulo * w = (y * a + b ) / k 1598195618Srpaulo * y = (w * a' + b') / k' 1599195618Srpaulo * 1600195618Srpaulo * In this implementation the values of k and k' are chosen to be 1601195618Srpaulo * smallest possible powers of two, so the division can be implemented 1602195618Srpaulo * as shifts if the optimiser chooses to do so. 1603195618Srpaulo * 1604195618Srpaulo * ================================================================== 1605195618Srpaulo */ 1606195618Srpaulo 1607195618Srpaulo/* 1608195618Srpaulo * Given a number of elapsed (ISO-)years since the begin of the 1609195618Srpaulo * christian era, return the number of elapsed weeks corresponding to 1610195618Srpaulo * the number of years. 1611195618Srpaulo */ 1612195618Srpauloint32_t 1613234878Smonthadarisocal_weeks_in_years( 1614195618Srpaulo int32_t years 1615195618Srpaulo ) 1616195618Srpaulo{ 1617195618Srpaulo /* 1618195618Srpaulo * use: w = (y * 53431 + b[c]) / 1024 as interpolation 1619195618Srpaulo */ 1620195618Srpaulo static const uint16_t bctab[4] = { 157, 449, 597, 889 }; 1621232480Sadrian 1622232480Sadrian int32_t cs, cw; 1623232480Sadrian uint32_t cc, ci, yu, sflag; 1624232480Sadrian 1625232480Sadrian sflag = int32_sflag(years); 1626232480Sadrian yu = int32_to_uint32_2cpl(years); 1627232480Sadrian 1628232480Sadrian /* split off centuries, using floor division */ 1629232480Sadrian cc = sflag ^ ((sflag ^ yu) / 100u); 1630232480Sadrian yu -= cc * 100u; 1631232480Sadrian 1632232480Sadrian /* calculate century cycles shift and cycle index: 1633232480Sadrian * Assuming a century is 5217 weeks, we have to add a cycle 1634232480Sadrian * shift that is 3 for every 4 centuries, because 3 of the four 1635232480Sadrian * centuries have 5218 weeks. So '(cc*3 + 1) / 4' is the actual 1636232480Sadrian * correction, and the second century is the defective one. 1637232480Sadrian * 1638232480Sadrian * Needs floor division by 4, which is done with masking and 1639232480Sadrian * shifting. 1640232480Sadrian */ 1641232480Sadrian ci = cc * 3u + 1; 1642232480Sadrian cs = uint32_2cpl_to_int32(sflag ^ ((sflag ^ ci) / 4u)); 1643232480Sadrian ci = ci % 4u; 1644232480Sadrian 1645232480Sadrian /* Get weeks in century. Can use plain division here as all ops 1646232480Sadrian * are >= 0, and let the compiler sort out the possible 1647232480Sadrian * optimisations. 1648232480Sadrian */ 1649232480Sadrian cw = (yu * 53431u + bctab[ci]) / 1024u; 1650232480Sadrian 1651232480Sadrian return uint32_2cpl_to_int32(cc) * 5217 + cs + cw; 1652232480Sadrian} 1653232480Sadrian 1654232480Sadrian/* 1655232480Sadrian * Given a number of elapsed weeks since the begin of the christian 1656344969Savos * era, split this number into the number of elapsed years in res.hi 1657232480Sadrian * and the excessive number of weeks in res.lo. (That is, res.lo is 1658232480Sadrian * the number of elapsed weeks in the remaining partial year.) 1659232480Sadrian */ 1660232480Sadrianntpcal_split 1661232480Sadrianisocal_split_eraweeks( 1662232480Sadrian int32_t weeks 1663232480Sadrian ) 1664232480Sadrian{ 1665232480Sadrian /* 1666232480Sadrian * use: y = (w * 157 + b[c]) / 8192 as interpolation 1667232480Sadrian */ 1668232480Sadrian 1669232480Sadrian static const uint16_t bctab[4] = { 85, 130, 17, 62 }; 1670232480Sadrian 1671232480Sadrian ntpcal_split res; 1672232480Sadrian int32_t cc, ci; 1673195618Srpaulo uint32_t sw, cy, Q, sflag; 1674195618Srpaulo 1675195618Srpaulo /* Use two fast cycle-split divisions here. This is again 1676195618Srpaulo * susceptible to internal overflow, so we check the range. This 1677195618Srpaulo * still permits more than +/-20 million years, so this is 1678195618Srpaulo * likely a pure academical problem. 1679195618Srpaulo * 1680195618Srpaulo * We want to execute '(weeks * 4 + 2) /% 20871' under floor 1681195618Srpaulo * division rules in the first step. 1682195618Srpaulo */ 1683195618Srpaulo sflag = int32_sflag(weeks); 1684195618Srpaulo sw = uint32_saturate(int32_to_uint32_2cpl(weeks), sflag); 1685195784Srpaulo sw = 4u * sw + 2; 1686195618Srpaulo Q = sflag ^ ((sflag ^ sw) / GREGORIAN_CYCLE_WEEKS); 1687195618Srpaulo sw -= Q * GREGORIAN_CYCLE_WEEKS; 1688195618Srpaulo ci = Q % 4u; 1689195618Srpaulo cc = uint32_2cpl_to_int32(Q); 1690234878Smonthadar 1691195784Srpaulo /* Split off years; sw >= 0 here! The scaled weeks in the years 1692234878Smonthadar * are scaled up by 157 afterwards. 1693195784Srpaulo */ 1694234878Smonthadar sw = (sw / 4u) * 157u + bctab[ci]; 1695234878Smonthadar cy = sw / 8192u; /* ws >> 13 , let the compiler sort it out */ 1696234878Smonthadar sw = sw % 8192u; /* ws & 8191, let the compiler sort it out */ 1697234878Smonthadar 1698234878Smonthadar /* assemble elapsed years and downscale the elapsed weeks in 1699234878Smonthadar * the year. 1700234878Smonthadar */ 1701234878Smonthadar res.hi = 100*cc + cy; 1702234878Smonthadar res.lo = sw / 157u; 1703234878Smonthadar 1704234878Smonthadar return res; 1705234878Smonthadar} 1706234878Smonthadar 1707298359Savos/* 1708195618Srpaulo * Given a second in the NTP time scale and a pivot, expand the NTP 1709195618Srpaulo * time stamp around the pivot and convert into an ISO calendar time 1710234878Smonthadar * stamp. 1711234878Smonthadar */ 1712195618Srpauloint 1713195618Srpauloisocal_ntp64_to_date( 1714195618Srpaulo struct isodate *id, 1715195618Srpaulo const vint64 *ntp 1716195618Srpaulo ) 1717195618Srpaulo{ 1718195618Srpaulo ntpcal_split ds; 1719195618Srpaulo int32_t ts[3]; 1720195618Srpaulo uint32_t uw, ud, sflag; 1721195618Srpaulo 1722195618Srpaulo /* 1723195618Srpaulo * Split NTP time into days and seconds, shift days into CE 1724195618Srpaulo * domain and process the parts. 1725234878Smonthadar */ 1726234878Smonthadar ds = ntpcal_daysplit(ntp); 1727234878Smonthadar 1728234878Smonthadar /* split time part */ 1729234878Smonthadar ds.hi += priv_timesplit(ts, ds.lo); 1730234878Smonthadar id->hour = (uint8_t)ts[0]; 1731234878Smonthadar id->minute = (uint8_t)ts[1]; 1732234878Smonthadar id->second = (uint8_t)ts[2]; 1733234878Smonthadar 1734234878Smonthadar /* split days into days and weeks, using floor division in unsigned */ 1735234878Smonthadar ds.hi += DAY_NTP_STARTS - 1; /* shift from NTP to RDN */ 1736234878Smonthadar sflag = int32_sflag(ds.hi); 1737234878Smonthadar ud = int32_to_uint32_2cpl(ds.hi); 1738234878Smonthadar uw = sflag ^ ((sflag ^ ud) / DAYSPERWEEK); 1739234878Smonthadar ud -= uw * DAYSPERWEEK; 1740234878Smonthadar ds.hi = uint32_2cpl_to_int32(uw); 1741195618Srpaulo ds.lo = ud; 1742195618Srpaulo 1743195618Srpaulo id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ 1744195618Srpaulo 1745195618Srpaulo /* get year and week in year */ 1746195618Srpaulo ds = isocal_split_eraweeks(ds.hi); /* elapsed years&week*/ 1747195618Srpaulo id->year = (uint16_t)ds.hi + 1; /* shift to current */ 1748195618Srpaulo id->week = (uint8_t )ds.lo + 1; 1749195784Srpaulo 1750195618Srpaulo return (ds.hi >= 0 && ds.hi < 0x0000FFFF); 1751195618Srpaulo} 1752195618Srpaulo 1753195618Srpauloint 1754195618Srpauloisocal_ntp_to_date( 1755195618Srpaulo struct isodate *id, 1756195618Srpaulo uint32_t ntp, 1757195618Srpaulo const time_t *piv 1758195618Srpaulo ) 1759195618Srpaulo{ 1760195618Srpaulo vint64 ntp64; 1761195618Srpaulo 1762232480Sadrian /* 1763195618Srpaulo * Unfold ntp time around current time into NTP domain, then 1764195618Srpaulo * convert the full time stamp. 1765195618Srpaulo */ 1766195784Srpaulo ntp64 = ntpcal_ntp_to_ntp(ntp, piv); 1767195618Srpaulo return isocal_ntp64_to_date(id, &ntp64); 1768195618Srpaulo} 1769195618Srpaulo 1770195618Srpaulo/* 1771195618Srpaulo * Convert a ISO date spec into a second in the NTP time scale, 1772195618Srpaulo * properly truncated to 32 bit. 1773195618Srpaulo */ 1774195618Srpaulovint64 1775195618Srpauloisocal_date_to_ntp64( 1776195618Srpaulo const struct isodate *id 1777195618Srpaulo ) 1778195618Srpaulo{ 1779195618Srpaulo int32_t weeks, days, secs; 1780195618Srpaulo 1781195618Srpaulo weeks = isocal_weeks_in_years((int32_t)id->year - 1) 1782195618Srpaulo + (int32_t)id->week - 1; 1783195618Srpaulo days = weeks * 7 + (int32_t)id->weekday; 1784195618Srpaulo /* days is RDN of ISO date now */ 1785195618Srpaulo secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second); 1786195618Srpaulo 1787195618Srpaulo return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs); 1788195618Srpaulo} 1789195618Srpaulo 1790298376Savosuint32_t 1791195618Srpauloisocal_date_to_ntp( 1792195618Srpaulo const struct isodate *id 1793195618Srpaulo ) 1794260444Skevlo{ 1795195618Srpaulo /* 1796195618Srpaulo * Get lower half of 64-bit NTP timestamp from date/time. 1797195618Srpaulo */ 1798195618Srpaulo return isocal_date_to_ntp64(id).d_s.lo; 1799195618Srpaulo} 1800283535Sadrian 1801195618Srpaulo/* -*-EOF-*- */ 1802195618Srpaulo