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