1/* $NetBSD: caltontp.c,v 1.1.1.1 2009/12/13 16:55:02 kardel Exp $ */ 2 3/* 4 * caltontp - convert a date to an NTP time 5 */ 6#include <sys/types.h> 7 8#include "ntp_types.h" 9#include "ntp_calendar.h" 10#include "ntp_stdlib.h" 11#include "ntp_assert.h" 12 13/* 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. 18 * 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. 27 * 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... 33 */ 34u_long 35caltontp( 36 const struct calendar *jt 37 ) 38{ 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 */ 42 43 NTP_INSIST(jt != NULL); 44 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); 51 52 /* 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. 58 */ 59 if (jt->yearday) { 60 /* 61 * Assume that the day-of-year contains a useable value and 62 * avoid all calculations involving month and day-of-month. 63 */ 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 { 72 /* 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. 82 */ 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 */ 94 } 95 96 /* 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. 102 */ 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; 107} 108