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