/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 1997 Wolfgang Helbig4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/cdefs.h>29#include "calendar.h"3031#ifndef NULL32#define NULL 033#endif3435/*36* For each month tabulate the number of days elapsed in a year before the37* month. This assumes the internal date representation, where a year38* starts on March 1st. So we don't need a special table for leap years.39* But we do need a special table for the year 1582, since 10 days are40* deleted in October. This is month1s for the switch from Julian to41* Gregorian calendar.42*/43static int const month1[] =44{0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337};45/* M A M J J A S O N D J */46static int const month1s[]=47{0, 31, 61, 92, 122, 153, 184, 214, 235, 265, 296, 327};4849typedef struct date date;5051/* The last day of Julian calendar, in internal and ndays representation */52static int nswitch; /* The last day of Julian calendar */53static date jiswitch = {1582, 7, 3};5455static date *date2idt(date *idt, date *dt);56static date *idt2date(date *dt, date *idt);57static int ndaysji(date *idt);58static int ndaysgi(date *idt);59static int firstweek(int year);6061/*62* Compute the Julian date from the number of days elapsed since63* March 1st of year zero.64*/65date *66jdate(int ndays, date *dt)67{68date idt; /* Internal date representation */69int r; /* hold the rest of days */7071/*72* Compute the year by starting with an approximation not smaller73* than the answer and using linear search for the greatest74* year which does not begin after ndays.75*/76idt.y = ndays / 365;77idt.m = 0;78idt.d = 0;79while ((r = ndaysji(&idt)) > ndays)80idt.y--;8182/*83* Set r to the days left in the year and compute the month by84* linear search as the largest month that does not begin after r85* days.86*/87r = ndays - r;88for (idt.m = 11; month1[idt.m] > r; idt.m--)89;9091/* Compute the days left in the month */92idt.d = r - month1[idt.m];9394/* return external representation of the date */95return (idt2date(dt, &idt));96}9798/*99* Return the number of days since March 1st of the year zero.100* The date is given according to Julian calendar.101*/102int103ndaysj(date *dt)104{105date idt; /* Internal date representation */106107if (date2idt(&idt, dt) == NULL)108return (-1);109else110return (ndaysji(&idt));111}112113/*114* Same as above, where the Julian date is given in internal notation.115* This formula shows the beauty of this notation.116*/117static int118ndaysji(date * idt)119{120121return (idt->d + month1[idt->m] + idt->y * 365 + idt->y / 4);122}123124/*125* Compute the date according to the Gregorian calendar from the number of126* days since March 1st, year zero. The date computed will be Julian if it127* is older than 1582-10-05. This is the reverse of the function ndaysg().128*/129date *130gdate(int ndays, date *dt)131{132int const *montht; /* month-table */133date idt; /* for internal date representation */134int r; /* holds the rest of days */135136/*137* Compute the year by starting with an approximation not smaller138* than the answer and search linearly for the greatest year not139* starting after ndays.140*/141idt.y = ndays / 365;142idt.m = 0;143idt.d = 0;144while ((r = ndaysgi(&idt)) > ndays)145idt.y--;146147/*148* Set ndays to the number of days left and compute by linear149* search the greatest month which does not start after ndays. We150* use the table month1 which provides for each month the number151* of days that elapsed in the year before that month. Here the152* year 1582 is special, as 10 days are left out in October to153* resynchronize the calendar with the earth's orbit. October 4th154* 1582 is followed by October 15th 1582. We use the "switch"155* table month1s for this year.156*/157ndays = ndays - r;158if (idt.y == 1582)159montht = month1s;160else161montht = month1;162163for (idt.m = 11; montht[idt.m] > ndays; idt.m--)164;165166idt.d = ndays - montht[idt.m]; /* the rest is the day in month */167168/* Advance ten days deleted from October if after switch in Oct 1582 */169if (idt.y == jiswitch.y && idt.m == jiswitch.m && jiswitch.d < idt.d)170idt.d += 10;171172/* return external representation of found date */173return (idt2date(dt, &idt));174}175176/*177* Return the number of days since March 1st of the year zero. The date is178* assumed Gregorian if younger than 1582-10-04 and Julian otherwise. This179* is the reverse of gdate.180*/181int182ndaysg(date *dt)183{184date idt; /* Internal date representation */185186if (date2idt(&idt, dt) == NULL)187return (-1);188return (ndaysgi(&idt));189}190191/*192* Same as above, but with the Gregorian date given in internal193* representation.194*/195static int196ndaysgi(date *idt)197{198int nd; /* Number of days--return value */199200/* Cache nswitch if not already done */201if (nswitch == 0)202nswitch = ndaysji(&jiswitch);203204/*205* Assume Julian calendar and adapt to Gregorian if necessary, i. e.206* younger than nswitch. Gregori deleted207* the ten days from Oct 5th to Oct 14th 1582.208* Thereafter years which are multiples of 100 and not multiples209* of 400 were not leap years anymore.210* This makes the average length of a year211* 365d +.25d - .01d + .0025d = 365.2425d. But the tropical212* year measures 365.2422d. So in 10000/3 years we are213* again one day ahead of the earth. Sigh :-)214* (d is the average length of a day and tropical year is the215* time from one spring point to the next.)216*/217if ((nd = ndaysji(idt)) == -1)218return (-1);219if (idt->y >= 1600)220nd = (nd - 10 - (idt->y - 1600) / 100 + (idt->y - 1600) / 400);221else if (nd > nswitch)222nd -= 10;223return (nd);224}225226/*227* Compute the week number from the number of days since March 1st year 0.228* The weeks are numbered per year starting with 1. If the first229* week of a year includes at least four days of that year it is week 1,230* otherwise it gets the number of the last week of the previous year.231* The variable y will be filled with the year that contains the greater232* part of the week.233*/234int235week(int nd, int *y)236{237date dt;238int fw; /* 1st day of week 1 of previous, this and239* next year */240gdate(nd, &dt);241for (*y = dt.y + 1; nd < (fw = firstweek(*y)); (*y)--)242;243return ((nd - fw) / 7 + 1);244}245246/* return the first day of week 1 of year y */247static int248firstweek(int y)249{250date idt;251int nd, wd;252253idt.y = y - 1; /* internal representation of y-1-1 */254idt.m = 10;255idt.d = 0;256257nd = ndaysgi(&idt);258/*259* If more than 3 days of this week are in the preceding year, the260* next week is week 1 (and the next monday is the answer),261* otherwise this week is week 1 and the last monday is the262* answer.263*/264if ((wd = weekday(nd)) > 3)265return (nd - wd + 7);266else267return (nd - wd);268}269270/* return the weekday (Mo = 0 .. Su = 6) */271int272weekday(int nd)273{274date dmondaygi = {1997, 8, 16}; /* Internal repr. of 1997-11-17 */275static int nmonday; /* ... which is a monday */276277/* Cache the daynumber of one monday */278if (nmonday == 0)279nmonday = ndaysgi(&dmondaygi);280281/* return (nd - nmonday) modulo 7 which is the weekday */282nd = (nd - nmonday) % 7;283if (nd < 0)284return (nd + 7);285else286return (nd);287}288289/*290* Convert a date to internal date representation: The year starts on291* March 1st, month and day numbering start at zero. E. g. March 1st of292* year zero is written as y=0, m=0, d=0.293*/294static date *295date2idt(date *idt, date *dt)296{297298idt->d = dt->d - 1;299if (dt->m > 2) {300idt->m = dt->m - 3;301idt->y = dt->y;302} else {303idt->m = dt->m + 9;304idt->y = dt->y - 1;305}306if (idt->m < 0 || idt->m > 11 || idt->y < 0)307return (NULL);308else309return idt;310}311312/* Reverse of date2idt */313static date *314idt2date(date *dt, date *idt)315{316317dt->d = idt->d + 1;318if (idt->m < 10) {319dt->m = idt->m + 3;320dt->y = idt->y;321} else {322dt->m = idt->m - 9;323dt->y = idt->y + 1;324}325if (dt->m < 1)326return (NULL);327else328return (dt);329}330331332