about summary refs log tree commit diff
path: root/src/time/__time_to_tm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/time/__time_to_tm.c')
-rw-r--r--src/time/__time_to_tm.c81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/time/__time_to_tm.c b/src/time/__time_to_tm.c
new file mode 100644
index 00000000..a1ebc452
--- /dev/null
+++ b/src/time/__time_to_tm.c
@@ -0,0 +1,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;
+}