Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libcalendar / calendar.c
1 /*-
2  * Copyright (c) 1997 Wolfgang Helbig
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/lib/libcalendar/calendar.c,v 1.3 1999/08/28 00:04:03 peter Exp $
27  */
28
29 #include "calendar.h"
30
31 #ifndef NULL
32 #define NULL 0
33 #endif
34
35 /*
36  * For each month tabulate the number of days elapsed in a year before the
37  * month. This assumes the internal date representation, where a year
38  * starts on March 1st. So we don't need a special table for leap years.
39  * But we do need a special table for the year 1582, since 10 days are
40  * deleted in October. This is month1s for the switch from Julian to
41  * Gregorian calendar.
42  */
43 static int const month1[] =
44     {0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337}; 
45    /*  M   A   M   J    J    A    S    O    N    D    J */
46 static int const month1s[]=
47     {0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327}; 
48
49 typedef struct date date;
50
51 /* The last day of Julian calendar, in internal and ndays representation */
52 static int nswitch;     /* The last day of Julian calendar */
53 static date jiswitch = {1582, 7, 3};
54
55 static date     *date2idt(date *idt, date *dt);
56 static date     *idt2date(date *dt, date *idt);
57 static int       ndaysji(date *idt);
58 static int       ndaysgi(date *idt);
59 static int       firstweek(int year);
60
61 /*
62  * Compute the Julian date from the number of days elapsed since
63  * March 1st of year zero.
64  */
65 date *
66 jdate(int ndays, date *dt)
67 {
68         date    idt;            /* Internal date representation */
69         int     r;              /* hold the rest of days */
70
71         /*
72          * Compute the year by starting with an approximation not smaller
73          * than the answer and using linear search for the greatest
74          * year which does not begin after ndays.
75          */
76         idt.y = ndays / 365;
77         idt.m = 0;
78         idt.d = 0;
79         while ((r = ndaysji(&idt)) > ndays)
80                 idt.y--;
81         
82         /*
83          * Set r to the days left in the year and compute the month by
84          * linear search as the largest month that does not begin after r
85          * days.
86          */
87         r = ndays - r;
88         for (idt.m = 11; month1[idt.m] > r; idt.m--)
89                 ;
90
91         /* Compute the days left in the month */
92         idt.d = r - month1[idt.m];
93
94         /* return external representation of the date */
95         return (idt2date(dt, &idt));
96 }
97
98 /*
99  * Return the number of days since March 1st of the year zero.
100  * The date is given according to Julian calendar.
101  */
102 int
103 ndaysj(date *dt)
104 {
105         date    idt;            /* Internal date representation */
106
107         if (date2idt(&idt, dt) == NULL)
108                 return (-1);
109         else
110                 return (ndaysji(&idt));
111 }
112
113 /*
114  * Same as above, where the Julian date is given in internal notation.
115  * This formula shows the beauty of this notation.
116  */
117 static int
118 ndaysji(date * idt)
119 {
120
121         return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4);
122 }
123
124 /*
125  * Compute the date according to the Gregorian calendar from the number of
126  * days since March 1st, year zero. The date computed will be Julian if it
127  * is older than 1582-10-05. This is the reverse of the function ndaysg().
128  */
129 date   *
130 gdate(int ndays, date *dt)
131 {
132         int const *montht;      /* month-table */
133         date    idt;            /* for internal date representation */
134         int     r;              /* holds the rest of days */
135
136         /*
137          * Compute the year by starting with an approximation not smaller
138          * than the answer and search linearly for the greatest year not
139          * starting after ndays.
140          */
141         idt.y = ndays / 365;
142         idt.m = 0;
143         idt.d = 0;
144         while ((r = ndaysgi(&idt)) > ndays)
145                 idt.y--;
146
147         /*
148          * Set ndays to the number of days left and compute by linear
149          * search the greatest month which does not start after ndays. We
150          * use the table month1 which provides for each month the number
151          * of days that elapsed in the year before that month. Here the
152          * year 1582 is special, as 10 days are left out in October to
153          * resynchronize the calendar with the earth's orbit. October 4th
154          * 1582 is followed by October 15th 1582. We use the "switch"
155          * table month1s for this year.
156          */
157         ndays = ndays - r;
158         if (idt.y == 1582)
159                 montht = month1s;
160         else
161                 montht = month1;
162
163         for (idt.m = 11; montht[idt.m] > ndays; idt.m--)
164                 ;
165
166         idt.d = ndays - montht[idt.m]; /* the rest is the day in month */
167
168         /* Advance ten days deleted from October if after switch in Oct 1582 */
169         if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d)
170                 idt.d += 10;
171
172         /* return external representation of found date */
173         return (idt2date(dt, &idt));
174 }
175
176 /*
177  * Return the number of days since March 1st of the year zero. The date is
178  * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This
179  * is the reverse of gdate.
180  */
181 int
182 ndaysg(date *dt)
183 {
184         date    idt;            /* Internal date representation */
185
186         if (date2idt(&idt, dt) == NULL)
187                 return (-1);
188         return (ndaysgi(&idt));
189 }
190
191 /*
192  * Same as above, but with the Gregorian date given in internal
193  * representation.
194  */
195 static int
196 ndaysgi(date *idt)
197 {
198         int     nd;             /* Number of days--return value */
199
200         /* Cache nswitch if not already done */
201         if (nswitch == 0)
202                 nswitch = ndaysji(&jiswitch);
203
204         /*
205          * Assume Julian calendar and adapt to Gregorian if necessary, i. e.
206          * younger than nswitch. Gregori deleted
207          * the ten days from Oct 5th to Oct 14th 1582.
208          * Thereafter years which are multiples of 100 and not multiples
209          * of 400 were not leap years anymore.
210          * This makes the average length of a year
211          * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical
212          * year measures 365.2422d. So in 10000/3 years we are
213          * again one day ahead of the earth. Sigh :-)
214          * (d is the average length of a day and tropical year is the
215          * time from one spring point to the next.)
216          */
217         if ((nd = ndaysji(idt)) == -1)
218                 return (-1);
219         if (idt->y >= 1600)
220                 nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400);
221         else if (nd > nswitch)
222                 nd -= 10;
223         return (nd);
224 }
225
226 /*
227  * Compute the week number from the number of days since March 1st year 0.
228  * The weeks are numbered per year starting with 1. If the first
229  * week of a year includes at least four days of that year it is week 1,
230  * otherwise it gets the number of the last week of the previous year.
231  * The variable y will be filled with the year that contains the greater
232  * part of the week.
233  */
234 int
235 week(int nd, int *y)
236 {
237         date    dt;
238         int     fw;             /* 1st day of week 1 of previous, this and
239                                  * next year */
240         gdate(nd, &dt);
241         for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--)
242                 ;
243         return ((nd - fw) / 7 + 1);
244 }
245                 
246 /* return the first day of week 1 of year y */
247 static int
248 firstweek(int y)
249 {
250         date idt;
251         int nd, wd;
252
253         idt.y = y - 1;   /* internal representation of y-1-1 */
254         idt.m = 10;
255         idt.d = 0;
256
257         nd = ndaysgi(&idt);
258         /*
259          * If more than 3 days of this week are in the preceding year, the
260          * next week is week 1 (and the next monday is the answer),
261          * otherwise this week is week 1 and the last monday is the
262          * answer.
263          */
264         if ((wd = weekday(nd)) > 3)
265                 return (nd - wd + 7);
266         else
267                 return (nd - wd);
268 }
269
270 /* return the weekday (Mo = 0 .. Su = 6) */
271 int
272 weekday(int nd)
273 {
274         date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */
275         static int nmonday;             /* ... which is a monday        */ 
276
277         /* Cache the daynumber of one monday */
278         if (nmonday == 0)
279                 nmonday = ndaysgi(&dmondaygi);
280
281         /* return (nd - nmonday) modulo 7 which is the weekday */
282         nd = (nd - nmonday) % 7;
283         if (nd < 0)
284                 return (nd + 7);
285         else
286                 return (nd);
287 }
288
289 /*
290  * Convert a date to internal date representation: The year starts on
291  * March 1st, month and day numbering start at zero. E. g. March 1st of
292  * year zero is written as y=0, m=0, d=0.
293  */
294 static date *
295 date2idt(date *idt, date *dt)
296 {
297
298         idt->d = dt->d - 1;
299         if (dt->m > 2) {
300                 idt->m = dt->m - 3;
301                 idt->y = dt->y;
302         } else {
303                 idt->m = dt->m + 9;
304                 idt->y = dt->y - 1;
305         }
306         if (idt->m < 0 || idt->m > 11 || idt->y < 0)
307                 return (NULL);
308         else
309                 return idt;
310 }
311
312 /* Reverse of date2idt */
313 static date *
314 idt2date(date *dt, date *idt)
315 {
316
317         dt->d = idt->d + 1;
318         if (idt->m < 10) {
319                 dt->m = idt->m + 3;
320                 dt->y = idt->y;
321         } else {
322                 dt->m = idt->m - 9;
323                 dt->y = idt->y + 1;
324         }
325         if (dt->m < 1)
326                 return (NULL);
327         else
328                 return (dt);
329 }