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