1/* 2 * caltontp - convert a date to an NTP time 3 */ 4#include <sys/types.h> 5 6#include "ntp_types.h" 7#include "ntp_calendar.h" 8#include "ntp_stdlib.h" 9#include "ntp_assert.h" 10 11/* 12 * Juergen Perlinger, 2008-11-12 13 * Add support for full calendar calculatios. If the day-of-year is provided 14 * (that is, not zero) it will be used instead of month and day-of-month; 15 * otherwise a full turn through the calendar calculations will be taken. 16 * 17 * I know that Harlan Stenn likes to see assertions in production code, and I 18 * agree there, but it would be a tricky thing here. The algorithm is quite 19 * capable of producing sensible answers even to seemingly weird inputs: the 20 * date <any year here>-03-00, the 0.th March of the year, will be automtically 21 * treated as the last day of February, no matter whether the year is a leap 22 * year or not. So adding constraints is merely for the benefit of the callers, 23 * because the only thing we can check for consistency is our input, produced 24 * by somebody else. 25 * 26 * BTW: A total roundtrip using 'caljulian' would be a quite shaky thing: 27 * Because of the truncation of the NTP time stamp to 32 bits and the epoch 28 * unfolding around the current time done by 'caljulian' the roundtrip does 29 * *not* necessarily reproduce the input, especially if the time spec is more 30 * than 68 years off from the current time... 31 */ 32u_long 33caltontp( 34 const struct calendar *jt 35 ) 36{ 37 ntp_u_int32_t days; /* full days in NTP epoch */ 38 ntp_u_int32_t years; /* complete ACE years before date */ 39 ntp_u_int32_t month; /* adjusted month for calendar */ 40 41 NTP_INSIST(jt != NULL); 42 43 NTP_REQUIRE(jt->month <= 13); /* permit month 0..13! */ 44 NTP_REQUIRE(jt->monthday <= 32); 45 NTP_REQUIRE(jt->yearday <= 366); 46 NTP_REQUIRE(jt->hour <= 24); 47 NTP_REQUIRE(jt->minute <= MINSPERHR); 48 NTP_REQUIRE(jt->second <= SECSPERMIN); 49 50 /* 51 * First convert the date to fully elapsed days since NTP epoch. The 52 * expressions used here give us initially days since 0001-01-01, the 53 * beginning of the christian era in the proleptic gregorian calendar; 54 * they are rebased on-the-fly into days since beginning of the NTP 55 * epoch, 1900-01-01. 56 */ 57 if (jt->yearday) { 58 /* 59 * Assume that the day-of-year contains a useable value and 60 * avoid all calculations involving month and day-of-month. 61 */ 62 years = jt->year - 1; 63 days = years * DAYSPERYEAR /* days in previous years */ 64 + years / 4 /* plus prior years's leap days */ 65 - years / 100 /* minus leapless century years */ 66 + years / 400 /* plus leapful Gregorian yrs */ 67 + jt->yearday /* days this year */ 68 - DAY_NTP_STARTS; /* rebase to NTP epoch */ 69 } else { 70 /* 71 * The following code is according to the excellent book 72 * 'Calendrical Calculations' by Nachum Dershowitz and Edward 73 * Reingold. It does a full calendar evaluation, using one of 74 * the alternate algorithms: Shift to a hypothetical year 75 * starting on the previous march,1st; merge years, month and 76 * days; undo the the 9 month shift (which is 306 days). The 77 * advantage is that we do NOT need to now whether a year is a 78 * leap year or not, because the leap day is the LAST day of 79 * the year. 80 */ 81 month = (ntp_u_int32_t)jt->month + 9; 82 years = jt->year - 1 + month / 12; 83 month %= 12; 84 days = years * DAYSPERYEAR /* days in previous years */ 85 + years / 4 /* plus prior years's leap days */ 86 - years / 100 /* minus leapless century years */ 87 + years / 400 /* plus leapful Gregorian yrs */ 88 + (month * 153 + 2) / 5 /* plus days before month */ 89 + jt->monthday /* plus day-of-month */ 90 - 306 /* minus 9 months */ 91 - DAY_NTP_STARTS; /* rebase to NTP epoch */ 92 } 93 94 /* 95 * Do the obvious: Merge everything together, making sure integer 96 * promotion doesn't play dirty tricks on us; there is probably some 97 * redundancy in the casts, but this drives it home with force. All 98 * arithmetic is done modulo 2**32, because the result is truncated 99 * anyway. 100 */ 101 return days * SECSPERDAY 102 + (ntp_u_int32_t)jt->hour * MINSPERHR*SECSPERMIN 103 + (ntp_u_int32_t)jt->minute * SECSPERMIN 104 + (ntp_u_int32_t)jt->second; 105} 106