dates.c revision 205872
1205821Sedwin/*-
2205872Sedwin * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
3205872Sedwin * All rights reserved.
4205821Sedwin *
5205821Sedwin * Redistribution and use in source and binary forms, with or without
6205821Sedwin * modification, are permitted provided that the following conditions
7205821Sedwin * are met:
8205821Sedwin * 1. Redistributions of source code must retain the above copyright
9205821Sedwin *    notice, this list of conditions and the following disclaimer.
10205821Sedwin * 2. Redistributions in binary form must reproduce the above copyright
11205821Sedwin *    notice, this list of conditions and the following disclaimer in the
12205821Sedwin *    documentation and/or other materials provided with the distribution.
13205821Sedwin *
14205821Sedwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15205821Sedwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16205821Sedwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17205821Sedwin * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18205821Sedwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19205821Sedwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20205821Sedwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21205821Sedwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22205821Sedwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23205821Sedwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24205821Sedwin * SUCH DAMAGE.
25205821Sedwin *
26205821Sedwin */
27205821Sedwin
28205821Sedwin#include <sys/cdefs.h>
29205821Sedwin__FBSDID("$FreeBSD: head/usr.bin/calendar/dates.c 205872 2010-03-30 06:42:01Z edwin $");
30205821Sedwin
31205821Sedwin#include <stdio.h>
32205821Sedwin#include <stdlib.h>
33205821Sedwin#include <err.h>
34205821Sedwin#include <time.h>
35205821Sedwin
36205821Sedwin#include "calendar.h"
37205821Sedwin
38205821Sedwinstruct cal_year {
39205821Sedwin	int year;	/* 19xx, 20xx, 21xx */
40205821Sedwin	int easter;	/* Julian day */
41205821Sedwin	int paskha;	/* Julian day */
42205821Sedwin	int cny;	/* Julian day */
43205821Sedwin	int firstdayofweek; /* 0 .. 6 */
44205821Sedwin	struct cal_month *months;
45205821Sedwin	struct cal_year	*nextyear;
46205821Sedwin} cal_year;
47205821Sedwin
48205821Sedwinstruct cal_month {
49205821Sedwin	int month;			/* 01 .. 12 */
50205821Sedwin	int firstdayjulian;		/* 000 .. 366 */
51205821Sedwin	int firstdayofweek;		/* 0 .. 6 */
52205821Sedwin	struct cal_year *year;		/* points back */
53205821Sedwin	struct cal_day *days;
54205821Sedwin	struct cal_month *nextmonth;
55205821Sedwin} cal_month;
56205821Sedwin
57205821Sedwinstruct cal_day {
58205821Sedwin	int dayofmonth;			/* 01 .. 31 */
59205821Sedwin	int julianday;			/* 000 .. 366 */
60205821Sedwin	int dayofweek;			/* 0 .. 6 */
61205821Sedwin	struct cal_day *nextday;
62205821Sedwin	struct cal_month *month;	/* points back */
63205821Sedwin	struct cal_year	*year;		/* points back */
64205821Sedwin	struct event *events;
65205821Sedwin} cal_day;
66205821Sedwin
67205821Sedwinint debug_remember = 0;
68205821Sedwinstruct cal_year	*hyear = NULL;
69205821Sedwin
70205821Sedwin/* 1-based month, 0-based days, cumulative */
71205821Sedwinint *cumdays;
72205821Sedwinint	cumdaytab[][14] = {
73205821Sedwin	{0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
74205821Sedwin	{0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
75205821Sedwin};
76205821Sedwin/* 1-based month, individual */
77205821Sedwinint *mondays;
78205821Sedwinint	mondaytab[][14] = {
79205821Sedwin	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
80205821Sedwin	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
81205821Sedwin};
82205821Sedwin
83205821Sedwinstatic struct cal_day *	find_day(int yy, int mm, int dd);
84205821Sedwin
85205821Sedwinstatic void
86205821Sedwincreatedate(int y, int m, int d)
87205821Sedwin{
88205821Sedwin	struct cal_year *py, *pyp;
89205821Sedwin	struct cal_month *pm, *pmp;
90205821Sedwin	struct cal_day *pd, *pdp;
91205821Sedwin	int *cumday;
92205821Sedwin
93205821Sedwin	pyp = NULL;
94205821Sedwin	py = hyear;
95205821Sedwin	while (py != NULL) {
96205821Sedwin		if (py->year == y + 1900)
97205821Sedwin			break;
98205821Sedwin		pyp = py;
99205821Sedwin		py = py->nextyear;
100205821Sedwin	}
101205821Sedwin
102205821Sedwin	if (py == NULL) {
103205821Sedwin		struct tm td;
104205821Sedwin		time_t t;
105205821Sedwin		py = (struct cal_year *)calloc(1, sizeof(struct cal_year));
106205821Sedwin		py->year = y + 1900;
107205821Sedwin		py->easter = easter(y);
108205821Sedwin		py->paskha = paskha(y);
109205821Sedwin
110205821Sedwin		td = tm0;
111205821Sedwin		td.tm_year = y;
112205821Sedwin		td.tm_mday = 1;
113205821Sedwin		t = mktime(&td);
114205821Sedwin		localtime_r(&t, &td);
115205821Sedwin		py->firstdayofweek = td.tm_wday;
116205821Sedwin
117205821Sedwin		if (pyp != NULL)
118205821Sedwin			pyp->nextyear = py;
119205821Sedwin	}
120205821Sedwin	if (pyp == NULL) {
121205821Sedwin		/* The very very very first one */
122205821Sedwin		hyear = py;
123205821Sedwin	}
124205821Sedwin
125205821Sedwin	pmp = NULL;
126205821Sedwin	pm = py->months;
127205821Sedwin	while (pm != NULL) {
128205821Sedwin		if (pm->month == m)
129205821Sedwin			break;
130205821Sedwin		pmp = pm;
131205821Sedwin		pm = pm->nextmonth;
132205821Sedwin	}
133205821Sedwin
134205821Sedwin	if (pm == NULL) {
135205821Sedwin		pm = (struct cal_month *)calloc(1, sizeof(struct cal_month));
136205821Sedwin		pm->year = py;
137205821Sedwin		pm->month = m;
138205821Sedwin		cumday = cumdaytab[isleap(y)];
139205821Sedwin		pm->firstdayjulian = cumday[m] + 2;
140205821Sedwin		pm->firstdayofweek =
141205821Sedwin		    (py->firstdayofweek + pm->firstdayjulian -1) % 7;
142205821Sedwin		if (pmp != NULL)
143205821Sedwin			pmp->nextmonth = pm;
144205821Sedwin	}
145205821Sedwin	if (pmp == NULL)
146205821Sedwin		py->months = pm;
147205821Sedwin
148205821Sedwin	pdp = NULL;
149205821Sedwin	pd = pm->days;
150205821Sedwin	while (pd != NULL) {
151205821Sedwin		pdp = pd;
152205821Sedwin		pd = pd->nextday;
153205821Sedwin	}
154205821Sedwin
155205821Sedwin	if (pd == NULL) {	/* Always true */
156205821Sedwin		pd = (struct cal_day *)calloc(1, sizeof(struct cal_day));
157205821Sedwin		pd->month = pm;
158205821Sedwin		pd->year = py;
159205821Sedwin		pd->dayofmonth = d;
160205821Sedwin		pd->julianday = pm->firstdayjulian + d - 1;
161205821Sedwin		pd->dayofweek = (pm->firstdayofweek + d - 1) % 7;
162205821Sedwin		if (pdp != NULL)
163205821Sedwin			pdp->nextday = pd;
164205821Sedwin	}
165205821Sedwin	if (pdp == NULL)
166205821Sedwin		pm->days = pd;
167205821Sedwin}
168205821Sedwin
169205821Sedwinvoid
170205821Sedwingeneratedates(struct tm *tp1, struct tm *tp2)
171205821Sedwin{
172205821Sedwin	int y1, m1, d1;
173205821Sedwin	int y2, m2, d2;
174205821Sedwin	int y, m, d;
175205821Sedwin
176205821Sedwin	y1 = tp1->tm_year;
177205821Sedwin	m1 = tp1->tm_mon + 1;
178205821Sedwin	d1 = tp1->tm_mday;
179205821Sedwin	y2 = tp2->tm_year;
180205821Sedwin	m2 = tp2->tm_mon + 1;
181205821Sedwin	d2 = tp2->tm_mday;
182205821Sedwin
183205821Sedwin	if (y1 == y2) {
184205821Sedwin		if (m1 == m2) {
185205821Sedwin			/* Same year, same month. Easy! */
186205821Sedwin			for (d = d1; d <= d2; d++)
187205821Sedwin				createdate(y1, m1, d);
188205821Sedwin			return;
189205821Sedwin		}
190205821Sedwin		/*
191205821Sedwin		 * Same year, different month.
192205821Sedwin		 * - Take the leftover days from m1
193205821Sedwin		 * - Take all days from <m1 .. m2>
194205821Sedwin		 * - Take the first days from m2
195205821Sedwin		 */
196205821Sedwin		mondays = mondaytab[isleap(y1)];
197205821Sedwin		for (d = d1; d <= mondays[m1]; d++)
198205821Sedwin			createdate(y1, m1, d);
199205821Sedwin		for (m = m1 + 1; m < m2; m++)
200205821Sedwin			for (d = 1; d <= mondays[m]; d++)
201205821Sedwin				createdate(y1, m, d);
202205821Sedwin		for (d = 1; d <= d2; d++)
203205821Sedwin			createdate(y1, m2, d);
204205821Sedwin		return;
205205821Sedwin	}
206205821Sedwin	/*
207205821Sedwin	 * Different year, different month.
208205821Sedwin	 * - Take the leftover days from y1-m1
209205821Sedwin	 * - Take all days from y1-<m1 .. 12]
210205821Sedwin	 * - Take all days from <y1 .. y2>
211205821Sedwin	 * - Take all days from y2-[1 .. m2>
212205821Sedwin	 * - Take the first days of y2-m2
213205821Sedwin	 */
214205821Sedwin	mondays = mondaytab[isleap(y1)];
215205821Sedwin	for (d = d1; d <= mondays[m1]; d++)
216205821Sedwin		createdate(y1, m1, d);
217205821Sedwin	for (m = m1 + 1; m <= 12; m++)
218205821Sedwin		for (d = 1; d <= mondays[m]; d++)
219205821Sedwin			createdate(y1, m, d);
220205821Sedwin	for (y = y1 + 1; y < y2; y++) {
221205821Sedwin		mondays = mondaytab[isleap(y)];
222205821Sedwin		for (m = 1; m <= 12; m++)
223205821Sedwin			for (d = 1; d <= mondays[m]; d++)
224205821Sedwin				createdate(y, m, d);
225205821Sedwin	}
226205821Sedwin	mondays = mondaytab[isleap(y2)];
227205821Sedwin	for (m = 1; m < m2; m++)
228205821Sedwin		for (d = 1; d <= mondays[m]; d++)
229205821Sedwin			createdate(y2, m, d);
230205821Sedwin	for (d = 1; d <= d2; d++)
231205821Sedwin		createdate(y2, m2, d);
232205821Sedwin}
233205821Sedwin
234205821Sedwinvoid
235205821Sedwindumpdates(void)
236205821Sedwin{
237205821Sedwin	struct cal_year *y;
238205821Sedwin	struct cal_month *m;
239205821Sedwin	struct cal_day *d;
240205821Sedwin
241205821Sedwin	y = hyear;
242205821Sedwin	while (y != NULL) {
243205821Sedwin		printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek);
244205821Sedwin		m = y->months;
245205821Sedwin		while (m != NULL) {
246205821Sedwin			printf("-- %-5d (julian:%d, dow:%d)\n", m->month,
247205821Sedwin			    m->firstdayjulian, m->firstdayofweek);
248205821Sedwin			d = m->days;
249205821Sedwin			while (d != NULL) {
250205821Sedwin				printf("  -- %-5d (julian:%d, dow:%d)\n",
251205821Sedwin				    d->dayofmonth, d->julianday, d->dayofweek);
252205821Sedwin				d = d->nextday;
253205821Sedwin			}
254205821Sedwin			m = m->nextmonth;
255205821Sedwin		}
256205821Sedwin		y = y->nextyear;
257205821Sedwin	}
258205821Sedwin}
259205821Sedwin
260205821Sedwinint
261205821Sedwinremember_ymd(int yy, int mm, int dd)
262205821Sedwin{
263205821Sedwin	struct cal_year *y;
264205821Sedwin	struct cal_month *m;
265205821Sedwin	struct cal_day *d;
266205821Sedwin
267205821Sedwin	if (debug_remember)
268205821Sedwin		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
269205821Sedwin
270205821Sedwin	y = hyear;
271205821Sedwin	while (y != NULL) {
272205821Sedwin		if (y->year != yy) {
273205821Sedwin			y = y->nextyear;
274205821Sedwin			continue;
275205821Sedwin		}
276205821Sedwin		m = y->months;
277205821Sedwin		while (m != NULL) {
278205821Sedwin			if (m->month != mm) {
279205821Sedwin				m = m->nextmonth;
280205821Sedwin				continue;
281205821Sedwin			}
282205821Sedwin			d = m->days;
283205821Sedwin			while (d != NULL) {
284205821Sedwin				if (d->dayofmonth == dd)
285205821Sedwin					return (1);
286205821Sedwin				d = d->nextday;
287205821Sedwin				continue;
288205821Sedwin			}
289205821Sedwin			return (0);
290205821Sedwin		}
291205821Sedwin		return (0);
292205821Sedwin	}
293205821Sedwin	return (0);
294205821Sedwin}
295205821Sedwin
296205821Sedwinint
297205821Sedwinremember_yd(int yy, int dd, int *rm, int *rd)
298205821Sedwin{
299205821Sedwin	struct cal_year *y;
300205821Sedwin	struct cal_month *m;
301205821Sedwin	struct cal_day *d;
302205821Sedwin
303205821Sedwin	if (debug_remember)
304205821Sedwin		printf("remember_yd: %d - %d\n", yy, dd);
305205821Sedwin
306205821Sedwin	y = hyear;
307205821Sedwin	while (y != NULL) {
308205821Sedwin		if (y->year != yy) {
309205821Sedwin			y = y->nextyear;
310205821Sedwin			continue;
311205821Sedwin		}
312205821Sedwin		m = y->months;
313205821Sedwin		while (m != NULL) {
314205821Sedwin			d = m->days;
315205821Sedwin			while (d != NULL) {
316205821Sedwin				if (d->julianday == dd) {
317205821Sedwin					*rm = m->month;
318205821Sedwin					*rd = d->dayofmonth;
319205821Sedwin					return (1);
320205821Sedwin				}
321205821Sedwin				d = d->nextday;
322205821Sedwin			}
323205821Sedwin			m = m->nextmonth;
324205821Sedwin		}
325205821Sedwin		return (0);
326205821Sedwin	}
327205821Sedwin	return (0);
328205821Sedwin}
329205821Sedwin
330205821Sedwinint
331205821Sedwinfirst_dayofweek_of_year(int yy)
332205821Sedwin{
333205821Sedwin	struct cal_year *y;
334205821Sedwin
335205821Sedwin	y = hyear;
336205821Sedwin	while (y != NULL) {
337205821Sedwin		if (y->year == yy)
338205821Sedwin			return (y->firstdayofweek);
339205821Sedwin		y = y->nextyear;
340205821Sedwin	}
341205821Sedwin
342205821Sedwin	/* Should not happen */
343205821Sedwin	return (-1);
344205821Sedwin}
345205821Sedwin
346205821Sedwinint
347205821Sedwinfirst_dayofweek_of_month(int yy, int mm)
348205821Sedwin{
349205821Sedwin	struct cal_year *y;
350205821Sedwin	struct cal_month *m;
351205821Sedwin
352205821Sedwin	y = hyear;
353205821Sedwin	while (y != NULL) {
354205821Sedwin		if (y->year != yy) {
355205821Sedwin			y = y->nextyear;
356205821Sedwin			continue;
357205821Sedwin		}
358205821Sedwin		m = y->months;
359205821Sedwin		while (m != NULL) {
360205821Sedwin			if (m->month == mm)
361205821Sedwin				return (m->firstdayofweek);
362205821Sedwin			m = m->nextmonth;
363205821Sedwin		}
364205821Sedwin		/* Should not happen */
365205821Sedwin		return (-1);
366205821Sedwin	}
367205821Sedwin
368205821Sedwin	/* Should not happen */
369205821Sedwin	return (-1);
370205821Sedwin}
371205821Sedwin
372205821Sedwinint
373205821Sedwinwalkthrough_dates(struct event **e)
374205821Sedwin{
375205821Sedwin	static struct cal_year *y = NULL;
376205821Sedwin	static struct cal_month *m = NULL;
377205821Sedwin	static struct cal_day *d = NULL;
378205821Sedwin
379205821Sedwin	if (y == NULL) {
380205821Sedwin		y = hyear;
381205821Sedwin		m = y->months;
382205821Sedwin		d = m->days;
383205821Sedwin		*e = d->events;
384205821Sedwin		return (1);
385205821Sedwin	};
386205821Sedwin	if (d->nextday != NULL) {
387205821Sedwin		d = d->nextday;
388205821Sedwin		*e = d->events;
389205821Sedwin		return (1);
390205821Sedwin	}
391205821Sedwin	if (m->nextmonth != NULL) {
392205821Sedwin		m = m->nextmonth;
393205821Sedwin		d = m->days;
394205821Sedwin		*e = d->events;
395205821Sedwin		return (1);
396205821Sedwin	}
397205821Sedwin	if (y->nextyear != NULL) {
398205821Sedwin		y = y->nextyear;
399205821Sedwin		m = y->months;
400205821Sedwin		d = m->days;
401205821Sedwin		*e = d->events;
402205821Sedwin		return (1);
403205821Sedwin	}
404205821Sedwin
405205821Sedwin	return (0);
406205821Sedwin}
407205821Sedwin
408205821Sedwinstatic struct cal_day *
409205821Sedwinfind_day(int yy, int mm, int dd)
410205821Sedwin{
411205821Sedwin	struct cal_year *y;
412205821Sedwin	struct cal_month *m;
413205821Sedwin	struct cal_day *d;
414205821Sedwin
415205821Sedwin	if (debug_remember)
416205821Sedwin		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
417205821Sedwin
418205821Sedwin	y = hyear;
419205821Sedwin	while (y != NULL) {
420205821Sedwin		if (y->year != yy) {
421205821Sedwin			y = y->nextyear;
422205821Sedwin			continue;
423205821Sedwin		}
424205821Sedwin		m = y->months;
425205821Sedwin		while (m != NULL) {
426205821Sedwin			if (m->month != mm) {
427205821Sedwin				m = m->nextmonth;
428205821Sedwin				continue;
429205821Sedwin			}
430205821Sedwin			d = m->days;
431205821Sedwin			while (d != NULL) {
432205821Sedwin				if (d->dayofmonth == dd)
433205821Sedwin					return (d);
434205821Sedwin				d = d->nextday;
435205821Sedwin				continue;
436205821Sedwin			}
437205821Sedwin			return (NULL);
438205821Sedwin		}
439205821Sedwin		return (NULL);
440205821Sedwin	}
441205821Sedwin	return (NULL);
442205821Sedwin}
443205821Sedwin
444205821Sedwinvoid
445205821Sedwinaddtodate(struct event *e, int year, int month, int day)
446205821Sedwin{
447205821Sedwin	struct cal_day *d;
448205821Sedwin
449205821Sedwin	d = find_day(year, month, day);
450205821Sedwin	e->next = d->events;
451205821Sedwin	d->events = e;
452205821Sedwin}
453