xref: /freebsd/usr.bin/calendar/dates.c (revision 39ee7a7a6bdd1557b1c3532abf60d139798ac88b)
1 /*-
2  * Copyright (c) 1992-2009 Edwin Groothuis <edwin@FreeBSD.org>.
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  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <err.h>
34 #include <time.h>
35 
36 #include "calendar.h"
37 
38 struct cal_year {
39 	int year;	/* 19xx, 20xx, 21xx */
40 	int easter;	/* Julian day */
41 	int paskha;	/* Julian day */
42 	int cny;	/* Julian day */
43 	int firstdayofweek; /* 0 .. 6 */
44 	struct cal_month *months;
45 	struct cal_year	*nextyear;
46 };
47 
48 struct cal_month {
49 	int month;			/* 01 .. 12 */
50 	int firstdayjulian;		/* 000 .. 366 */
51 	int firstdayofweek;		/* 0 .. 6 */
52 	struct cal_year *year;		/* points back */
53 	struct cal_day *days;
54 	struct cal_month *nextmonth;
55 };
56 
57 struct cal_day {
58 	int dayofmonth;			/* 01 .. 31 */
59 	int julianday;			/* 000 .. 366 */
60 	int dayofweek;			/* 0 .. 6 */
61 	struct cal_day *nextday;
62 	struct cal_month *month;	/* points back */
63 	struct cal_year	*year;		/* points back */
64 	struct event *events;
65 };
66 
67 int debug_remember = 0;
68 static struct cal_year *hyear = NULL;
69 
70 /* 1-based month, 0-based days, cumulative */
71 int	cumdaytab[][14] = {
72 	{0, -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
73 	{0, -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
74 };
75 /* 1-based month, individual */
76 static int *monthdays;
77 int	monthdaytab[][14] = {
78 	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
79 	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 30},
80 };
81 
82 static struct cal_day *	find_day(int yy, int mm, int dd);
83 
84 static void
85 createdate(int y, int m, int d)
86 {
87 	struct cal_year *py, *pyp;
88 	struct cal_month *pm, *pmp;
89 	struct cal_day *pd, *pdp;
90 	int *cumday;
91 
92 	pyp = NULL;
93 	py = hyear;
94 	while (py != NULL) {
95 		if (py->year == y + 1900)
96 			break;
97 		pyp = py;
98 		py = py->nextyear;
99 	}
100 
101 	if (py == NULL) {
102 		struct tm td;
103 		time_t t;
104 		py = (struct cal_year *)calloc(1, sizeof(struct cal_year));
105 		py->year = y + 1900;
106 		py->easter = easter(y);
107 		py->paskha = paskha(y);
108 
109 		td = tm0;
110 		td.tm_year = y;
111 		td.tm_mday = 1;
112 		t = mktime(&td);
113 		localtime_r(&t, &td);
114 		py->firstdayofweek = td.tm_wday;
115 
116 		if (pyp != NULL)
117 			pyp->nextyear = py;
118 	}
119 	if (pyp == NULL) {
120 		/* The very very very first one */
121 		hyear = py;
122 	}
123 
124 	pmp = NULL;
125 	pm = py->months;
126 	while (pm != NULL) {
127 		if (pm->month == m)
128 			break;
129 		pmp = pm;
130 		pm = pm->nextmonth;
131 	}
132 
133 	if (pm == NULL) {
134 		pm = (struct cal_month *)calloc(1, sizeof(struct cal_month));
135 		pm->year = py;
136 		pm->month = m;
137 		cumday = cumdaytab[isleap(y)];
138 		pm->firstdayjulian = cumday[m] + 2;
139 		pm->firstdayofweek =
140 		    (py->firstdayofweek + pm->firstdayjulian -1) % 7;
141 		if (pmp != NULL)
142 			pmp->nextmonth = pm;
143 	}
144 	if (pmp == NULL)
145 		py->months = pm;
146 
147 	pdp = NULL;
148 	pd = pm->days;
149 	while (pd != NULL) {
150 		pdp = pd;
151 		pd = pd->nextday;
152 	}
153 
154 	if (pd == NULL) {	/* Always true */
155 		pd = (struct cal_day *)calloc(1, sizeof(struct cal_day));
156 		pd->month = pm;
157 		pd->year = py;
158 		pd->dayofmonth = d;
159 		pd->julianday = pm->firstdayjulian + d - 1;
160 		pd->dayofweek = (pm->firstdayofweek + d - 1) % 7;
161 		if (pdp != NULL)
162 			pdp->nextday = pd;
163 	}
164 	if (pdp == NULL)
165 		pm->days = pd;
166 }
167 
168 void
169 generatedates(struct tm *tp1, struct tm *tp2)
170 {
171 	int y1, m1, d1;
172 	int y2, m2, d2;
173 	int y, m, d;
174 
175 	y1 = tp1->tm_year;
176 	m1 = tp1->tm_mon + 1;
177 	d1 = tp1->tm_mday;
178 	y2 = tp2->tm_year;
179 	m2 = tp2->tm_mon + 1;
180 	d2 = tp2->tm_mday;
181 
182 	if (y1 == y2) {
183 		if (m1 == m2) {
184 			/* Same year, same month. Easy! */
185 			for (d = d1; d <= d2; d++)
186 				createdate(y1, m1, d);
187 			return;
188 		}
189 		/*
190 		 * Same year, different month.
191 		 * - Take the leftover days from m1
192 		 * - Take all days from <m1 .. m2>
193 		 * - Take the first days from m2
194 		 */
195 		monthdays = monthdaytab[isleap(y1)];
196 		for (d = d1; d <= monthdays[m1]; d++)
197 			createdate(y1, m1, d);
198 		for (m = m1 + 1; m < m2; m++)
199 			for (d = 1; d <= monthdays[m]; d++)
200 				createdate(y1, m, d);
201 		for (d = 1; d <= d2; d++)
202 			createdate(y1, m2, d);
203 		return;
204 	}
205 	/*
206 	 * Different year, different month.
207 	 * - Take the leftover days from y1-m1
208 	 * - Take all days from y1-<m1 .. 12]
209 	 * - Take all days from <y1 .. y2>
210 	 * - Take all days from y2-[1 .. m2>
211 	 * - Take the first days of y2-m2
212 	 */
213 	monthdays = monthdaytab[isleap(y1)];
214 	for (d = d1; d <= monthdays[m1]; d++)
215 		createdate(y1, m1, d);
216 	for (m = m1 + 1; m <= 12; m++)
217 		for (d = 1; d <= monthdays[m]; d++)
218 			createdate(y1, m, d);
219 	for (y = y1 + 1; y < y2; y++) {
220 		monthdays = monthdaytab[isleap(y)];
221 		for (m = 1; m <= 12; m++)
222 			for (d = 1; d <= monthdays[m]; d++)
223 				createdate(y, m, d);
224 	}
225 	monthdays = monthdaytab[isleap(y2)];
226 	for (m = 1; m < m2; m++)
227 		for (d = 1; d <= monthdays[m]; d++)
228 			createdate(y2, m, d);
229 	for (d = 1; d <= d2; d++)
230 		createdate(y2, m2, d);
231 }
232 
233 void
234 dumpdates(void)
235 {
236 	struct cal_year *y;
237 	struct cal_month *m;
238 	struct cal_day *d;
239 
240 	y = hyear;
241 	while (y != NULL) {
242 		printf("%-5d (wday:%d)\n", y->year, y->firstdayofweek);
243 		m = y->months;
244 		while (m != NULL) {
245 			printf("-- %-5d (julian:%d, dow:%d)\n", m->month,
246 			    m->firstdayjulian, m->firstdayofweek);
247 			d = m->days;
248 			while (d != NULL) {
249 				printf("  -- %-5d (julian:%d, dow:%d)\n",
250 				    d->dayofmonth, d->julianday, d->dayofweek);
251 				d = d->nextday;
252 			}
253 			m = m->nextmonth;
254 		}
255 		y = y->nextyear;
256 	}
257 }
258 
259 int
260 remember_ymd(int yy, int mm, int dd)
261 {
262 	struct cal_year *y;
263 	struct cal_month *m;
264 	struct cal_day *d;
265 
266 	if (debug_remember)
267 		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
268 
269 	y = hyear;
270 	while (y != NULL) {
271 		if (y->year != yy) {
272 			y = y->nextyear;
273 			continue;
274 		}
275 		m = y->months;
276 		while (m != NULL) {
277 			if (m->month != mm) {
278 				m = m->nextmonth;
279 				continue;
280 			}
281 			d = m->days;
282 			while (d != NULL) {
283 				if (d->dayofmonth == dd)
284 					return (1);
285 				d = d->nextday;
286 				continue;
287 			}
288 			return (0);
289 		}
290 		return (0);
291 	}
292 	return (0);
293 }
294 
295 int
296 remember_yd(int yy, int dd, int *rm, int *rd)
297 {
298 	struct cal_year *y;
299 	struct cal_month *m;
300 	struct cal_day *d;
301 
302 	if (debug_remember)
303 		printf("remember_yd: %d - %d\n", yy, dd);
304 
305 	y = hyear;
306 	while (y != NULL) {
307 		if (y->year != yy) {
308 			y = y->nextyear;
309 			continue;
310 		}
311 		m = y->months;
312 		while (m != NULL) {
313 			d = m->days;
314 			while (d != NULL) {
315 				if (d->julianday == dd) {
316 					*rm = m->month;
317 					*rd = d->dayofmonth;
318 					return (1);
319 				}
320 				d = d->nextday;
321 			}
322 			m = m->nextmonth;
323 		}
324 		return (0);
325 	}
326 	return (0);
327 }
328 
329 int
330 first_dayofweek_of_year(int yy)
331 {
332 	struct cal_year *y;
333 
334 	y = hyear;
335 	while (y != NULL) {
336 		if (y->year == yy)
337 			return (y->firstdayofweek);
338 		y = y->nextyear;
339 	}
340 
341 	/* Should not happen */
342 	return (-1);
343 }
344 
345 int
346 first_dayofweek_of_month(int yy, int mm)
347 {
348 	struct cal_year *y;
349 	struct cal_month *m;
350 
351 	y = hyear;
352 	while (y != NULL) {
353 		if (y->year != yy) {
354 			y = y->nextyear;
355 			continue;
356 		}
357 		m = y->months;
358 		while (m != NULL) {
359 			if (m->month == mm)
360 				return (m->firstdayofweek);
361 			m = m->nextmonth;
362 		}
363 		/* No data for this month */
364 		return (-1);
365 	}
366 
367 	/* No data for this year.  Error? */
368         return (-1);
369 }
370 
371 int
372 walkthrough_dates(struct event **e)
373 {
374 	static struct cal_year *y = NULL;
375 	static struct cal_month *m = NULL;
376 	static struct cal_day *d = NULL;
377 
378 	if (y == NULL) {
379 		y = hyear;
380 		m = y->months;
381 		d = m->days;
382 		*e = d->events;
383 		return (1);
384 	};
385 	if (d->nextday != NULL) {
386 		d = d->nextday;
387 		*e = d->events;
388 		return (1);
389 	}
390 	if (m->nextmonth != NULL) {
391 		m = m->nextmonth;
392 		d = m->days;
393 		*e = d->events;
394 		return (1);
395 	}
396 	if (y->nextyear != NULL) {
397 		y = y->nextyear;
398 		m = y->months;
399 		d = m->days;
400 		*e = d->events;
401 		return (1);
402 	}
403 
404 	return (0);
405 }
406 
407 static struct cal_day *
408 find_day(int yy, int mm, int dd)
409 {
410 	struct cal_year *y;
411 	struct cal_month *m;
412 	struct cal_day *d;
413 
414 	if (debug_remember)
415 		printf("remember_ymd: %d - %d - %d\n", yy, mm, dd);
416 
417 	y = hyear;
418 	while (y != NULL) {
419 		if (y->year != yy) {
420 			y = y->nextyear;
421 			continue;
422 		}
423 		m = y->months;
424 		while (m != NULL) {
425 			if (m->month != mm) {
426 				m = m->nextmonth;
427 				continue;
428 			}
429 			d = m->days;
430 			while (d != NULL) {
431 				if (d->dayofmonth == dd)
432 					return (d);
433 				d = d->nextday;
434 				continue;
435 			}
436 			return (NULL);
437 		}
438 		return (NULL);
439 	}
440 	return (NULL);
441 }
442 
443 void
444 addtodate(struct event *e, int year, int month, int day)
445 {
446 	struct cal_day *d;
447 
448 	d = find_day(year, month, day);
449 	e->next = d->events;
450 	d->events = e;
451 }
452