From: Harlan Stenn Date: Wed, 12 Nov 2008 21:09:45 +0000 (-0500) Subject: New caltontp.c and calyearstart.c from Juergen Perlinger X-Git-Tag: NTP_4_2_5P141~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1cd06a053d4693a12e04f0957ba4896294c98a16;p=thirdparty%2Fntp.git New caltontp.c and calyearstart.c from Juergen Perlinger bk: 491b4619qh7bTmt0j6-9PbneDECD-Q --- diff --git a/ChangeLog b/ChangeLog index 23ed73ca9..c904934c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +* New caltontp.c and calyearstart.c from Juergen Perlinger. (4.2.5p140) 2008/11/12 Released by Harlan Stenn * Cleanup lint from the ntp_scanner files. * [Bug 1011] gmtime() returns NULL on windows where it would not under Unix. diff --git a/libntp/caltontp.c b/libntp/caltontp.c index 9ec106416..9c4136842 100644 --- a/libntp/caltontp.c +++ b/libntp/caltontp.c @@ -6,37 +6,100 @@ #include "ntp_types.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" +#include "ntp_assert.h" +/* + * Juergen Perlinger, 2008-11-12 + * Add support for full calendar calculatios. If the day-of-year is provided + * (that is, not zero) it will be used instead of month and day-of-month; + * otherwise a full turn through the calendar calculations will be taken. + * + * I know that Harlan Stenn likes to see assertions in production code, and I + * agree there, but it would be a tricky thing here. The algorithm is quite + * capable of producing sensible answers even to seemingly weird inputs: the + * date -03-00, the 0.th March of the year, will be automtically + * treated as the last day of February, no matter whether the year is a leap + * year or not. So adding constraints is merely for the benefit of the callers, + * because the only thing we can check for consistency is our input, produced + * by somebody else. + * + * BTW: A total roundtrip using 'caljulian' would be a quite shaky thing: + * Because of the truncation of the NTP time stamp to 32 bits and the epoch + * unfolding around the current time done by 'caljulian' the roundtrip does + * *not* necessarily reproduce the input, especially if the time spec is more + * than 68 years off from the current time... + */ u_long caltontp( - register const struct calendar *jt + const struct calendar *jt ) { - u_long ace_days; /* absolute Christian Era days */ - u_long ntp_days; - int prior_years; - u_long ntp_time; - - /* - * First convert today's date to absolute days past 12/1/1 BC - */ - prior_years = jt->year-1; - ace_days = jt->yearday /* days this year */ - +(DAYSPERYEAR*prior_years) /* plus days in previous years */ - +(prior_years/4) /* plus prior years's leap days */ - -(prior_years/100) /* minus leapless century years */ - +(prior_years/400); /* plus leapful Gregorian yrs */ + ntp_u_int32_t days; /* full days in NTP epoch */ + ntp_u_int32_t years; /* complete ACE years before date */ + ntp_u_int32_t month; /* adjusted month for calendar */ + + NTP_INSIST(jt != NULL); - /* - * Subtract out 1/1/1900, the beginning of the NTP epoch - */ - ntp_days = ace_days - DAY_NTP_STARTS; + NTP_REQUIRE(jt->month <= 13); /* permit month 0..13! */ + NTP_REQUIRE(jt->monthday <= 32); + NTP_REQUIRE(jt->yearday <= 366); + NTP_REQUIRE(jt->hour <= 24); + NTP_REQUIRE(jt->minute <= MINSPERHR); + NTP_REQUIRE(jt->second <= SECSPERMIN); - /* - * Do the obvious: - */ - ntp_time = - ntp_days*SECSPERDAY+SECSPERMIN*(MINSPERHR*jt->hour + jt->minute); + /* + * First convert the date to fully elapsed days since NTP epoch. The + * expressions used here give us initially days since 0001-01-01, the + * beginning of the christian era in the proleptic gregorian calendar; + * they are rebased on-the-fly into days since beginning of the NTP + * epoch, 1900-01-01. + */ + if (jt->yearday) { + /* + * Assume that the day-of-year contains a useable value and + * avoid all calculations involving month and day-of-month. + */ + years = jt->year - 1; + days = years * DAYSPERYEAR /* days in previous years */ + + years / 4 /* plus prior years's leap days */ + - years / 100 /* minus leapless century years */ + + years / 400 /* plus leapful Gregorian yrs */ + + jt->yearday /* days this year */ + - DAY_NTP_STARTS; /* rebase to NTP epoch */ + } else { + /* + * The following code is according to the excellent book + * 'Calendrical Calculations' by Nachum Dershowitz and Edward + * Reingold. It does a full calendar evaluation, using one of + * the alternate algorithms: Shift to a hypothetical year + * starting on the previous march,1st; merge years, month and + * days; undo the the 9 month shift (which is 306 days). The + * advantage is that we do NOT need to now whether a year is a + * leap year or not, because the leap day is the LAST day of + * the year. + */ + month = (ntp_u_int32_t)jt->month + 9; + years = jt->year - 1 + month / 12; + month %= 12; + days = years * DAYSPERYEAR /* days in previous years */ + + years / 4 /* plus prior years's leap days */ + - years / 100 /* minus leapless century years */ + + years / 400 /* plus leapful Gregorian yrs */ + + (month * 153 + 2) / 5 /* plus days before month */ + + jt->monthday /* plus day-of-month */ + - 306 /* minus 9 months */ + - DAY_NTP_STARTS; /* rebase to NTP epoch */ + } - return ntp_time; + /* + * Do the obvious: Merge everything together, making sure integer + * promotion doesn't play dirty tricks on us; there is probably some + * redundancy in the casts, but this drives it home with force. All + * arithmetic is done modulo 2**32, because the result is truncated + * anyway. + */ + return days * SECSPERDAY + + (ntp_u_int32_t)jt->hour * MINSPERHR*SECSPERMIN + + (ntp_u_int32_t)jt->minute * SECSPERMIN + + (ntp_u_int32_t)jt->second; } diff --git a/libntp/calyearstart.c b/libntp/calyearstart.c index 0f7ca4f9d..a6af05402 100644 --- a/libntp/calyearstart.c +++ b/libntp/calyearstart.c @@ -7,16 +7,47 @@ #include "ntp_types.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" +#include "ntp_assert.h" +/* + * Juergen Perlinger, 2008-11-12 + * Use the result of 'caljulian' to get the delta from the time stamp to the + * beginning of the year. Do not make a second trip through 'caltontp' after + * fixing the date, apart for invariant tests. + */ u_long calyearstart(u_long ntp_time) { - struct calendar jt; + struct calendar jt; + ntp_u_int32_t delta; + + caljulian(ntp_time,&jt); + + /* + * Now we have days since yearstart (unity-based) and the time in that + * day. Simply merge these together to seconds and subtract that from + * input time. That's faster than going through the calendar stuff + * again... + */ + delta = (ntp_u_int32_t)jt.yearday * SECSPERDAY + + (ntp_u_int32_t)jt.hour * MINSPERHR * SECSPERMIN + + (ntp_u_int32_t)jt.minute * SECSPERMIN + + (ntp_u_int32_t)jt.second + - SECSPERDAY; /* yearday is unity-based... */ + +# if ISC_CHECK_INVARIANT + /* + * check that this computes properly: do a roundtrip! That's the only + * sensible test here, but it's a rather expensive invariant... + */ + jt.yearday = 0; + jt.month = 1; + jt.monthday = 1; + jt.hour = 0; + jt.minute = 0; + jt.second = 0; + NTP_INVARIANT(caltontp(&jt) + delta == ntp_time); +# endif - caljulian(ntp_time,&jt); - jt.yearday = 1; - jt.monthday = 1; - jt.month = 1; - jt.hour = jt.minute = jt.second = 0; - return caltontp(&jt); + return ntp_time - delta; }