/* $Id$ Julian Date Routines Written 1999 by Tobias Ernst and release to the Public Domain. This file is part of NLTOOLS, the nodelist processor of the Husky fidonet software project. These routines calculate a Julian day number for a given date. Julian day numbers are a consecutive sequence of day numbers, with the origin at the January 1st, 4713 (B.C.) (Julian Day #1). Julian day numbers can be used to calculate date differences in days (just subtract the day numbers), to determine the day of week (julian day number modulo 7 yields 0 for Monday, 6 for Sunday), etc. The calculcations in these routines are based on the Gregorian calendar being in effect since Oct. 15, 1582 (European convention). In any case, result for dates previous to September 14, 1752, will probably not be too much in coincidence with the local reality at that time. Conventions are: dd (day in month): 1 .. 31 mm (month): January = 1, December = 12 yy (year): Positive four digit year number (e.g. 1999). The routines do not work year numbers < 1. There is no problem with big year numbers, i.E. the code is year 2000 safe (and it of course respects Feburary 29, 2000). */ #include #include #include "julian.h" /* See julian.h for license and documentation */ #ifdef TEST # include #endif static int is_leap_year( int year ) { if( year % 4 ) { return 0; } if( year < 1582 ) { return 1; } if( ( year % 100 ) || ( !( year % 400 ) ) ) { return 1; } return 0; } static long s_days_in_month[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; long get_julian_date( int dd, int mm, int yy ) { long julian; /* Account all preceding years */ julian = ( yy - 1 ) * 365L; /* Account the leap years */ julian += ( yy - 1 ) / 4L; if( yy - 1 > 1582L ) { /* Subtract all xx00 years */ julian -= ( yy - 1 ) / 100L; /* But all xx00 years that can be divided by 400 are leap years! */ julian += ( yy - 1 ) / 400L; /* Account for the centuries before 1582, which all were leap years */ julian += 12L; /* 100, 200, 300, 500, 600, 700, 900, 1000, 1100, 1300, */ } if( mm ) { /* mm > 0: assume dd is day number in month */ /* add the days of the current year */ while( --mm ) { julian += s_days_in_month[is_leap_year( yy )][mm - 1]; } /* days in current month */ julian += dd; } else { /* mm == 0: assume dd is day number in year as per fts */ julian += dd; } /* adjust for the cut from oct 4 to oct 15, 1582 */ if( julian > 577737L ) { julian -= 10L; } /* By now, we have days since Jan 1, 1 (AC). Now convert to Julian day numbers, which by convention start at Jan 1, 4713 (BC) */ julian += ( 2299161L - 577738L ); return julian; } void decode_julian_date( long julian, int *pdd, int *pmm, int *pyy, int *pddiny ) { int yy, dd, mm; long days; int i; /* convert to days since B.C. */ julian -= ( 2299161L - 577738L ); /* undo the oct. 1582 gap */ if( julian > 577737L ) { julian += 10L; } yy = ( int ) ( julian / 365L ); days = ( julian % 365L ); /* account for the leap years */ if( yy < 1700 ) { days -= ( yy / 4 ); } else { days -= ( yy / 4 ); days += ( yy / 100 ); days -= ( yy / 400 ); days -= 12; } /* if we did a backwards overrun, adjust properly */ while( days <= 0 ) { if( is_leap_year( yy ) ) { yy -= 1; days += 366; } else { yy -= 1; days += 365; } } dd = ( int ) days; yy++; /* store year, and days in year */ if( pyy != NULL ) { *pyy = yy; } if( pddiny != NULL ) { *pddiny = dd; } /* now calculate the month number and decrease the day number accordingly */ for( i = 1; i <= 12; i++ ) { mm = i; if( dd <= s_days_in_month[is_leap_year( yy )][i - 1] ) break; else dd -= s_days_in_month[is_leap_year( yy )][i - 1]; } if( pdd != NULL ) { *pdd = dd; } if( pmm != NULL ) { *pmm = mm; } } long julian_today( void ) { time_t t; struct tm *tm; time( &t ); tm = localtime( &t ); return get_julian_date( tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900 ); } #ifdef TEST void test( int dd, int mm, int yy ) { int rdd, rmm, ryy, rdiny; long l; l = get_julian_date( dd, mm, yy ); decode_julian_date( l, &rdd, &rmm, &ryy, &rdiny ); printf( "%2d.%2d.%4d %8ld %2d.%2d.%4d. (%3d)\n", dd, mm, yy, l, rdd, rmm, ryy, rdiny ); } int main( void ) { test( 31, 12, 1899 ); test( 1, 1, 1900 ); test( 28, 2, 1900 ); test( 1, 3, 1900 ); test( 31, 12, 1999 ); test( 1, 1, 2000 ); test( 28, 2, 2000 ); test( 1, 3, 2000 ); test( 31, 12, 2099 ); test( 1, 1, 2100 ); test( 28, 2, 2100 ); test( 1, 3, 2100 ); test( 22, 6, 1976 ); /* tobi's birthday */ test( 11, 10, 1999 ); test( 1, 1, 1 ); test( 4, 10, 1582 ); test( 15, 10, 1582 ); return 0; } #endif