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