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: */
32: u_long
33: caltontp(
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>