1 /* $Id$
2    Julian Date Routines
3    Written 1999 by Tobias Ernst and release to the Public Domain.
4 
5    This file is part of NLTOOLS, the nodelist processor of the Husky fidonet
6    software project.
7 
8 
9    These routines calculate a Julian day number for a given date. Julian day
10    numbers are a consecutive sequence of day numbers, with the origin at the
11    January 1st, 4713 (B.C.) (Julian Day #1). Julian day numbers can be used to
12    calculate date differences in days (just subtract the day numbers), to
13    determine the day of week (julian day number modulo 7 yields 0 for Monday, 6
14    for Sunday), etc.
15 
16    The calculcations in these routines are based on the Gregorian calendar
17    being in effect since Oct. 15, 1582 (European convention). In any case,
18    result for dates previous to September 14, 1752, will probably not be too
19    much in coincidence with the local reality at that time.
20 
21    Conventions are: dd (day in month): 1 .. 31
22                     mm (month): January = 1, December = 12
23                     yy (year):  Positive four digit year number (e.g. 1999).
24 
25    The routines do not work year numbers < 1. There is no problem with big year
26    numbers, i.E. the code is year 2000 safe (and it of course respects Feburary
27    29, 2000).
28 */
29 #include <stdlib.h>
30 #include <time.h>
31 
32 #include "julian.h" /* See julian.h for license and documentation */
33 
34 #ifdef TEST
35 # include <stdio.h>
36 #endif
37 
is_leap_year(int year)38 static int is_leap_year( int year )
39 {
40   if( year % 4 )
41   {
42     return 0;
43   }
44   if( year < 1582 )
45   {
46     return 1;
47   }
48   if( ( year % 100 ) || ( !( year % 400 ) ) )
49   {
50     return 1;
51   }
52   return 0;
53 }
54 
55 static long s_days_in_month[2][12] = {
56   {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
57   {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
58 };
59 
get_julian_date(int dd,int mm,int yy)60 long get_julian_date( int dd, int mm, int yy )
61 {
62   long julian;
63 
64   /* Account all preceding years */
65   julian = ( yy - 1 ) * 365L;
66 
67   /* Account the leap years */
68   julian += ( yy - 1 ) / 4L;
69 
70   if( yy - 1 > 1582L )
71   {
72 
73     /* Subtract all xx00 years */
74     julian -= ( yy - 1 ) / 100L;
75 
76     /* But all xx00 years that can be divided by 400 are leap years! */
77     julian += ( yy - 1 ) / 400L;
78 
79     /* Account for the centuries before 1582, which all were leap years */
80     julian += 12L;              /* 100, 200, 300, 500, 600, 700, 900,
81                                    1000, 1100, 1300, */
82   }
83 
84   if( mm )
85   {
86     /* mm > 0: assume dd is day number in month */
87 
88     /* add the days of the current year */
89     while( --mm )
90     {
91       julian += s_days_in_month[is_leap_year( yy )][mm - 1];
92     }
93 
94     /* days in current month */
95     julian += dd;
96   }
97   else
98   {
99     /* mm == 0: assume dd is day number in year as per fts */
100     julian += dd;
101   }
102 
103   /* adjust for the cut from oct 4 to oct 15, 1582 */
104   if( julian > 577737L )
105   {
106     julian -= 10L;
107   }
108 
109   /* By now, we have days since Jan 1, 1 (AC). Now convert to Julian day
110      numbers, which by convention start at Jan 1, 4713 (BC) */
111 
112   julian += ( 2299161L - 577738L );
113 
114   return julian;
115 }
116 
decode_julian_date(long julian,int * pdd,int * pmm,int * pyy,int * pddiny)117 void decode_julian_date( long julian, int *pdd, int *pmm, int *pyy, int *pddiny )
118 {
119   int yy, dd, mm;
120   long days;
121   int i;
122 
123   /* convert to days since B.C. */
124   julian -= ( 2299161L - 577738L );
125 
126   /* undo the oct. 1582 gap */
127   if( julian > 577737L )
128   {
129     julian += 10L;
130   }
131 
132   yy = ( int ) ( julian / 365L );
133   days = ( julian % 365L );
134 
135   /* account for the leap years */
136   if( yy < 1700 )
137   {
138     days -= ( yy / 4 );
139   }
140   else
141   {
142     days -= ( yy / 4 );
143     days += ( yy / 100 );
144     days -= ( yy / 400 );
145     days -= 12;
146   }
147 
148   /* if we did a backwards overrun, adjust properly */
149   while( days <= 0 )
150   {
151     if( is_leap_year( yy ) )
152     {
153       yy -= 1;
154       days += 366;
155     }
156     else
157     {
158       yy -= 1;
159       days += 365;
160     }
161   }
162 
163   dd = ( int ) days;
164   yy++;
165 
166   /* store year, and days in year */
167   if( pyy != NULL )
168   {
169     *pyy = yy;
170   }
171   if( pddiny != NULL )
172   {
173     *pddiny = dd;
174   }
175 
176   /* now calculate the month number and decrease the day number
177      accordingly */
178   for( i = 1; i <= 12; i++ )
179   {
180     mm = i;
181     if( dd <= s_days_in_month[is_leap_year( yy )][i - 1] )
182       break;
183     else
184       dd -= s_days_in_month[is_leap_year( yy )][i - 1];
185   }
186 
187   if( pdd != NULL )
188   {
189     *pdd = dd;
190   }
191 
192   if( pmm != NULL )
193   {
194     *pmm = mm;
195   }
196 }
197 
julian_today(void)198 long julian_today( void )
199 {
200   time_t t;
201   struct tm *tm;
202 
203   time( &t );
204   tm = localtime( &t );
205 
206   return get_julian_date( tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900 );
207 }
208 
209 #ifdef TEST
test(int dd,int mm,int yy)210 void test( int dd, int mm, int yy )
211 {
212   int rdd, rmm, ryy, rdiny;
213   long l;
214 
215   l = get_julian_date( dd, mm, yy );
216   decode_julian_date( l, &rdd, &rmm, &ryy, &rdiny );
217   printf( "%2d.%2d.%4d %8ld %2d.%2d.%4d. (%3d)\n", dd, mm, yy, l, rdd, rmm, ryy, rdiny );
218 }
219 
main(void)220 int main( void )
221 {
222   test( 31, 12, 1899 );
223   test( 1, 1, 1900 );
224   test( 28, 2, 1900 );
225   test( 1, 3, 1900 );
226   test( 31, 12, 1999 );
227   test( 1, 1, 2000 );
228   test( 28, 2, 2000 );
229   test( 1, 3, 2000 );
230   test( 31, 12, 2099 );
231   test( 1, 1, 2100 );
232   test( 28, 2, 2100 );
233   test( 1, 3, 2100 );
234   test( 22, 6, 1976 );          /* tobi's birthday <g> */
235   test( 11, 10, 1999 );
236   test( 1, 1, 1 );
237   test( 4, 10, 1582 );
238   test( 15, 10, 1582 );
239 
240   return 0;
241 }
242 #endif
243