dates.c revision 251647
1193326Sed/*-
2193326Sed * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
3193326Sed * All rights reserved.
4193326Sed *
5193326Sed * Redistribution and use in source and binary forms, with or without
6193326Sed * modification, are permitted provided that the following conditions
7193326Sed * are met:
8193326Sed * 1. Redistributions of source code must retain the above copyright
9193326Sed *    notice, this list of conditions and the following disclaimer.
10193326Sed * 2. Redistributions in binary form must reproduce the above copyright
11193326Sed *    notice, this list of conditions and the following disclaimer in the
12193326Sed *    documentation and/or other materials provided with the distribution.
13193326Sed *
14193326Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16193326Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17249423Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18249423Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19249423Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20263508Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21249423Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24193326Sed * SUCH DAMAGE.
25249423Sdim *
26218893Sdim */
27263508Sdim
28193326Sed#include <sys/cdefs.h>
29249423Sdim__FBSDID("$FreeBSD: head/usr.bin/calendar/dates.c 251647 2013-06-12 07:52:49Z grog $");
30224145Sdim
31193326Sed#include <stdio.h>
32193326Sed#include <stdlib.h>
33249423Sdim#include <err.h>
34193326Sed#include <time.h>
35193326Sed
36193326Sed#include "calendar.h"
37193326Sed
38198092Srdivackystruct cal_year {
39207619Srdivacky	int year;	/* 19xx, 20xx, 21xx */
40193326Sed	int easter;	/* Julian day */
41193326Sed	int paskha;	/* Julian day */
42198398Srdivacky	int cny;	/* Julian day */
43193326Sed	int firstdayofweek; /* 0 .. 6 */
44210299Sed	struct cal_month *months;
45193326Sed	struct cal_year	*nextyear;
46193326Sed};
47193326Sed
48193326Sedstruct cal_month {
49234353Sdim	int month;			/* 01 .. 12 */
50193326Sed	int firstdayjulian;		/* 000 .. 366 */
51221345Sdim	int firstdayofweek;		/* 0 .. 6 */
52198092Srdivacky	struct cal_year *year;		/* points back */
53193326Sed	struct cal_day *days;
54218893Sdim	struct cal_month *nextmonth;
55193326Sed};
56193326Sed
57193326Sedstruct cal_day {
58193326Sed	int dayofmonth;			/* 01 .. 31 */
59193326Sed	int julianday;			/* 000 .. 366 */
60193326Sed	int dayofweek;			/* 0 .. 6 */
61193326Sed	struct cal_day *nextday;
62193326Sed	struct cal_month *month;	/* points back */
63193326Sed	struct cal_year	*year;		/* points back */
64193326Sed	struct event *events;
65193326Sed};
66204643Srdivacky
67193326Sedint debug_remember = 0;
68193326Sedstatic struct cal_year *hyear = NULL;
69193326Sed
70193326Sed/* 1-based month, 0-based days, cumulative */
71193326Sedint	cumdaytab[][14] = {
72224145Sdim	{0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
73193326Sed	{0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
74193326Sed};
75193326Sed/* 1-based month, individual */
76193326Sedstatic int *monthdays;
77193326Sedint	monthdaytab[][14] = {
78208600Srdivacky	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
79212904Sdim	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
80218893Sdim};
81218893Sdim
82193326Sedstatic struct cal_day *	find_day(int yy, int mm, int dd);
83249423Sdim
84249423Sdimstatic void
85249423Sdimcreatedate(int y, int m, int d)
86249423Sdim{
87249423Sdim	struct cal_year *py, *pyp;
88249423Sdim	struct cal_month *pm, *pmp;
89249423Sdim	struct cal_day *pd, *pdp;
90249423Sdim	int *cumday;
91249423Sdim
92249423Sdim	pyp = NULL;
93249423Sdim	py = hyear;
94193326Sed	while (py != NULL) {
95193326Sed		if (py->year == y + 1900)
96218893Sdim			break;
97243830Sdim		pyp = py;
98243830Sdim		py = py->nextyear;
99212904Sdim	}
100212904Sdim
101193326Sed	if (py == NULL) {
102212904Sdim		struct tm td;
103212904Sdim		time_t t;
104210299Sed		py = (struct cal_year *)calloc(1, sizeof(struct cal_year));
105212904Sdim		py->year = y + 1900;
106212904Sdim		py->easter = easter(y);
107212904Sdim		py->paskha = paskha(y);
108212904Sdim
109212904Sdim		td = tm0;
110212904Sdim		td.tm_year = y;
111212904Sdim		td.tm_mday = 1;
112212904Sdim		t = mktime(&td);
113212904Sdim		localtime_r(&t, &td);
114212904Sdim		py->firstdayofweek = td.tm_wday;
115218893Sdim
116249423Sdim		if (pyp != NULL)
117249423Sdim			pyp->nextyear = py;
118249423Sdim	}
119249423Sdim	if (pyp == NULL) {
120249423Sdim		/* The very very very first one */
121212904Sdim		hyear = py;
122210299Sed	}
123210299Sed
124212904Sdim	pmp = NULL;
125210299Sed	pm = py->months;
126210299Sed	while (pm != NULL) {
127193326Sed		if (pm->month == m)
128199482Srdivacky			break;
129193326Sed		pmp = pm;
130193326Sed		pm = pm->nextmonth;
131193326Sed	}
132193326Sed
133251662Sdim	if (pm == NULL) {
134251662Sdim		pm = (struct cal_month *)calloc(1, sizeof(struct cal_month));
135193326Sed		pm->year = py;
136193326Sed		pm->month = m;
137193326Sed		cumday = cumdaytab[isleap(y)];
138193326Sed		pm->firstdayjulian = cumday[m] + 2;
139193326Sed		pm->firstdayofweek =
140193326Sed		    (py->firstdayofweek + pm->firstdayjulian -1) % 7;
141193326Sed		if (pmp != NULL)
142200583Srdivacky			pmp->nextmonth = pm;
143200583Srdivacky	}
144200583Srdivacky	if (pmp == NULL)
145224145Sdim		py->months = pm;
146224145Sdim
147224145Sdim	pdp = NULL;
148224145Sdim	pd = pm->days;
149193326Sed	while (pd != NULL) {
150210299Sed		pdp = pd;
151210299Sed		pd = pd->nextday;
152193326Sed	}
153193326Sed
154200583Srdivacky	if (pd == NULL) {	/* Always true */
155193326Sed		pd = (struct cal_day *)calloc(1, sizeof(struct cal_day));
156193326Sed		pd->month = pm;
157193326Sed		pd->year = py;
158193326Sed		pd->dayofmonth = d;
159193326Sed		pd->julianday = pm->firstdayjulian + d - 1;
160263508Sdim		pd->dayofweek = (pm->firstdayofweek + d - 1) % 7;
161263508Sdim		if (pdp != NULL)
162263508Sdim			pdp->nextday = pd;
163263508Sdim	}
164263508Sdim	if (pdp == NULL)
165263508Sdim		pm->days = pd;
166263508Sdim}
167263508Sdim
168263508Sdimvoid
169263508Sdimgeneratedates(struct tm *tp1, struct tm *tp2)
170263508Sdim{
171263508Sdim	int y1, m1, d1;
172263508Sdim	int y2, m2, d2;
173263508Sdim	int y, m, d;
174263508Sdim
175263508Sdim	y1 = tp1->tm_year;
176263508Sdim	m1 = tp1->tm_mon + 1;
177263508Sdim	d1 = tp1->tm_mday;
178263508Sdim	y2 = tp2->tm_year;
179263508Sdim	m2 = tp2->tm_mon + 1;
180263508Sdim	d2 = tp2->tm_mday;
181263508Sdim
182263508Sdim	if (y1 == y2) {
183263508Sdim		if (m1 == m2) {
184263508Sdim			/* Same year, same month. Easy! */
185263508Sdim			for (d = d1; d <= d2; d++)
186263508Sdim				createdate(y1, m1, d);
187263508Sdim			return;
188263508Sdim		}
189263508Sdim		/*
190263508Sdim		 * Same year, different month.
191263508Sdim		 * - Take the leftover days from m1
192263508Sdim		 * - Take all days from <m1 .. m2>
193263508Sdim		 * - Take the first days from m2
194263508Sdim		 */
195263508Sdim		monthdays = monthdaytab[isleap(y1)];
196263508Sdim		for (d = d1; d <= monthdays[m1]; d++)
197263508Sdim			createdate(y1, m1, d);
198263508Sdim		for (m = m1 + 1; m < m2; m++)
199263508Sdim			for (d = 1; d <= monthdays[m]; d++)
200263508Sdim				createdate(y1, m, d);
201263508Sdim		for (d = 1; d <= d2; d++)
202263508Sdim			createdate(y1, m2, d);
203263508Sdim		return;
204263508Sdim	}
205263508Sdim	/*
206263508Sdim	 * Different year, different month.
207263508Sdim	 * - Take the leftover days from y1-m1
208263508Sdim	 * - Take all days from y1-<m1 .. 12]
209263508Sdim	 * - Take all days from <y1 .. y2>
210263508Sdim	 * - Take all days from y2-[1 .. m2>
211263508Sdim	 * - Take the first days of y2-m2
212263508Sdim	 */
213263508Sdim	monthdays = monthdaytab[isleap(y1)];
214263508Sdim	for (d = d1; d <= monthdays[m1]; d++)
215263508Sdim		createdate(y1, m1, d);
216263508Sdim	for (m = m1 + 1; m <= 12; m++)
217263508Sdim		for (d = 1; d <= monthdays[m]; d++)
218263508Sdim			createdate(y1, m, d);
219239462Sdim	for (y = y1 + 1; y < y2; y++) {
220239462Sdim		monthdays = monthdaytab[isleap(y)];
221239462Sdim		for (m = 1; m <= 12; m++)
222239462Sdim			for (d = 1; d <= monthdays[m]; d++)
223243830Sdim				createdate(y, m, d);
224243830Sdim	}
225243830Sdim	monthdays = monthdaytab[isleap(y2)];
226193326Sed	for (m = 1; m < m2; m++)
227249423Sdim		for (d = 1; d <= monthdays[m]; d++)
228249423Sdim			createdate(y2, m, d);
229249423Sdim	for (d = 1; d <= d2; d++)
230224145Sdim		createdate(y2, m2, d);
231224145Sdim}
232224145Sdim
233218893Sdimvoid
234218893Sdimdumpdates(void)
235218893Sdim{
236234353Sdim	struct cal_year *y;
237234353Sdim	struct cal_month *m;
238234353Sdim	struct cal_day *d;
239208600Srdivacky
240208600Srdivacky	y = hyear;
241208600Srdivacky	while (y != NULL) {
242193326Sed		printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek);
243210299Sed		m = y->months;
244263508Sdim		while (m != NULL) {
245193326Sed			printf("-- %-5d (julian:%d, dow:%d)\n", m->month,
246263508Sdim			    m->firstdayjulian, m->firstdayofweek);
247263508Sdim			d = m->days;
248263508Sdim			while (d != NULL) {
249263508Sdim				printf("  -- %-5d (julian:%d, dow:%d)\n",
250263508Sdim				    d->dayofmonth, d->julianday, d->dayofweek);
251263508Sdim				d = d->nextday;
252263508Sdim			}
253263508Sdim			m = m->nextmonth;
254263508Sdim		}
255263508Sdim		y = y->nextyear;
256263508Sdim	}
257212904Sdim}
258212904Sdim
259212904Sdimint
260212904Sdimremember_ymd(int yy, int mm, int dd)
261212904Sdim{
262234353Sdim	struct cal_year *y;
263234353Sdim	struct cal_month *m;
264234353Sdim	struct cal_day *d;
265226633Sdim
266226633Sdim	if (debug_remember)
267226633Sdim		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
268226633Sdim
269226633Sdim	y = hyear;
270210299Sed	while (y != NULL) {
271193326Sed		if (y->year != yy) {
272226633Sdim			y = y->nextyear;
273226633Sdim			continue;
274223017Sdim		}
275223017Sdim		m = y->months;
276210299Sed		while (m != NULL) {
277210299Sed			if (m->month != mm) {
278193326Sed				m = m->nextmonth;
279210299Sed				continue;
280193326Sed			}
281218893Sdim			d = m->days;
282218893Sdim			while (d != NULL) {
283218893Sdim				if (d->dayofmonth == dd)
284218893Sdim					return (1);
285218893Sdim				d = d->nextday;
286210299Sed				continue;
287210299Sed			}
288210299Sed			return (0);
289226633Sdim		}
290200583Srdivacky		return (0);
291224145Sdim	}
292224145Sdim	return (0);
293224145Sdim}
294224145Sdim
295224145Sdimint
296224145Sdimremember_yd(int yy, int dd, int *rm, int *rd)
297224145Sdim{
298224145Sdim	struct cal_year *y;
299224145Sdim	struct cal_month *m;
300224145Sdim	struct cal_day *d;
301224145Sdim
302224145Sdim	if (debug_remember)
303224145Sdim		printf("remember_yd: %d - %d\n", yy, dd);
304224145Sdim
305224145Sdim	y = hyear;
306224145Sdim	while (y != NULL) {
307224145Sdim		if (y->year != yy) {
308224145Sdim			y = y->nextyear;
309224145Sdim			continue;
310224145Sdim		}
311224145Sdim		m = y->months;
312193326Sed		while (m != NULL) {
313193326Sed			d = m->days;
314218893Sdim			while (d != NULL) {
315218893Sdim				if (d->julianday == dd) {
316218893Sdim					*rm = m->month;
317218893Sdim					*rd = d->dayofmonth;
318218893Sdim					return (1);
319218893Sdim				}
320218893Sdim				d = d->nextday;
321224145Sdim			}
322224145Sdim			m = m->nextmonth;
323218893Sdim		}
324218893Sdim		return (0);
325218893Sdim	}
326218893Sdim	return (0);
327218893Sdim}
328218893Sdim
329218893Sdimint
330218893Sdimfirst_dayofweek_of_year(int yy)
331218893Sdim{
332218893Sdim	struct cal_year *y;
333218893Sdim
334218893Sdim	y = hyear;
335218893Sdim	while (y != NULL) {
336218893Sdim		if (y->year == yy)
337218893Sdim			return (y->firstdayofweek);
338224145Sdim		y = y->nextyear;
339224145Sdim	}
340218893Sdim
341218893Sdim	/* Should not happen */
342218893Sdim	return (-1);
343218893Sdim}
344218893Sdim
345218893Sdimint
346218893Sdimfirst_dayofweek_of_month(int yy, int mm)
347218893Sdim{
348218893Sdim	struct cal_year *y;
349224145Sdim	struct cal_month *m;
350224145Sdim
351224145Sdim	y = hyear;
352224145Sdim	while (y != NULL) {
353224145Sdim		if (y->year != yy) {
354224145Sdim			y = y->nextyear;
355224145Sdim			continue;
356224145Sdim		}
357224145Sdim		m = y->months;
358224145Sdim		while (m != NULL) {
359224145Sdim			if (m->month == mm)
360224145Sdim				return (m->firstdayofweek);
361224145Sdim			m = m->nextmonth;
362224145Sdim		}
363224145Sdim		/* No data for this month */
364224145Sdim		return (-1);
365224145Sdim	}
366224145Sdim
367224145Sdim	/* No data for this year.  Error? */
368224145Sdim        return (-1);
369224145Sdim}
370224145Sdim
371224145Sdimint
372224145Sdimwalkthrough_dates(struct event **e)
373224145Sdim{
374224145Sdim	static struct cal_year *y = NULL;
375224145Sdim	static struct cal_month *m = NULL;
376224145Sdim	static struct cal_day *d = NULL;
377224145Sdim
378224145Sdim	if (y == NULL) {
379224145Sdim		y = hyear;
380224145Sdim		m = y->months;
381224145Sdim		d = m->days;
382224145Sdim		*e = d->events;
383224145Sdim		return (1);
384224145Sdim	};
385224145Sdim	if (d->nextday != NULL) {
386224145Sdim		d = d->nextday;
387224145Sdim		*e = d->events;
388224145Sdim		return (1);
389224145Sdim	}
390224145Sdim	if (m->nextmonth != NULL) {
391263508Sdim		m = m->nextmonth;
392263508Sdim		d = m->days;
393263508Sdim		*e = d->events;
394263508Sdim		return (1);
395263508Sdim	}
396263508Sdim	if (y->nextyear != NULL) {
397263508Sdim		y = y->nextyear;
398263508Sdim		m = y->months;
399263508Sdim		d = m->days;
400263508Sdim		*e = d->events;
401263508Sdim		return (1);
402263508Sdim	}
403263508Sdim
404263508Sdim	return (0);
405263508Sdim}
406263508Sdim
407263508Sdimstatic struct cal_day *
408234353Sdimfind_day(int yy, int mm, int dd)
409234353Sdim{
410234353Sdim	struct cal_year *y;
411234353Sdim	struct cal_month *m;
412210299Sed	struct cal_day *d;
413210299Sed
414210299Sed	if (debug_remember)
415210299Sed		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
416210299Sed
417193326Sed	y = hyear;
418212904Sdim	while (y != NULL) {
419212904Sdim		if (y->year != yy) {
420212904Sdim			y = y->nextyear;
421212904Sdim			continue;
422212904Sdim		}
423212904Sdim		m = y->months;
424210299Sed		while (m != NULL) {
425210299Sed			if (m->month != mm) {
426263508Sdim				m = m->nextmonth;
427210299Sed				continue;
428218893Sdim			}
429218893Sdim			d = m->days;
430218893Sdim			while (d != NULL) {
431234353Sdim				if (d->dayofmonth == dd)
432234353Sdim					return (d);
433234353Sdim				d = d->nextday;
434234353Sdim				continue;
435234353Sdim			}
436234353Sdim			return (NULL);
437234353Sdim		}
438193326Sed		return (NULL);
439218893Sdim	}
440218893Sdim	return (NULL);
441234353Sdim}
442234353Sdim
443234353Sdimvoid
444234353Sdimaddtodate(struct event *e, int year, int month, int day)
445234353Sdim{
446234353Sdim	struct cal_day *d;
447234353Sdim
448218893Sdim	d = find_day(year, month, day);
449210299Sed	e->next = d->events;
450210299Sed	d->events = e;
451210299Sed}
452210299Sed