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