i386 removal, part 68/x: Remove a number of obsolete Makefiles from gnu/.
[dragonfly.git] / lib / libcalendar / calendar.c
blob3dc853806942142cb0d94e638f8013470167a558
1 /*-
2 * Copyright (c) 1997 Wolfgang Helbig
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/lib/libcalendar/calendar.c,v 1.3 1999/08/28 00:04:03 peter Exp $
29 #include <sys/param.h>
30 #include "calendar.h"
33 * For each month tabulate the number of days elapsed in a year before the
34 * month. This assumes the internal date representation, where a year
35 * starts on March 1st. So we don't need a special table for leap years.
36 * But we do need a special table for the year 1582, since 10 days are
37 * deleted in October. This is month1s for the switch from Julian to
38 * Gregorian calendar.
40 static int const month1[] =
41 {0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337};
42 /* M A M J J A S O N D J */
43 static int const month1s[]=
44 {0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327};
46 typedef struct date date;
48 /* The last day of Julian calendar, in internal and ndays representation */
49 static int nswitch; /* The last day of Julian calendar */
50 static date jiswitch = {1582, 7, 3};
52 static date *date2idt(date *idt, date *dt);
53 static date *idt2date(date *dt, date *idt);
54 static int ndaysji(date *idt);
55 static int ndaysgi(date *idt);
56 static int firstweek(int year);
59 * Compute the Julian date from the number of days elapsed since
60 * March 1st of year zero.
62 date *
63 jdate(int ndays, date *dt)
65 date idt; /* Internal date representation */
66 int r; /* hold the rest of days */
69 * Compute the year by starting with an approximation not smaller
70 * than the answer and using linear search for the greatest
71 * year which does not begin after ndays.
73 idt.y = ndays / 365;
74 idt.m = 0;
75 idt.d = 0;
76 while ((r = ndaysji(&idt)) > ndays)
77 idt.y--;
80 * Set r to the days left in the year and compute the month by
81 * linear search as the largest month that does not begin after r
82 * days.
84 r = ndays - r;
85 for (idt.m = 11; month1[idt.m] > r; idt.m--)
88 /* Compute the days left in the month */
89 idt.d = r - month1[idt.m];
91 /* return external representation of the date */
92 return (idt2date(dt, &idt));
96 * Return the number of days since March 1st of the year zero.
97 * The date is given according to Julian calendar.
99 int
100 ndaysj(date *dt)
102 date idt; /* Internal date representation */
104 if (date2idt(&idt, dt) == NULL)
105 return (-1);
106 else
107 return (ndaysji(&idt));
111 * Same as above, where the Julian date is given in internal notation.
112 * This formula shows the beauty of this notation.
114 static int
115 ndaysji(date * idt)
118 return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4);
122 * Compute the date according to the Gregorian calendar from the number of
123 * days since March 1st, year zero. The date computed will be Julian if it
124 * is older than 1582-10-05. This is the reverse of the function ndaysg().
126 date *
127 gdate(int ndays, date *dt)
129 int const *montht; /* month-table */
130 date idt; /* for internal date representation */
131 int r; /* holds the rest of days */
134 * Compute the year by starting with an approximation not smaller
135 * than the answer and search linearly for the greatest year not
136 * starting after ndays.
138 idt.y = ndays / 365;
139 idt.m = 0;
140 idt.d = 0;
141 while ((r = ndaysgi(&idt)) > ndays)
142 idt.y--;
145 * Set ndays to the number of days left and compute by linear
146 * search the greatest month which does not start after ndays. We
147 * use the table month1 which provides for each month the number
148 * of days that elapsed in the year before that month. Here the
149 * year 1582 is special, as 10 days are left out in October to
150 * resynchronize the calendar with the earth's orbit. October 4th
151 * 1582 is followed by October 15th 1582. We use the "switch"
152 * table month1s for this year.
154 ndays = ndays - r;
155 if (idt.y == 1582)
156 montht = month1s;
157 else
158 montht = month1;
160 for (idt.m = 11; montht[idt.m] > ndays; idt.m--)
163 idt.d = ndays - montht[idt.m]; /* the rest is the day in month */
165 /* Advance ten days deleted from October if after switch in Oct 1582 */
166 if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d)
167 idt.d += 10;
169 /* return external representation of found date */
170 return (idt2date(dt, &idt));
174 * Return the number of days since March 1st of the year zero. The date is
175 * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This
176 * is the reverse of gdate.
179 ndaysg(date *dt)
181 date idt; /* Internal date representation */
183 if (date2idt(&idt, dt) == NULL)
184 return (-1);
185 return (ndaysgi(&idt));
189 * Same as above, but with the Gregorian date given in internal
190 * representation.
192 static int
193 ndaysgi(date *idt)
195 int nd; /* Number of days--return value */
197 /* Cache nswitch if not already done */
198 if (nswitch == 0)
199 nswitch = ndaysji(&jiswitch);
202 * Assume Julian calendar and adapt to Gregorian if necessary, i. e.
203 * younger than nswitch. Gregori deleted
204 * the ten days from Oct 5th to Oct 14th 1582.
205 * Thereafter years which are multiples of 100 and not multiples
206 * of 400 were not leap years anymore.
207 * This makes the average length of a year
208 * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical
209 * year measures 365.2422d. So in 10000/3 years we are
210 * again one day ahead of the earth. Sigh :-)
211 * (d is the average length of a day and tropical year is the
212 * time from one spring point to the next.)
214 if ((nd = ndaysji(idt)) == -1)
215 return (-1);
216 if (idt->y >= 1600)
217 nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400);
218 else if (nd > nswitch)
219 nd -= 10;
220 return (nd);
224 * Compute the week number from the number of days since March 1st year 0.
225 * The weeks are numbered per year starting with 1. If the first
226 * week of a year includes at least four days of that year it is week 1,
227 * otherwise it gets the number of the last week of the previous year.
228 * The variable y will be filled with the year that contains the greater
229 * part of the week.
232 week(int nd, int *y)
234 date dt;
235 int fw; /* 1st day of week 1 of previous, this and
236 * next year */
237 gdate(nd, &dt);
238 for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--)
240 return ((nd - fw) / 7 + 1);
243 /* return the first day of week 1 of year y */
244 static int
245 firstweek(int y)
247 date idt;
248 int nd, wd;
250 idt.y = y - 1; /* internal representation of y-1-1 */
251 idt.m = 10;
252 idt.d = 0;
254 nd = ndaysgi(&idt);
256 * If more than 3 days of this week are in the preceding year, the
257 * next week is week 1 (and the next monday is the answer),
258 * otherwise this week is week 1 and the last monday is the
259 * answer.
261 if ((wd = weekday(nd)) > 3)
262 return (nd - wd + 7);
263 else
264 return (nd - wd);
267 /* return the weekday (Mo = 0 .. Su = 6) */
269 weekday(int nd)
271 date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */
272 static int nmonday; /* ... which is a monday */
274 /* Cache the daynumber of one monday */
275 if (nmonday == 0)
276 nmonday = ndaysgi(&dmondaygi);
278 /* return (nd - nmonday) modulo 7 which is the weekday */
279 nd = (nd - nmonday) % 7;
280 if (nd < 0)
281 return (nd + 7);
282 else
283 return (nd);
287 * Convert a date to internal date representation: The year starts on
288 * March 1st, month and day numbering start at zero. E. g. March 1st of
289 * year zero is written as y=0, m=0, d=0.
291 static date *
292 date2idt(date *idt, date *dt)
295 idt->d = dt->d - 1;
296 if (dt->m > 2) {
297 idt->m = dt->m - 3;
298 idt->y = dt->y;
299 } else {
300 idt->m = dt->m + 9;
301 idt->y = dt->y - 1;
303 if (idt->m < 0 || idt->m > 11 || idt->y < 0)
304 return (NULL);
305 else
306 return idt;
309 /* Reverse of date2idt */
310 static date *
311 idt2date(date *dt, date *idt)
314 dt->d = idt->d + 1;
315 if (idt->m < 10) {
316 dt->m = idt->m + 3;
317 dt->y = idt->y;
318 } else {
319 dt->m = idt->m - 9;
320 dt->y = idt->y + 1;
322 if (dt->m < 1)
323 return (NULL);
324 else
325 return (dt);