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