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