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 * 26306a501fSWolfgang Helbig * $Id$ 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 49306a501fSWolfgang Helbig /* The last day of Julian calendar, in internal and ndays representation */ 50306a501fSWolfgang Helbig static int nswitch; /* The last day of Julian calendar */ 51306a501fSWolfgang Helbig static date jiswitch = {1582, 7, 3}; 52306a501fSWolfgang Helbig 53306a501fSWolfgang Helbig static date *date2idt(date *idt, date *dt); 54306a501fSWolfgang Helbig static date *idt2date(date *dt, date *idt); 55306a501fSWolfgang Helbig static int ndaysji(date *idt); 56306a501fSWolfgang Helbig static int ndaysgi(date *idt); 57306a501fSWolfgang Helbig static int firstweek(int year); 58306a501fSWolfgang Helbig 59306a501fSWolfgang Helbig /* 60306a501fSWolfgang Helbig * Compute the Julian date from the number of days elapsed since 61306a501fSWolfgang Helbig * March 1st of year zero. 62306a501fSWolfgang Helbig */ 63306a501fSWolfgang Helbig date * 64306a501fSWolfgang Helbig jdate(int ndays, date *dt) 65306a501fSWolfgang Helbig { 66306a501fSWolfgang Helbig date idt; /* Internal date representation */ 67306a501fSWolfgang Helbig int r; /* hold the rest of days */ 68306a501fSWolfgang Helbig 69306a501fSWolfgang Helbig /* 70306a501fSWolfgang Helbig * Compute the year by starting with an approximation not smaller 71306a501fSWolfgang Helbig * than the answer and using linear search for the greatest 72306a501fSWolfgang Helbig * year which does not begin after ndays. 73306a501fSWolfgang Helbig */ 74306a501fSWolfgang Helbig idt.y = ndays / 365; 75306a501fSWolfgang Helbig idt.m = 0; 76306a501fSWolfgang Helbig idt.d = 0; 77306a501fSWolfgang Helbig while ((r = ndaysji(&idt)) > ndays) 78306a501fSWolfgang Helbig idt.y--; 79306a501fSWolfgang Helbig 80306a501fSWolfgang Helbig /* 81306a501fSWolfgang Helbig * Set r to the days left in the year and compute the month by 82306a501fSWolfgang Helbig * linear search as the largest month that does not begin after r 83306a501fSWolfgang Helbig * days. 84306a501fSWolfgang Helbig */ 85306a501fSWolfgang Helbig r = ndays - r; 86306a501fSWolfgang Helbig for (idt.m = 11; month1[idt.m] > r; idt.m--) 87306a501fSWolfgang Helbig ; 88306a501fSWolfgang Helbig 89306a501fSWolfgang Helbig /* Compute the days left in the month */ 90306a501fSWolfgang Helbig idt.d = r - month1[idt.m]; 91306a501fSWolfgang Helbig 92306a501fSWolfgang Helbig /* return external representation of the date */ 93306a501fSWolfgang Helbig return (idt2date(dt, &idt)); 94306a501fSWolfgang Helbig } 95306a501fSWolfgang Helbig 96306a501fSWolfgang Helbig /* 97306a501fSWolfgang Helbig * Return the number of days since March 1st of the year zero. 98306a501fSWolfgang Helbig * The date is given according to Julian calendar. 99306a501fSWolfgang Helbig */ 100306a501fSWolfgang Helbig int 101306a501fSWolfgang Helbig ndaysj(date *dt) 102306a501fSWolfgang Helbig { 103306a501fSWolfgang Helbig date idt; /* Internal date representation */ 104306a501fSWolfgang Helbig 105306a501fSWolfgang Helbig if (date2idt(&idt, dt) == NULL) 106306a501fSWolfgang Helbig return (-1); 107306a501fSWolfgang Helbig else 108306a501fSWolfgang Helbig return (ndaysji(&idt)); 109306a501fSWolfgang Helbig } 110306a501fSWolfgang Helbig 111306a501fSWolfgang Helbig /* 112306a501fSWolfgang Helbig * Same as above, where the Julian date is given in internal notation. 113306a501fSWolfgang Helbig * This formula shows the beauty of this notation. 114306a501fSWolfgang Helbig */ 115306a501fSWolfgang Helbig static int 116306a501fSWolfgang Helbig ndaysji(date * idt) 117306a501fSWolfgang Helbig { 118306a501fSWolfgang Helbig 119306a501fSWolfgang Helbig return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4); 120306a501fSWolfgang Helbig } 121306a501fSWolfgang Helbig 122306a501fSWolfgang Helbig /* 123306a501fSWolfgang Helbig * Compute the date according to the Gregorian calendar from the number of 124306a501fSWolfgang Helbig * days since March 1st, year zero. The date computed will be Julian if it 125306a501fSWolfgang Helbig * is older than 1582-10-05. This is the reverse of the function ndaysg(). 126306a501fSWolfgang Helbig */ 127306a501fSWolfgang Helbig date * 128306a501fSWolfgang Helbig gdate(int ndays, date *dt) 129306a501fSWolfgang Helbig { 130306a501fSWolfgang Helbig int const *montht; /* month-table */ 131306a501fSWolfgang Helbig date idt; /* for internal date representation */ 132306a501fSWolfgang Helbig int r; /* holds the rest of days */ 133306a501fSWolfgang Helbig 134306a501fSWolfgang Helbig /* 135306a501fSWolfgang Helbig * Compute the year by starting with an approximation not smaller 136306a501fSWolfgang Helbig * than the answer and search linearly for the greatest year not 137306a501fSWolfgang Helbig * starting after ndays. 138306a501fSWolfgang Helbig */ 139306a501fSWolfgang Helbig idt.y = ndays / 365; 140306a501fSWolfgang Helbig idt.m = 0; 141306a501fSWolfgang Helbig idt.d = 0; 142306a501fSWolfgang Helbig while ((r = ndaysgi(&idt)) > ndays) 143306a501fSWolfgang Helbig idt.y--; 144306a501fSWolfgang Helbig 145306a501fSWolfgang Helbig /* 146306a501fSWolfgang Helbig * Set ndays to the number of days left and compute by linear 147306a501fSWolfgang Helbig * search the greatest month which does not start after ndays. We 148306a501fSWolfgang Helbig * use the table month1 which provides for each month the number 149306a501fSWolfgang Helbig * of days that elapsed in the year before that month. Here the 150306a501fSWolfgang Helbig * year 1582 is special, as 10 days are left out in October to 151306a501fSWolfgang Helbig * resynchronize the calendar with the earth's orbit. October 4th 152306a501fSWolfgang Helbig * 1582 is followed by October 15th 1582. We use the "switch" 153306a501fSWolfgang Helbig * table month1s for this year. 154306a501fSWolfgang Helbig */ 155306a501fSWolfgang Helbig ndays = ndays - r; 156306a501fSWolfgang Helbig if (idt.y == 1582) 157306a501fSWolfgang Helbig montht = month1s; 158306a501fSWolfgang Helbig else 159306a501fSWolfgang Helbig montht = month1; 160306a501fSWolfgang Helbig 161306a501fSWolfgang Helbig for (idt.m = 11; montht[idt.m] > ndays; idt.m--) 162306a501fSWolfgang Helbig ; 163306a501fSWolfgang Helbig 164306a501fSWolfgang Helbig idt.d = ndays - montht[idt.m]; /* the rest is the day in month */ 165306a501fSWolfgang Helbig 166306a501fSWolfgang Helbig /* Advance ten days deleted from October if after switch in Oct 1582 */ 167306a501fSWolfgang Helbig if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d) 168306a501fSWolfgang Helbig idt.d += 10; 169306a501fSWolfgang Helbig 170306a501fSWolfgang Helbig /* return external representation of found date */ 171306a501fSWolfgang Helbig return (idt2date(dt, &idt)); 172306a501fSWolfgang Helbig } 173306a501fSWolfgang Helbig 174306a501fSWolfgang Helbig /* 175306a501fSWolfgang Helbig * Return the number of days since March 1st of the year zero. The date is 176306a501fSWolfgang Helbig * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This 177306a501fSWolfgang Helbig * is the reverse of gdate. 178306a501fSWolfgang Helbig */ 179306a501fSWolfgang Helbig int 180306a501fSWolfgang Helbig ndaysg(date *dt) 181306a501fSWolfgang Helbig { 182306a501fSWolfgang Helbig date idt; /* Internal date representation */ 183306a501fSWolfgang Helbig 184306a501fSWolfgang Helbig if (date2idt(&idt, dt) == NULL) 185306a501fSWolfgang Helbig return (-1); 186306a501fSWolfgang Helbig return (ndaysgi(&idt)); 187306a501fSWolfgang Helbig } 188306a501fSWolfgang Helbig 189306a501fSWolfgang Helbig /* 190306a501fSWolfgang Helbig * Same as above, but with the Gregorian date given in internal 191306a501fSWolfgang Helbig * representation. 192306a501fSWolfgang Helbig */ 193306a501fSWolfgang Helbig static int 194306a501fSWolfgang Helbig ndaysgi(date *idt) 195306a501fSWolfgang Helbig { 196306a501fSWolfgang Helbig int nd; /* Number of days--return value */ 197306a501fSWolfgang Helbig 198306a501fSWolfgang Helbig /* Cache nswitch if not already done */ 199306a501fSWolfgang Helbig if (nswitch == 0) 200306a501fSWolfgang Helbig nswitch = ndaysji(&jiswitch); 201306a501fSWolfgang Helbig 202306a501fSWolfgang Helbig /* 203306a501fSWolfgang Helbig * Assume Julian calendar and adapt to Gregorian if necessary, i. e. 204306a501fSWolfgang Helbig * younger than nswitch. Gregori deleted 205306a501fSWolfgang Helbig * the ten days from Oct 5th to Oct 14th 1582. 206306a501fSWolfgang Helbig * Thereafter years which are multiples of 100 and not multiples 207306a501fSWolfgang Helbig * of 400 were not leap years anymore. 208306a501fSWolfgang Helbig * This makes the average length of a year 209306a501fSWolfgang Helbig * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical 210306a501fSWolfgang Helbig * year measures 365.2422d. So in 10000/3 years we are 211306a501fSWolfgang Helbig * again one day ahead of the earth. Sigh :-) 212306a501fSWolfgang Helbig * (d is the average length of a day and tropical year is the 213306a501fSWolfgang Helbig * time from one spring point to the next.) 214306a501fSWolfgang Helbig */ 215306a501fSWolfgang Helbig if ((nd = ndaysji(idt)) == -1) 216306a501fSWolfgang Helbig return (-1); 217306a501fSWolfgang Helbig if (idt->y >= 1600) 218306a501fSWolfgang Helbig nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400); 219306a501fSWolfgang Helbig else if (nd > nswitch) 220306a501fSWolfgang Helbig nd -= 10; 221306a501fSWolfgang Helbig return (nd); 222306a501fSWolfgang Helbig } 223306a501fSWolfgang Helbig 224306a501fSWolfgang Helbig /* 225306a501fSWolfgang Helbig * Compute the week number from the number of days since March 1st year 0. 226306a501fSWolfgang Helbig * The weeks are numbered per year starting with 1. If the first 227306a501fSWolfgang Helbig * week of a year includes at least four days of that year it is week 1, 228306a501fSWolfgang Helbig * otherwise it gets the number of the last week of the previous year. 229306a501fSWolfgang Helbig * The variable y will be filled with the year that contains the greater 230306a501fSWolfgang Helbig * part of the week. 231306a501fSWolfgang Helbig */ 232306a501fSWolfgang Helbig int 233306a501fSWolfgang Helbig week(int nd, int *y) 234306a501fSWolfgang Helbig { 235306a501fSWolfgang Helbig date dt; 236306a501fSWolfgang Helbig int fw; /* 1st day of week 1 of previous, this and 237306a501fSWolfgang Helbig * next year */ 238306a501fSWolfgang Helbig gdate(nd, &dt); 239306a501fSWolfgang Helbig for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--) 240306a501fSWolfgang Helbig ; 241306a501fSWolfgang Helbig return ((nd - fw) / 7 + 1); 242306a501fSWolfgang Helbig } 243306a501fSWolfgang Helbig 244306a501fSWolfgang Helbig /* return the first day of week 1 of year y */ 245306a501fSWolfgang Helbig static int 246306a501fSWolfgang Helbig firstweek(int y) 247306a501fSWolfgang Helbig { 248306a501fSWolfgang Helbig date idt; 249306a501fSWolfgang Helbig int nd, wd; 250306a501fSWolfgang Helbig 251306a501fSWolfgang Helbig idt.y = y - 1; /* internal representation of y-1-1 */ 252306a501fSWolfgang Helbig idt.m = 10; 253306a501fSWolfgang Helbig idt.d = 0; 254306a501fSWolfgang Helbig 255306a501fSWolfgang Helbig nd = ndaysgi(&idt); 256306a501fSWolfgang Helbig /* 257306a501fSWolfgang Helbig * If more than 3 days of this week are in the preceding year, the 258306a501fSWolfgang Helbig * next week is week 1 (and the next monday is the answer), 259306a501fSWolfgang Helbig * otherwise this week is week 1 and the last monday is the 260306a501fSWolfgang Helbig * answer. 261306a501fSWolfgang Helbig */ 262306a501fSWolfgang Helbig if ((wd = weekday(nd)) > 3) 263306a501fSWolfgang Helbig return (nd - wd + 7); 264306a501fSWolfgang Helbig else 265306a501fSWolfgang Helbig return (nd - wd); 266306a501fSWolfgang Helbig } 267306a501fSWolfgang Helbig 268306a501fSWolfgang Helbig /* return the weekday (Mo = 0 .. Su = 6) */ 269306a501fSWolfgang Helbig int 270306a501fSWolfgang Helbig weekday(int nd) 271306a501fSWolfgang Helbig { 272306a501fSWolfgang Helbig date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */ 273306a501fSWolfgang Helbig static int nmonday; /* ... which is a monday */ 274306a501fSWolfgang Helbig 275306a501fSWolfgang Helbig /* Cache the daynumber of one monday */ 276306a501fSWolfgang Helbig if (nmonday == 0) 277306a501fSWolfgang Helbig nmonday = ndaysgi(&dmondaygi); 278306a501fSWolfgang Helbig 279306a501fSWolfgang Helbig /* return (nd - nmonday) modulo 7 which is the weekday */ 280306a501fSWolfgang Helbig nd = (nd - nmonday) % 7; 281306a501fSWolfgang Helbig if (nd < 0) 282306a501fSWolfgang Helbig return (nd + 7); 283306a501fSWolfgang Helbig else 284306a501fSWolfgang Helbig return (nd); 285306a501fSWolfgang Helbig } 286306a501fSWolfgang Helbig 287306a501fSWolfgang Helbig /* 288306a501fSWolfgang Helbig * Convert a date to internal date representation: The year starts on 289306a501fSWolfgang Helbig * March 1st, month and day numbering start at zero. E. g. March 1st of 290306a501fSWolfgang Helbig * year zero is written as y=0, m=0, d=0. 291306a501fSWolfgang Helbig */ 292306a501fSWolfgang Helbig static date * 293306a501fSWolfgang Helbig date2idt(date *idt, date *dt) 294306a501fSWolfgang Helbig { 295306a501fSWolfgang Helbig 296306a501fSWolfgang Helbig idt->d = dt->d - 1; 297306a501fSWolfgang Helbig if (dt->m > 2) { 298306a501fSWolfgang Helbig idt->m = dt->m - 3; 299306a501fSWolfgang Helbig idt->y = dt->y; 300306a501fSWolfgang Helbig } else { 301306a501fSWolfgang Helbig idt->m = dt->m + 9; 302306a501fSWolfgang Helbig idt->y = dt->y - 1; 303306a501fSWolfgang Helbig } 304306a501fSWolfgang Helbig if (idt->m < 0 || idt->m > 11 || idt->y < 0) 305306a501fSWolfgang Helbig return (NULL); 306306a501fSWolfgang Helbig else 307306a501fSWolfgang Helbig return idt; 308306a501fSWolfgang Helbig } 309306a501fSWolfgang Helbig 310306a501fSWolfgang Helbig /* Reverse of date2idt */ 311306a501fSWolfgang Helbig static date * 312306a501fSWolfgang Helbig idt2date(date *dt, date *idt) 313306a501fSWolfgang Helbig { 314306a501fSWolfgang Helbig 315306a501fSWolfgang Helbig dt->d = idt->d + 1; 316306a501fSWolfgang Helbig if (idt->m < 10) { 317306a501fSWolfgang Helbig dt->m = idt->m + 3; 318306a501fSWolfgang Helbig dt->y = idt->y; 319306a501fSWolfgang Helbig } else { 320306a501fSWolfgang Helbig dt->m = idt->m - 9; 321306a501fSWolfgang Helbig dt->y = idt->y + 1; 322306a501fSWolfgang Helbig } 323306a501fSWolfgang Helbig if (dt->m < 1) 324306a501fSWolfgang Helbig return (NULL); 325306a501fSWolfgang Helbig else 326306a501fSWolfgang Helbig return (dt); 327306a501fSWolfgang Helbig } 328