Annotation of embedaddon/php/ext/calendar/gregor.c, revision 1.1.1.1
1.1 misho 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: #include <limits.h>
131:
132: #define GREGOR_SDN_OFFSET 32045
133: #define DAYS_PER_5_MONTHS 153
134: #define DAYS_PER_4_YEARS 1461
135: #define DAYS_PER_400_YEARS 146097
136:
137: void SdnToGregorian(
138: long int sdn,
139: int *pYear,
140: int *pMonth,
141: int *pDay)
142: {
143: int century;
144: int year;
145: int month;
146: int day;
147: long int temp;
148: int dayOfYear;
149:
150: if (sdn <= 0 ||
151: sdn > (LONG_MAX - 4 * GREGOR_SDN_OFFSET) / 4) {
152: goto fail;
153: }
154: temp = (sdn + GREGOR_SDN_OFFSET) * 4 - 1;
155:
156: /* Calculate the century (year/100). */
157: century = temp / DAYS_PER_400_YEARS;
158:
159: /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
160: temp = ((temp % DAYS_PER_400_YEARS) / 4) * 4 + 3;
161: year = (century * 100) + (temp / DAYS_PER_4_YEARS);
162: dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
163:
164: /* Calculate the month and day of month. */
165: temp = dayOfYear * 5 - 3;
166: month = temp / DAYS_PER_5_MONTHS;
167: day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
168:
169: /* Convert to the normal beginning of the year. */
170: if (month < 10) {
171: month += 3;
172: } else {
173: year += 1;
174: month -= 9;
175: }
176:
177: /* Adjust to the B.C./A.D. type numbering. */
178: year -= 4800;
179: if (year <= 0)
180: year--;
181:
182: *pYear = year;
183: *pMonth = month;
184: *pDay = day;
185: return;
186:
187: fail:
188: *pYear = 0;
189: *pMonth = 0;
190: *pDay = 0;
191: }
192:
193: long int GregorianToSdn(
194: int inputYear,
195: int inputMonth,
196: int inputDay)
197: {
198: int year;
199: int month;
200:
201: /* check for invalid dates */
202: if (inputYear == 0 || inputYear < -4714 ||
203: inputMonth <= 0 || inputMonth > 12 ||
204: inputDay <= 0 || inputDay > 31) {
205: return (0);
206: }
207: /* check for dates before SDN 1 (Nov 25, 4714 B.C.) */
208: if (inputYear == -4714) {
209: if (inputMonth < 11) {
210: return (0);
211: }
212: if (inputMonth == 11 && inputDay < 25) {
213: return (0);
214: }
215: }
216: /* Make year always a positive number. */
217: if (inputYear < 0) {
218: year = inputYear + 4801;
219: } else {
220: year = inputYear + 4800;
221: }
222:
223: /* Adjust the start of the year. */
224: if (inputMonth > 2) {
225: month = inputMonth - 3;
226: } else {
227: month = inputMonth + 9;
228: year--;
229: }
230:
231: return (((year / 100) * DAYS_PER_400_YEARS) / 4
232: + ((year % 100) * DAYS_PER_4_YEARS) / 4
233: + (month * DAYS_PER_5_MONTHS + 2) / 5
234: + inputDay
235: - GREGOR_SDN_OFFSET);
236: }
237:
238: char *MonthNameShort[13] =
239: {
240: "",
241: "Jan",
242: "Feb",
243: "Mar",
244: "Apr",
245: "May",
246: "Jun",
247: "Jul",
248: "Aug",
249: "Sep",
250: "Oct",
251: "Nov",
252: "Dec"
253: };
254:
255: char *MonthNameLong[13] =
256: {
257: "",
258: "January",
259: "February",
260: "March",
261: "April",
262: "May",
263: "June",
264: "July",
265: "August",
266: "September",
267: "October",
268: "November",
269: "December"
270: };
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>