calendar.c revision 31698
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 * 2631698Shelbig * $Id: calendar.c,v 1.1.1.1 1997/12/04 10:41:49 helbig Exp $ 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 4931698Shelbigtypedef struct date date; 5031698Shelbig 5131529Shelbig/* The last day of Julian calendar, in internal and ndays representation */ 5231529Shelbigstatic int nswitch; /* The last day of Julian calendar */ 5331529Shelbigstatic date jiswitch = {1582, 7, 3}; 5431529Shelbig 5531529Shelbigstatic date *date2idt(date *idt, date *dt); 5631529Shelbigstatic date *idt2date(date *dt, date *idt); 5731529Shelbigstatic int ndaysji(date *idt); 5831529Shelbigstatic int ndaysgi(date *idt); 5931529Shelbigstatic int firstweek(int year); 6031529Shelbig 6131529Shelbig/* 6231529Shelbig * Compute the Julian date from the number of days elapsed since 6331529Shelbig * March 1st of year zero. 6431529Shelbig */ 6531529Shelbigdate * 6631529Shelbigjdate(int ndays, date *dt) 6731529Shelbig{ 6831529Shelbig date idt; /* Internal date representation */ 6931529Shelbig int r; /* hold the rest of days */ 7031529Shelbig 7131529Shelbig /* 7231529Shelbig * Compute the year by starting with an approximation not smaller 7331529Shelbig * than the answer and using linear search for the greatest 7431529Shelbig * year which does not begin after ndays. 7531529Shelbig */ 7631529Shelbig idt.y = ndays / 365; 7731529Shelbig idt.m = 0; 7831529Shelbig idt.d = 0; 7931529Shelbig while ((r = ndaysji(&idt)) > ndays) 8031529Shelbig idt.y--; 8131529Shelbig 8231529Shelbig /* 8331529Shelbig * Set r to the days left in the year and compute the month by 8431529Shelbig * linear search as the largest month that does not begin after r 8531529Shelbig * days. 8631529Shelbig */ 8731529Shelbig r = ndays - r; 8831529Shelbig for (idt.m = 11; month1[idt.m] > r; idt.m--) 8931529Shelbig ; 9031529Shelbig 9131529Shelbig /* Compute the days left in the month */ 9231529Shelbig idt.d = r - month1[idt.m]; 9331529Shelbig 9431529Shelbig /* return external representation of the date */ 9531529Shelbig return (idt2date(dt, &idt)); 9631529Shelbig} 9731529Shelbig 9831529Shelbig/* 9931529Shelbig * Return the number of days since March 1st of the year zero. 10031529Shelbig * The date is given according to Julian calendar. 10131529Shelbig */ 10231529Shelbigint 10331529Shelbigndaysj(date *dt) 10431529Shelbig{ 10531529Shelbig date idt; /* Internal date representation */ 10631529Shelbig 10731529Shelbig if (date2idt(&idt, dt) == NULL) 10831529Shelbig return (-1); 10931529Shelbig else 11031529Shelbig return (ndaysji(&idt)); 11131529Shelbig} 11231529Shelbig 11331529Shelbig/* 11431529Shelbig * Same as above, where the Julian date is given in internal notation. 11531529Shelbig * This formula shows the beauty of this notation. 11631529Shelbig */ 11731529Shelbigstatic int 11831529Shelbigndaysji(date * idt) 11931529Shelbig{ 12031529Shelbig 12131529Shelbig return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4); 12231529Shelbig} 12331529Shelbig 12431529Shelbig/* 12531529Shelbig * Compute the date according to the Gregorian calendar from the number of 12631529Shelbig * days since March 1st, year zero. The date computed will be Julian if it 12731529Shelbig * is older than 1582-10-05. This is the reverse of the function ndaysg(). 12831529Shelbig */ 12931529Shelbigdate * 13031529Shelbiggdate(int ndays, date *dt) 13131529Shelbig{ 13231529Shelbig int const *montht; /* month-table */ 13331529Shelbig date idt; /* for internal date representation */ 13431529Shelbig int r; /* holds the rest of days */ 13531529Shelbig 13631529Shelbig /* 13731529Shelbig * Compute the year by starting with an approximation not smaller 13831529Shelbig * than the answer and search linearly for the greatest year not 13931529Shelbig * starting after ndays. 14031529Shelbig */ 14131529Shelbig idt.y = ndays / 365; 14231529Shelbig idt.m = 0; 14331529Shelbig idt.d = 0; 14431529Shelbig while ((r = ndaysgi(&idt)) > ndays) 14531529Shelbig idt.y--; 14631529Shelbig 14731529Shelbig /* 14831529Shelbig * Set ndays to the number of days left and compute by linear 14931529Shelbig * search the greatest month which does not start after ndays. We 15031529Shelbig * use the table month1 which provides for each month the number 15131529Shelbig * of days that elapsed in the year before that month. Here the 15231529Shelbig * year 1582 is special, as 10 days are left out in October to 15331529Shelbig * resynchronize the calendar with the earth's orbit. October 4th 15431529Shelbig * 1582 is followed by October 15th 1582. We use the "switch" 15531529Shelbig * table month1s for this year. 15631529Shelbig */ 15731529Shelbig ndays = ndays - r; 15831529Shelbig if (idt.y == 1582) 15931529Shelbig montht = month1s; 16031529Shelbig else 16131529Shelbig montht = month1; 16231529Shelbig 16331529Shelbig for (idt.m = 11; montht[idt.m] > ndays; idt.m--) 16431529Shelbig ; 16531529Shelbig 16631529Shelbig idt.d = ndays - montht[idt.m]; /* the rest is the day in month */ 16731529Shelbig 16831529Shelbig /* Advance ten days deleted from October if after switch in Oct 1582 */ 16931529Shelbig if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d) 17031529Shelbig idt.d += 10; 17131529Shelbig 17231529Shelbig /* return external representation of found date */ 17331529Shelbig return (idt2date(dt, &idt)); 17431529Shelbig} 17531529Shelbig 17631529Shelbig/* 17731529Shelbig * Return the number of days since March 1st of the year zero. The date is 17831529Shelbig * assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This 17931529Shelbig * is the reverse of gdate. 18031529Shelbig */ 18131529Shelbigint 18231529Shelbigndaysg(date *dt) 18331529Shelbig{ 18431529Shelbig date idt; /* Internal date representation */ 18531529Shelbig 18631529Shelbig if (date2idt(&idt, dt) == NULL) 18731529Shelbig return (-1); 18831529Shelbig return (ndaysgi(&idt)); 18931529Shelbig} 19031529Shelbig 19131529Shelbig/* 19231529Shelbig * Same as above, but with the Gregorian date given in internal 19331529Shelbig * representation. 19431529Shelbig */ 19531529Shelbigstatic int 19631529Shelbigndaysgi(date *idt) 19731529Shelbig{ 19831529Shelbig int nd; /* Number of days--return value */ 19931529Shelbig 20031529Shelbig /* Cache nswitch if not already done */ 20131529Shelbig if (nswitch == 0) 20231529Shelbig nswitch = ndaysji(&jiswitch); 20331529Shelbig 20431529Shelbig /* 20531529Shelbig * Assume Julian calendar and adapt to Gregorian if necessary, i. e. 20631529Shelbig * younger than nswitch. Gregori deleted 20731529Shelbig * the ten days from Oct 5th to Oct 14th 1582. 20831529Shelbig * Thereafter years which are multiples of 100 and not multiples 20931529Shelbig * of 400 were not leap years anymore. 21031529Shelbig * This makes the average length of a year 21131529Shelbig * 365d +.25d - .01d + .0025d = 365.2425d. But the tropical 21231529Shelbig * year measures 365.2422d. So in 10000/3 years we are 21331529Shelbig * again one day ahead of the earth. Sigh :-) 21431529Shelbig * (d is the average length of a day and tropical year is the 21531529Shelbig * time from one spring point to the next.) 21631529Shelbig */ 21731529Shelbig if ((nd = ndaysji(idt)) == -1) 21831529Shelbig return (-1); 21931529Shelbig if (idt->y >= 1600) 22031529Shelbig nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400); 22131529Shelbig else if (nd > nswitch) 22231529Shelbig nd -= 10; 22331529Shelbig return (nd); 22431529Shelbig} 22531529Shelbig 22631529Shelbig/* 22731529Shelbig * Compute the week number from the number of days since March 1st year 0. 22831529Shelbig * The weeks are numbered per year starting with 1. If the first 22931529Shelbig * week of a year includes at least four days of that year it is week 1, 23031529Shelbig * otherwise it gets the number of the last week of the previous year. 23131529Shelbig * The variable y will be filled with the year that contains the greater 23231529Shelbig * part of the week. 23331529Shelbig */ 23431529Shelbigint 23531529Shelbigweek(int nd, int *y) 23631529Shelbig{ 23731529Shelbig date dt; 23831529Shelbig int fw; /* 1st day of week 1 of previous, this and 23931529Shelbig * next year */ 24031529Shelbig gdate(nd, &dt); 24131529Shelbig for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--) 24231529Shelbig ; 24331529Shelbig return ((nd - fw) / 7 + 1); 24431529Shelbig} 24531529Shelbig 24631529Shelbig/* return the first day of week 1 of year y */ 24731529Shelbigstatic int 24831529Shelbigfirstweek(int y) 24931529Shelbig{ 25031529Shelbig date idt; 25131529Shelbig int nd, wd; 25231529Shelbig 25331529Shelbig idt.y = y - 1; /* internal representation of y-1-1 */ 25431529Shelbig idt.m = 10; 25531529Shelbig idt.d = 0; 25631529Shelbig 25731529Shelbig nd = ndaysgi(&idt); 25831529Shelbig /* 25931529Shelbig * If more than 3 days of this week are in the preceding year, the 26031529Shelbig * next week is week 1 (and the next monday is the answer), 26131529Shelbig * otherwise this week is week 1 and the last monday is the 26231529Shelbig * answer. 26331529Shelbig */ 26431529Shelbig if ((wd = weekday(nd)) > 3) 26531529Shelbig return (nd - wd + 7); 26631529Shelbig else 26731529Shelbig return (nd - wd); 26831529Shelbig} 26931529Shelbig 27031529Shelbig/* return the weekday (Mo = 0 .. Su = 6) */ 27131529Shelbigint 27231529Shelbigweekday(int nd) 27331529Shelbig{ 27431529Shelbig date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */ 27531529Shelbig static int nmonday; /* ... which is a monday */ 27631529Shelbig 27731529Shelbig /* Cache the daynumber of one monday */ 27831529Shelbig if (nmonday == 0) 27931529Shelbig nmonday = ndaysgi(&dmondaygi); 28031529Shelbig 28131529Shelbig /* return (nd - nmonday) modulo 7 which is the weekday */ 28231529Shelbig nd = (nd - nmonday) % 7; 28331529Shelbig if (nd < 0) 28431529Shelbig return (nd + 7); 28531529Shelbig else 28631529Shelbig return (nd); 28731529Shelbig} 28831529Shelbig 28931529Shelbig/* 29031529Shelbig * Convert a date to internal date representation: The year starts on 29131529Shelbig * March 1st, month and day numbering start at zero. E. g. March 1st of 29231529Shelbig * year zero is written as y=0, m=0, d=0. 29331529Shelbig */ 29431529Shelbigstatic date * 29531529Shelbigdate2idt(date *idt, date *dt) 29631529Shelbig{ 29731529Shelbig 29831529Shelbig idt->d = dt->d - 1; 29931529Shelbig if (dt->m > 2) { 30031529Shelbig idt->m = dt->m - 3; 30131529Shelbig idt->y = dt->y; 30231529Shelbig } else { 30331529Shelbig idt->m = dt->m + 9; 30431529Shelbig idt->y = dt->y - 1; 30531529Shelbig } 30631529Shelbig if (idt->m < 0 || idt->m > 11 || idt->y < 0) 30731529Shelbig return (NULL); 30831529Shelbig else 30931529Shelbig return idt; 31031529Shelbig} 31131529Shelbig 31231529Shelbig/* Reverse of date2idt */ 31331529Shelbigstatic date * 31431529Shelbigidt2date(date *dt, date *idt) 31531529Shelbig{ 31631529Shelbig 31731529Shelbig dt->d = idt->d + 1; 31831529Shelbig if (idt->m < 10) { 31931529Shelbig dt->m = idt->m + 3; 32031529Shelbig dt->y = idt->y; 32131529Shelbig } else { 32231529Shelbig dt->m = idt->m - 9; 32331529Shelbig dt->y = idt->y + 1; 32431529Shelbig } 32531529Shelbig if (dt->m < 1) 32631529Shelbig return (NULL); 32731529Shelbig else 32831529Shelbig return (dt); 32931529Shelbig} 330