Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / ntp / dist / libntp / caltontp.c
blob241ba78c5b6e40265bb304302400730531bf1513
1 /* $NetBSD$ */
3 /*
4 * caltontp - convert a date to an NTP time
5 */
6 #include <sys/types.h>
8 #include "ntp_types.h"
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
26 * by somebody else.
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...
34 u_long
35 caltontp(
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
57 * epoch, 1900-01-01.
59 if (jt->yearday) {
61 * Assume that the day-of-year contains a useable value and
62 * avoid all calculations involving month and day-of-month.
64 years = jt->year - 1;
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 */
71 } else {
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
81 * the year.
83 month = (ntp_u_int32_t)jt->month + 9;
84 years = jt->year - 1 + month / 12;
85 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
101 * anyway.
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;