1 /* $selId: julian.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 * SdnToJulian(
14 * zend_long sdn,
15 * int *pYear,
16 * int *pMonth,
17 * int *pDay);
18 *
19 * Convert a SDN to a Julian calendar date. If the input SDN is less than
20 * 1, the three output values will all be set to zero, otherwise *pYear
21 * will be >= -4713 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 * zend_long
25 * JulianToSdn(
26 * int inputYear,
27 * int inputMonth,
28 * int inputDay);
29 *
30 * Convert a Julian 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 * VALID RANGE
38 *
39 * 4713 B.C. to at least 10000 A.D.
40 *
41 * Although this software can handle dates all the way back to 4713
42 * B.C., such use may not be meaningful. The calendar was created in
43 * 46 B.C., but the details did not stabilize until at least 8 A.D.,
44 * and perhaps as late at the 4th century. Also, the beginning of a
45 * year varied from one culture to another - not all accepted January
46 * as the first month.
47 *
48 * CALENDAR OVERVIEW
49 *
50 * Julius Caesar created the calendar in 46 B.C. as a modified form of
51 * the old Roman republican calendar which was based on lunar cycles.
52 * The new Julian calendar set fixed lengths for the months, abandoning
53 * the lunar cycle. It also specified that there would be exactly 12
54 * months per year and 365.25 days per year with every 4th year being a
55 * leap year.
56 *
57 * Note that the current accepted value for the tropical year is
58 * 365.242199 days, not 365.25. This lead to an 11 day shift in the
59 * calendar with respect to the seasons by the 16th century when the
60 * Gregorian calendar was created to replace the Julian calendar.
61 *
62 * The difference between the Julian and today's Gregorian calendar is
63 * that the Gregorian does not make centennial years leap years unless
64 * they are a multiple of 400, which leads to a year of 365.2425 days.
65 * In other words, in the Gregorian calendar, 1700, 1800 and 1900 are
66 * not leap years, but 2000 is. All centennial years are leap years in
67 * the Julian calendar.
68 *
69 * The details are unknown, but the lengths of the months were adjusted
70 * until they finally stabilized in 8 A.D. with their current lengths:
71 *
72 * January 31
73 * February 28/29
74 * March 31
75 * April 30
76 * May 31
77 * June 30
78 * Quintilis/July 31
79 * Sextilis/August 31
80 * September 30
81 * October 31
82 * November 30
83 * December 31
84 *
85 * In the early days of the calendar, the days of the month were not
86 * numbered as we do today. The numbers ran backwards (decreasing) and
87 * were counted from the Ides (15th of the month - which in the old
88 * Roman republican lunar calendar would have been the full moon) or
89 * from the Nonae (9th day before the Ides) or from the beginning of
90 * the next month.
91 *
92 * In the early years, the beginning of the year varied, sometimes
93 * based on the ascension of rulers. It was not always the first of
94 * January.
95 *
96 * Also, today's epoch, 1 A.D. or the birth of Jesus Christ, did not
97 * come into use until several centuries later when Christianity became
98 * a dominant religion.
99 *
100 * ALGORITHMS
101 *
102 * The calculations are based on two different cycles: a 4 year cycle
103 * of leap years and a 5 month cycle of month lengths.
104 *
105 * The 5 month cycle is used to account for the varying lengths of
106 * months. You will notice that the lengths alternate between 30 and
107 * 31 days, except for three anomalies: both July and August have 31
108 * days, both December and January have 31, and February is less than
109 * 30. Starting with March, the lengths are in a cycle of 5 months
110 * (31, 30, 31, 30, 31):
111 *
112 * Mar 31 days \
113 * Apr 30 days |
114 * May 31 days > First cycle
115 * Jun 30 days |
116 * Jul 31 days /
117 *
118 * Aug 31 days \
119 * Sep 30 days |
120 * Oct 31 days > Second cycle
121 * Nov 30 days |
122 * Dec 31 days /
123 *
124 * Jan 31 days \
125 * Feb 28/9 days |
126 * > Third cycle (incomplete)
127 *
128 * For this reason the calculations (internally) assume that the year
129 * starts with March 1.
130 *
131 * TESTING
132 *
133 * This algorithm has been tested from the year 4713 B.C. to 10000 A.D.
134 * The source code of the verification program is included in this
135 * package.
136 *
137 * REFERENCES
138 *
139 * Conversions Between Calendar Date and Julian Day Number by Robert J.
140 * Tantzen, Communications of the Association for Computing Machinery
141 * August 1963. (Also published in Collected Algorithms from CACM,
142 * algorithm number 199). [Note: the published algorithm is for the
143 * Gregorian calendar, but was adjusted to use the Julian calendar's
144 * simpler leap year rule.]
145 *
146 **************************************************************************/
147
148 #include "sdncal.h"
149 #include <limits.h>
150
151 #define JULIAN_SDN_OFFSET 32083
152 #define DAYS_PER_5_MONTHS 153
153 #define DAYS_PER_4_YEARS 1461
154
SdnToJulian(zend_long sdn,int * pYear,int * pMonth,int * pDay)155 void SdnToJulian(
156 zend_long sdn,
157 int *pYear,
158 int *pMonth,
159 int *pDay)
160 {
161 int year;
162 int month;
163 int day;
164 zend_long temp;
165 int dayOfYear;
166
167 if (sdn <= 0) {
168 goto fail;
169 }
170 /* Check for overflow */
171 if (sdn > (LONG_MAX - JULIAN_SDN_OFFSET * 4 + 1) / 4 || sdn < LONG_MIN / 4) {
172 goto fail;
173 }
174 temp = sdn * 4 + (JULIAN_SDN_OFFSET * 4 - 1);
175
176 /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
177 {
178 long yearl = temp / DAYS_PER_4_YEARS;
179 if (yearl > INT_MAX || yearl < INT_MIN) {
180 goto fail;
181 }
182 year = (int) yearl;
183 }
184 dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
185
186 /* Calculate the month and day of month. */
187 temp = dayOfYear * 5 - 3;
188 month = temp / DAYS_PER_5_MONTHS;
189 day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
190
191 /* Convert to the normal beginning of the year. */
192 if (month < 10) {
193 month += 3;
194 } else {
195 year += 1;
196 month -= 9;
197 }
198
199 /* Adjust to the B.C./A.D. type numbering. */
200 year -= 4800;
201 if (year <= 0)
202 year--;
203
204 *pYear = year;
205 *pMonth = month;
206 *pDay = day;
207 return;
208
209 fail:
210 *pYear = 0;
211 *pMonth = 0;
212 *pDay = 0;
213 }
214
JulianToSdn(int inputYear,int inputMonth,int inputDay)215 zend_long JulianToSdn(
216 int inputYear,
217 int inputMonth,
218 int inputDay)
219 {
220 zend_long year;
221 int month;
222
223 /* check for invalid dates */
224 if (inputYear == 0 || inputYear < -4713 ||
225 inputMonth <= 0 || inputMonth > 12 ||
226 inputDay <= 0 || inputDay > 31) {
227 return (0);
228 }
229 /* check for dates before SDN 1 (Jan 2, 4713 B.C.) */
230 if (inputYear == -4713) {
231 if (inputMonth == 1 && inputDay == 1) {
232 return (0);
233 }
234 }
235 /* Make year always a positive number. */
236 if (inputYear < 0) {
237 year = inputYear + 4801;
238 } else {
239 year = inputYear + 4800;
240 }
241
242 /* Adjust the start of the year. */
243 if (inputMonth > 2) {
244 month = inputMonth - 3;
245 } else {
246 month = inputMonth + 9;
247 year--;
248 }
249
250 return ((year * DAYS_PER_4_YEARS) / 4
251 + (month * DAYS_PER_5_MONTHS + 2) / 5
252 + inputDay
253 - JULIAN_SDN_OFFSET);
254 }
255