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