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 <sys/param.h> 30 #include "calendar.h" 31 32 /* 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. 39 */ 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}; 45 46 typedef struct date date; 47 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}; 51 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); 57 58 /* 59 * Compute the Julian date from the number of days elapsed since 60 * March 1st of year zero. 61 */ 62 date * 63 jdate(int ndays, date *dt) 64 { 65 date idt; /* Internal date representation */ 66 int r; /* hold the rest of days */ 67 68 /* 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. 72 */ 73 idt.y = ndays / 365; 74 idt.m = 0; 75 idt.d = 0; 76 while ((r = ndaysji(&idt)) > ndays) 77 idt.y--; 78 79 /* 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. 83 */ 84 r = ndays - r; 85 for (idt.m = 11; month1[idt.m] > r; idt.m--) 86 ; 87 88 /* Compute the days left in the month */ 89 idt.d = r - month1[idt.m]; 90 91 /* return external representation of the date */ 92 return (idt2date(dt, &idt)); 93 } 94 95 /* 96 * Return the number of days since March 1st of the year zero. 97 * The date is given according to Julian calendar. 98 */ 99 int 100 ndaysj(date *dt) 101 { 102 date idt; /* Internal date representation */ 103 104 if (date2idt(&idt, dt) == NULL) 105 return (-1); 106 else 107 return (ndaysji(&idt)); 108 } 109 110 /* 111 * Same as above, where the Julian date is given in internal notation. 112 * This formula shows the beauty of this notation. 113 */ 114 static int 115 ndaysji(date * idt) 116 { 117 118 return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4); 119 } 120 121 /* 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(). 125 */ 126 date * 127 gdate(int ndays, date *dt) 128 { 129 int const *montht; /* month-table */ 130 date idt; /* for internal date representation */ 131 int r; /* holds the rest of days */ 132 133 /* 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. 137 */ 138 idt.y = ndays / 365; 139 idt.m = 0; 140 idt.d = 0; 141 while ((r = ndaysgi(&idt)) > ndays) 142 idt.y--; 143 144 /* 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. 153 */ 154 ndays = ndays - r; 155 if (idt.y == 1582) 156 montht = month1s; 157 else 158 montht = month1; 159 160 for (idt.m = 11; montht[idt.m] > ndays; idt.m--) 161 ; 162 163 idt.d = ndays - montht[idt.m]; /* the rest is the day in month */ 164 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; 168 169 /* return external representation of found date */ 170 return (idt2date(dt, &idt)); 171 } 172 173 /* 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. 177 */ 178 int 179 ndaysg(date *dt) 180 { 181 date idt; /* Internal date representation */ 182 183 if (date2idt(&idt, dt) == NULL) 184 return (-1); 185 return (ndaysgi(&idt)); 186 } 187 188 /* 189 * Same as above, but with the Gregorian date given in internal 190 * representation. 191 */ 192 static int 193 ndaysgi(date *idt) 194 { 195 int nd; /* Number of days--return value */ 196 197 /* Cache nswitch if not already done */ 198 if (nswitch == 0) 199 nswitch = ndaysji(&jiswitch); 200 201 /* 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.) 213 */ 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); 221 } 222 223 /* 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. 230 */ 231 int 232 week(int nd, int *y) 233 { 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)--) 239 ; 240 return ((nd - fw) / 7 + 1); 241 } 242 243 /* return the first day of week 1 of year y */ 244 static int 245 firstweek(int y) 246 { 247 date idt; 248 int nd, wd; 249 250 idt.y = y - 1; /* internal representation of y-1-1 */ 251 idt.m = 10; 252 idt.d = 0; 253 254 nd = ndaysgi(&idt); 255 /* 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. 260 */ 261 if ((wd = weekday(nd)) > 3) 262 return (nd - wd + 7); 263 else 264 return (nd - wd); 265 } 266 267 /* return the weekday (Mo = 0 .. Su = 6) */ 268 int 269 weekday(int nd) 270 { 271 date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */ 272 static int nmonday; /* ... which is a monday */ 273 274 /* Cache the daynumber of one monday */ 275 if (nmonday == 0) 276 nmonday = ndaysgi(&dmondaygi); 277 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); 284 } 285 286 /* 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. 290 */ 291 static date * 292 date2idt(date *idt, date *dt) 293 { 294 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; 302 } 303 if (idt->m < 0 || idt->m > 11 || idt->y < 0) 304 return (NULL); 305 else 306 return idt; 307 } 308 309 /* Reverse of date2idt */ 310 static date * 311 idt2date(date *dt, date *idt) 312 { 313 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; 321 } 322 if (dt->m < 1) 323 return (NULL); 324 else 325 return (dt); 326 } 327