calendar.c revision 31529
1/*-
2 * Copyright (c) 1997 Wolfgang Helbig
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id$
27 */
28
29#include "calendar.h"
30
31#ifndef NULL
32#define NULL 0
33#endif
34
35/*
36 * For each month tabulate the number of days elapsed in a year before the
37 * month. This assumes the internal date representation, where a year
38 * starts on March 1st. So we don't need a special table for leap years.
39 * But we do need a special table for the year 1582, since 10 days are
40 * deleted in October. This is month1s for the switch from Julian to
41 * Gregorian calendar.
42 */
43static int const month1[] =
44    {0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337};
45   /*  M   A   M   J    J    A    S    O    N    D    J */
46static int const month1s[]=
47    {0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327};
48
49/* The last day of Julian calendar, in internal and ndays representation */
50static int nswitch;	/* The last day of Julian calendar */
51static date jiswitch = {1582, 7, 3};
52
53static date	*date2idt(date *idt, date *dt);
54static date	*idt2date(date *dt, date *idt);
55static int	 ndaysji(date *idt);
56static int	 ndaysgi(date *idt);
57static int	 firstweek(int year);
58
59/*
60 * Compute the Julian date from the number of days elapsed since
61 * March 1st of year zero.
62 */
63date *
64jdate(int ndays, date *dt)
65{
66	date    idt;		/* Internal date representation */
67	int     r;		/* hold the rest of days */
68
69	/*
70	 * Compute the year by starting with an approximation not smaller
71	 * than the answer and using linear search for the greatest
72	 * year which does not begin after ndays.
73	 */
74	idt.y = ndays / 365;
75	idt.m = 0;
76	idt.d = 0;
77	while ((r = ndaysji(&idt)) > ndays)
78		idt.y--;
79
80	/*
81	 * Set r to the days left in the year and compute the month by
82	 * linear search as the largest month that does not begin after r
83	 * days.
84	 */
85	r = ndays - r;
86	for (idt.m = 11; month1[idt.m] > r; idt.m--)
87		;
88
89	/* Compute the days left in the month */
90	idt.d = r - month1[idt.m];
91
92	/* return external representation of the date */
93	return (idt2date(dt, &idt));
94}
95
96/*
97 * Return the number of days since March 1st of the year zero.
98 * The date is given according to Julian calendar.
99 */
100int
101ndaysj(date *dt)
102{
103	date    idt;		/* Internal date representation */
104
105	if (date2idt(&idt, dt) == NULL)
106		return (-1);
107	else
108		return (ndaysji(&idt));
109}
110
111/*
112 * Same as above, where the Julian date is given in internal notation.
113 * This formula shows the beauty of this notation.
114 */
115static int
116ndaysji(date * idt)
117{
118
119	return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4);
120}
121
122/*
123 * Compute the date according to the Gregorian calendar from the number of
124 * days since March 1st, year zero. The date computed will be Julian if it
125 * is older than 1582-10-05. This is the reverse of the function ndaysg().
126 */
127date   *
128gdate(int ndays, date *dt)
129{
130	int const *montht;	/* month-table */
131	date    idt;		/* for internal date representation */
132	int     r;		/* holds the rest of days */
133
134	/*
135	 * Compute the year by starting with an approximation not smaller
136	 * than the answer and search linearly for the greatest year not
137	 * starting after ndays.
138	 */
139	idt.y = ndays / 365;
140	idt.m = 0;
141	idt.d = 0;
142	while ((r = ndaysgi(&idt)) > ndays)
143		idt.y--;
144
145	/*
146	 * Set ndays to the number of days left and compute by linear
147	 * search the greatest month which does not start after ndays. We
148	 * use the table month1 which provides for each month the number
149	 * of days that elapsed in the year before that month. Here the
150	 * year 1582 is special, as 10 days are left out in October to
151	 * resynchronize the calendar with the earth's orbit. October 4th
152	 * 1582 is followed by October 15th 1582. We use the "switch"
153	 * table month1s for this year.
154	 */
155	ndays = ndays - r;
156	if (idt.y == 1582)
157		montht = month1s;
158	else
159		montht = month1;
160
161	for (idt.m = 11; montht[idt.m] > ndays; idt.m--)
162		;
163
164	idt.d = ndays - montht[idt.m]; /* the rest is the day in month */
165
166	/* Advance ten days deleted from October if after switch in Oct 1582 */
167	if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d)
168		idt.d += 10;
169
170	/* return external representation of found date */
171	return (idt2date(dt, &idt));
172}
173
174/*
175 * Return the number of days since March 1st of the year zero. The date is
176 * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This
177 * is the reverse of gdate.
178 */
179int
180ndaysg(date *dt)
181{
182	date    idt;		/* Internal date representation */
183
184	if (date2idt(&idt, dt) == NULL)
185		return (-1);
186	return (ndaysgi(&idt));
187}
188
189/*
190 * Same as above, but with the Gregorian date given in internal
191 * representation.
192 */
193static int
194ndaysgi(date *idt)
195{
196	int     nd;		/* Number of days--return value */
197
198	/* Cache nswitch if not already done */
199	if (nswitch == 0)
200		nswitch = ndaysji(&jiswitch);
201
202	/*
203	 * Assume Julian calendar and adapt to Gregorian if necessary, i. e.
204	 * younger than nswitch. Gregori deleted
205	 * the ten days from Oct 5th to Oct 14th 1582.
206	 * Thereafter years which are multiples of 100 and not multiples
207	 * of 400 were not leap years anymore.
208	 * This makes the average length of a year
209	 * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical
210	 * year measures 365.2422d. So in 10000/3 years we are
211	 * again one day ahead of the earth. Sigh :-)
212	 * (d is the average length of a day and tropical year is the
213	 * time from one spring point to the next.)
214	 */
215	if ((nd = ndaysji(idt)) == -1)
216		return (-1);
217	if (idt->y >= 1600)
218		nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400);
219	else if (nd > nswitch)
220		nd -= 10;
221	return (nd);
222}
223
224/*
225 * Compute the week number from the number of days since March 1st year 0.
226 * The weeks are numbered per year starting with 1. If the first
227 * week of a year includes at least four days of that year it is week 1,
228 * otherwise it gets the number of the last week of the previous year.
229 * The variable y will be filled with the year that contains the greater
230 * part of the week.
231 */
232int
233week(int nd, int *y)
234{
235	date    dt;
236	int     fw;		/* 1st day of week 1 of previous, this and
237				 * next year */
238	gdate(nd, &dt);
239	for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--)
240		;
241	return ((nd - fw) / 7 + 1);
242}
243
244/* return the first day of week 1 of year y */
245static int
246firstweek(int y)
247{
248	date idt;
249	int nd, wd;
250
251	idt.y = y - 1;   /* internal representation of y-1-1 */
252	idt.m = 10;
253	idt.d = 0;
254
255	nd = ndaysgi(&idt);
256	/*
257	 * If more than 3 days of this week are in the preceding year, the
258	 * next week is week 1 (and the next monday is the answer),
259	 * otherwise this week is week 1 and the last monday is the
260	 * answer.
261	 */
262	if ((wd = weekday(nd)) > 3)
263		return (nd - wd + 7);
264	else
265		return (nd - wd);
266}
267
268/* return the weekday (Mo = 0 .. Su = 6) */
269int
270weekday(int nd)
271{
272	date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */
273	static int nmonday;             /* ... which is a monday        */
274
275	/* Cache the daynumber of one monday */
276	if (nmonday == 0)
277		nmonday = ndaysgi(&dmondaygi);
278
279	/* return (nd - nmonday) modulo 7 which is the weekday */
280	nd = (nd - nmonday) % 7;
281	if (nd < 0)
282		return (nd + 7);
283	else
284		return (nd);
285}
286
287/*
288 * Convert a date to internal date representation: The year starts on
289 * March 1st, month and day numbering start at zero. E. g. March 1st of
290 * year zero is written as y=0, m=0, d=0.
291 */
292static date *
293date2idt(date *idt, date *dt)
294{
295
296	idt->d = dt->d - 1;
297	if (dt->m > 2) {
298		idt->m = dt->m - 3;
299		idt->y = dt->y;
300	} else {
301		idt->m = dt->m + 9;
302		idt->y = dt->y - 1;
303	}
304	if (idt->m < 0 || idt->m > 11 || idt->y < 0)
305		return (NULL);
306	else
307		return idt;
308}
309
310/* Reverse of date2idt */
311static date *
312idt2date(date *dt, date *idt)
313{
314
315	dt->d = idt->d + 1;
316	if (idt->m < 10) {
317		dt->m = idt->m + 3;
318		dt->y = idt->y;
319	} else {
320		dt->m = idt->m - 9;
321		dt->y = idt->y + 1;
322	}
323	if (dt->m < 1)
324		return (NULL);
325	else
326		return (dt);
327}
328