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 __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 } 333