/*
**	Functions that do absolute date calculations.
**	An absolute date is the number of days since 12/31/1 BC.  
**	These functions are mostly translations of emacs lisp code.
**
**	This work is derived from GNU software, so you should read
**	the GNU license agreement if you are plannning to use
**	them in commercial software.
*/
#include <stdio.h>
#include <common/portability.h>

#include "timelib.h"
#include "absolute.h"

extern void _date_components ();

/*
**	Number of days since 12/31/1 BC.
**
**	This assumes that that days, months, and years are numbered 1-x.
**	This is important if you are manipulating the Unix tm structure.
*/
ABSOLUTE_DAY
gregorian_to_absolute (day, month, year)
	int 	day, 
		month, 
		year;
{
	return (day_number (day, month, year)
		+ (365 * (year - 1))	/* Days in prior years */
		+ ((year - 1) / 4)	/* Julian leap years */
		- ((year - 1) / 100)	/* Century years are not leap years */
		+ ((year - 1) / 400));	/* Gregorian leap years */
}

ABSOLUTE_DAY
current_absolute_date ()
{
	struct timeval t;
	struct tm *tmp;

	gettimeofday (&t, (struct timezone *) NULL);
	tmp = localtime (&(t.tv_sec));

	return gregorian_to_absolute (tmp->tm_mday, 
			     tmp->tm_mon + 1, 
			     tmp->tm_year + 1900);
}

/*
**	absolute_to_gregorian_day(), absolute_to_gregorian_month(), 
**	absolute_to_gregorian_year() extract the required component 
**	from an absolute date.
*/

int
absolute_to_gregorian_day (date)
	ABSOLUTE_DAY date;
{
	int day, month, year;

	_date_components (date, &day, &month, &year);

	return day;
}

int
absolute_to_gregorian_month (date)
	ABSOLUTE_DAY	date;
{
	int day, month, year;

	_date_components (date, &day, &month, &year);

	return month;
}

int
absolute_to_gregorian_year (date)
        ABSOLUTE_DAY    date;
{
	int day, month, year;

	_date_components (date, &day, &month, &year);

	return year;
}

/*
**	Extracts all the date components at once.
*/
void
_date_components (date, day, month, year)
	ABSOLUTE_DAY date; 
	int 	*day, 
		*month, 
		*year;
{
    int d0, d1, d2, d3, n400, n100, n4, n1;

    d0 = date - 1;
    n400 = d0 / 146097;
    d1 = d0 % 146097;
    n100 = d1 / 36524;
    d2 = d1 % 36524;
    n4 = d2 / 1461;
    d3 = d2 % 1461;
    n1 = d3 / 365;

    *day = d3 % 365 + 1;
    *year = 400 * n400 + 100 * n100 + 4 * n4 + n1;

    if (n100 == 4 || n1 == 4)
    {
	*month = 12;
	*day = 31;
    }
    else
    {
	int month_days;

	*year += 1;
	*month = 1;

	while ((month_days = month_length(*month, *year)) < *day)
	{
	    *day -= month_days;
	    *month += 1;
	}
    }
}

WEEKDAY
absolute_to_weekday (date)
	ABSOLUTE_DAY	date;
{
	return (WEEKDAY) date % 7;
}
