/*
**	Functions that sit on top of the libc time functions.  Their
**	interface is as close to that of the absolute and julian date/time
**	functions in this library as possible.
**
**	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"

extern void _date_time_components ();

time_t 
gregorian_to_unix (second, minute, hour, day, month, year)
	int 	second, 
		minute, 
		hour, 
		day, 
		month, 
		year;
{
	struct tm gmttime;
	struct tm *tmptime;
	time_t t, tmp_t;
	int the_offset, tmp_hour, tmp_minute;

	bzero(&gmttime, sizeof(gmttime));
	gmttime.tm_sec = second;
	gmttime.tm_min = minute;
	gmttime.tm_hour = hour;
	gmttime.tm_mday = day;
	gmttime.tm_mon = month - 1;
	gmttime.tm_year = year - 1900;

	t = mktime (&gmttime);
	tmptime = localtime (&t);

	/*
	**	Now we have the offset we were looking for, maybe.  It will
	**	be wrong during the period of time preceding or following
	**	the daylight savings time switch.  We check the hour and 
	**	minute value of the adjusted time.  If the hour is wrong, 
	**	we adjust the offset by 1 hour and start over.  If the 
	**	minute is wront, we adjust the offset by 1/2 hour and start
	**	over.  This will take care of most timezone.  It will
	**	fail in the few oddball timezones where 15 minute 
	**	adjustments are made.
	*/
	the_offset = seconds_from_gmt(0);

	tmp_t = gregorian_to_unix_at_offset (the_offset, second, minute, hour,
					    day, month, year);

	if ((tmp_hour = unix_to_hour (tmp_t + seconds_from_gmt (tmp_t))) 
	    != hour)
	{
#ifdef DEBUG
		fprintf (stderr, "Adjusting offset...\n");
#endif
		if (tmp_hour > hour)
			the_offset += (60 * 60);
		else
			the_offset -= (60 * 60);

		tmp_t = gregorian_to_unix_at_offset (the_offset, 
						     second, minute, hour,
						     day, month, year);

	}
	else if ((tmp_minute = unix_to_minute (tmp_t + 
					       seconds_from_gmt (tmp_t))) 
		 != minute)
	{
	}

	return tmp_t;
}

#ifdef notdef
/*
**	Algorithm works except for during 24 hour period surrounding the 
**	switchover between DST and EST.
*/
time_t 
gregorian_to_unix (int second, int minute, int hour, 
		   int day, int month, int year)
{
	struct tm gmttime;
	struct tm *tmptime;
	time_t t;
	int the_offset;

	bzero(&gmttime, sizeof(gmttime));
	gmttime.tm_sec = second;
	gmttime.tm_min = minute;
	gmttime.tm_hour = hour;
	gmttime.tm_mday = day;
	gmttime.tm_mon = month - 1;
	gmttime.tm_year = year - 1900;

	t = mktime (&gmttime);
	tmptime = localtime (&t);
	/*
	**	Now we have the offset we were looking for.
	*/
	the_offset = seconds_from_gmt(0);

	return gregorian_to_unix_at_offset (the_offset, second, minute, hour,
					    day, month, year);
}
#endif

time_t 
gregorian_to_unix_at_offset (gmt_offset, second, minute, hour, day, month, year)
	int 	gmt_offset, 
		second, 
		minute, 
		hour, 
		day, 
		month, 
		year;
{
	struct tm newtime;

	newtime.tm_sec = second;
	newtime.tm_min = minute;
	newtime.tm_hour = hour;
	newtime.tm_mday = day;
	newtime.tm_mon = month - 1;
	newtime.tm_year = year - 1900;
	
	return mktime (&newtime);
}

time_t
current_unix_time ()
{
	struct timeval t;

	gettimeofday (&t, (struct timezone *) NULL);
	
	return t.tv_sec;
}
	
/* 
** 	unix_to_second(), unix_to_minute(), unix_to_hour(), 
**	unix_to_gregorian_day(), unix_to_gregorian_month(),
**	unix_to_gregorian_year() extract the required component 
**	from a unix date/time value (time_t) and assume it is
**	GMT.
*/

int
unix_to_second (time)
	time_t	time;
{
	int second, minute, hour, day, month, year;

	_date_time_components (time, &second, &minute, &hour, 
			       &day, &month, &year);

	return second;
}

int
unix_to_minute (time)
	time_t	time;
{
	int second, minute, hour, day, month, year;

	_date_time_components (time, &second, &minute, &hour, 
			       &day, &month, &year);

	return minute;
}

int
unix_to_hour (time)
	time_t	time;
{
	int second, minute, hour, day, month, year;

	_date_time_components (time, &second, &minute, &hour, 
			       &day, &month, &year);

	return hour;
}

int
unix_to_gregorian_day (time)
	time_t	time;
{
	int second, minute, hour, day, month, year;

	_date_time_components (time, &second, &minute, &hour, 
			       &day, &month, &year);

	return day;
}

int
unix_to_gregorian_month (time)
	time_t	time;
{
	int second, minute, hour, day, month, year;

	_date_time_components (time, &second, &minute, &hour, 
			       &day, &month, &year);

	return month;
}

int
unix_to_gregorian_year (time)
	time_t	time;
{
	int second, minute, hour, day, month, year;

	_date_time_components (time, &second, &minute, &hour, 
			       &day, &month, &year);

	return year;
}

WEEKDAY 
unix_to_weekday (time)
	time_t	time;
{
	struct tm *tmp = gmtime (&time);

	return tmp->tm_wday;
}

/*
**	Extracts all the date and time components at once.
*/
void
_date_time_components (time, second, minute, hour, day, month, year)
	time_t	time;
	int 	*second, 
		*minute, 
		*hour,
		*day, 
		*month, 
		*year;
{
	struct tm *tmp = gmtime (&time);

	*second = tmp->tm_sec;
	*minute = tmp->tm_min;
	*hour = tmp->tm_hour;
	*day = tmp->tm_mday;
	*month = tmp->tm_mon + 1;
	*year = tmp->tm_year + 1900;
}



