calendar.c revision 50476
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 * $FreeBSD: head/lib/libcalendar/calendar.c 50476 1999-08-28 00:22:10Z peter $
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
49typedef struct date date;
50
51/* The last day of Julian calendar, in internal and ndays representation */
52static int nswitch;	/* The last day of Julian calendar */
53static date jiswitch = {1582, 7, 3};
54
55static date	*date2idt(date *idt, date *dt);
56static date	*idt2date(date *dt, date *idt);
57static int	 ndaysji(date *idt);
58static int	 ndaysgi(date *idt);
59static int	 firstweek(int year);
60
61/*
62 * Compute the Julian date from the number of days elapsed since
63 * March 1st of year zero.
64 */
65date *
66jdate(int ndays, date *dt)
67{
68	date    idt;		/* Internal date representation */
69	int     r;		/* hold the rest of days */
70
71	/*
72	 * Compute the year by starting with an approximation not smaller
73	 * than the answer and using linear search for the greatest
74	 * year which does not begin after ndays.
75	 */
76	idt.y = ndays / 365;
77	idt.m = 0;
78	idt.d = 0;
79	while ((r = ndaysji(&idt)) > ndays)
80		idt.y--;
81
82	/*
83	 * Set r to the days left in the year and compute the month by
84	 * linear search as the largest month that does not begin after r
85	 * days.
86	 */
87	r = ndays - r;
88	for (idt.m = 11; month1[idt.m] > r; idt.m--)
89		;
90
91	/* Compute the days left in the month */
92	idt.d = r - month1[idt.m];
93
94	/* return external representation of the date */
95	return (idt2date(dt, &idt));
96}
97
98/*
99 * Return the number of days since March 1st of the year zero.
100 * The date is given according to Julian calendar.
101 */
102int
103ndaysj(date *dt)
104{
105	date    idt;		/* Internal date representation */
106
107	if (date2idt(&idt, dt) == NULL)
108		return (-1);
109	else
110		return (ndaysji(&idt));
111}
112
113/*
114 * Same as above, where the Julian date is given in internal notation.
115 * This formula shows the beauty of this notation.
116 */
117static int
118ndaysji(date * idt)
119{
120
121	return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4);
122}
123
124/*
125 * Compute the date according to the Gregorian calendar from the number of
126 * days since March 1st, year zero. The date computed will be Julian if it
127 * is older than 1582-10-05. This is the reverse of the function ndaysg().
128 */
129date   *
130gdate(int ndays, date *dt)
131{
132	int const *montht;	/* month-table */
133	date    idt;		/* for internal date representation */
134	int     r;		/* holds the rest of days */
135
136	/*
137	 * Compute the year by starting with an approximation not smaller
138	 * than the answer and search linearly for the greatest year not
139	 * starting after ndays.
140	 */
141	idt.y = ndays / 365;
142	idt.m = 0;
143	idt.d = 0;
144	while ((r = ndaysgi(&idt)) > ndays)
145		idt.y--;
146
147	/*
148	 * Set ndays to the number of days left and compute by linear
149	 * search the greatest month which does not start after ndays. We
150	 * use the table month1 which provides for each month the number
151	 * of days that elapsed in the year before that month. Here the
152	 * year 1582 is special, as 10 days are left out in October to
153	 * resynchronize the calendar with the earth's orbit. October 4th
154	 * 1582 is followed by October 15th 1582. We use the "switch"
155	 * table month1s for this year.
156	 */
157	ndays = ndays - r;
158	if (idt.y == 1582)
159		montht = month1s;
160	else
161		montht = month1;
162
163	for (idt.m = 11; montht[idt.m] > ndays; idt.m--)
164		;
165
166	idt.d = ndays - montht[idt.m]; /* the rest is the day in month */
167
168	/* Advance ten days deleted from October if after switch in Oct 1582 */
169	if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d)
170		idt.d += 10;
171
172	/* return external representation of found date */
173	return (idt2date(dt, &idt));
174}
175
176/*
177 * Return the number of days since March 1st of the year zero. The date is
178 * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This
179 * is the reverse of gdate.
180 */
181int
182ndaysg(date *dt)
183{
184	date    idt;		/* Internal date representation */
185
186	if (date2idt(&idt, dt) == NULL)
187		return (-1);
188	return (ndaysgi(&idt));
189}
190
191/*
192 * Same as above, but with the Gregorian date given in internal
193 * representation.
194 */
195static int
196ndaysgi(date *idt)
197{
198	int     nd;		/* Number of days--return value */
199
200	/* Cache nswitch if not already done */
201	if (nswitch == 0)
202		nswitch = ndaysji(&jiswitch);
203
204	/*
205	 * Assume Julian calendar and adapt to Gregorian if necessary, i. e.
206	 * younger than nswitch. Gregori deleted
207	 * the ten days from Oct 5th to Oct 14th 1582.
208	 * Thereafter years which are multiples of 100 and not multiples
209	 * of 400 were not leap years anymore.
210	 * This makes the average length of a year
211	 * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical
212	 * year measures 365.2422d. So in 10000/3 years we are
213	 * again one day ahead of the earth. Sigh :-)
214	 * (d is the average length of a day and tropical year is the
215	 * time from one spring point to the next.)
216	 */
217	if ((nd = ndaysji(idt)) == -1)
218		return (-1);
219	if (idt->y >= 1600)
220		nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400);
221	else if (nd > nswitch)
222		nd -= 10;
223	return (nd);
224}
225
226/*
227 * Compute the week number from the number of days since March 1st year 0.
228 * The weeks are numbered per year starting with 1. If the first
229 * week of a year includes at least four days of that year it is week 1,
230 * otherwise it gets the number of the last week of the previous year.
231 * The variable y will be filled with the year that contains the greater
232 * part of the week.
233 */
234int
235week(int nd, int *y)
236{
237	date    dt;
238	int     fw;		/* 1st day of week 1 of previous, this and
239				 * next year */
240	gdate(nd, &dt);
241	for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--)
242		;
243	return ((nd - fw) / 7 + 1);
244}
245
246/* return the first day of week 1 of year y */
247static int
248firstweek(int y)
249{
250	date idt;
251	int nd, wd;
252
253	idt.y = y - 1;   /* internal representation of y-1-1 */
254	idt.m = 10;
255	idt.d = 0;
256
257	nd = ndaysgi(&idt);
258	/*
259	 * If more than 3 days of this week are in the preceding year, the
260	 * next week is week 1 (and the next monday is the answer),
261	 * otherwise this week is week 1 and the last monday is the
262	 * answer.
263	 */
264	if ((wd = weekday(nd)) > 3)
265		return (nd - wd + 7);
266	else
267		return (nd - wd);
268}
269
270/* return the weekday (Mo = 0 .. Su = 6) */
271int
272weekday(int nd)
273{
274	date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */
275	static int nmonday;             /* ... which is a monday        */
276
277	/* Cache the daynumber of one monday */
278	if (nmonday == 0)
279		nmonday = ndaysgi(&dmondaygi);
280
281	/* return (nd - nmonday) modulo 7 which is the weekday */
282	nd = (nd - nmonday) % 7;
283	if (nd < 0)
284		return (nd + 7);
285	else
286		return (nd);
287}
288
289/*
290 * Convert a date to internal date representation: The year starts on
291 * March 1st, month and day numbering start at zero. E. g. March 1st of
292 * year zero is written as y=0, m=0, d=0.
293 */
294static date *
295date2idt(date *idt, date *dt)
296{
297
298	idt->d = dt->d - 1;
299	if (dt->m > 2) {
300		idt->m = dt->m - 3;
301		idt->y = dt->y;
302	} else {
303		idt->m = dt->m + 9;
304		idt->y = dt->y - 1;
305	}
306	if (idt->m < 0 || idt->m > 11 || idt->y < 0)
307		return (NULL);
308	else
309		return idt;
310}
311
312/* Reverse of date2idt */
313static date *
314idt2date(date *dt, date *idt)
315{
316
317	dt->d = idt->d + 1;
318	if (idt->m < 10) {
319		dt->m = idt->m + 3;
320		dt->y = idt->y;
321	} else {
322		dt->m = idt->m - 9;
323		dt->y = idt->y + 1;
324	}
325	if (dt->m < 1)
326		return (NULL);
327	else
328		return (dt);
329}
330