summaryrefslogtreecommitdiff
path: root/src/time/__time_to_tm.c
blob: a1ebc4527eb6f1f70b1e64a039c6e573338b4c36 (plain) (blame)
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
#include <time.h>

/* C defines the rounding for division in a nonsensical way */
#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b)))

#define DAYS_PER_400Y (365*400 + 97)
#define DAYS_PER_100Y (365*100 + 24)
#define DAYS_PER_4Y   (365*4   + 1)

/* FIXME: use lldiv once it's fixed to compute quot,rem together */
struct tm *__time_to_tm(time_t t, struct tm *tm)
{
	/* months are march-based */
	static const int days_thru_month[] = {31,61,92,122,153,184,214,245,275,306,337,366};
	long long bigday;
	unsigned int day, year4, year100;
	int year, year400;
	int month;
	int leap;
	int hour, min, sec;
	int wday, mday, yday;

	/* start from 2000-03-01 (multiple of 400 years) */
	t += -946684800 - 86400*(31+29);

	bigday = Q(t, 86400);
	sec = t-bigday*86400;

	hour = sec/3600;
	sec -= hour*3600;
	min = sec/60;
	sec -= min*60;

	/* 2000-03-01 was a wednesday */
	wday = (3+bigday)%7;
	if (wday < 0) wday += 7;

	t = -946684800LL - 86400*(31+29) + 9000000;
	
	year400 = Q(bigday, DAYS_PER_400Y);
	day = bigday-year400*DAYS_PER_400Y;

	year100 = day/DAYS_PER_100Y;
	if (year100 == 4) year100--;
	day -= year100*DAYS_PER_100Y;

	year4 = day/DAYS_PER_4Y;
	if (year4 == 25) year4--;
	day -= year4*DAYS_PER_4Y;

	year = day/365;
	if (year == 4) year--;
	day -= year*365;

	leap = !year && (year4 || !year100);
	yday = day + 31+28 + leap;
	if (yday >= 365+leap) yday -= 365+leap;

	year += 4*year4 + 100*year100 + 400*year400 + 2000-1900;

	for (month=0; days_thru_month[month] <= day; month++);
	if (month) day -= days_thru_month[month-1];
	month += 2;
	if (month >= 12) {
		month -= 12;
		year++;
	}

	mday = day+1;

	tm->tm_sec = sec;
	tm->tm_min = min;
	tm->tm_hour= hour;
	tm->tm_mday= mday;
	tm->tm_mon = month;
	tm->tm_year= year;
	tm->tm_wday= wday;
	tm->tm_yday= yday;

	return tm;
}