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