1 /* $NetBSD: ntp_calendar.c,v 1.3 2014/01/15 14:40:30 apb Exp $ */ 2 3 /* 4 * ntp_calendar.c - calendar and helper functions 5 * 6 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 7 * The contents of 'html/copyright.html' apply. 8 */ 9 #include <config.h> 10 #include <sys/types.h> 11 12 #include "ntp_types.h" 13 #include "ntp_calendar.h" 14 #include "ntp_stdlib.h" 15 #include "ntp_fp.h" 16 #include "ntp_unixtime.h" 17 18 /* 19 *--------------------------------------------------------------------- 20 * replacing the 'time()' function 21 * -------------------------------------------------------------------- 22 */ 23 24 static systime_func_ptr systime_func = &time; 25 static inline time_t now(void); 26 27 28 systime_func_ptr 29 ntpcal_set_timefunc( 30 systime_func_ptr nfunc 31 ) 32 { 33 systime_func_ptr res; 34 35 res = systime_func; 36 if (NULL == nfunc) 37 nfunc = &time; 38 systime_func = nfunc; 39 40 return res; 41 } 42 43 44 static inline time_t 45 now(void) 46 { 47 return (*systime_func)(NULL); 48 } 49 50 /* 51 *--------------------------------------------------------------------- 52 * Convert between 'time_t' and 'vint64' 53 *--------------------------------------------------------------------- 54 */ 55 vint64 56 time_to_vint64( 57 const time_t * ptt 58 ) 59 { 60 vint64 res; 61 time_t tt; 62 63 tt = *ptt; 64 65 #if SIZEOF_TIME_T <= 4 66 67 res.D_s.hi = 0; 68 if (tt < 0) { 69 res.D_s.lo = (uint32_t)-tt; 70 M_NEG(res.D_s.hi, res.D_s.lo); 71 } else { 72 res.D_s.lo = (uint32_t)tt; 73 } 74 75 #elif defined(HAVE_INT64) 76 77 res.q_s = tt; 78 79 #else 80 /* 81 * shifting negative signed quantities is compiler-dependent, so 82 * we better avoid it and do it all manually. And shifting more 83 * than the width of a quantity is undefined. Also a don't do! 84 */ 85 if (tt < 0) { 86 tt = -tt; 87 res.D_s.lo = (uint32_t)tt; 88 res.D_s.hi = (uint32_t)(tt >> 32); 89 M_NEG(res.D_s.hi, res.D_s.lo); 90 } else { 91 res.D_s.lo = (uint32_t)tt; 92 res.D_s.hi = (uint32_t)(tt >> 32); 93 } 94 95 #endif 96 97 return res; 98 } 99 100 101 time_t 102 vint64_to_time( 103 const vint64 *tv 104 ) 105 { 106 time_t res; 107 108 #if SIZEOF_TIME_T <= 4 109 110 res = (time_t)tv->D_s.lo; 111 112 #elif defined(HAVE_INT64) 113 114 res = (time_t)tv->q_s; 115 116 #else 117 118 res = ((time_t)tv->d_s.hi << 32) | tv->D_s.lo; 119 120 #endif 121 122 return res; 123 } 124 125 /* 126 *--------------------------------------------------------------------- 127 * Get the build date & time 128 *--------------------------------------------------------------------- 129 */ 130 int 131 ntpcal_get_build_date( 132 struct calendar * jd 133 ) 134 { 135 /* The C standard tells us the format of '__DATE__': 136 * 137 * __DATE__ The date of translation of the preprocessing 138 * translation unit: a character string literal of the form "Mmm 139 * dd yyyy", where the names of the months are the same as those 140 * generated by the asctime function, and the first character of 141 * dd is a space character if the value is less than 10. If the 142 * date of translation is not available, an 143 * implementation-defined valid date shall be supplied. 144 * 145 * __TIME__ The time of translation of the preprocessing 146 * translation unit: a character string literal of the form 147 * "hh:mm:ss" as in the time generated by the asctime 148 * function. If the time of translation is not available, an 149 * implementation-defined valid time shall be supplied. 150 * 151 * Note that MSVC declares DATE and TIME to be in the local time 152 * zone, while neither the C standard nor the GCC docs make any 153 * statement about this. As a result, we may be +/-12hrs off 154 * UTC. But for practical purposes, this should not be a 155 * problem. 156 * 157 */ 158 #ifdef MKREPRO_DATE 159 static const char build[] = MKREPRO_TIME "/" MKREPRO_DATE; 160 #else 161 static const char build[] = __TIME__ "/" __DATE__; 162 #endif 163 static const char mlist[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 164 char monstr[4]; 165 const char * cp; 166 unsigned short hour, minute, second, day, year; 167 /* Note: The above quantities are used for sscanf 'hu' format, 168 * so using 'uint16_t' is contra-indicated! 169 */ 170 171 ZERO(*jd); 172 jd->year = 1970; 173 jd->month = 1; 174 jd->monthday = 1; 175 176 if (6 == sscanf(build, "%hu:%hu:%hu/%3s %hu %hu", 177 &hour, &minute, &second, monstr, &day, &year)) { 178 cp = strstr(mlist, monstr); 179 if (NULL != cp) { 180 jd->year = year; 181 jd->month = (uint8_t)((cp - mlist) / 3 + 1); 182 jd->monthday = (uint8_t)day; 183 jd->hour = (uint8_t)hour; 184 jd->minute = (uint8_t)minute; 185 jd->second = (uint8_t)second; 186 187 return TRUE; 188 } 189 } 190 191 return FALSE; 192 } 193 194 195 /* 196 *--------------------------------------------------------------------- 197 * basic calendar stuff 198 * -------------------------------------------------------------------- 199 */ 200 201 /* month table for a year starting with March,1st */ 202 static const uint16_t shift_month_table[13] = { 203 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 204 }; 205 206 /* month tables for years starting with January,1st; regular & leap */ 207 static const uint16_t real_month_table[2][13] = { 208 /* -*- table for regular years -*- */ 209 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 210 /* -*- table for leap years -*- */ 211 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 212 }; 213 214 /* 215 * Some notes on the terminology: 216 * 217 * We use the proleptic Gregorian calendar, which is the Gregorian 218 * calendar extended in both directions ad infinitum. This totally 219 * disregards the fact that this calendar was invented in 1582, and 220 * was adopted at various dates over the world; sometimes even after 221 * the start of the NTP epoch. 222 * 223 * Normally date parts are given as current cycles, while time parts 224 * are given as elapsed cycles: 225 * 226 * 1970-01-01/03:04:05 means 'IN the 1970st. year, IN the first month, 227 * ON the first day, with 3hrs, 4minutes and 5 seconds elapsed. 228 * 229 * The basic calculations for this calendar implementation deal with 230 * ELAPSED date units, which is the number of full years, full months 231 * and full days before a date: 1970-01-01 would be (1969, 0, 0) in 232 * that notation. 233 * 234 * To ease the numeric computations, month and day values outside the 235 * normal range are acceptable: 2001-03-00 will be treated as the day 236 * before 2001-03-01, 2000-13-32 will give the same result as 237 * 2001-02-01 and so on. 238 * 239 * 'rd' or 'RD' is used as an abbreviation for the latin 'rata die' 240 * (day number). This is the number of days elapsed since 0000-12-31 241 * in the proleptic Gregorian calendar. The begin of the Christian Era 242 * (0001-01-01) is RD(1). 243 * 244 * 245 * Some notes on the implementation: 246 * 247 * Calendar algorithms thrive on the division operation, which is one of 248 * the slowest numerical operations in any CPU. What saves us here from 249 * abysmal performance is the fact that all divisions are divisions by 250 * constant numbers, and most compilers can do this by a multiplication 251 * operation. But this might not work when using the div/ldiv/lldiv 252 * function family, because many compilers are not able to do inline 253 * expansion of the code with following optimisation for the 254 * constant-divider case. 255 * 256 * Also div/ldiv/lldiv are defined in terms of int/long/longlong, which 257 * are inherently target dependent. Nothing that could not be cured with 258 * autoconf, but still a mess... 259 * 260 * Furthermore, we need floor division while C demands truncation to 261 * zero, so additional steps are required to make sure the algorithms 262 * work. 263 * 264 * For all this, all divisions by constant are coded manually, even when 265 * there is a joined div/mod operation: The optimiser should sort that 266 * out, if possible. 267 * 268 * Finally, the functions do not check for overflow conditions. This 269 * is a sacrifice made for execution speed; since a 32-bit day counter 270 * covers +/- 5,879,610 years, this should not pose a problem here. 271 */ 272 273 274 /* 275 * ================================================================== 276 * 277 * General algorithmic stuff 278 * 279 * ================================================================== 280 */ 281 282 /* 283 *--------------------------------------------------------------------- 284 * Do a periodic extension of 'value' around 'pivot' with a period of 285 * 'cycle'. 286 * 287 * The result 'res' is a number that holds to the following properties: 288 * 289 * 1) res MOD cycle == value MOD cycle 290 * 2) pivot <= res < pivot + cycle 291 * (replace </<= with >/>= for negative cycles) 292 * 293 * where 'MOD' denotes the modulo operator for FLOOR DIVISION, which 294 * is not the same as the '%' operator in C: C requires division to be 295 * a truncated division, where remainder and dividend have the same 296 * sign if the remainder is not zero, whereas floor division requires 297 * divider and modulus to have the same sign for a non-zero modulus. 298 * 299 * This function has some useful applications: 300 * 301 * + let Y be a calendar year and V a truncated 2-digit year: then 302 * periodic_extend(Y-50, V, 100) 303 * is the closest expansion of the truncated year with respect to 304 * the full year, that is a 4-digit year with a difference of less 305 * than 50 years to the year Y. ("century unfolding") 306 * 307 * + let T be a UN*X time stamp and V be seconds-of-day: then 308 * perodic_extend(T-43200, V, 86400) 309 * is a time stamp that has the same seconds-of-day as the input 310 * value, with an absolute difference to T of <= 12hrs. ("day 311 * unfolding") 312 * 313 * + Wherever you have a truncated periodic value and a non-truncated 314 * base value and you want to match them somehow... 315 * 316 * Basically, the function delivers 'pivot + (value - pivot) % cycle', 317 * but the implementation takes some pains to avoid internal signed 318 * integer overflows in the '(value - pivot) % cycle' part and adheres 319 * to the floor division convention. 320 * 321 * If 64bit scalars where available on all intended platforms, writing a 322 * version that uses 64 bit ops would be easy; writing a general 323 * division routine for 64bit ops on a platform that can only do 324 * 32/16bit divisions and is still performant is a bit more 325 * difficult. Since most usecases can be coded in a way that does only 326 * require the 32-bit version a 64bit version is NOT provided here. 327 * --------------------------------------------------------------------- 328 */ 329 int32_t 330 ntpcal_periodic_extend( 331 int32_t pivot, 332 int32_t value, 333 int32_t cycle 334 ) 335 { 336 uint32_t diff; 337 char cpl = 0; /* modulo complement flag */ 338 char neg = 0; /* sign change flag */ 339 340 /* make the cycle positive and adjust the flags */ 341 if (cycle < 0) { 342 cycle = - cycle; 343 neg ^= 1; 344 cpl ^= 1; 345 } 346 /* guard against div by zero or one */ 347 if (cycle > 1) { 348 /* 349 * Get absolute difference as unsigned quantity and 350 * the complement flag. This is done by always 351 * subtracting the smaller value from the bigger 352 * one. This implementation works only on a two's 353 * complement machine! 354 */ 355 if (value >= pivot) { 356 diff = (uint32_t)value - (uint32_t)pivot; 357 } else { 358 diff = (uint32_t)pivot - (uint32_t)value; 359 cpl ^= 1; 360 } 361 diff %= (uint32_t)cycle; 362 if (diff) { 363 if (cpl) 364 diff = cycle - diff; 365 if (neg) 366 diff = ~diff + 1; 367 pivot += diff; 368 } 369 } 370 return pivot; 371 } 372 373 /* 374 *------------------------------------------------------------------- 375 * Convert a timestamp in NTP scale to a 64bit seconds value in the UN*X 376 * scale with proper epoch unfolding around a given pivot or the current 377 * system time. This function happily accepts negative pivot values as 378 * timestamps befor 1970-01-01, so be aware of possible trouble on 379 * platforms with 32bit 'time_t'! 380 * 381 * This is also a periodic extension, but since the cycle is 2^32 and 382 * the shift is 2^31, we can do some *very* fast math without explicit 383 * divisions. 384 *------------------------------------------------------------------- 385 */ 386 vint64 387 ntpcal_ntp_to_time( 388 uint32_t ntp, 389 const time_t * pivot 390 ) 391 { 392 vint64 res; 393 394 #ifdef HAVE_INT64 395 396 res.q_s = (pivot != NULL) 397 ? *pivot 398 : now(); 399 res.Q_s -= 0x80000000; /* unshift of half range */ 400 ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 401 ntp -= res.D_s.lo; /* cycle difference */ 402 res.Q_s += (uint64_t)ntp; /* get expanded time */ 403 404 #else /* no 64bit scalars */ 405 406 time_t tmp; 407 408 tmp = (pivot != NULL) 409 ? *pivot 410 : now(); 411 res = time_to_vint64(&tmp); 412 M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000); 413 ntp -= (uint32_t)JAN_1970; /* warp into UN*X domain */ 414 ntp -= res.D_s.lo; /* cycle difference */ 415 M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 416 417 #endif /* no 64bit scalars */ 418 419 return res; 420 } 421 422 /* 423 *------------------------------------------------------------------- 424 * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP 425 * scale with proper epoch unfolding around a given pivot or the current 426 * system time. 427 * 428 * Note: The pivot must be given in the UN*X time domain! 429 * 430 * This is also a periodic extension, but since the cycle is 2^32 and 431 * the shift is 2^31, we can do some *very* fast math without explicit 432 * divisions. 433 *------------------------------------------------------------------- 434 */ 435 vint64 436 ntpcal_ntp_to_ntp( 437 uint32_t ntp, 438 const time_t *pivot 439 ) 440 { 441 vint64 res; 442 443 #ifdef HAVE_INT64 444 445 res.q_s = (pivot) 446 ? *pivot 447 : now(); 448 res.Q_s -= 0x80000000; /* unshift of half range */ 449 res.Q_s += (uint32_t)JAN_1970; /* warp into NTP domain */ 450 ntp -= res.D_s.lo; /* cycle difference */ 451 res.Q_s += (uint64_t)ntp; /* get expanded time */ 452 453 #else /* no 64bit scalars */ 454 455 time_t tmp; 456 457 tmp = (pivot) 458 ? *pivot 459 : now(); 460 res = time_to_vint64(&tmp); 461 M_SUB(res.D_s.hi, res.D_s.lo, 0, 0x80000000u); 462 M_ADD(res.D_s.hi, res.D_s.lo, 0, (uint32_t)JAN_1970);/*into NTP */ 463 ntp -= res.D_s.lo; /* cycle difference */ 464 M_ADD(res.D_s.hi, res.D_s.lo, 0, ntp); 465 466 #endif /* no 64bit scalars */ 467 468 return res; 469 } 470 471 472 /* 473 * ================================================================== 474 * 475 * Splitting values to composite entities 476 * 477 * ================================================================== 478 */ 479 480 /* 481 *------------------------------------------------------------------- 482 * Split a 64bit seconds value into elapsed days in 'res.hi' and 483 * elapsed seconds since midnight in 'res.lo' using explicit floor 484 * division. This function happily accepts negative time values as 485 * timestamps before the respective epoch start. 486 * ------------------------------------------------------------------- 487 */ 488 ntpcal_split 489 ntpcal_daysplit( 490 const vint64 *ts 491 ) 492 { 493 ntpcal_split res; 494 495 #ifdef HAVE_INT64 496 497 /* manual floor division by SECSPERDAY */ 498 res.hi = (int32_t)(ts->q_s / SECSPERDAY); 499 res.lo = (int32_t)(ts->q_s % SECSPERDAY); 500 if (res.lo < 0) { 501 res.hi -= 1; 502 res.lo += SECSPERDAY; 503 } 504 505 #else 506 507 /* 508 * since we do not have 64bit ops, we have to this by hand. 509 * Luckily SECSPERDAY is 86400 is 675*128, so we do the division 510 * using chained 32/16 bit divisions and shifts. 511 */ 512 vint64 op; 513 uint32_t q, r, a; 514 int isneg; 515 516 memcpy(&op, ts, sizeof(op)); 517 /* fix sign */ 518 isneg = M_ISNEG(op.D_s.hi); 519 if (isneg) 520 M_NEG(op.D_s.hi, op.D_s.lo); 521 522 /* save remainder of DIV 128, shift for divide */ 523 r = op.D_s.lo & 127; /* save remainder bits */ 524 op.D_s.lo = (op.D_s.lo >> 7) | (op.D_s.hi << 25); 525 op.D_s.hi = (op.D_s.hi >> 7); 526 527 /* now do a mnual division, trying to remove as many ops as 528 * possible -- division is always slow! An since we do not have 529 * the advantage of a specific 64/32 bit or even a specific 32/16 530 * bit division op, but must use the general 32/32bit division 531 * even if we *know* the divider fits into unsigned 16 bits, the 532 * exra code pathes should pay off. 533 */ 534 a = op.D_s.hi; 535 if (a > 675u) 536 a = a % 675u; 537 if (a) { 538 a = (a << 16) | op.W_s.lh; 539 q = a / 675u; 540 a = a % 675u; 541 542 a = (a << 16) | op.W_s.ll; 543 q = (q << 16) | (a / 675u); 544 } else { 545 a = op.D_s.lo; 546 q = a / 675u; 547 } 548 a = a % 675u; 549 550 /* assemble remainder */ 551 r |= a << 7; 552 553 /* fix sign of result */ 554 if (isneg) { 555 if (r) { 556 r = SECSPERDAY - r; 557 q = ~q; 558 } else 559 q = ~q + 1; 560 } 561 562 res.hi = q; 563 res.lo = r; 564 565 #endif 566 return res; 567 } 568 569 /* 570 *------------------------------------------------------------------- 571 * Split a 32bit seconds value into h/m/s and excessive days. This 572 * function happily accepts negative time values as timestamps before 573 * midnight. 574 * ------------------------------------------------------------------- 575 */ 576 static int32_t 577 priv_timesplit( 578 int32_t split[3], 579 int32_t ts 580 ) 581 { 582 int32_t days = 0; 583 584 /* make sure we have a positive offset into a day */ 585 if (ts < 0 || ts >= SECSPERDAY) { 586 days = ts / SECSPERDAY; 587 ts = ts % SECSPERDAY; 588 if (ts < 0) { 589 days -= 1; 590 ts += SECSPERDAY; 591 } 592 } 593 594 /* get secs, mins, hours */ 595 split[2] = (uint8_t)(ts % SECSPERMIN); 596 ts /= SECSPERMIN; 597 split[1] = (uint8_t)(ts % MINSPERHR); 598 split[0] = (uint8_t)(ts / MINSPERHR); 599 600 return days; 601 } 602 603 /* 604 * --------------------------------------------------------------------- 605 * Given the number of elapsed days in the calendar era, split this 606 * number into the number of elapsed years in 'res.hi' and the number 607 * of elapsed days of that year in 'res.lo'. 608 * 609 * if 'isleapyear' is not NULL, it will receive an integer that is 0 for 610 * regular years and a non-zero value for leap years. 611 *--------------------------------------------------------------------- 612 */ 613 ntpcal_split 614 ntpcal_split_eradays( 615 int32_t days, 616 int *isleapyear 617 ) 618 { 619 ntpcal_split res; 620 int32_t n400, n100, n004, n001, yday; /* calendar year cycles */ 621 622 /* 623 * Split off calendar cycles, using floor division in the first 624 * step. After that first step, simple division does it because 625 * all operands are positive; alas, we have to be aware of the 626 * possibe cycle overflows for 100 years and 1 year, caused by 627 * the additional leap day. 628 */ 629 n400 = days / GREGORIAN_CYCLE_DAYS; 630 yday = days % GREGORIAN_CYCLE_DAYS; 631 if (yday < 0) { 632 n400 -= 1; 633 yday += GREGORIAN_CYCLE_DAYS; 634 } 635 n100 = yday / GREGORIAN_NORMAL_CENTURY_DAYS; 636 yday = yday % GREGORIAN_NORMAL_CENTURY_DAYS; 637 n004 = yday / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 638 yday = yday % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS; 639 n001 = yday / DAYSPERYEAR; 640 yday = yday % DAYSPERYEAR; 641 642 /* 643 * check for leap cycle overflows and calculate the leap flag 644 * if needed 645 */ 646 if ((n001 | n100) > 3) { 647 /* hit last day of leap year */ 648 n001 -= 1; 649 yday += DAYSPERYEAR; 650 if (isleapyear) 651 *isleapyear = 1; 652 } else if (isleapyear) 653 *isleapyear = (n001 == 3) && ((n004 != 24) || (n100 == 3)); 654 655 /* now merge the cycles to elapsed years, using horner scheme */ 656 res.hi = ((4*n400 + n100)*25 + n004)*4 + n001; 657 res.lo = yday; 658 659 return res; 660 } 661 662 /* 663 *--------------------------------------------------------------------- 664 * Given a number of elapsed days in a year and a leap year indicator, 665 * split the number of elapsed days into the number of elapsed months in 666 * 'res.hi' and the number of elapsed days of that month in 'res.lo'. 667 * 668 * This function will fail and return {-1,-1} if the number of elapsed 669 * days is not in the valid range! 670 *--------------------------------------------------------------------- 671 */ 672 ntpcal_split 673 ntpcal_split_yeardays( 674 int32_t eyd, 675 int isleapyear 676 ) 677 { 678 ntpcal_split res; 679 const uint16_t *lt; /* month length table */ 680 681 /* check leap year flag and select proper table */ 682 lt = real_month_table[(isleapyear != 0)]; 683 if (0 <= eyd && eyd < lt[12]) { 684 /* get zero-based month by approximation & correction step */ 685 res.hi = eyd >> 5; /* approx month; might be 1 too low */ 686 if (lt[res.hi + 1] <= eyd) /* fixup approximative month value */ 687 res.hi += 1; 688 res.lo = eyd - lt[res.hi]; 689 } else { 690 res.lo = res.hi = -1; 691 } 692 693 return res; 694 } 695 696 /* 697 *--------------------------------------------------------------------- 698 * Convert a RD into the date part of a 'struct calendar'. 699 *--------------------------------------------------------------------- 700 */ 701 int 702 ntpcal_rd_to_date( 703 struct calendar *jd, 704 int32_t rd 705 ) 706 { 707 ntpcal_split split; 708 int leaps; 709 int retv; 710 711 leaps = 0; 712 retv = 0; 713 /* get day-of-week first */ 714 jd->weekday = rd % 7; 715 if (jd->weekday >= 7) /* unsigned! */ 716 jd->weekday += 7; 717 718 split = ntpcal_split_eradays(rd - 1, &leaps); 719 retv = leaps; 720 /* get year and day-of-year */ 721 jd->year = (uint16_t)split.hi + 1; 722 if (jd->year != split.hi + 1) { 723 jd->year = 0; 724 retv = -1; /* bletch. overflow trouble. */ 725 } 726 jd->yearday = (uint16_t)split.lo + 1; 727 728 /* convert to month and mday */ 729 split = ntpcal_split_yeardays(split.lo, leaps); 730 jd->month = (uint8_t)split.hi + 1; 731 jd->monthday = (uint8_t)split.lo + 1; 732 733 return retv ? retv : leaps; 734 } 735 736 /* 737 *--------------------------------------------------------------------- 738 * Convert a RD into the date part of a 'struct tm'. 739 *--------------------------------------------------------------------- 740 */ 741 int 742 ntpcal_rd_to_tm( 743 struct tm *utm, 744 int32_t rd 745 ) 746 { 747 ntpcal_split split; 748 int leaps; 749 750 leaps = 0; 751 /* get day-of-week first */ 752 utm->tm_wday = rd % 7; 753 if (utm->tm_wday < 0) 754 utm->tm_wday += 7; 755 756 /* get year and day-of-year */ 757 split = ntpcal_split_eradays(rd - 1, &leaps); 758 utm->tm_year = split.hi - 1899; 759 utm->tm_yday = split.lo; /* 0-based */ 760 761 /* convert to month and mday */ 762 split = ntpcal_split_yeardays(split.lo, leaps); 763 utm->tm_mon = split.hi; /* 0-based */ 764 utm->tm_mday = split.lo + 1; /* 1-based */ 765 766 return leaps; 767 } 768 769 /* 770 *--------------------------------------------------------------------- 771 * Take a value of seconds since midnight and split it into hhmmss in a 772 * 'struct calendar'. 773 *--------------------------------------------------------------------- 774 */ 775 int32_t 776 ntpcal_daysec_to_date( 777 struct calendar *jd, 778 int32_t sec 779 ) 780 { 781 int32_t days; 782 int ts[3]; 783 784 days = priv_timesplit(ts, sec); 785 jd->hour = (uint8_t)ts[0]; 786 jd->minute = (uint8_t)ts[1]; 787 jd->second = (uint8_t)ts[2]; 788 789 return days; 790 } 791 792 /* 793 *--------------------------------------------------------------------- 794 * Take a value of seconds since midnight and split it into hhmmss in a 795 * 'struct tm'. 796 *--------------------------------------------------------------------- 797 */ 798 int32_t 799 ntpcal_daysec_to_tm( 800 struct tm *utm, 801 int32_t sec 802 ) 803 { 804 int32_t days; 805 int32_t ts[3]; 806 807 days = priv_timesplit(ts, sec); 808 utm->tm_hour = ts[0]; 809 utm->tm_min = ts[1]; 810 utm->tm_sec = ts[2]; 811 812 return days; 813 } 814 815 /* 816 *--------------------------------------------------------------------- 817 * take a split representation for day/second-of-day and day offset 818 * and convert it to a 'struct calendar'. The seconds will be normalised 819 * into the range of a day, and the day will be adjusted accordingly. 820 * 821 * returns >0 if the result is in a leap year, 0 if in a regular 822 * year and <0 if the result did not fit into the calendar struct. 823 *--------------------------------------------------------------------- 824 */ 825 int 826 ntpcal_daysplit_to_date( 827 struct calendar *jd, 828 const ntpcal_split *ds, 829 int32_t dof 830 ) 831 { 832 dof += ntpcal_daysec_to_date(jd, ds->lo); 833 return ntpcal_rd_to_date(jd, ds->hi + dof); 834 } 835 836 /* 837 *--------------------------------------------------------------------- 838 * take a split representation for day/second-of-day and day offset 839 * and convert it to a 'struct tm'. The seconds will be normalised 840 * into the range of a day, and the day will be adjusted accordingly. 841 * 842 * returns 1 if the result is in a leap year and zero if in a regular 843 * year. 844 *--------------------------------------------------------------------- 845 */ 846 int 847 ntpcal_daysplit_to_tm( 848 struct tm *utm, 849 const ntpcal_split *ds , 850 int32_t dof 851 ) 852 { 853 dof += ntpcal_daysec_to_tm(utm, ds->lo); 854 855 return ntpcal_rd_to_tm(utm, ds->hi + dof); 856 } 857 858 /* 859 *--------------------------------------------------------------------- 860 * Take a UN*X time and convert to a calendar structure. 861 *--------------------------------------------------------------------- 862 */ 863 int 864 ntpcal_time_to_date( 865 struct calendar *jd, 866 const vint64 *ts 867 ) 868 { 869 ntpcal_split ds; 870 871 ds = ntpcal_daysplit(ts); 872 ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 873 ds.hi += DAY_UNIX_STARTS; 874 875 return ntpcal_rd_to_date(jd, ds.hi); 876 } 877 878 879 /* 880 * ================================================================== 881 * 882 * merging composite entities 883 * 884 * ================================================================== 885 */ 886 887 /* 888 *--------------------------------------------------------------------- 889 * Merge a number of days and a number of seconds into seconds, 890 * expressed in 64 bits to avoid overflow. 891 *--------------------------------------------------------------------- 892 */ 893 vint64 894 ntpcal_dayjoin( 895 int32_t days, 896 int32_t secs 897 ) 898 { 899 vint64 res; 900 901 #ifdef HAVE_INT64 902 903 res.q_s = days; 904 res.q_s *= SECSPERDAY; 905 res.q_s += secs; 906 907 #else 908 909 uint32_t p1, p2; 910 int isneg; 911 912 /* 913 * res = days *86400 + secs, using manual 16/32 bit 914 * multiplications and shifts. 915 */ 916 isneg = (days < 0); 917 if (isneg) 918 days = -days; 919 920 /* assemble days * 675 */ 921 res.D_s.lo = (days & 0xFFFF) * 675u; 922 res.D_s.hi = 0; 923 p1 = (days >> 16) * 675u; 924 p2 = p1 >> 16; 925 p1 = p1 << 16; 926 M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 927 928 /* mul by 128, using shift */ 929 res.D_s.hi = (res.D_s.hi << 7) | (res.D_s.lo >> 25); 930 res.D_s.lo = (res.D_s.lo << 7); 931 932 /* fix sign */ 933 if (isneg) 934 M_NEG(res.D_s.hi, res.D_s.lo); 935 936 /* properly add seconds */ 937 p2 = 0; 938 if (secs < 0) { 939 p1 = (uint32_t)-secs; 940 M_NEG(p2, p1); 941 } else { 942 p1 = (uint32_t)secs; 943 } 944 M_ADD(res.D_s.hi, res.D_s.lo, p2, p1); 945 946 #endif 947 948 return res; 949 } 950 951 /* 952 *--------------------------------------------------------------------- 953 * Convert elapsed years in Era into elapsed days in Era. 954 * 955 * To accomodate for negative values of years, floor division would be 956 * required for all division operations. This can be eased by first 957 * splitting the years into full 400-year cycles and years in the 958 * cycle. Only this operation must be coded as a full floor division; as 959 * the years in the cycle is a non-negative number, all other divisions 960 * can be regular truncated divisions. 961 *--------------------------------------------------------------------- 962 */ 963 int32_t 964 ntpcal_days_in_years( 965 int32_t years 966 ) 967 { 968 int32_t cycle; /* full gregorian cycle */ 969 970 /* split off full calendar cycles, using floor division */ 971 cycle = years / 400; 972 years = years % 400; 973 if (years < 0) { 974 cycle -= 1; 975 years += 400; 976 } 977 978 /* 979 * Calculate days in cycle. years now is a non-negative number, 980 * holding the number of years in the 400-year cycle. 981 */ 982 return cycle * GREGORIAN_CYCLE_DAYS 983 + years * DAYSPERYEAR /* days inregular years */ 984 + years / 4 /* 4 year leap rule */ 985 - years / 100; /* 100 year leap rule */ 986 /* the 400-year rule does not apply due to full-cycle split-off */ 987 } 988 989 /* 990 *--------------------------------------------------------------------- 991 * Convert a number of elapsed month in a year into elapsed days in year. 992 * 993 * The month will be normalized, and 'res.hi' will contain the 994 * excessive years that must be considered when converting the years, 995 * while 'res.lo' will contain the number of elapsed days since start 996 * of the year. 997 * 998 * This code uses the shifted-month-approach to convert month to days, 999 * because then there is no need to have explicit leap year 1000 * information. The slight disadvantage is that for most month values 1001 * the result is a negative value, and the year excess is one; the 1002 * conversion is then simply based on the start of the following year. 1003 *--------------------------------------------------------------------- 1004 */ 1005 ntpcal_split 1006 ntpcal_days_in_months( 1007 int32_t m 1008 ) 1009 { 1010 ntpcal_split res; 1011 1012 /* normalize month into range */ 1013 res.hi = 0; 1014 res.lo = m; 1015 if (res.lo < 0 || res.lo >= 12) { 1016 res.hi = res.lo / 12; 1017 res.lo = res.lo % 12; 1018 if (res.lo < 0) { 1019 res.hi -= 1; 1020 res.lo += 12; 1021 } 1022 } 1023 1024 /* add 10 month for year starting with march */ 1025 if (res.lo < 2) 1026 res.lo += 10; 1027 else { 1028 res.hi += 1; 1029 res.lo -= 2; 1030 } 1031 1032 /* get cummulated days in year with unshift */ 1033 res.lo = shift_month_table[res.lo] - 306; 1034 1035 return res; 1036 } 1037 1038 /* 1039 *--------------------------------------------------------------------- 1040 * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1041 * days in Gregorian epoch. 1042 * 1043 * If you want to convert years and days-of-year, just give a month of 1044 * zero. 1045 *--------------------------------------------------------------------- 1046 */ 1047 int32_t 1048 ntpcal_edate_to_eradays( 1049 int32_t years, 1050 int32_t mons, 1051 int32_t mdays 1052 ) 1053 { 1054 ntpcal_split tmp; 1055 int32_t res; 1056 1057 if (mons) { 1058 tmp = ntpcal_days_in_months(mons); 1059 res = ntpcal_days_in_years(years + tmp.hi) + tmp.lo; 1060 } else 1061 res = ntpcal_days_in_years(years); 1062 res += mdays; 1063 1064 return res; 1065 } 1066 1067 /* 1068 *--------------------------------------------------------------------- 1069 * Convert ELAPSED years/months/days of gregorian calendar to elapsed 1070 * days in year. 1071 * 1072 * Note: This will give the true difference to the start of the given year, 1073 * even if months & days are off-scale. 1074 *--------------------------------------------------------------------- 1075 */ 1076 int32_t 1077 ntpcal_edate_to_yeardays( 1078 int32_t years, 1079 int32_t mons, 1080 int32_t mdays 1081 ) 1082 { 1083 ntpcal_split tmp; 1084 1085 if (0 <= mons && mons < 12) { 1086 years += 1; 1087 mdays += real_month_table[is_leapyear(years)][mons]; 1088 } else { 1089 tmp = ntpcal_days_in_months(mons); 1090 mdays += tmp.lo 1091 + ntpcal_days_in_years(years + tmp.hi) 1092 - ntpcal_days_in_years(years); 1093 } 1094 1095 return mdays; 1096 } 1097 1098 /* 1099 *--------------------------------------------------------------------- 1100 * Convert elapsed days and the hour/minute/second information into 1101 * total seconds. 1102 * 1103 * If 'isvalid' is not NULL, do a range check on the time specification 1104 * and tell if the time input is in the normal range, permitting for a 1105 * single leapsecond. 1106 *--------------------------------------------------------------------- 1107 */ 1108 int32_t 1109 ntpcal_etime_to_seconds( 1110 int32_t hours, 1111 int32_t minutes, 1112 int32_t seconds 1113 ) 1114 { 1115 int32_t res; 1116 1117 res = (hours * MINSPERHR + minutes) * SECSPERMIN + seconds; 1118 1119 return res; 1120 } 1121 1122 /* 1123 *--------------------------------------------------------------------- 1124 * Convert the date part of a 'struct tm' (that is, year, month, 1125 * day-of-month) into the RD of that day. 1126 *--------------------------------------------------------------------- 1127 */ 1128 int32_t 1129 ntpcal_tm_to_rd( 1130 const struct tm *utm 1131 ) 1132 { 1133 return ntpcal_edate_to_eradays(utm->tm_year + 1899, 1134 utm->tm_mon, 1135 utm->tm_mday - 1) + 1; 1136 } 1137 1138 /* 1139 *--------------------------------------------------------------------- 1140 * Convert the date part of a 'struct calendar' (that is, year, month, 1141 * day-of-month) into the RD of that day. 1142 *--------------------------------------------------------------------- 1143 */ 1144 int32_t 1145 ntpcal_date_to_rd( 1146 const struct calendar *jd 1147 ) 1148 { 1149 return ntpcal_edate_to_eradays((int32_t)jd->year - 1, 1150 (int32_t)jd->month - 1, 1151 (int32_t)jd->monthday - 1) + 1; 1152 } 1153 1154 /* 1155 *--------------------------------------------------------------------- 1156 * convert a year number to rata die of year start 1157 *--------------------------------------------------------------------- 1158 */ 1159 int32_t 1160 ntpcal_year_to_ystart( 1161 int32_t year 1162 ) 1163 { 1164 return ntpcal_days_in_years(year - 1) + 1; 1165 } 1166 1167 /* 1168 *--------------------------------------------------------------------- 1169 * For a given RD, get the RD of the associated year start, 1170 * that is, the RD of the last January,1st on or before that day. 1171 *--------------------------------------------------------------------- 1172 */ 1173 int32_t 1174 ntpcal_rd_to_ystart( 1175 int32_t rd 1176 ) 1177 { 1178 /* 1179 * Rather simple exercise: split the day number into elapsed 1180 * years and elapsed days, then remove the elapsed days from the 1181 * input value. Nice'n sweet... 1182 */ 1183 return rd - ntpcal_split_eradays(rd - 1, NULL).lo; 1184 } 1185 1186 /* 1187 *--------------------------------------------------------------------- 1188 * For a given RD, get the RD of the associated month start. 1189 *--------------------------------------------------------------------- 1190 */ 1191 int32_t 1192 ntpcal_rd_to_mstart( 1193 int32_t rd 1194 ) 1195 { 1196 ntpcal_split split; 1197 int leaps; 1198 1199 split = ntpcal_split_eradays(rd - 1, &leaps); 1200 split = ntpcal_split_yeardays(split.lo, leaps); 1201 1202 return rd - split.lo; 1203 } 1204 1205 /* 1206 *--------------------------------------------------------------------- 1207 * take a 'struct calendar' and get the seconds-of-day from it. 1208 *--------------------------------------------------------------------- 1209 */ 1210 int32_t 1211 ntpcal_date_to_daysec( 1212 const struct calendar *jd 1213 ) 1214 { 1215 return ntpcal_etime_to_seconds(jd->hour, jd->minute, 1216 jd->second); 1217 } 1218 1219 /* 1220 *--------------------------------------------------------------------- 1221 * take a 'struct tm' and get the seconds-of-day from it. 1222 *--------------------------------------------------------------------- 1223 */ 1224 int32_t 1225 ntpcal_tm_to_daysec( 1226 const struct tm *utm 1227 ) 1228 { 1229 return ntpcal_etime_to_seconds(utm->tm_hour, utm->tm_min, 1230 utm->tm_sec); 1231 } 1232 1233 /* 1234 *--------------------------------------------------------------------- 1235 * take a 'struct calendar' and convert it to a 'time_t' 1236 *--------------------------------------------------------------------- 1237 */ 1238 time_t 1239 ntpcal_date_to_time( 1240 const struct calendar *jd 1241 ) 1242 { 1243 vint64 join; 1244 int32_t days, secs; 1245 1246 days = ntpcal_date_to_rd(jd) - DAY_UNIX_STARTS; 1247 secs = ntpcal_date_to_daysec(jd); 1248 join = ntpcal_dayjoin(days, secs); 1249 1250 return vint64_to_time(&join); 1251 } 1252 1253 1254 /* 1255 * ================================================================== 1256 * 1257 * extended and unchecked variants of caljulian/caltontp 1258 * 1259 * ================================================================== 1260 */ 1261 int 1262 ntpcal_ntp_to_date( 1263 struct calendar *jd, 1264 uint32_t ntp, 1265 const time_t *piv 1266 ) 1267 { 1268 vint64 vl; 1269 ntpcal_split ds; 1270 1271 /* 1272 * Unfold ntp time around current time into NTP domain. Split 1273 * into days and seconds, shift days into CE domain and 1274 * process the parts. 1275 */ 1276 vl = ntpcal_ntp_to_ntp(ntp, piv); 1277 ds = ntpcal_daysplit(&vl); 1278 ds.hi += ntpcal_daysec_to_date(jd, ds.lo); 1279 1280 return ntpcal_rd_to_date(jd, ds.hi + DAY_NTP_STARTS); 1281 } 1282 1283 1284 uint32_t 1285 ntpcal_date_to_ntp( 1286 const struct calendar *jd 1287 ) 1288 { 1289 /* 1290 * Convert date to NTP. Ignore yearday, use d/m/y only. 1291 */ 1292 return ntpcal_dayjoin(ntpcal_date_to_rd(jd) - DAY_NTP_STARTS, 1293 ntpcal_date_to_daysec(jd)).d_s.lo; 1294 } 1295 1296 /* 1297 * ================================================================== 1298 * 1299 * day-of-week calculations 1300 * 1301 * ================================================================== 1302 */ 1303 /* 1304 * Given a RataDie and a day-of-week, calculate a RDN that is reater-than, 1305 * greater-or equal, closest, less-or-equal or less-than the given RDN 1306 * and denotes the given day-of-week 1307 */ 1308 int32_t 1309 ntpcal_weekday_gt( 1310 int32_t rdn, 1311 int32_t dow 1312 ) 1313 { 1314 return ntpcal_periodic_extend(rdn+1, dow, 7); 1315 } 1316 1317 int32_t 1318 ntpcal_weekday_ge( 1319 int32_t rdn, 1320 int32_t dow 1321 ) 1322 { 1323 return ntpcal_periodic_extend(rdn, dow, 7); 1324 } 1325 1326 int32_t 1327 ntpcal_weekday_close( 1328 int32_t rdn, 1329 int32_t dow 1330 ) 1331 { 1332 return ntpcal_periodic_extend(rdn-3, dow, 7); 1333 } 1334 1335 int32_t 1336 ntpcal_weekday_le( 1337 int32_t rdn, 1338 int32_t dow 1339 ) 1340 { 1341 return ntpcal_periodic_extend(rdn, dow, -7); 1342 } 1343 1344 int32_t 1345 ntpcal_weekday_lt( 1346 int32_t rdn, 1347 int32_t dow 1348 ) 1349 { 1350 return ntpcal_periodic_extend(rdn-1, dow, -7); 1351 } 1352 1353 /* 1354 * ================================================================== 1355 * 1356 * ISO week-calendar conversions 1357 * 1358 * The ISO8601 calendar defines a calendar of years, weeks and weekdays. 1359 * It is related to the Gregorian calendar, and a ISO year starts at the 1360 * Monday closest to Jan,1st of the corresponding Gregorian year. A ISO 1361 * calendar year has always 52 or 53 weeks, and like the Grogrian 1362 * calendar the ISO8601 calendar repeats itself every 400 years, or 1363 * 146097 days, or 20871 weeks. 1364 * 1365 * While it is possible to write ISO calendar functions based on the 1366 * Gregorian calendar functions, the following implementation takes a 1367 * different approach, based directly on years and weeks. 1368 * 1369 * Analysis of the tabulated data shows that it is not possible to 1370 * interpolate from years to weeks over a full 400 year range; cyclic 1371 * shifts over 400 years do not provide a solution here. But it *is* 1372 * possible to interpolate over every single century of the 400-year 1373 * cycle. (The centennial leap year rule seems to be the culprit here.) 1374 * 1375 * It can be shown that a conversion from years to weeks can be done 1376 * using a linear transformation of the form 1377 * 1378 * w = floor( y * a + b ) 1379 * 1380 * where the slope a must hold to 1381 * 1382 * 52.1780821918 <= a < 52.1791044776 1383 * 1384 * and b must be chosen according to the selected slope and the number 1385 * of the century in a 400-year period. 1386 * 1387 * The inverse calculation can also be done in this way. Careful scaling 1388 * provides an unlimited set of integer coefficients a,k,b that enable 1389 * us to write the calulation in the form 1390 * 1391 * w = (y * a + b ) / k 1392 * y = (w * a' + b') / k' 1393 * 1394 * In this implementation the values of k and k' are chosen to be 1395 * smallest possible powers of two, so the division can be implemented 1396 * as shifts if the optimiser chooses to do so. 1397 * 1398 * ================================================================== 1399 */ 1400 1401 /* 1402 * Given a number of elapsed (ISO-)years since the begin of the 1403 * christian era, return the number of elapsed weeks corresponding to 1404 * the number of years. 1405 */ 1406 int32_t 1407 isocal_weeks_in_years( 1408 int32_t years 1409 ) 1410 { 1411 /* 1412 * use: w = (y * 53431 + b[c]) / 1024 as interpolation 1413 */ 1414 static const int32_t bctab[4] = { 449, 157, 889, 597 }; 1415 int32_t cycle; /* full gregorian cycle */ 1416 int32_t cents; /* full centuries */ 1417 int32_t weeks; /* accumulated weeks */ 1418 1419 /* split off full calendar cycles, using floor division */ 1420 cycle = years / 400; 1421 years = years % 400; 1422 if (years < 0) { 1423 cycle -= 1; 1424 years += 400; 1425 } 1426 1427 /* split off full centuries */ 1428 cents = years / 100; 1429 years = years % 100; 1430 1431 /* 1432 * calculate elapsed weeks, taking into account that the 1433 * first, third and fourth century have 5218 weeks but the 1434 * second century falls short by one week. 1435 */ 1436 weeks = (years * 53431 + bctab[cents]) / 1024; 1437 1438 return cycle * GREGORIAN_CYCLE_WEEKS 1439 + cents * 5218 - (cents > 1) 1440 + weeks; 1441 } 1442 1443 /* 1444 * Given a number of elapsed weeks since the begin of the christian 1445 * era, split this number into the number of elapsed years in res.hi 1446 * and the excessive number of weeks in res.lo. (That is, res.lo is 1447 * the number of elapsed weeks in the remaining partial year.) 1448 */ 1449 ntpcal_split 1450 isocal_split_eraweeks( 1451 int32_t weeks 1452 ) 1453 { 1454 /* 1455 * use: y = (w * 157 + b[c]) / 8192 as interpolation 1456 */ 1457 static const int32_t bctab[4] = { 85, 131, 17, 62 }; 1458 ntpcal_split res; 1459 int32_t cents; 1460 1461 /* 1462 * split off 400-year cycles, using the fact that a 400-year 1463 * cycle has 146097 days, which is exactly 20871 weeks. 1464 */ 1465 res.hi = weeks / GREGORIAN_CYCLE_WEEKS; 1466 res.lo = weeks % GREGORIAN_CYCLE_WEEKS; 1467 if (res.lo < 0) { 1468 res.hi -= 1; 1469 res.lo += GREGORIAN_CYCLE_WEEKS; 1470 } 1471 res.hi *= 400; 1472 1473 /* 1474 * split off centuries, taking into account that the first, 1475 * third and fourth century have 5218 weeks but that the 1476 * second century falls short by one week. 1477 */ 1478 res.lo += (res.lo >= 10435); 1479 cents = res.lo / 5218; 1480 res.lo %= 5218; /* res.lo is weeks in century now */ 1481 1482 /* convert elapsed weeks in century to elapsed years and weeks */ 1483 res.lo = res.lo * 157 + bctab[cents]; 1484 res.hi += cents * 100 + res.lo / 8192; 1485 res.lo = (res.lo % 8192) / 157; 1486 1487 return res; 1488 } 1489 1490 /* 1491 * Given a second in the NTP time scale and a pivot, expand the NTP 1492 * time stamp around the pivot and convert into an ISO calendar time 1493 * stamp. 1494 */ 1495 int 1496 isocal_ntp_to_date( 1497 struct isodate *id, 1498 uint32_t ntp, 1499 const time_t *piv 1500 ) 1501 { 1502 vint64 vl; 1503 ntpcal_split ds; 1504 int32_t ts[3]; 1505 1506 /* 1507 * Unfold ntp time around current time into NTP domain. Split 1508 * into days and seconds, shift days into CE domain and 1509 * process the parts. 1510 */ 1511 vl = ntpcal_ntp_to_ntp(ntp, piv); 1512 ds = ntpcal_daysplit(&vl); 1513 1514 /* split time part */ 1515 ds.hi += priv_timesplit(ts, ds.lo); 1516 id->hour = (uint8_t)ts[0]; 1517 id->minute = (uint8_t)ts[1]; 1518 id->second = (uint8_t)ts[2]; 1519 1520 /* split date part */ 1521 ds.lo = ds.hi + DAY_NTP_STARTS - 1; /* elapsed era days */ 1522 ds.hi = ds.lo / 7; /* elapsed era weeks */ 1523 ds.lo = ds.lo % 7; /* elapsed week days */ 1524 if (ds.lo < 0) { /* floor division! */ 1525 ds.hi -= 1; 1526 ds.lo += 7; 1527 } 1528 id->weekday = (uint8_t)ds.lo + 1; /* weekday result */ 1529 1530 ds = isocal_split_eraweeks(ds.hi); /* elapsed years&week*/ 1531 id->year = (uint16_t)ds.hi + 1; /* shift to current */ 1532 id->week = (uint8_t )ds.lo + 1; 1533 1534 return (ds.hi >= 0 && ds.hi < 0x0000FFFF); 1535 } 1536 1537 /* 1538 * Convert a ISO date spec into a second in the NTP time scale, 1539 * properly truncated to 32 bit. 1540 */ 1541 uint32_t 1542 isocal_date_to_ntp( 1543 const struct isodate *id 1544 ) 1545 { 1546 int32_t weeks, days, secs; 1547 1548 weeks = isocal_weeks_in_years((int32_t)id->year - 1) 1549 + (int32_t)id->week - 1; 1550 days = weeks * 7 + (int32_t)id->weekday; 1551 /* days is RDN of ISO date now */ 1552 secs = ntpcal_etime_to_seconds(id->hour, id->minute, id->second); 1553 1554 return ntpcal_dayjoin(days - DAY_NTP_STARTS, secs).d_s.lo; 1555 } 1556 1557 /* -*-EOF-*- */ 1558