1 : /* $selId: gregor.c,v 2.0 1995/10/24 01:13:06 lees Exp $
2 : * Copyright 1993-1995, Scott E. Lee, all rights reserved.
3 : * Permission granted to use, copy, modify, distribute and sell so long as
4 : * the above copyright and this permission statement are retained in all
5 : * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
6 : */
7 :
8 : /**************************************************************************
9 : *
10 : * These are the externally visible components of this file:
11 : *
12 : * void
13 : * SdnToGregorian(
14 : * long int sdn,
15 : * int *pYear,
16 : * int *pMonth,
17 : * int *pDay);
18 : *
19 : * Convert a SDN to a Gregorian calendar date. If the input SDN is less
20 : * than 1, the three output values will all be set to zero, otherwise
21 : * *pYear will be >= -4714 and != 0; *pMonth will be in the range 1 to 12
22 : * inclusive; *pDay will be in the range 1 to 31 inclusive.
23 : *
24 : * long int
25 : * GregorianToSdn(
26 : * int inputYear,
27 : * int inputMonth,
28 : * int inputDay);
29 : *
30 : * Convert a Gregorian calendar date to a SDN. Zero is returned when the
31 : * input date is detected as invalid or out of the supported range. The
32 : * return value will be > 0 for all valid, supported dates, but there are
33 : * some invalid dates that will return a positive value. To verify that a
34 : * date is valid, convert it to SDN and then back and compare with the
35 : * original.
36 : *
37 : * char *MonthNameShort[13];
38 : *
39 : * Convert a Gregorian month number (1 to 12) to the abbreviated (three
40 : * character) name of the Gregorian month (null terminated). An index of
41 : * zero will return a zero length string.
42 : *
43 : * char *MonthNameLong[13];
44 : *
45 : * Convert a Gregorian month number (1 to 12) to the name of the Gregorian
46 : * month (null terminated). An index of zero will return a zero length
47 : * string.
48 : *
49 : * VALID RANGE
50 : *
51 : * 4714 B.C. to at least 10000 A.D.
52 : *
53 : * Although this software can handle dates all the way back to 4714
54 : * B.C., such use may not be meaningful. The Gregorian calendar was
55 : * not instituted until October 15, 1582 (or October 5, 1582 in the
56 : * Julian calendar). Some countries did not accept it until much
57 : * later. For example, Britain converted in 1752, The USSR in 1918 and
58 : * Greece in 1923. Most European countries used the Julian calendar
59 : * prior to the Gregorian.
60 : *
61 : * CALENDAR OVERVIEW
62 : *
63 : * The Gregorian calendar is a modified version of the Julian calendar.
64 : * The only difference being the specification of leap years. The
65 : * Julian calendar specifies that every year that is a multiple of 4
66 : * will be a leap year. This leads to a year that is 365.25 days long,
67 : * but the current accepted value for the tropical year is 365.242199
68 : * days.
69 : *
70 : * To correct this error in the length of the year and to bring the
71 : * vernal equinox back to March 21, Pope Gregory XIII issued a papal
72 : * bull declaring that Thursday October 4, 1582 would be followed by
73 : * Friday October 15, 1582 and that centennial years would only be a
74 : * leap year if they were a multiple of 400. This shortened the year
75 : * by 3 days per 400 years, giving a year of 365.2425 days.
76 : *
77 : * Another recently proposed change in the leap year rule is to make
78 : * years that are multiples of 4000 not a leap year, but this has never
79 : * been officially accepted and this rule is not implemented in these
80 : * algorithms.
81 : *
82 : * ALGORITHMS
83 : *
84 : * The calculations are based on three different cycles: a 400 year
85 : * cycle of leap years, a 4 year cycle of leap years and a 5 month
86 : * cycle of month lengths.
87 : *
88 : * The 5 month cycle is used to account for the varying lengths of
89 : * months. You will notice that the lengths alternate between 30
90 : * and 31 days, except for three anomalies: both July and August
91 : * have 31 days, both December and January have 31, and February
92 : * is less than 30. Starting with March, the lengths are in a
93 : * cycle of 5 months (31, 30, 31, 30, 31):
94 : *
95 : * Mar 31 days \
96 : * Apr 30 days |
97 : * May 31 days > First cycle
98 : * Jun 30 days |
99 : * Jul 31 days /
100 : *
101 : * Aug 31 days \
102 : * Sep 30 days |
103 : * Oct 31 days > Second cycle
104 : * Nov 30 days |
105 : * Dec 31 days /
106 : *
107 : * Jan 31 days \
108 : * Feb 28/9 days |
109 : * > Third cycle (incomplete)
110 : *
111 : * For this reason the calculations (internally) assume that the
112 : * year starts with March 1.
113 : *
114 : * TESTING
115 : *
116 : * This algorithm has been tested from the year 4714 B.C. to 10000
117 : * A.D. The source code of the verification program is included in
118 : * this package.
119 : *
120 : * REFERENCES
121 : *
122 : * Conversions Between Calendar Date and Julian Day Number by Robert J.
123 : * Tantzen, Communications of the Association for Computing Machinery
124 : * August 1963. (Also published in Collected Algorithms from CACM,
125 : * algorithm number 199).
126 : *
127 : **************************************************************************/
128 :
129 : #include "sdncal.h"
130 :
131 : #define GREGOR_SDN_OFFSET 32045
132 : #define DAYS_PER_5_MONTHS 153
133 : #define DAYS_PER_4_YEARS 1461
134 : #define DAYS_PER_400_YEARS 146097
135 :
136 : void SdnToGregorian(
137 : long int sdn,
138 : int *pYear,
139 : int *pMonth,
140 : int *pDay)
141 131 : {
142 : int century;
143 : int year;
144 : int month;
145 : int day;
146 : long int temp;
147 : int dayOfYear;
148 :
149 131 : if (sdn <= 0) {
150 3 : *pYear = 0;
151 3 : *pMonth = 0;
152 3 : *pDay = 0;
153 3 : return;
154 : }
155 128 : temp = (sdn + GREGOR_SDN_OFFSET) * 4 - 1;
156 :
157 128 : if (temp < 0) {
158 0 : *pYear = 0;
159 0 : *pMonth = 0;
160 0 : *pDay = 0;
161 0 : return;
162 : }
163 :
164 : /* Calculate the century (year/100). */
165 128 : century = temp / DAYS_PER_400_YEARS;
166 :
167 : /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
168 128 : temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
169 128 : year = (century * 100) + (temp / DAYS_PER_4_YEARS);
170 128 : dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
171 :
172 : /* Calculate the month and day of month. */
173 128 : temp = dayOfYear * 5 - 3;
174 128 : month = temp / DAYS_PER_5_MONTHS;
175 128 : day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
176 :
177 : /* Convert to the normal beginning of the year. */
178 128 : if (month < 10) {
179 103 : month += 3;
180 : } else {
181 25 : year += 1;
182 25 : month -= 9;
183 : }
184 :
185 : /* Adjust to the B.C./A.D. type numbering. */
186 128 : year -= 4800;
187 128 : if (year <= 0)
188 0 : year--;
189 :
190 128 : *pYear = year;
191 128 : *pMonth = month;
192 128 : *pDay = day;
193 : }
194 :
195 : long int GregorianToSdn(
196 : int inputYear,
197 : int inputMonth,
198 : int inputDay)
199 29 : {
200 : int year;
201 : int month;
202 :
203 : /* check for invalid dates */
204 29 : if (inputYear == 0 || inputYear < -4714 ||
205 : inputMonth <= 0 || inputMonth > 12 ||
206 : inputDay <= 0 || inputDay > 31) {
207 3 : return (0);
208 : }
209 : /* check for dates before SDN 1 (Nov 25, 4714 B.C.) */
210 26 : if (inputYear == -4714) {
211 0 : if (inputMonth < 11) {
212 0 : return (0);
213 : }
214 0 : if (inputMonth == 11 && inputDay < 25) {
215 0 : return (0);
216 : }
217 : }
218 : /* Make year always a positive number. */
219 26 : if (inputYear < 0) {
220 0 : year = inputYear + 4801;
221 : } else {
222 26 : year = inputYear + 4800;
223 : }
224 :
225 : /* Adjust the start of the year. */
226 26 : if (inputMonth > 2) {
227 19 : month = inputMonth - 3;
228 : } else {
229 7 : month = inputMonth + 9;
230 7 : year--;
231 : }
232 :
233 26 : return (((year / 100) * DAYS_PER_400_YEARS) / 4
234 : + ((year % 100) * DAYS_PER_4_YEARS) / 4
235 : + (month * DAYS_PER_5_MONTHS + 2) / 5
236 : + inputDay
237 : - GREGOR_SDN_OFFSET);
238 : }
239 :
240 : char *MonthNameShort[13] =
241 : {
242 : "",
243 : "Jan",
244 : "Feb",
245 : "Mar",
246 : "Apr",
247 : "May",
248 : "Jun",
249 : "Jul",
250 : "Aug",
251 : "Sep",
252 : "Oct",
253 : "Nov",
254 : "Dec"
255 : };
256 :
257 : char *MonthNameLong[13] =
258 : {
259 : "",
260 : "January",
261 : "February",
262 : "March",
263 : "April",
264 : "May",
265 : "June",
266 : "July",
267 : "August",
268 : "September",
269 : "October",
270 : "November",
271 : "December"
272 : };
|