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