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