Home | History | Annotate | Line # | Download | only in libntp
caljulian.c revision 1.1
      1  1.1  kardel /*	$NetBSD: caljulian.c,v 1.1 2009/12/13 16:55:02 kardel Exp $	*/
      2  1.1  kardel 
      3  1.1  kardel /*
      4  1.1  kardel  * caljulian - determine the Julian date from an NTP time.
      5  1.1  kardel  */
      6  1.1  kardel #include <sys/types.h>
      7  1.1  kardel 
      8  1.1  kardel #include "ntp_types.h"
      9  1.1  kardel #include "ntp_calendar.h"
     10  1.1  kardel #include "ntp_stdlib.h"
     11  1.1  kardel #include "ntp_fp.h"
     12  1.1  kardel #include "ntp_unixtime.h"
     13  1.1  kardel 
     14  1.1  kardel #if !(defined(ISC_CHECK_ALL) || defined(ISC_CHECK_NONE) || \
     15  1.1  kardel       defined(ISC_CHECK_ENSURE) || defined(ISC_CHECK_INSIST) || \
     16  1.1  kardel       defined(ISC_CHECK_INVARIANT))
     17  1.1  kardel # define ISC_CHECK_ALL
     18  1.1  kardel #endif
     19  1.1  kardel 
     20  1.1  kardel #include "ntp_assert.h"
     21  1.1  kardel 
     22  1.1  kardel #if 1
     23  1.1  kardel 
     24  1.1  kardel /* Updated 2008-11-10 Juergen Perlinger <juergen.perlinger (at) t-online.de>
     25  1.1  kardel  *
     26  1.1  kardel  * Make the conversion 2038-proof with proper NTP epoch unfolding and extended
     27  1.1  kardel  * precision calculations. Though we should really get a 'time_t' with more
     28  1.1  kardel  * than 32 bits at least until 2037, because the unfolding cannot work after
     29  1.1  kardel  * the wrap of the 32-bit 'time_t'.
     30  1.1  kardel  */
     31  1.1  kardel 
     32  1.1  kardel void
     33  1.1  kardel caljulian(
     34  1.1  kardel 	u_long		  		ntptime,
     35  1.1  kardel 	register struct calendar	*jt
     36  1.1  kardel 	)
     37  1.1  kardel {
     38  1.1  kardel 	u_long  saved_time = ntptime;
     39  1.1  kardel 	u_long  ntp_day; /* days (since christian era or in year) */
     40  1.1  kardel 	u_long  n400;    /* # of Gregorian cycles */
     41  1.1  kardel 	u_long  n100;    /* # of normal centuries */
     42  1.1  kardel 	u_long  n4;      /* # of 4-year cycles */
     43  1.1  kardel 	u_long  n1;      /* # of years into a leap year cycle */
     44  1.1  kardel 	u_long  sclday;  /* scaled days for month conversion */
     45  1.1  kardel 	int     leaps;   /* # of leaps days in year */
     46  1.1  kardel 	time_t  now;     /* current system time */
     47  1.1  kardel 	u_int32 tmplo;   /* double precision tmp value / lo part */
     48  1.1  kardel 	int32   tmphi;   /* double precision tmp value / hi part */
     49  1.1  kardel 
     50  1.1  kardel 	NTP_INSIST(NULL != jt);
     51  1.1  kardel 
     52  1.1  kardel 	/*
     53  1.1  kardel 	 * First we have to unfold the ntp time stamp around the current time
     54  1.1  kardel 	 * to make sure we are in the right epoch. Also we we do *NOT* fold
     55  1.1  kardel 	 * before the begin of the first NTP epoch, so we WILL have a
     56  1.1  kardel 	 * non-negative time stamp afterwards. Though at the time of this
     57  1.1  kardel 	 * writing (2008 A.D.) it would be really strange to have systems
     58  1.1  kardel 	 * running with clock set to he 1960's or before...
     59  1.1  kardel 	 *
     60  1.1  kardel 	 * But's important to use a 32 bit max signed value -- LONG_MAX is 64
     61  1.1  kardel 	 * bit on a 64-bit system, and it will give wrong results.
     62  1.1  kardel 	 */
     63  1.1  kardel 	now   = time(NULL);
     64  1.1  kardel 	tmplo = (u_int32)now;
     65  1.1  kardel #if ( SIZEOF_TIME_T > 4 )
     66  1.1  kardel 	tmphi = (int32)(now >> 16 >> 16);
     67  1.1  kardel #else
     68  1.1  kardel 	/*
     69  1.1  kardel 	 * Get the correct sign extension in the high part.
     70  1.1  kardel 	 * (now >> 32) may not work correctly on every 32 bit
     71  1.1  kardel 	 * system, e.g. it yields garbage under Win32/VC6.
     72  1.1  kardel 	 */
     73  1.1  kardel     tmphi = (int32)(now >> 31);
     74  1.1  kardel #endif
     75  1.1  kardel 
     76  1.1  kardel 	M_ADD(tmphi, tmplo, 0, ((1UL << 31)-1)); /* 32-bit max signed */
     77  1.1  kardel 	M_ADD(tmphi, tmplo, 0, JAN_1970);
     78  1.1  kardel 	if ((ntptime > tmplo) && (tmphi > 0))
     79  1.1  kardel 		--tmphi;
     80  1.1  kardel 	tmplo = ntptime;
     81  1.1  kardel 
     82  1.1  kardel 	/*
     83  1.1  kardel 	 * Now split into days and seconds-of-day, using the fact that
     84  1.1  kardel 	 * SECSPERDAY (86400) == 675 * 128; we can get roughly 17000 years of
     85  1.1  kardel 	 * time scale, using only 32-bit calculations. Some magic numbers here,
     86  1.1  kardel 	 * sorry for that. (This could be streamlined for 64 bit machines, but
     87  1.1  kardel 	 * is worth the trouble?)
     88  1.1  kardel 	 */
     89  1.1  kardel 	ntptime  = tmplo & 127;	/* save remainder bits */
     90  1.1  kardel 	tmplo    = (tmplo >> 7) | (tmphi << 25);
     91  1.1  kardel 	ntp_day  =  (u_int32)tmplo / 675;
     92  1.1  kardel 	ntptime += ((u_int32)tmplo % 675) << 7;
     93  1.1  kardel 
     94  1.1  kardel 	/* some checks for the algorithm
     95  1.1  kardel 	 * There's some 64-bit trouble out there: the original NTP time stamp
     96  1.1  kardel 	 * had only 32 bits, so our calculation invariant only holds in 32 bits!
     97  1.1  kardel 	 */
     98  1.1  kardel 	NTP_ENSURE(ntptime < SECSPERDAY);
     99  1.1  kardel 	NTP_INVARIANT((u_int32)(ntptime + ntp_day * SECSPERDAY) == (u_int32)saved_time);
    100  1.1  kardel 
    101  1.1  kardel 	/*
    102  1.1  kardel 	 * Do the easy stuff first: take care of hh:mm:ss, ignoring leap
    103  1.1  kardel 	 * seconds
    104  1.1  kardel 	 */
    105  1.1  kardel 	jt->second = (u_char)(ntptime % SECSPERMIN);
    106  1.1  kardel 	ntptime   /= SECSPERMIN;
    107  1.1  kardel 	jt->minute = (u_char)(ntptime % MINSPERHR);
    108  1.1  kardel 	ntptime   /= MINSPERHR;
    109  1.1  kardel 	jt->hour   = (u_char)(ntptime);
    110  1.1  kardel 
    111  1.1  kardel 	/* check time invariants */
    112  1.1  kardel 	NTP_ENSURE(jt->second < SECSPERMIN);
    113  1.1  kardel 	NTP_ENSURE(jt->minute < MINSPERHR);
    114  1.1  kardel 	NTP_ENSURE(jt->hour   < HRSPERDAY);
    115  1.1  kardel 
    116  1.1  kardel 	/*
    117  1.1  kardel 	 * Find the day past 1900/01/01 00:00 UTC
    118  1.1  kardel 	 */
    119  1.1  kardel 	ntp_day += DAY_NTP_STARTS - 1;	/* convert to days in CE */
    120  1.1  kardel 	n400	 = ntp_day / GREGORIAN_CYCLE_DAYS; /* split off cycles */
    121  1.1  kardel 	ntp_day %= GREGORIAN_CYCLE_DAYS;
    122  1.1  kardel 	n100	 = ntp_day / GREGORIAN_NORMAL_CENTURY_DAYS;
    123  1.1  kardel 	ntp_day %= GREGORIAN_NORMAL_CENTURY_DAYS;
    124  1.1  kardel 	n4	 = ntp_day / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
    125  1.1  kardel 	ntp_day %= GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
    126  1.1  kardel 	n1	 = ntp_day / DAYSPERYEAR;
    127  1.1  kardel 	ntp_day %= DAYSPERYEAR; /* now zero-based day-of-year */
    128  1.1  kardel 
    129  1.1  kardel 	NTP_ENSURE(ntp_day < 366);
    130  1.1  kardel 
    131  1.1  kardel 	/*
    132  1.1  kardel 	 * Calculate the year and day-of-year
    133  1.1  kardel 	 */
    134  1.1  kardel 	jt->year = (u_short)(400*n400 + 100*n100 + 4*n4 + n1);
    135  1.1  kardel 
    136  1.1  kardel 	if ((n100 | n1) > 3) {
    137  1.1  kardel 		/*
    138  1.1  kardel 		 * If the cycle year ever comes out to 4, it must be December
    139  1.1  kardel 		 * 31st of a leap year.
    140  1.1  kardel 		 */
    141  1.1  kardel 		jt->month    = 12;
    142  1.1  kardel 		jt->monthday = 31;
    143  1.1  kardel 		jt->yearday  = 366;
    144  1.1  kardel 	} else {
    145  1.1  kardel 		/*
    146  1.1  kardel 		 * The following code is according to the excellent book
    147  1.1  kardel 		 * 'Calendrical Calculations' by Nachum Dershowitz and Edward
    148  1.1  kardel 		 * Reingold. It converts the day-of-year into month and
    149  1.1  kardel 		 * day-of-month, using a linear transformation with integer
    150  1.1  kardel 		 * truncation. Magic numbers again, but they will not be used
    151  1.1  kardel 		 * anywhere else.
    152  1.1  kardel 		 */
    153  1.1  kardel 		sclday = ntp_day * 7 + 217;
    154  1.1  kardel 		leaps  = ((n1 == 3) && ((n4 != 24) || (n100 == 3))) ? 1 : 0;
    155  1.1  kardel 		if (ntp_day >= (u_long)(JAN + FEB + leaps))
    156  1.1  kardel 			sclday += (2 - leaps) * 7;
    157  1.1  kardel 		++jt->year;
    158  1.1  kardel 		jt->month    = (u_char)(sclday / 214);
    159  1.1  kardel 		jt->monthday = (u_char)((sclday % 214) / 7 + 1);
    160  1.1  kardel 		jt->yearday  = (u_short)(1 + ntp_day);
    161  1.1  kardel 	}
    162  1.1  kardel 
    163  1.1  kardel 	/* check date invariants */
    164  1.1  kardel 	NTP_ENSURE(1 <= jt->month    && jt->month    <=  12);
    165  1.1  kardel 	NTP_ENSURE(1 <= jt->monthday && jt->monthday <=  31);
    166  1.1  kardel 	NTP_ENSURE(1 <= jt->yearday  && jt->yearday  <= 366);
    167  1.1  kardel }
    168  1.1  kardel 
    169  1.1  kardel #else
    170  1.1  kardel 
    171  1.1  kardel /* Updated 2003-12-30 TMa
    172  1.1  kardel 
    173  1.1  kardel    Uses common code with the *prettydate functions to convert an ntp
    174  1.1  kardel    seconds count into a calendar date.
    175  1.1  kardel    Will handle ntp epoch wraparound as long as the underlying os/library
    176  1.1  kardel    does so for the unix epoch, i.e. works after 2038.
    177  1.1  kardel */
    178  1.1  kardel 
    179  1.1  kardel void
    180  1.1  kardel caljulian(
    181  1.1  kardel 	u_long		  		ntptime,
    182  1.1  kardel 	register struct calendar	*jt
    183  1.1  kardel 	)
    184  1.1  kardel {
    185  1.1  kardel 	struct tm *tm;
    186  1.1  kardel 	NTP_REQUIRE(jt != NULL);
    187  1.1  kardel 
    188  1.1  kardel 	tm = ntp2unix_tm(ntptime, 0);
    189  1.1  kardel 	NTP_INSIST(tm != NULL);
    190  1.1  kardel 
    191  1.1  kardel 	jt->hour = (u_char) tm->tm_hour;
    192  1.1  kardel 	jt->minute = (u_char) tm->tm_min;
    193  1.1  kardel 	jt->month = (u_char) (tm->tm_mon + 1);
    194  1.1  kardel 	jt->monthday = (u_char) tm->tm_mday;
    195  1.1  kardel 	jt->second = (u_char) tm->tm_sec;
    196  1.1  kardel 	jt->year = (u_short) (tm->tm_year + 1900);
    197  1.1  kardel 	jt->yearday = (u_short) (tm->tm_yday + 1);  /* Assumes tm_yday starts with day 0! */
    198  1.1  kardel }
    199  1.1  kardel #endif
    200