summaryrefslogtreecommitdiffstats
path: root/libc/time.c
blob: 3411bdc0b580fdaa9b4230ab05d123eec8742066 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#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;
}
OpenPOWER on IntegriCloud