]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
New caltontp.c and calyearstart.c from Juergen Perlinger
authorHarlan Stenn <stenn@ntp.org>
Wed, 12 Nov 2008 21:09:45 +0000 (16:09 -0500)
committerHarlan Stenn <stenn@ntp.org>
Wed, 12 Nov 2008 21:09:45 +0000 (16:09 -0500)
bk: 491b4619qh7bTmt0j6-9PbneDECD-Q

ChangeLog
libntp/caltontp.c
libntp/calyearstart.c

index 23ed73ca9e5c25efdfcf156649013b334e4fe6fa..c904934c197b147950d0beb84a6b7848d14492e9 100644 (file)
--- 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 <stenn@ntp.org>
 * Cleanup lint from the ntp_scanner files.
 * [Bug 1011] gmtime() returns NULL on windows where it would not under Unix.
index 9ec106416dd08e92499e97f869df290e389559f3..9c4136842d9db71846f1e14ebefb4d1cb1dee1b5 100644 (file)
 #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 <any year here>-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;
 }
index 0f7ca4f9d06581b9dbb4bd618ce3a5253f40ac8c..a6af054025a9b58bc88a9f2bff97d95a9fbb0501 100644 (file)
@@ -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;
 }