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