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