aboutsummaryrefslogtreecommitdiff
path: root/libntp/caljulian.c
diff options
context:
space:
mode:
Diffstat (limited to 'libntp/caljulian.c')
-rw-r--r--libntp/caljulian.c213
1 files changed, 132 insertions, 81 deletions
diff --git a/libntp/caljulian.c b/libntp/caljulian.c
index 71123dfe20d3..7673061b57ce 100644
--- a/libntp/caljulian.c
+++ b/libntp/caljulian.c
@@ -7,24 +7,25 @@
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_fp.h"
+#include "ntp_unixtime.h"
-#if 0
-/*
- * calmonthtab - days-in-the-month table
+#if !(defined(ISC_CHECK_ALL) || defined(ISC_CHECK_NONE) || \
+ defined(ISC_CHECK_ENSURE) || defined(ISC_CHECK_INSIST) || \
+ defined(ISC_CHECK_INVARIANT))
+# define ISC_CHECK_ALL
+#endif
+
+#include "ntp_assert.h"
+
+#if 1
+
+/* Updated 2008-11-10 Juergen Perlinger <juergen.perlinger@t-online.de>
+ *
+ * Make the conversion 2038-proof with proper NTP epoch unfolding and extended
+ * precision calculations. Though we should really get a 'time_t' with more
+ * than 32 bits at least until 2037, because the unfolding cannot work after
+ * the wrap of the 32-bit 'time_t'.
*/
-static u_short calmonthtab[11] = {
- JAN,
- FEB,
- MAR,
- APR,
- MAY,
- JUN,
- JUL,
- AUG,
- SEP,
- OCT,
- NOV
-};
void
caljulian(
@@ -32,89 +33,137 @@ caljulian(
register struct calendar *jt
)
{
- u_long ntp_day;
- u_long minutes;
+ u_long saved_time = ntptime;
+ u_long ntp_day; /* days (since christian era or in year) */
+ u_long n400; /* # of Gregorian cycles */
+ u_long n100; /* # of normal centuries */
+ u_long n4; /* # of 4-year cycles */
+ u_long n1; /* # of years into a leap year cycle */
+ u_long sclday; /* scaled days for month conversion */
+ int leaps; /* # of leaps days in year */
+ time_t now; /* current system time */
+ u_int32 tmplo; /* double precision tmp value / lo part */
+ int32 tmphi; /* double precision tmp value / hi part */
+
+ NTP_INSIST(NULL != jt);
+
/*
- * Absolute, zero-adjusted Christian era day, starting from the
- * mythical day 12/1/1 BC
+ * First we have to unfold the ntp time stamp around the current time
+ * to make sure we are in the right epoch. Also we we do *NOT* fold
+ * before the begin of the first NTP epoch, so we WILL have a
+ * non-negative time stamp afterwards. Though at the time of this
+ * writing (2008 A.D.) it would be really strange to have systems
+ * running with clock set to he 1960's or before...
+ *
+ * But's important to use a 32 bit max signed value -- LONG_MAX is 64
+ * bit on a 64-bit system, and it will give wrong results.
*/
- u_long acez_day;
-
- u_long d400; /* Days into a Gregorian cycle */
- u_long d100; /* Days into a normal century */
- u_long d4; /* Days into a 4-year cycle */
- u_long n400; /* # of Gregorian cycles */
- u_long n100; /* # of normal centuries */
- u_long n4; /* # of 4-year cycles */
- u_long n1; /* # of years into a leap year */
- /* cycle */
+ now = time(NULL);
+ tmplo = (u_int32)now;
+#if ( SIZEOF_TIME_T > 4 )
+ tmphi = (int32)(now >> 16 >> 16);
+#else
+ /*
+ * Get the correct sign extension in the high part.
+ * (now >> 32) may not work correctly on every 32 bit
+ * system, e.g. it yields garbage under Win32/VC6.
+ */
+ tmphi = (int32)(now >> 31);
+#endif
+
+ M_ADD(tmphi, tmplo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
+ M_ADD(tmphi, tmplo, 0, JAN_1970);
+ if ((ntptime > tmplo) && (tmphi > 0))
+ --tmphi;
+ tmplo = ntptime;
+
+ /*
+ * Now split into days and seconds-of-day, using the fact that
+ * SECSPERDAY (86400) == 675 * 128; we can get roughly 17000 years of
+ * time scale, using only 32-bit calculations. Some magic numbers here,
+ * sorry for that. (This could be streamlined for 64 bit machines, but
+ * is worth the trouble?)
+ */
+ ntptime = tmplo & 127; /* save remainder bits */
+ tmplo = (tmplo >> 7) | (tmphi << 25);
+ ntp_day = (u_int32)tmplo / 675;
+ ntptime += ((u_int32)tmplo % 675) << 7;
+
+ /* some checks for the algorithm
+ * There's some 64-bit trouble out there: the original NTP time stamp
+ * had only 32 bits, so our calculation invariant only holds in 32 bits!
+ */
+ NTP_ENSURE(ntptime < SECSPERDAY);
+ NTP_INVARIANT((u_int32)(ntptime + ntp_day * SECSPERDAY) == (u_int32)saved_time);
/*
* Do the easy stuff first: take care of hh:mm:ss, ignoring leap
* seconds
*/
jt->second = (u_char)(ntptime % SECSPERMIN);
- minutes = ntptime / SECSPERMIN;
- jt->minute = (u_char)(minutes % MINSPERHR);
- jt->hour = (u_char)((minutes / MINSPERHR) % HRSPERDAY);
+ ntptime /= SECSPERMIN;
+ jt->minute = (u_char)(ntptime % MINSPERHR);
+ ntptime /= MINSPERHR;
+ jt->hour = (u_char)(ntptime);
- /*
- * Find the day past 1900/01/01 00:00 UTC
- */
- ntp_day = ntptime / SECSPERDAY;
- acez_day = DAY_NTP_STARTS + ntp_day - 1;
- n400 = acez_day/GREGORIAN_CYCLE_DAYS;
- d400 = acez_day%GREGORIAN_CYCLE_DAYS;
- n100 = d400 / GREGORIAN_NORMAL_CENTURY_DAYS;
- d100 = d400 % GREGORIAN_NORMAL_CENTURY_DAYS;
- n4 = d100 / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
- d4 = d100 % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
- n1 = d4 / DAYSPERYEAR;
+ /* check time invariants */
+ NTP_ENSURE(jt->second < SECSPERMIN);
+ NTP_ENSURE(jt->minute < MINSPERHR);
+ NTP_ENSURE(jt->hour < HRSPERDAY);
/*
- * Calculate the year and year-of-day
+ * Find the day past 1900/01/01 00:00 UTC
*/
- jt->yearday = (u_short)(1 + d4%DAYSPERYEAR);
- jt->year = (u_short)(400*n400 + 100*n100 + n4*4 + n1);
+ ntp_day += DAY_NTP_STARTS - 1; /* convert to days in CE */
+ n400 = ntp_day / GREGORIAN_CYCLE_DAYS; /* split off cycles */
+ ntp_day %= GREGORIAN_CYCLE_DAYS;
+ n100 = ntp_day / GREGORIAN_NORMAL_CENTURY_DAYS;
+ ntp_day %= GREGORIAN_NORMAL_CENTURY_DAYS;
+ n4 = ntp_day / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
+ ntp_day %= GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
+ n1 = ntp_day / DAYSPERYEAR;
+ ntp_day %= DAYSPERYEAR; /* now zero-based day-of-year */
+
+ NTP_ENSURE(ntp_day < 366);
- if (n100 == 4 || n1 == 4)
- {
/*
- * If the cycle year ever comes out to 4, it must be December 31st
- * of a leap year.
+ * Calculate the year and day-of-year
*/
- jt->month = 12;
- jt->monthday = 31;
- jt->yearday = 366;
+ jt->year = (u_short)(400*n400 + 100*n100 + 4*n4 + n1);
+
+ if ((n100 | n1) > 3) {
+ /*
+ * If the cycle year ever comes out to 4, it must be December
+ * 31st of a leap year.
+ */
+ jt->month = 12;
+ jt->monthday = 31;
+ jt->yearday = 366;
+ } else {
+ /*
+ * The following code is according to the excellent book
+ * 'Calendrical Calculations' by Nachum Dershowitz and Edward
+ * Reingold. It converts the day-of-year into month and
+ * day-of-month, using a linear transformation with integer
+ * truncation. Magic numbers again, but they will not be used
+ * anywhere else.
+ */
+ sclday = ntp_day * 7 + 217;
+ leaps = ((n1 == 3) && ((n4 != 24) || (n100 == 3))) ? 1 : 0;
+ if (ntp_day >= (u_long)(JAN + FEB + leaps))
+ sclday += (2 - leaps) * 7;
+ ++jt->year;
+ jt->month = (u_char)(sclday / 214);
+ jt->monthday = (u_char)((sclday % 214) / 7 + 1);
+ jt->yearday = (u_short)(1 + ntp_day);
}
- else
- {
- /*
- * Else, search forwards through the months to get the right month
- * and date.
- */
- int monthday;
- jt->year++;
- monthday = jt->yearday;
-
- for (jt->month=0;jt->month<11; jt->month++)
- {
- int t;
-
- t = monthday - calmonthtab[jt->month];
- if (jt->month == 1 && is_leapyear(jt->year))
- t--;
-
- if (t > 0)
- monthday = t;
- else
- break;
- }
- jt->month++;
- jt->monthday = (u_char) monthday;
- }
+ /* check date invariants */
+ NTP_ENSURE(1 <= jt->month && jt->month <= 12);
+ NTP_ENSURE(1 <= jt->monthday && jt->monthday <= 31);
+ NTP_ENSURE(1 <= jt->yearday && jt->yearday <= 366);
}
+
#else
/* Updated 2003-12-30 TMa
@@ -132,8 +181,10 @@ caljulian(
)
{
struct tm *tm;
+ NTP_REQUIRE(jt != NULL);
tm = ntp2unix_tm(ntptime, 0);
+ NTP_INSIST(tm != NULL);
jt->hour = (u_char) tm->tm_hour;
jt->minute = (u_char) tm->tm_min;