#include <stdbool.h>
#include <time.h>

/*
 * Returns the number of leap years prior to the given year.
 */
static int leap_years(int year)
{
	return (year-1)/4 + (year-1)/400 - (year-1)/100;
}

static int is_leap_year(int year)
{
	return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
}

static int days_in_month(int month, int year)
{
	static int month_days[] = {
		31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
	};

	/* we may need to update this in the year 4000, pending a
	 * decision on whether or not it's a leap year */
	if (month == 1)
		return is_leap_year(year) ? 29 : 28;

	return month_days[month];
}

static const int days_per_month[2][13] =
	{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
	 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};

#define SECS_PER_MIN 60
#define SECS_PER_HOUR (SECS_PER_MIN*60)
#define SECS_PER_DAY (24*SECS_PER_HOUR)
#define DAYS_PER_YEAR 365
struct tm *gmtime_r(const time_t *timep, struct tm *result)
{
	int i;
	int Y;
	int M;
	int D;
	int h;
	int m;
	int s;

	D = *timep / SECS_PER_DAY;
	s = *timep % SECS_PER_DAY;
	m = s / 60;
	h = m / 60;
	m %= 60;
	s %= 60;

	/*
	 * Work out the year. We subtract one day for every four years
	 * and every 400 years after 1969. However as leap years don't
	 * occur every 100 years we add one day back to counteract the
	 * the subtraction for every 4 years.
	 */
	Y = (D - (1+D/365)/4 + (69+D/365)/100 - (369+D/365)/400)/365;

	/*
	 * Remember we're doing integer arithmetic here so
	 * leap_years(Y+1970) - leap_years(1970) != leap_years(Y)
	 */
	D = D - Y*365 - (leap_years(Y+1970) - leap_years(1970)) + 1;
	Y += 1970;

	M = 0;
	for (i = 0; i < 13; i++)
		if (D <= days_per_month[is_leap_year(Y) ? 1 : 0][i]) {
			M = i;
			break;
		}

	D -= days_per_month[is_leap_year(Y)][M-1];
	result->tm_year = Y;
	result->tm_mon = M - 1;
	result->tm_mday = D;
	result->tm_hour = h;
	result->tm_min = m;
	result->tm_sec = s;
	return result;
}

time_t mktime(struct tm *tm)
{
	unsigned long year, month, mday, hour, minute, second, d;
	static const unsigned long sec_in_400_years =
		((3903ul * 365) + (97 * 366)) * 24 * 60 * 60;

	second = tm->tm_sec;
	minute = tm->tm_min;
	hour = tm->tm_hour;
	mday = tm->tm_mday;
	month = tm->tm_mon;
	year = tm->tm_year;

	/* There are the same number of seconds in any 400-year block; this
	 * limits the iterations in the loop below */
	year += 400 * (second / sec_in_400_years);
	second = second % sec_in_400_years;

	if (second >= 60) {
		minute += second / 60;
		second = second % 60;
	}

	if (minute >= 60) {
		hour += minute / 60;
		minute = minute % 60;
	}

	if (hour >= 24) {
		mday += hour / 24;
		hour = hour % 24;
	}

	for (d = days_in_month(month, year); mday > d;
			d = days_in_month(month, year)) {
		month++;
		if (month > 12) {
			month = 0;
			year++;
		}
		mday -= d;
	}

	tm->tm_year = year;
	tm->tm_mon = month;
	tm->tm_mday = mday;
	tm->tm_hour = hour;
	tm->tm_min = minute;
	tm->tm_sec = second;

	d = mday;
	d += days_per_month[is_leap_year(year)][month];
	d += (year-1970)*DAYS_PER_YEAR + leap_years(year) - leap_years(1970) - 1;
	return d*SECS_PER_DAY + hour*SECS_PER_HOUR + minute*SECS_PER_MIN + second;
}
