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