calendar.c revision 31529
131529Shelbig/*- 231529Shelbig * Copyright (c) 1997 Wolfgang Helbig 331529Shelbig * All rights reserved. 431529Shelbig * 531529Shelbig * Redistribution and use in source and binary forms, with or without 631529Shelbig * modification, are permitted provided that the following conditions 731529Shelbig * are met: 831529Shelbig * 1. Redistributions of source code must retain the above copyright 931529Shelbig * notice, this list of conditions and the following disclaimer. 1031529Shelbig * 2. Redistributions in binary form must reproduce the above copyright 1131529Shelbig * notice, this list of conditions and the following disclaimer in the 1231529Shelbig * documentation and/or other materials provided with the distribution. 1331529Shelbig * 1431529Shelbig * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1531529Shelbig * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1631529Shelbig * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1731529Shelbig * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1831529Shelbig * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1931529Shelbig * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2031529Shelbig * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2131529Shelbig * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2231529Shelbig * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2331529Shelbig * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2431529Shelbig * SUCH DAMAGE. 2531529Shelbig * 2631529Shelbig * $Id$ 2731529Shelbig */ 2831529Shelbig 2931529Shelbig#include "calendar.h" 3031529Shelbig 3131529Shelbig#ifndef NULL 3231529Shelbig#define NULL 0 3331529Shelbig#endif 3431529Shelbig 3531529Shelbig/* 3631529Shelbig * For each month tabulate the number of days elapsed in a year before the 3731529Shelbig * month. This assumes the internal date representation, where a year 3831529Shelbig * starts on March 1st. So we don't need a special table for leap years. 3931529Shelbig * But we do need a special table for the year 1582, since 10 days are 4031529Shelbig * deleted in October. This is month1s for the switch from Julian to 4131529Shelbig * Gregorian calendar. 4231529Shelbig */ 4331529Shelbigstatic int const month1[] = 4431529Shelbig {0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337}; 4531529Shelbig /* M A M J J A S O N D J */ 4631529Shelbigstatic int const month1s[]= 4731529Shelbig {0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327}; 4831529Shelbig 4931529Shelbig/* The last day of Julian calendar, in internal and ndays representation */ 5031529Shelbigstatic int nswitch; /* The last day of Julian calendar */ 5131529Shelbigstatic date jiswitch = {1582, 7, 3}; 5231529Shelbig 5331529Shelbigstatic date *date2idt(date *idt, date *dt); 5431529Shelbigstatic date *idt2date(date *dt, date *idt); 5531529Shelbigstatic int ndaysji(date *idt); 5631529Shelbigstatic int ndaysgi(date *idt); 5731529Shelbigstatic int firstweek(int year); 5831529Shelbig 5931529Shelbig/* 6031529Shelbig * Compute the Julian date from the number of days elapsed since 6131529Shelbig * March 1st of year zero. 6231529Shelbig */ 6331529Shelbigdate * 6431529Shelbigjdate(int ndays, date *dt) 6531529Shelbig{ 6631529Shelbig date idt; /* Internal date representation */ 6731529Shelbig int r; /* hold the rest of days */ 6831529Shelbig 6931529Shelbig /* 7031529Shelbig * Compute the year by starting with an approximation not smaller 7131529Shelbig * than the answer and using linear search for the greatest 7231529Shelbig * year which does not begin after ndays. 7331529Shelbig */ 7431529Shelbig idt.y = ndays / 365; 7531529Shelbig idt.m = 0; 7631529Shelbig idt.d = 0; 7731529Shelbig while ((r = ndaysji(&idt)) > ndays) 7831529Shelbig idt.y--; 7931529Shelbig 8031529Shelbig /* 8131529Shelbig * Set r to the days left in the year and compute the month by 8231529Shelbig * linear search as the largest month that does not begin after r 8331529Shelbig * days. 8431529Shelbig */ 8531529Shelbig r = ndays - r; 8631529Shelbig for (idt.m = 11; month1[idt.m] > r; idt.m--) 8731529Shelbig ; 8831529Shelbig 8931529Shelbig /* Compute the days left in the month */ 9031529Shelbig idt.d = r - month1[idt.m]; 9131529Shelbig 9231529Shelbig /* return external representation of the date */ 9331529Shelbig return (idt2date(dt, &idt)); 9431529Shelbig} 9531529Shelbig 9631529Shelbig/* 9731529Shelbig * Return the number of days since March 1st of the year zero. 9831529Shelbig * The date is given according to Julian calendar. 9931529Shelbig */ 10031529Shelbigint 10131529Shelbigndaysj(date *dt) 10231529Shelbig{ 10331529Shelbig date idt; /* Internal date representation */ 10431529Shelbig 10531529Shelbig if (date2idt(&idt, dt) == NULL) 10631529Shelbig return (-1); 10731529Shelbig else 10831529Shelbig return (ndaysji(&idt)); 10931529Shelbig} 11031529Shelbig 11131529Shelbig/* 11231529Shelbig * Same as above, where the Julian date is given in internal notation. 11331529Shelbig * This formula shows the beauty of this notation. 11431529Shelbig */ 11531529Shelbigstatic int 11631529Shelbigndaysji(date * idt) 11731529Shelbig{ 11831529Shelbig 11931529Shelbig return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4); 12031529Shelbig} 12131529Shelbig 12231529Shelbig/* 12331529Shelbig * Compute the date according to the Gregorian calendar from the number of 12431529Shelbig * days since March 1st, year zero. The date computed will be Julian if it 12531529Shelbig * is older than 1582-10-05. This is the reverse of the function ndaysg(). 12631529Shelbig */ 12731529Shelbigdate * 12831529Shelbiggdate(int ndays, date *dt) 12931529Shelbig{ 13031529Shelbig int const *montht; /* month-table */ 13131529Shelbig date idt; /* for internal date representation */ 13231529Shelbig int r; /* holds the rest of days */ 13331529Shelbig 13431529Shelbig /* 13531529Shelbig * Compute the year by starting with an approximation not smaller 13631529Shelbig * than the answer and search linearly for the greatest year not 13731529Shelbig * starting after ndays. 13831529Shelbig */ 13931529Shelbig idt.y = ndays / 365; 14031529Shelbig idt.m = 0; 14131529Shelbig idt.d = 0; 14231529Shelbig while ((r = ndaysgi(&idt)) > ndays) 14331529Shelbig idt.y--; 14431529Shelbig 14531529Shelbig /* 14631529Shelbig * Set ndays to the number of days left and compute by linear 14731529Shelbig * search the greatest month which does not start after ndays. We 14831529Shelbig * use the table month1 which provides for each month the number 14931529Shelbig * of days that elapsed in the year before that month. Here the 15031529Shelbig * year 1582 is special, as 10 days are left out in October to 15131529Shelbig * resynchronize the calendar with the earth's orbit. October 4th 15231529Shelbig * 1582 is followed by October 15th 1582. We use the "switch" 15331529Shelbig * table month1s for this year. 15431529Shelbig */ 15531529Shelbig ndays = ndays - r; 15631529Shelbig if (idt.y == 1582) 15731529Shelbig montht = month1s; 15831529Shelbig else 15931529Shelbig montht = month1; 16031529Shelbig 16131529Shelbig for (idt.m = 11; montht[idt.m] > ndays; idt.m--) 16231529Shelbig ; 16331529Shelbig 16431529Shelbig idt.d = ndays - montht[idt.m]; /* the rest is the day in month */ 16531529Shelbig 16631529Shelbig /* Advance ten days deleted from October if after switch in Oct 1582 */ 16731529Shelbig if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d) 16831529Shelbig idt.d += 10; 16931529Shelbig 17031529Shelbig /* return external representation of found date */ 17131529Shelbig return (idt2date(dt, &idt)); 17231529Shelbig} 17331529Shelbig 17431529Shelbig/* 17531529Shelbig * Return the number of days since March 1st of the year zero. The date is 17631529Shelbig * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This 17731529Shelbig * is the reverse of gdate. 17831529Shelbig */ 17931529Shelbigint 18031529Shelbigndaysg(date *dt) 18131529Shelbig{ 18231529Shelbig date idt; /* Internal date representation */ 18331529Shelbig 18431529Shelbig if (date2idt(&idt, dt) == NULL) 18531529Shelbig return (-1); 18631529Shelbig return (ndaysgi(&idt)); 18731529Shelbig} 18831529Shelbig 18931529Shelbig/* 19031529Shelbig * Same as above, but with the Gregorian date given in internal 19131529Shelbig * representation. 19231529Shelbig */ 19331529Shelbigstatic int 19431529Shelbigndaysgi(date *idt) 19531529Shelbig{ 19631529Shelbig int nd; /* Number of days--return value */ 19731529Shelbig 19831529Shelbig /* Cache nswitch if not already done */ 19931529Shelbig if (nswitch == 0) 20031529Shelbig nswitch = ndaysji(&jiswitch); 20131529Shelbig 20231529Shelbig /* 20331529Shelbig * Assume Julian calendar and adapt to Gregorian if necessary, i. e. 20431529Shelbig * younger than nswitch. Gregori deleted 20531529Shelbig * the ten days from Oct 5th to Oct 14th 1582. 20631529Shelbig * Thereafter years which are multiples of 100 and not multiples 20731529Shelbig * of 400 were not leap years anymore. 20831529Shelbig * This makes the average length of a year 20931529Shelbig * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical 21031529Shelbig * year measures 365.2422d. So in 10000/3 years we are 21131529Shelbig * again one day ahead of the earth. Sigh :-) 21231529Shelbig * (d is the average length of a day and tropical year is the 21331529Shelbig * time from one spring point to the next.) 21431529Shelbig */ 21531529Shelbig if ((nd = ndaysji(idt)) == -1) 21631529Shelbig return (-1); 21731529Shelbig if (idt->y >= 1600) 21831529Shelbig nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400); 21931529Shelbig else if (nd > nswitch) 22031529Shelbig nd -= 10; 22131529Shelbig return (nd); 22231529Shelbig} 22331529Shelbig 22431529Shelbig/* 22531529Shelbig * Compute the week number from the number of days since March 1st year 0. 22631529Shelbig * The weeks are numbered per year starting with 1. If the first 22731529Shelbig * week of a year includes at least four days of that year it is week 1, 22831529Shelbig * otherwise it gets the number of the last week of the previous year. 22931529Shelbig * The variable y will be filled with the year that contains the greater 23031529Shelbig * part of the week. 23131529Shelbig */ 23231529Shelbigint 23331529Shelbigweek(int nd, int *y) 23431529Shelbig{ 23531529Shelbig date dt; 23631529Shelbig int fw; /* 1st day of week 1 of previous, this and 23731529Shelbig * next year */ 23831529Shelbig gdate(nd, &dt); 23931529Shelbig for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--) 24031529Shelbig ; 24131529Shelbig return ((nd - fw) / 7 + 1); 24231529Shelbig} 24331529Shelbig 24431529Shelbig/* return the first day of week 1 of year y */ 24531529Shelbigstatic int 24631529Shelbigfirstweek(int y) 24731529Shelbig{ 24831529Shelbig date idt; 24931529Shelbig int nd, wd; 25031529Shelbig 25131529Shelbig idt.y = y - 1; /* internal representation of y-1-1 */ 25231529Shelbig idt.m = 10; 25331529Shelbig idt.d = 0; 25431529Shelbig 25531529Shelbig nd = ndaysgi(&idt); 25631529Shelbig /* 25731529Shelbig * If more than 3 days of this week are in the preceding year, the 25831529Shelbig * next week is week 1 (and the next monday is the answer), 25931529Shelbig * otherwise this week is week 1 and the last monday is the 26031529Shelbig * answer. 26131529Shelbig */ 26231529Shelbig if ((wd = weekday(nd)) > 3) 26331529Shelbig return (nd - wd + 7); 26431529Shelbig else 26531529Shelbig return (nd - wd); 26631529Shelbig} 26731529Shelbig 26831529Shelbig/* return the weekday (Mo = 0 .. Su = 6) */ 26931529Shelbigint 27031529Shelbigweekday(int nd) 27131529Shelbig{ 27231529Shelbig date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */ 27331529Shelbig static int nmonday; /* ... which is a monday */ 27431529Shelbig 27531529Shelbig /* Cache the daynumber of one monday */ 27631529Shelbig if (nmonday == 0) 27731529Shelbig nmonday = ndaysgi(&dmondaygi); 27831529Shelbig 27931529Shelbig /* return (nd - nmonday) modulo 7 which is the weekday */ 28031529Shelbig nd = (nd - nmonday) % 7; 28131529Shelbig if (nd < 0) 28231529Shelbig return (nd + 7); 28331529Shelbig else 28431529Shelbig return (nd); 28531529Shelbig} 28631529Shelbig 28731529Shelbig/* 28831529Shelbig * Convert a date to internal date representation: The year starts on 28931529Shelbig * March 1st, month and day numbering start at zero. E. g. March 1st of 29031529Shelbig * year zero is written as y=0, m=0, d=0. 29131529Shelbig */ 29231529Shelbigstatic date * 29331529Shelbigdate2idt(date *idt, date *dt) 29431529Shelbig{ 29531529Shelbig 29631529Shelbig idt->d = dt->d - 1; 29731529Shelbig if (dt->m > 2) { 29831529Shelbig idt->m = dt->m - 3; 29931529Shelbig idt->y = dt->y; 30031529Shelbig } else { 30131529Shelbig idt->m = dt->m + 9; 30231529Shelbig idt->y = dt->y - 1; 30331529Shelbig } 30431529Shelbig if (idt->m < 0 || idt->m > 11 || idt->y < 0) 30531529Shelbig return (NULL); 30631529Shelbig else 30731529Shelbig return idt; 30831529Shelbig} 30931529Shelbig 31031529Shelbig/* Reverse of date2idt */ 31131529Shelbigstatic date * 31231529Shelbigidt2date(date *dt, date *idt) 31331529Shelbig{ 31431529Shelbig 31531529Shelbig dt->d = idt->d + 1; 31631529Shelbig if (idt->m < 10) { 31731529Shelbig dt->m = idt->m + 3; 31831529Shelbig dt->y = idt->y; 31931529Shelbig } else { 32031529Shelbig dt->m = idt->m - 9; 32131529Shelbig dt->y = idt->y + 1; 32231529Shelbig } 32331529Shelbig if (dt->m < 1) 32431529Shelbig return (NULL); 32531529Shelbig else 32631529Shelbig return (dt); 32731529Shelbig} 328