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