1306a501fSWolfgang Helbig /*- 2306a501fSWolfgang Helbig * Copyright (c) 1997 Wolfgang Helbig 3306a501fSWolfgang Helbig * All rights reserved. 4306a501fSWolfgang Helbig * 5306a501fSWolfgang Helbig * Redistribution and use in source and binary forms, with or without 6306a501fSWolfgang Helbig * modification, are permitted provided that the following conditions 7306a501fSWolfgang Helbig * are met: 8306a501fSWolfgang Helbig * 1. Redistributions of source code must retain the above copyright 9306a501fSWolfgang Helbig * notice, this list of conditions and the following disclaimer. 10306a501fSWolfgang Helbig * 2. Redistributions in binary form must reproduce the above copyright 11306a501fSWolfgang Helbig * notice, this list of conditions and the following disclaimer in the 12306a501fSWolfgang Helbig * documentation and/or other materials provided with the distribution. 13306a501fSWolfgang Helbig * 14306a501fSWolfgang Helbig * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15306a501fSWolfgang Helbig * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16306a501fSWolfgang Helbig * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17306a501fSWolfgang Helbig * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18306a501fSWolfgang Helbig * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19306a501fSWolfgang Helbig * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20306a501fSWolfgang Helbig * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21306a501fSWolfgang Helbig * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22306a501fSWolfgang Helbig * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23306a501fSWolfgang Helbig * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24306a501fSWolfgang Helbig * SUCH DAMAGE. 25306a501fSWolfgang Helbig * 26fa183770SWolfgang Helbig * $Id: calendar.c,v 1.1.1.1 1997/12/04 10:41:49 helbig Exp $ 27306a501fSWolfgang Helbig */ 28306a501fSWolfgang Helbig 29306a501fSWolfgang Helbig #include "calendar.h" 30306a501fSWolfgang Helbig 31306a501fSWolfgang Helbig #ifndef NULL 32306a501fSWolfgang Helbig #define NULL 0 33306a501fSWolfgang Helbig #endif 34306a501fSWolfgang Helbig 35306a501fSWolfgang Helbig /* 36306a501fSWolfgang Helbig * For each month tabulate the number of days elapsed in a year before the 37306a501fSWolfgang Helbig * month. This assumes the internal date representation, where a year 38306a501fSWolfgang Helbig * starts on March 1st. So we don't need a special table for leap years. 39306a501fSWolfgang Helbig * But we do need a special table for the year 1582, since 10 days are 40306a501fSWolfgang Helbig * deleted in October. This is month1s for the switch from Julian to 41306a501fSWolfgang Helbig * Gregorian calendar. 42306a501fSWolfgang Helbig */ 43306a501fSWolfgang Helbig static int const month1[] = 44306a501fSWolfgang Helbig {0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337}; 45306a501fSWolfgang Helbig /* M A M J J A S O N D J */ 46306a501fSWolfgang Helbig static int const month1s[]= 47306a501fSWolfgang Helbig {0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327}; 48306a501fSWolfgang Helbig 49fa183770SWolfgang Helbig typedef struct date date; 50fa183770SWolfgang Helbig 51306a501fSWolfgang Helbig /* The last day of Julian calendar, in internal and ndays representation */ 52306a501fSWolfgang Helbig static int nswitch; /* The last day of Julian calendar */ 53306a501fSWolfgang Helbig static date jiswitch = {1582, 7, 3}; 54306a501fSWolfgang Helbig 55306a501fSWolfgang Helbig static date *date2idt(date *idt, date *dt); 56306a501fSWolfgang Helbig static date *idt2date(date *dt, date *idt); 57306a501fSWolfgang Helbig static int ndaysji(date *idt); 58306a501fSWolfgang Helbig static int ndaysgi(date *idt); 59306a501fSWolfgang Helbig static int firstweek(int year); 60306a501fSWolfgang Helbig 61306a501fSWolfgang Helbig /* 62306a501fSWolfgang Helbig * Compute the Julian date from the number of days elapsed since 63306a501fSWolfgang Helbig * March 1st of year zero. 64306a501fSWolfgang Helbig */ 65306a501fSWolfgang Helbig date * 66306a501fSWolfgang Helbig jdate(int ndays, date *dt) 67306a501fSWolfgang Helbig { 68306a501fSWolfgang Helbig date idt; /* Internal date representation */ 69306a501fSWolfgang Helbig int r; /* hold the rest of days */ 70306a501fSWolfgang Helbig 71306a501fSWolfgang Helbig /* 72306a501fSWolfgang Helbig * Compute the year by starting with an approximation not smaller 73306a501fSWolfgang Helbig * than the answer and using linear search for the greatest 74306a501fSWolfgang Helbig * year which does not begin after ndays. 75306a501fSWolfgang Helbig */ 76306a501fSWolfgang Helbig idt.y = ndays / 365; 77306a501fSWolfgang Helbig idt.m = 0; 78306a501fSWolfgang Helbig idt.d = 0; 79306a501fSWolfgang Helbig while ((r = ndaysji(&idt)) > ndays) 80306a501fSWolfgang Helbig idt.y--; 81306a501fSWolfgang Helbig 82306a501fSWolfgang Helbig /* 83306a501fSWolfgang Helbig * Set r to the days left in the year and compute the month by 84306a501fSWolfgang Helbig * linear search as the largest month that does not begin after r 85306a501fSWolfgang Helbig * days. 86306a501fSWolfgang Helbig */ 87306a501fSWolfgang Helbig r = ndays - r; 88306a501fSWolfgang Helbig for (idt.m = 11; month1[idt.m] > r; idt.m--) 89306a501fSWolfgang Helbig ; 90306a501fSWolfgang Helbig 91306a501fSWolfgang Helbig /* Compute the days left in the month */ 92306a501fSWolfgang Helbig idt.d = r - month1[idt.m]; 93306a501fSWolfgang Helbig 94306a501fSWolfgang Helbig /* return external representation of the date */ 95306a501fSWolfgang Helbig return (idt2date(dt, &idt)); 96306a501fSWolfgang Helbig } 97306a501fSWolfgang Helbig 98306a501fSWolfgang Helbig /* 99306a501fSWolfgang Helbig * Return the number of days since March 1st of the year zero. 100306a501fSWolfgang Helbig * The date is given according to Julian calendar. 101306a501fSWolfgang Helbig */ 102306a501fSWolfgang Helbig int 103306a501fSWolfgang Helbig ndaysj(date *dt) 104306a501fSWolfgang Helbig { 105306a501fSWolfgang Helbig date idt; /* Internal date representation */ 106306a501fSWolfgang Helbig 107306a501fSWolfgang Helbig if (date2idt(&idt, dt) == NULL) 108306a501fSWolfgang Helbig return (-1); 109306a501fSWolfgang Helbig else 110306a501fSWolfgang Helbig return (ndaysji(&idt)); 111306a501fSWolfgang Helbig } 112306a501fSWolfgang Helbig 113306a501fSWolfgang Helbig /* 114306a501fSWolfgang Helbig * Same as above, where the Julian date is given in internal notation. 115306a501fSWolfgang Helbig * This formula shows the beauty of this notation. 116306a501fSWolfgang Helbig */ 117306a501fSWolfgang Helbig static int 118306a501fSWolfgang Helbig ndaysji(date * idt) 119306a501fSWolfgang Helbig { 120306a501fSWolfgang Helbig 121306a501fSWolfgang Helbig return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4); 122306a501fSWolfgang Helbig } 123306a501fSWolfgang Helbig 124306a501fSWolfgang Helbig /* 125306a501fSWolfgang Helbig * Compute the date according to the Gregorian calendar from the number of 126306a501fSWolfgang Helbig * days since March 1st, year zero. The date computed will be Julian if it 127306a501fSWolfgang Helbig * is older than 1582-10-05. This is the reverse of the function ndaysg(). 128306a501fSWolfgang Helbig */ 129306a501fSWolfgang Helbig date * 130306a501fSWolfgang Helbig gdate(int ndays, date *dt) 131306a501fSWolfgang Helbig { 132306a501fSWolfgang Helbig int const *montht; /* month-table */ 133306a501fSWolfgang Helbig date idt; /* for internal date representation */ 134306a501fSWolfgang Helbig int r; /* holds the rest of days */ 135306a501fSWolfgang Helbig 136306a501fSWolfgang Helbig /* 137306a501fSWolfgang Helbig * Compute the year by starting with an approximation not smaller 138306a501fSWolfgang Helbig * than the answer and search linearly for the greatest year not 139306a501fSWolfgang Helbig * starting after ndays. 140306a501fSWolfgang Helbig */ 141306a501fSWolfgang Helbig idt.y = ndays / 365; 142306a501fSWolfgang Helbig idt.m = 0; 143306a501fSWolfgang Helbig idt.d = 0; 144306a501fSWolfgang Helbig while ((r = ndaysgi(&idt)) > ndays) 145306a501fSWolfgang Helbig idt.y--; 146306a501fSWolfgang Helbig 147306a501fSWolfgang Helbig /* 148306a501fSWolfgang Helbig * Set ndays to the number of days left and compute by linear 149306a501fSWolfgang Helbig * search the greatest month which does not start after ndays. We 150306a501fSWolfgang Helbig * use the table month1 which provides for each month the number 151306a501fSWolfgang Helbig * of days that elapsed in the year before that month. Here the 152306a501fSWolfgang Helbig * year 1582 is special, as 10 days are left out in October to 153306a501fSWolfgang Helbig * resynchronize the calendar with the earth's orbit. October 4th 154306a501fSWolfgang Helbig * 1582 is followed by October 15th 1582. We use the "switch" 155306a501fSWolfgang Helbig * table month1s for this year. 156306a501fSWolfgang Helbig */ 157306a501fSWolfgang Helbig ndays = ndays - r; 158306a501fSWolfgang Helbig if (idt.y == 1582) 159306a501fSWolfgang Helbig montht = month1s; 160306a501fSWolfgang Helbig else 161306a501fSWolfgang Helbig montht = month1; 162306a501fSWolfgang Helbig 163306a501fSWolfgang Helbig for (idt.m = 11; montht[idt.m] > ndays; idt.m--) 164306a501fSWolfgang Helbig ; 165306a501fSWolfgang Helbig 166306a501fSWolfgang Helbig idt.d = ndays - montht[idt.m]; /* the rest is the day in month */ 167306a501fSWolfgang Helbig 168306a501fSWolfgang Helbig /* Advance ten days deleted from October if after switch in Oct 1582 */ 169306a501fSWolfgang Helbig if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d) 170306a501fSWolfgang Helbig idt.d += 10; 171306a501fSWolfgang Helbig 172306a501fSWolfgang Helbig /* return external representation of found date */ 173306a501fSWolfgang Helbig return (idt2date(dt, &idt)); 174306a501fSWolfgang Helbig } 175306a501fSWolfgang Helbig 176306a501fSWolfgang Helbig /* 177306a501fSWolfgang Helbig * Return the number of days since March 1st of the year zero. The date is 178306a501fSWolfgang Helbig * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This 179306a501fSWolfgang Helbig * is the reverse of gdate. 180306a501fSWolfgang Helbig */ 181306a501fSWolfgang Helbig int 182306a501fSWolfgang Helbig ndaysg(date *dt) 183306a501fSWolfgang Helbig { 184306a501fSWolfgang Helbig date idt; /* Internal date representation */ 185306a501fSWolfgang Helbig 186306a501fSWolfgang Helbig if (date2idt(&idt, dt) == NULL) 187306a501fSWolfgang Helbig return (-1); 188306a501fSWolfgang Helbig return (ndaysgi(&idt)); 189306a501fSWolfgang Helbig } 190306a501fSWolfgang Helbig 191306a501fSWolfgang Helbig /* 192306a501fSWolfgang Helbig * Same as above, but with the Gregorian date given in internal 193306a501fSWolfgang Helbig * representation. 194306a501fSWolfgang Helbig */ 195306a501fSWolfgang Helbig static int 196306a501fSWolfgang Helbig ndaysgi(date *idt) 197306a501fSWolfgang Helbig { 198306a501fSWolfgang Helbig int nd; /* Number of days--return value */ 199306a501fSWolfgang Helbig 200306a501fSWolfgang Helbig /* Cache nswitch if not already done */ 201306a501fSWolfgang Helbig if (nswitch == 0) 202306a501fSWolfgang Helbig nswitch = ndaysji(&jiswitch); 203306a501fSWolfgang Helbig 204306a501fSWolfgang Helbig /* 205306a501fSWolfgang Helbig * Assume Julian calendar and adapt to Gregorian if necessary, i. e. 206306a501fSWolfgang Helbig * younger than nswitch. Gregori deleted 207306a501fSWolfgang Helbig * the ten days from Oct 5th to Oct 14th 1582. 208306a501fSWolfgang Helbig * Thereafter years which are multiples of 100 and not multiples 209306a501fSWolfgang Helbig * of 400 were not leap years anymore. 210306a501fSWolfgang Helbig * This makes the average length of a year 211306a501fSWolfgang Helbig * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical 212306a501fSWolfgang Helbig * year measures 365.2422d. So in 10000/3 years we are 213306a501fSWolfgang Helbig * again one day ahead of the earth. Sigh :-) 214306a501fSWolfgang Helbig * (d is the average length of a day and tropical year is the 215306a501fSWolfgang Helbig * time from one spring point to the next.) 216306a501fSWolfgang Helbig */ 217306a501fSWolfgang Helbig if ((nd = ndaysji(idt)) == -1) 218306a501fSWolfgang Helbig return (-1); 219306a501fSWolfgang Helbig if (idt->y >= 1600) 220306a501fSWolfgang Helbig nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400); 221306a501fSWolfgang Helbig else if (nd > nswitch) 222306a501fSWolfgang Helbig nd -= 10; 223306a501fSWolfgang Helbig return (nd); 224306a501fSWolfgang Helbig } 225306a501fSWolfgang Helbig 226306a501fSWolfgang Helbig /* 227306a501fSWolfgang Helbig * Compute the week number from the number of days since March 1st year 0. 228306a501fSWolfgang Helbig * The weeks are numbered per year starting with 1. If the first 229306a501fSWolfgang Helbig * week of a year includes at least four days of that year it is week 1, 230306a501fSWolfgang Helbig * otherwise it gets the number of the last week of the previous year. 231306a501fSWolfgang Helbig * The variable y will be filled with the year that contains the greater 232306a501fSWolfgang Helbig * part of the week. 233306a501fSWolfgang Helbig */ 234306a501fSWolfgang Helbig int 235306a501fSWolfgang Helbig week(int nd, int *y) 236306a501fSWolfgang Helbig { 237306a501fSWolfgang Helbig date dt; 238306a501fSWolfgang Helbig int fw; /* 1st day of week 1 of previous, this and 239306a501fSWolfgang Helbig * next year */ 240306a501fSWolfgang Helbig gdate(nd, &dt); 241306a501fSWolfgang Helbig for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--) 242306a501fSWolfgang Helbig ; 243306a501fSWolfgang Helbig return ((nd - fw) / 7 + 1); 244306a501fSWolfgang Helbig } 245306a501fSWolfgang Helbig 246306a501fSWolfgang Helbig /* return the first day of week 1 of year y */ 247306a501fSWolfgang Helbig static int 248306a501fSWolfgang Helbig firstweek(int y) 249306a501fSWolfgang Helbig { 250306a501fSWolfgang Helbig date idt; 251306a501fSWolfgang Helbig int nd, wd; 252306a501fSWolfgang Helbig 253306a501fSWolfgang Helbig idt.y = y - 1; /* internal representation of y-1-1 */ 254306a501fSWolfgang Helbig idt.m = 10; 255306a501fSWolfgang Helbig idt.d = 0; 256306a501fSWolfgang Helbig 257306a501fSWolfgang Helbig nd = ndaysgi(&idt); 258306a501fSWolfgang Helbig /* 259306a501fSWolfgang Helbig * If more than 3 days of this week are in the preceding year, the 260306a501fSWolfgang Helbig * next week is week 1 (and the next monday is the answer), 261306a501fSWolfgang Helbig * otherwise this week is week 1 and the last monday is the 262306a501fSWolfgang Helbig * answer. 263306a501fSWolfgang Helbig */ 264306a501fSWolfgang Helbig if ((wd = weekday(nd)) > 3) 265306a501fSWolfgang Helbig return (nd - wd + 7); 266306a501fSWolfgang Helbig else 267306a501fSWolfgang Helbig return (nd - wd); 268306a501fSWolfgang Helbig } 269306a501fSWolfgang Helbig 270306a501fSWolfgang Helbig /* return the weekday (Mo = 0 .. Su = 6) */ 271306a501fSWolfgang Helbig int 272306a501fSWolfgang Helbig weekday(int nd) 273306a501fSWolfgang Helbig { 274306a501fSWolfgang Helbig date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */ 275306a501fSWolfgang Helbig static int nmonday; /* ... which is a monday */ 276306a501fSWolfgang Helbig 277306a501fSWolfgang Helbig /* Cache the daynumber of one monday */ 278306a501fSWolfgang Helbig if (nmonday == 0) 279306a501fSWolfgang Helbig nmonday = ndaysgi(&dmondaygi); 280306a501fSWolfgang Helbig 281306a501fSWolfgang Helbig /* return (nd - nmonday) modulo 7 which is the weekday */ 282306a501fSWolfgang Helbig nd = (nd - nmonday) % 7; 283306a501fSWolfgang Helbig if (nd < 0) 284306a501fSWolfgang Helbig return (nd + 7); 285306a501fSWolfgang Helbig else 286306a501fSWolfgang Helbig return (nd); 287306a501fSWolfgang Helbig } 288306a501fSWolfgang Helbig 289306a501fSWolfgang Helbig /* 290306a501fSWolfgang Helbig * Convert a date to internal date representation: The year starts on 291306a501fSWolfgang Helbig * March 1st, month and day numbering start at zero. E. g. March 1st of 292306a501fSWolfgang Helbig * year zero is written as y=0, m=0, d=0. 293306a501fSWolfgang Helbig */ 294306a501fSWolfgang Helbig static date * 295306a501fSWolfgang Helbig date2idt(date *idt, date *dt) 296306a501fSWolfgang Helbig { 297306a501fSWolfgang Helbig 298306a501fSWolfgang Helbig idt->d = dt->d - 1; 299306a501fSWolfgang Helbig if (dt->m > 2) { 300306a501fSWolfgang Helbig idt->m = dt->m - 3; 301306a501fSWolfgang Helbig idt->y = dt->y; 302306a501fSWolfgang Helbig } else { 303306a501fSWolfgang Helbig idt->m = dt->m + 9; 304306a501fSWolfgang Helbig idt->y = dt->y - 1; 305306a501fSWolfgang Helbig } 306306a501fSWolfgang Helbig if (idt->m < 0 || idt->m > 11 || idt->y < 0) 307306a501fSWolfgang Helbig return (NULL); 308306a501fSWolfgang Helbig else 309306a501fSWolfgang Helbig return idt; 310306a501fSWolfgang Helbig } 311306a501fSWolfgang Helbig 312306a501fSWolfgang Helbig /* Reverse of date2idt */ 313306a501fSWolfgang Helbig static date * 314306a501fSWolfgang Helbig idt2date(date *dt, date *idt) 315306a501fSWolfgang Helbig { 316306a501fSWolfgang Helbig 317306a501fSWolfgang Helbig dt->d = idt->d + 1; 318306a501fSWolfgang Helbig if (idt->m < 10) { 319306a501fSWolfgang Helbig dt->m = idt->m + 3; 320306a501fSWolfgang Helbig dt->y = idt->y; 321306a501fSWolfgang Helbig } else { 322306a501fSWolfgang Helbig dt->m = idt->m - 9; 323306a501fSWolfgang Helbig dt->y = idt->y + 1; 324306a501fSWolfgang Helbig } 325306a501fSWolfgang Helbig if (dt->m < 1) 326306a501fSWolfgang Helbig return (NULL); 327306a501fSWolfgang Helbig else 328306a501fSWolfgang Helbig return (dt); 329306a501fSWolfgang Helbig } 330