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