]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcalendar/calendar.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / lib / libcalendar / calendar.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 1997 Wolfgang Helbig
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 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "calendar.h"
33
34 #ifndef NULL
35 #define NULL 0
36 #endif
37
38 /*
39  * For each month tabulate the number of days elapsed in a year before the
40  * month. This assumes the internal date representation, where a year
41  * starts on March 1st. So we don't need a special table for leap years.
42  * But we do need a special table for the year 1582, since 10 days are
43  * deleted in October. This is month1s for the switch from Julian to
44  * Gregorian calendar.
45  */
46 static int const month1[] =
47     {0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337}; 
48    /*  M   A   M   J    J    A    S    O    N    D    J */
49 static int const month1s[]=
50     {0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327}; 
51
52 typedef struct date date;
53
54 /* The last day of Julian calendar, in internal and ndays representation */
55 static int nswitch;     /* The last day of Julian calendar */
56 static date jiswitch = {1582, 7, 3};
57
58 static date     *date2idt(date *idt, date *dt);
59 static date     *idt2date(date *dt, date *idt);
60 static int       ndaysji(date *idt);
61 static int       ndaysgi(date *idt);
62 static int       firstweek(int year);
63
64 /*
65  * Compute the Julian date from the number of days elapsed since
66  * March 1st of year zero.
67  */
68 date *
69 jdate(int ndays, date *dt)
70 {
71         date    idt;            /* Internal date representation */
72         int     r;              /* hold the rest of days */
73
74         /*
75          * Compute the year by starting with an approximation not smaller
76          * than the answer and using linear search for the greatest
77          * year which does not begin after ndays.
78          */
79         idt.y = ndays / 365;
80         idt.m = 0;
81         idt.d = 0;
82         while ((r = ndaysji(&idt)) > ndays)
83                 idt.y--;
84         
85         /*
86          * Set r to the days left in the year and compute the month by
87          * linear search as the largest month that does not begin after r
88          * days.
89          */
90         r = ndays - r;
91         for (idt.m = 11; month1[idt.m] > r; idt.m--)
92                 ;
93
94         /* Compute the days left in the month */
95         idt.d = r - month1[idt.m];
96
97         /* return external representation of the date */
98         return (idt2date(dt, &idt));
99 }
100
101 /*
102  * Return the number of days since March 1st of the year zero.
103  * The date is given according to Julian calendar.
104  */
105 int
106 ndaysj(date *dt)
107 {
108         date    idt;            /* Internal date representation */
109
110         if (date2idt(&idt, dt) == NULL)
111                 return (-1);
112         else
113                 return (ndaysji(&idt));
114 }
115
116 /*
117  * Same as above, where the Julian date is given in internal notation.
118  * This formula shows the beauty of this notation.
119  */
120 static int
121 ndaysji(date * idt)
122 {
123
124         return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4);
125 }
126
127 /*
128  * Compute the date according to the Gregorian calendar from the number of
129  * days since March 1st, year zero. The date computed will be Julian if it
130  * is older than 1582-10-05. This is the reverse of the function ndaysg().
131  */
132 date   *
133 gdate(int ndays, date *dt)
134 {
135         int const *montht;      /* month-table */
136         date    idt;            /* for internal date representation */
137         int     r;              /* holds the rest of days */
138
139         /*
140          * Compute the year by starting with an approximation not smaller
141          * than the answer and search linearly for the greatest year not
142          * starting after ndays.
143          */
144         idt.y = ndays / 365;
145         idt.m = 0;
146         idt.d = 0;
147         while ((r = ndaysgi(&idt)) > ndays)
148                 idt.y--;
149
150         /*
151          * Set ndays to the number of days left and compute by linear
152          * search the greatest month which does not start after ndays. We
153          * use the table month1 which provides for each month the number
154          * of days that elapsed in the year before that month. Here the
155          * year 1582 is special, as 10 days are left out in October to
156          * resynchronize the calendar with the earth's orbit. October 4th
157          * 1582 is followed by October 15th 1582. We use the "switch"
158          * table month1s for this year.
159          */
160         ndays = ndays - r;
161         if (idt.y == 1582)
162                 montht = month1s;
163         else
164                 montht = month1;
165
166         for (idt.m = 11; montht[idt.m] > ndays; idt.m--)
167                 ;
168
169         idt.d = ndays - montht[idt.m]; /* the rest is the day in month */
170
171         /* Advance ten days deleted from October if after switch in Oct 1582 */
172         if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d)
173                 idt.d += 10;
174
175         /* return external representation of found date */
176         return (idt2date(dt, &idt));
177 }
178
179 /*
180  * Return the number of days since March 1st of the year zero. The date is
181  * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This
182  * is the reverse of gdate.
183  */
184 int
185 ndaysg(date *dt)
186 {
187         date    idt;            /* Internal date representation */
188
189         if (date2idt(&idt, dt) == NULL)
190                 return (-1);
191         return (ndaysgi(&idt));
192 }
193
194 /*
195  * Same as above, but with the Gregorian date given in internal
196  * representation.
197  */
198 static int
199 ndaysgi(date *idt)
200 {
201         int     nd;             /* Number of days--return value */
202
203         /* Cache nswitch if not already done */
204         if (nswitch == 0)
205                 nswitch = ndaysji(&jiswitch);
206
207         /*
208          * Assume Julian calendar and adapt to Gregorian if necessary, i. e.
209          * younger than nswitch. Gregori deleted
210          * the ten days from Oct 5th to Oct 14th 1582.
211          * Thereafter years which are multiples of 100 and not multiples
212          * of 400 were not leap years anymore.
213          * This makes the average length of a year
214          * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical
215          * year measures 365.2422d. So in 10000/3 years we are
216          * again one day ahead of the earth. Sigh :-)
217          * (d is the average length of a day and tropical year is the
218          * time from one spring point to the next.)
219          */
220         if ((nd = ndaysji(idt)) == -1)
221                 return (-1);
222         if (idt->y >= 1600)
223                 nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400);
224         else if (nd > nswitch)
225                 nd -= 10;
226         return (nd);
227 }
228
229 /*
230  * Compute the week number from the number of days since March 1st year 0.
231  * The weeks are numbered per year starting with 1. If the first
232  * week of a year includes at least four days of that year it is week 1,
233  * otherwise it gets the number of the last week of the previous year.
234  * The variable y will be filled with the year that contains the greater
235  * part of the week.
236  */
237 int
238 week(int nd, int *y)
239 {
240         date    dt;
241         int     fw;             /* 1st day of week 1 of previous, this and
242                                  * next year */
243         gdate(nd, &dt);
244         for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--)
245                 ;
246         return ((nd - fw) / 7 + 1);
247 }
248                 
249 /* return the first day of week 1 of year y */
250 static int
251 firstweek(int y)
252 {
253         date idt;
254         int nd, wd;
255
256         idt.y = y - 1;   /* internal representation of y-1-1 */
257         idt.m = 10;
258         idt.d = 0;
259
260         nd = ndaysgi(&idt);
261         /*
262          * If more than 3 days of this week are in the preceding year, the
263          * next week is week 1 (and the next monday is the answer),
264          * otherwise this week is week 1 and the last monday is the
265          * answer.
266          */
267         if ((wd = weekday(nd)) > 3)
268                 return (nd - wd + 7);
269         else
270                 return (nd - wd);
271 }
272
273 /* return the weekday (Mo = 0 .. Su = 6) */
274 int
275 weekday(int nd)
276 {
277         date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */
278         static int nmonday;             /* ... which is a monday        */ 
279
280         /* Cache the daynumber of one monday */
281         if (nmonday == 0)
282                 nmonday = ndaysgi(&dmondaygi);
283
284         /* return (nd - nmonday) modulo 7 which is the weekday */
285         nd = (nd - nmonday) % 7;
286         if (nd < 0)
287                 return (nd + 7);
288         else
289                 return (nd);
290 }
291
292 /*
293  * Convert a date to internal date representation: The year starts on
294  * March 1st, month and day numbering start at zero. E. g. March 1st of
295  * year zero is written as y=0, m=0, d=0.
296  */
297 static date *
298 date2idt(date *idt, date *dt)
299 {
300
301         idt->d = dt->d - 1;
302         if (dt->m > 2) {
303                 idt->m = dt->m - 3;
304                 idt->y = dt->y;
305         } else {
306                 idt->m = dt->m + 9;
307                 idt->y = dt->y - 1;
308         }
309         if (idt->m < 0 || idt->m > 11 || idt->y < 0)
310                 return (NULL);
311         else
312                 return idt;
313 }
314
315 /* Reverse of date2idt */
316 static date *
317 idt2date(date *dt, date *idt)
318 {
319
320         dt->d = idt->d + 1;
321         if (idt->m < 10) {
322                 dt->m = idt->m + 3;
323                 dt->y = idt->y;
324         } else {
325                 dt->m = idt->m - 9;
326                 dt->y = idt->y + 1;
327         }
328         if (dt->m < 1)
329                 return (NULL);
330         else
331                 return (dt);
332 }