/*
 * mktime - convert local time into calendar time
 */
/* $Id$ */

#include <time.h>
#include <limits.h>
#include "loc_time.h"

/* The code assumes that unsigned long can be converted to time_t.
 * A time_t should not be wider than unsigned long, since this would mean
 * that the check for overflow at the end could fail.
 */
time_t
mktime(register struct tm* timep)
{
	register long day, year;
	register int tm_year;
	int yday, month;
	register unsigned long seconds;
	int overflow;
	unsigned dst;

	timep->tm_min += timep->tm_sec / 60;
	timep->tm_sec %= 60;
	if (timep->tm_sec < 0)
	{
		timep->tm_sec += 60;
		timep->tm_min--;
	}
	timep->tm_hour += timep->tm_min / 60;
	timep->tm_min = timep->tm_min % 60;
	if (timep->tm_min < 0)
	{
		timep->tm_min += 60;
		timep->tm_hour--;
	}
	day = timep->tm_hour / 24;
	timep->tm_hour = timep->tm_hour % 24;
	if (timep->tm_hour < 0)
	{
		timep->tm_hour += 24;
		day--;
	}
	timep->tm_year += timep->tm_mon / 12;
	timep->tm_mon %= 12;
	if (timep->tm_mon < 0)
	{
		timep->tm_mon += 12;
		timep->tm_year--;
	}
	day += (timep->tm_mday - 1);
	while (day < 0)
	{
		day += YEARSIZE(YEAR0 + timep->tm_year - 1);
		timep->tm_year--;
	}
	while (day >= YEARSIZE(YEAR0 + timep->tm_year))
	{
		day -= YEARSIZE(YEAR0 + timep->tm_year);
		timep->tm_year++;
	}
	while (day >= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon])
	{
		day -= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon];
		if (++(timep->tm_mon) == 12)
		{
			timep->tm_mon = 0;
			timep->tm_year++;
		}
	}
	timep->tm_mday = day + 1;
	_tzset(); /* set timezone and dst info  */
	year = EPOCH_YR;
	if (timep->tm_year < year - YEAR0)
		return (time_t)-1;
	seconds = 0;
	day = 0; /* means days since day 0 now */
	overflow = 0;

/* Assume that when day becomes negative, there will certainly
	 * be overflow on seconds.
	 * The check for overflow needs not to be done for leapyears
	 * divisible by 400.
	 * The code only works when year (1970) is not a leapyear.
	 */
#if EPOCH_YR != 1970
#error EPOCH_YR != 1970
#endif
	tm_year = timep->tm_year + YEAR0;

	if (LONG_MAX / 365 < tm_year - year)
		overflow++;
	day = (tm_year - year) * 365;
	if (LONG_MAX - day < (tm_year - year) / 4 + 1)
		overflow++;
	day += (tm_year - year) / 4
	    + ((tm_year % 4) && tm_year % 4 < year % 4);
	day -= (tm_year - year) / 100
	    + ((tm_year % 100) && tm_year % 100 < year % 100);
	day += (tm_year - year) / 400
	    + ((tm_year % 400) && tm_year % 400 < year % 400);

	yday = month = 0;
	while (month < timep->tm_mon)
	{
		yday += _ytab[LEAPYEAR(tm_year)][month];
		month++;
	}
	yday += (timep->tm_mday - 1);
	if (day + yday < 0)
		overflow++;
	day += yday;

	timep->tm_yday = yday;
	timep->tm_wday = (day + 4) % 7; /* day 0 was thursday (4) */

	seconds = ((timep->tm_hour * 60L) + timep->tm_min) * 60L + timep->tm_sec;

	if ((TIME_MAX - seconds) / SECS_DAY < day)
		overflow++;
	seconds += day * SECS_DAY;

	/* Now adjust according to timezone and daylight saving time */

	if (((_timezone > 0) && (TIME_MAX - _timezone < seconds))
	    || ((_timezone < 0) && (seconds < -_timezone)))
		overflow++;
	seconds += _timezone;

	if (timep->tm_isdst < 0)
		dst = _dstget(timep);
	else if (timep->tm_isdst)
		dst = _dst_off;
	else
		dst = 0;

	if (dst > seconds)
		overflow++; /* dst is always non-negative */
	seconds -= dst;

	if (overflow)
		return (time_t)-1;

	if ((time_t)seconds != seconds)
		return (time_t)-1;
	return (time_t)seconds;
}
