about summary refs log tree commit diff
path: root/src/time
diff options
context:
space:
mode:
Diffstat (limited to 'src/time')
-rw-r--r--src/time/__asctime.c27
-rw-r--r--src/time/__time.h9
-rw-r--r--src/time/__time_to_tm.c81
-rw-r--r--src/time/__tm_to_time.c33
-rw-r--r--src/time/asctime.c9
-rw-r--r--src/time/asctime_r.c8
-rw-r--r--src/time/clock.c9
-rw-r--r--src/time/clock_gettime.c7
-rw-r--r--src/time/ctime.c6
-rw-r--r--src/time/ctime_r.c8
-rw-r--r--src/time/difftime.c6
-rw-r--r--src/time/gettimeofday.c9
-rw-r--r--src/time/gmtime.c11
-rw-r--r--src/time/gmtime_r.c10
-rw-r--r--src/time/localtime.c12
-rw-r--r--src/time/localtime_r.c11
-rw-r--r--src/time/mktime.c24
-rw-r--r--src/time/nanosleep.c13
-rw-r--r--src/time/strftime.c172
-rw-r--r--src/time/strptime.c178
-rw-r--r--src/time/time.c12
-rw-r--r--src/time/times.c7
-rw-r--r--src/time/timezone.s27
-rw-r--r--src/time/tzset.c173
-rw-r--r--src/time/utime.c12
25 files changed, 874 insertions, 0 deletions
diff --git a/src/time/__asctime.c b/src/time/__asctime.c
new file mode 100644
index 00000000..18535802
--- /dev/null
+++ b/src/time/__asctime.c
@@ -0,0 +1,27 @@
+#include <time.h>
+#include <stdio.h>
+#include <langinfo.h>
+
+const char *__langinfo(nl_item);
+
+char *__asctime(const struct tm *tm, char *buf)
+{
+	/* FIXME: change __langinfo to __C_langinfo once we have locales */
+	if (snprintf(buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
+		__langinfo(ABDAY_1+tm->tm_wday),
+		__langinfo(ABMON_1+tm->tm_mon),
+		tm->tm_mday, tm->tm_hour,
+		tm->tm_min, tm->tm_sec,
+		1900 + tm->tm_year) >= 26)
+	{
+		/* ISO C requires us to use the above format string,
+		 * even if it will not fit in the buffer. Thus asctime_r
+		 * is _supposed_ to crash if the fields in tm are too large.
+		 * We follow this behavior and crash "gracefully" to warn
+		 * application developers that they may not be so lucky
+		 * on other implementations (e.g. stack smashing..).
+		 */
+		*(int*)0 = 0;
+	}
+	return buf;
+}
diff --git a/src/time/__time.h b/src/time/__time.h
new file mode 100644
index 00000000..967e5180
--- /dev/null
+++ b/src/time/__time.h
@@ -0,0 +1,9 @@
+time_t __tm_to_time(struct tm *);
+struct tm *__time_to_tm(time_t, struct tm *);
+void __tzset(void);
+struct tm *__dst_adjust(struct tm *tm);
+
+extern long __timezone;
+extern int __daylight;
+extern int __dst_offset;
+extern char *__tzname[2];
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;
+}
diff --git a/src/time/__tm_to_time.c b/src/time/__tm_to_time.c
new file mode 100644
index 00000000..3fa15fad
--- /dev/null
+++ b/src/time/__tm_to_time.c
@@ -0,0 +1,33 @@
+#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)))
+
+time_t __tm_to_time(struct tm *tm)
+{
+	time_t year  = tm->tm_year + -100;
+	int    month = tm->tm_mon;
+	int    day   = tm->tm_mday;
+	int z4, z100, z400;
+
+	/* normalize month */
+	if (month >= 12) {
+		year += month/12;
+		month %= 12;
+	} else if (month < 0) {
+		year += month/12;
+		month %= 12;
+		if (month) {
+			month += 12;
+			year--;
+		}
+	}
+	z4 = Q(year - (month < 2), 4);
+	z100 = Q(z4, 25);
+	z400 = Q(z100, 4);
+	day += year*365 + z4 - z100 + z400 +
+		month[(int []){0,31,59,90,120,151,181,212,243,273,304,335}];
+	return (long long)day*86400
+		+ tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec
+		- -946684800; /* the dawn of time :) */
+}
diff --git a/src/time/asctime.c b/src/time/asctime.c
new file mode 100644
index 00000000..3102eb87
--- /dev/null
+++ b/src/time/asctime.c
@@ -0,0 +1,9 @@
+#include <time.h>
+
+char *__asctime(const struct tm *, char *);
+
+char *asctime(const struct tm *tm)
+{
+	static char buf[26];
+	return __asctime(tm, buf);
+}
diff --git a/src/time/asctime_r.c b/src/time/asctime_r.c
new file mode 100644
index 00000000..e51b8804
--- /dev/null
+++ b/src/time/asctime_r.c
@@ -0,0 +1,8 @@
+#include <time.h>
+
+char *__asctime(const struct tm *, char *);
+
+char *asctime_r(const struct tm *tm, char *buf)
+{
+	return __asctime(tm, buf);
+}
diff --git a/src/time/clock.c b/src/time/clock.c
new file mode 100644
index 00000000..2feddb36
--- /dev/null
+++ b/src/time/clock.c
@@ -0,0 +1,9 @@
+#include <time.h>
+#include <sys/times.h>
+
+/* this function assumes 100 hz linux and corrects for it */
+clock_t clock()
+{
+	struct tms tms;
+	return (unsigned long)times(&tms)*10000;
+}
diff --git a/src/time/clock_gettime.c b/src/time/clock_gettime.c
new file mode 100644
index 00000000..dab09d50
--- /dev/null
+++ b/src/time/clock_gettime.c
@@ -0,0 +1,7 @@
+#include <time.h>
+#include "syscall.h"
+
+int clock_gettime(clockid_t clk, struct timespec *ts)
+{
+	return syscall2(__NR_clock_gettime, clk, (long)ts);
+}
diff --git a/src/time/ctime.c b/src/time/ctime.c
new file mode 100644
index 00000000..185ec554
--- /dev/null
+++ b/src/time/ctime.c
@@ -0,0 +1,6 @@
+#include <time.h>
+
+char *ctime(const time_t *t)
+{
+	return asctime(localtime(t));
+}
diff --git a/src/time/ctime_r.c b/src/time/ctime_r.c
new file mode 100644
index 00000000..d2260a16
--- /dev/null
+++ b/src/time/ctime_r.c
@@ -0,0 +1,8 @@
+#include <time.h>
+
+char *ctime_r(const time_t *t, char *buf)
+{
+	struct tm tm;
+	localtime_r(t, &tm);
+	return asctime_r(&tm, buf);
+}
diff --git a/src/time/difftime.c b/src/time/difftime.c
new file mode 100644
index 00000000..80a18cc0
--- /dev/null
+++ b/src/time/difftime.c
@@ -0,0 +1,6 @@
+#include <time.h>
+
+double difftime(time_t t1, time_t t0)
+{
+	return t1-t0;
+}
diff --git a/src/time/gettimeofday.c b/src/time/gettimeofday.c
new file mode 100644
index 00000000..2b8a287d
--- /dev/null
+++ b/src/time/gettimeofday.c
@@ -0,0 +1,9 @@
+#define SYSCALL_RETURN_ERRNO
+#include <sys/time.h>
+#include "syscall.h"
+
+int gettimeofday(struct timeval *tv, void *tz)
+{
+	syscall2(__NR_gettimeofday, (long)tv, 0);
+	return 0;
+}
diff --git a/src/time/gmtime.c b/src/time/gmtime.c
new file mode 100644
index 00000000..d4d5d1f1
--- /dev/null
+++ b/src/time/gmtime.c
@@ -0,0 +1,11 @@
+#include <time.h>
+
+#include "__time.h"
+
+struct tm *gmtime(const time_t *t)
+{
+	static struct tm tm;
+	__time_to_tm(*t, &tm);
+	tm.tm_isdst = 0;
+	return &tm;
+}
diff --git a/src/time/gmtime_r.c b/src/time/gmtime_r.c
new file mode 100644
index 00000000..5b565a65
--- /dev/null
+++ b/src/time/gmtime_r.c
@@ -0,0 +1,10 @@
+#include <time.h>
+
+#include "__time.h"
+
+struct tm *gmtime_r(const time_t *t, struct tm *result)
+{
+	__time_to_tm(*t, result);
+	result->tm_isdst = 0;
+	return result;
+}
diff --git a/src/time/localtime.c b/src/time/localtime.c
new file mode 100644
index 00000000..abd5e84d
--- /dev/null
+++ b/src/time/localtime.c
@@ -0,0 +1,12 @@
+#include <time.h>
+
+#include "__time.h"
+
+struct tm *localtime(const time_t *t)
+{
+	static struct tm tm;
+	__tzset();
+	__time_to_tm(*t - __timezone, &tm);
+	tm.tm_isdst = -1;
+	return __dst_adjust(&tm);
+}
diff --git a/src/time/localtime_r.c b/src/time/localtime_r.c
new file mode 100644
index 00000000..2bf10378
--- /dev/null
+++ b/src/time/localtime_r.c
@@ -0,0 +1,11 @@
+#include <time.h>
+
+#include "__time.h"
+
+struct tm *localtime_r(const time_t *t, struct tm *result)
+{
+	__tzset();
+	__time_to_tm(*t - __timezone, result);
+	result->tm_isdst = -1;
+	return __dst_adjust(result);
+}
diff --git a/src/time/mktime.c b/src/time/mktime.c
new file mode 100644
index 00000000..858cd50d
--- /dev/null
+++ b/src/time/mktime.c
@@ -0,0 +1,24 @@
+#include <time.h>
+
+#include "__time.h"
+
+time_t mktime(struct tm *tm)
+{
+	int isdst = tm->tm_isdst;
+	time_t t, lt;
+
+	__tzset();
+
+	tm->tm_sec += __timezone;
+	if (isdst > 0) tm->tm_sec += __dst_offset;
+
+	t = __tm_to_time(tm);
+	
+	lt = t - __timezone;
+	if (isdst > 0) lt -= __dst_offset;
+	__time_to_tm(lt, tm);
+
+	__dst_adjust(tm);
+	
+	return t;
+}
diff --git a/src/time/nanosleep.c b/src/time/nanosleep.c
new file mode 100644
index 00000000..5ac4c359
--- /dev/null
+++ b/src/time/nanosleep.c
@@ -0,0 +1,13 @@
+#include <unistd.h>
+#include <time.h>
+#include "syscall.h"
+#include "libc.h"
+
+int nanosleep(const struct timespec *req, struct timespec *rem)
+{
+	int ret;
+	CANCELPT_BEGIN;
+	ret = syscall2(__NR_nanosleep, (long)req, (long)rem);
+	CANCELPT_END;
+	return ret;
+}
diff --git a/src/time/strftime.c b/src/time/strftime.c
new file mode 100644
index 00000000..f1b94631
--- /dev/null
+++ b/src/time/strftime.c
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <langinfo.h>
+#include <time.h>
+#include "__time.h"
+
+// FIXME: integer overflows
+
+const char *__langinfo(nl_item);
+
+size_t strftime(char *s, size_t n, const char *f, const struct tm *tm)
+{
+	nl_item item;
+	int val;
+	const char *fmt;
+	size_t l;
+	for (l=0; *f && l<n; f++) {
+		if (*f == '%') {
+do_fmt:
+		switch (*++f) {
+		case '%':
+			goto literal;
+		case 'E':
+		case 'O':
+			goto do_fmt;
+		case 'a':
+			item = ABDAY_1 + tm->tm_wday;
+			goto nl_strcat;
+		case 'A':
+			item = DAY_1 + tm->tm_wday;
+			goto nl_strcat;
+		case 'h':
+		case 'b':
+			item = ABMON_1 + tm->tm_mon;
+			goto nl_strcat;
+		case 'B':
+			item = MON_1 + tm->tm_mon;
+			goto nl_strcat;
+		case 'c':
+			item = D_T_FMT;
+			goto nl_strftime;
+		case 'C':
+			val = (1900+tm->tm_year) / 100;
+			fmt = "%02d";
+			goto number;
+		case 'd':
+			val = tm->tm_mday;
+			fmt = "%02d";
+			goto number;
+		case 'D':
+			fmt = "%m/%d/%y";
+			goto recu_strftime;
+		case 'e':
+			val = tm->tm_mday;
+			fmt = "%2d";
+			goto number;
+		case 'F':
+			fmt = "%Y-%m-%d";
+			goto recu_strftime;
+		case 'g':
+			// FIXME
+			val = 0; //week_based_year(tm)%100;
+			fmt = "%02d";
+			goto number;
+		case 'G':
+			// FIXME
+			val = 0; //week_based_year(tm);
+			fmt = "%04d";
+			goto number;
+		case 'H':
+			val = tm->tm_hour;
+			fmt = "%02d";
+			goto number;
+		case 'I':
+			val = tm->tm_hour;
+			if (!val) val = 12;
+			else if (val > 12) val -= 12;
+			fmt = "%02d";
+			goto number;
+		case 'j':
+			val = tm->tm_yday+1;
+			fmt = "%03d";
+			goto number;
+		case 'm':
+			val = tm->tm_mon+1;
+			fmt = "%02d";
+			goto number;
+		case 'M':
+			val = tm->tm_min;
+			fmt = "%02d";
+			goto number;
+		case 'n':
+			s[l++] = '\n';
+			continue;
+		case 'p':
+			item = tm->tm_hour >= 12 ? PM_STR : AM_STR;
+			goto nl_strcat;
+		case 'r':
+			item = T_FMT_AMPM;
+			goto nl_strftime;
+		case 'R':
+			fmt = "%H:%M";
+			goto recu_strftime;
+		case 'S':
+			val = tm->tm_sec;
+			fmt = "%02d";
+			goto number;
+		case 't':
+			s[l++] = '\t';
+			continue;
+		case 'T':
+			fmt = "%H:%M:%S";
+			goto recu_strftime;
+		case 'u':
+			val = tm->tm_wday ? tm->tm_wday : 7;
+			fmt = "%d";
+			goto number;
+		case 'U':
+		case 'V':
+		case 'W':
+			// FIXME: week number mess..
+			continue;
+		case 'w':
+			val = tm->tm_wday;
+			fmt = "%d";
+			goto number;
+		case 'x':
+			item = D_FMT;
+			goto nl_strftime;
+		case 'X':
+			item = T_FMT;
+			goto nl_strftime;
+		case 'y':
+			val = tm->tm_year % 100;
+			fmt = "%02d";
+			goto number;
+		case 'Y':
+			val = tm->tm_year + 1900;
+			fmt = "%04d";
+			goto number;
+		case 'z':
+			if (tm->tm_isdst < 0) continue;
+			val = -__timezone - (tm->tm_isdst ? __dst_offset : 0);
+			l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60);
+			continue;
+		case 'Z':
+			if (tm->tm_isdst < 0 || !__tzname[0] || !__tzname[0][0])
+				continue;
+			l += snprintf(s+l, n-l, "%s", __tzname[!!tm->tm_isdst]);
+			continue;
+		default:
+			return 0;
+		}
+		}
+literal:
+		s[l++] = *f;
+		continue;
+number:
+		l += snprintf(s+l, n-l, fmt, val);
+		continue;
+nl_strcat:
+		l += snprintf(s+l, n-l, "%s", __langinfo(item));
+		continue;
+nl_strftime:
+		fmt = __langinfo(item);
+recu_strftime:
+		l += strftime(s+l, n-l, fmt, tm);
+	}
+	if (l >= n) return 0;
+	s[l] = 0;
+	return l;
+}
diff --git a/src/time/strptime.c b/src/time/strptime.c
new file mode 100644
index 00000000..db72e610
--- /dev/null
+++ b/src/time/strptime.c
@@ -0,0 +1,178 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <langinfo.h>
+#include <time.h>
+
+const char *__langinfo(nl_item);
+
+char *strptime(const char *s, const char *f, struct tm *tm)
+{
+	return NULL;
+}
+
+#if 0
+
+char *strptime(const char *s, const char *f, struct tm *tm)
+{
+	nl_item item;
+	int *dest;
+	const char *fmt;
+	for (; *f; f++) {
+		if (isspace(*f)) goto whitespace;
+		if (*f == '%') {
+do_fmt:
+		switch (*++f) {
+		case '%':
+			goto literal;
+		case 'E':
+		case 'O':
+			goto do_fmt;
+		case 'a':
+			item = ABDAY_1 + tm->tm_wday;
+			goto nl_strcat;
+		case 'A':
+			item = DAY_1 + tm->tm_wday;
+			goto nl_strcat;
+		case 'h':
+		case 'b':
+			item = ABMON_1 + tm->tm_mon;
+			goto nl_strcat;
+		case 'B':
+			item = MON_1 + tm->tm_mon;
+			goto nl_strcat;
+		case 'c':
+			item = D_T_FMT;
+			goto nl_strftime;
+		case 'C':
+			val = (1900+tm->tm_year) / 100;
+			fmt = "%02d";
+			goto number;
+		case 'd':
+			val = tm->tm_mday;
+			fmt = "%02d";
+			goto number;
+		case 'D':
+			fmt = "%m/%d/%y";
+			goto recu_strftime;
+		case 'e':
+			val = tm->tm_mday;
+			fmt = "%2d";
+			goto number;
+		case 'F':
+			fmt = "%Y-%m-%d";
+			goto recu_strftime;
+		case 'g':
+			// FIXME
+			val = 0; //week_based_year(tm)%100;
+			fmt = "%02d";
+			goto number;
+		case 'G':
+			// FIXME
+			val = 0; //week_based_year(tm);
+			fmt = "%04d";
+			goto number;
+		case 'H':
+			val = tm->tm_hour;
+			fmt = "%02d";
+			goto number;
+		case 'I':
+			val = tm->tm_hour;
+			if (!val) val = 12;
+			else if (val > 12) val -= 12;
+			fmt = "%02d";
+			goto number;
+		case 'j':
+			val = tm->tm_yday+1;
+			fmt = "%03d";
+			goto number;
+		case 'm':
+			val = tm->tm_mon+1;
+			fmt = "%02d";
+			goto number;
+		case 'M':
+			val = tm->tm_min;
+			fmt = "%02d";
+			goto number;
+		case 'n':
+		case 't':
+			goto whitespace;
+		case 'p':
+			item = tm->tm_hour >= 12 ? PM_STR : AM_STR;
+			goto nl_strcat;
+		case 'r':
+			item = T_FMT_AMPM;
+			goto nl_strftime;
+		case 'R':
+			fmt = "%H:%M";
+			goto recu_strftime;
+		case 'S':
+			val = tm->tm_sec;
+			fmt = "%02d";
+			goto number;
+		case 'T':
+			fmt = "%H:%M:%S";
+			goto recu_strftime;
+		case 'u':
+			val = tm->tm_wday ? tm->tm_wday : 7;
+			fmt = "%d";
+			goto number;
+		case 'U':
+		case 'V':
+		case 'W':
+			// FIXME: week number mess..
+			continue;
+		case 'w':
+			val = tm->tm_wday;
+			fmt = "%d";
+			goto number;
+		case 'x':
+			item = D_FMT;
+			goto nl_strftime;
+		case 'X':
+			item = T_FMT;
+			goto nl_strftime;
+		case 'y':
+			val = tm->tm_year % 100;
+			fmt = "%02d";
+			goto number;
+		case 'Y':
+			val = tm->tm_year + 1900;
+			fmt = "%04d";
+			goto number;
+		case 'z':
+			if (tm->tm_isdst < 0) continue;
+			val = timezone + (tm->tm_isdst) ? __dst_offset : 0;
+			l += snprintf(s+l, n-l, "%+02d%02d", val/60, abs(val%60));
+			continue;
+		case 'Z':
+			if (tm->tm_isdst < 0 || !tzname[0] || !tzname[0][0])
+				continue;
+			l += snprintf(s+l, n-l, "%s", tzname[!!tm->tm_isdst]);
+			continue;
+		}
+		default:
+			return NULL;
+		}
+literal:
+		if (*s++ != *f) return NULL;
+		continue;
+whitespace:
+		while(isspace(*s)) s++;
+		continue;
+number:
+		l += snprintf(s+l, n-l, fmt, val);
+		continue;
+nl_strcat:
+		l += snprintf(s+l, n-l, "%s", __langinfo(item));
+		continue;
+nl_strftime:
+		fmt = __langinfo(item);
+recu_strftime:
+		l += strftime(s+l, n-l, fmt, tm);
+	}
+	if (l >= n) return 0;
+	s[l] = 0;
+	return l;
+}
+
+#endif
diff --git a/src/time/time.c b/src/time/time.c
new file mode 100644
index 00000000..3457dade
--- /dev/null
+++ b/src/time/time.c
@@ -0,0 +1,12 @@
+#define SYSCALL_RETURN_ERRNO
+#include <time.h>
+#include <sys/time.h>
+#include "syscall.h"
+
+time_t time(time_t *t)
+{
+	struct timeval tv;
+	syscall2(__NR_gettimeofday, (long)&tv, 0);
+	if (t) *t = tv.tv_sec;
+	return tv.tv_sec;
+}
diff --git a/src/time/times.c b/src/time/times.c
new file mode 100644
index 00000000..e9b5a823
--- /dev/null
+++ b/src/time/times.c
@@ -0,0 +1,7 @@
+#include <sys/times.h>
+#include "syscall.h"
+
+clock_t times(struct tms *tms)
+{
+	return syscall1(__NR_times, (long)&tms);
+}
diff --git a/src/time/timezone.s b/src/time/timezone.s
new file mode 100644
index 00000000..4e167b0b
--- /dev/null
+++ b/src/time/timezone.s
@@ -0,0 +1,27 @@
+.data
+
+.global timezone
+.global __timezone
+.global daylight
+.global __daylight
+.global tzname
+.global __tzname
+
+__timezone:
+timezone:
+	.long 0
+.size timezone,.-timezone
+.size __timezone,.-__timezone
+
+__daylight:
+daylight:
+	.long 0
+.size daylight,.-daylight
+.size __daylight,.-__daylight
+
+__tzname:
+tzname:
+	.long 0
+	.long 0
+.size tzname,.-tzname
+.size __tzname,.-__tzname
diff --git a/src/time/tzset.c b/src/time/tzset.c
new file mode 100644
index 00000000..6d69957e
--- /dev/null
+++ b/src/time/tzset.c
@@ -0,0 +1,173 @@
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libc.h"
+
+#include "__time.h"
+
+long  __timezone = 0;
+int   __daylight = 0;
+char *__tzname[2] = { 0, 0 };
+int   __dst_offset = 0;
+
+weak_alias(__timezone, timezone);
+weak_alias(__daylight, daylight);
+weak_alias(__tzname, tzname);
+weak_alias(__dst_offset, dst_offset);
+
+static char std_name[TZNAME_MAX+1];
+static char dst_name[TZNAME_MAX+1];
+
+/* all elements are zero-based */
+static struct rule {
+	char month;
+	char week;
+	short day;
+	int time;
+} __dst_start, __dst_end;
+
+static void zname(char *d, char **s)
+{
+	int i;
+	for (i=0; i<TZNAME_MAX && isalpha(d[i]=**s); i++, (*s)++);
+	d[i] = 0;
+}
+
+static int hhmmss(char **s)
+{
+	int ofs = strtol(*s, s, 10)*3600;
+	if (ofs >= 0) {
+		if (**s == ':') ofs += strtol(*s+1, s, 10)*60;
+		if (**s == ':') ofs += strtol(*s+1, s, 10);
+	} else {
+		if (**s == ':') ofs -= strtol(*s+1, s, 10)*60;
+		if (**s == ':') ofs -= strtol(*s+1, s, 10);
+	}
+	return ofs;
+}
+
+static int dstrule(struct rule *rule, char **s)
+{
+	if (**s != ',') return -1;
+	switch (*++*s) {
+	case 'J':
+		rule->month = 'J';
+		rule->day = strtol(*s+1, s, 10)-1;
+		break;
+	case 'M':
+		rule->month = strtol(*s+1, s, 10)-1;
+		if (**s != '.' || rule->month < 0 || rule->month > 11)
+			return -1;
+		rule->week = strtol(*s+1, s, 10)-1;
+		if (**s != '.' || rule->week < 0 || rule->week > 4)
+			return -1;
+		rule->day = strtol(*s+1, s, 10);
+		if (rule->day < 0 || rule->day > 6)
+			return -1;
+		break;
+	default:
+		rule->month = 'L';
+		rule->day = strtol(*s+1, s, 10);
+		break;
+	}
+	if (**s == '/') {
+		(*s)++;
+		rule->time = hhmmss(s);
+	} else rule->time = 7200;
+	return 0;
+}
+
+void tzset(void)
+{
+	char *z, *a;
+	
+	strcpy(std_name, "GMT");
+	strcpy(dst_name, "GMT");
+	__tzname[0] = std_name;
+	__tzname[1] = dst_name;
+	__timezone = 0;
+	__daylight = 0;
+	
+	if (!(z = getenv("TZ")) || !isalpha(*z)) return;
+
+	zname(std_name, &z);
+	__timezone = hhmmss(&z);
+
+	zname(dst_name, &z);
+	if (dst_name[0]) __daylight=1;
+	a = z;
+	__dst_offset = hhmmss(&z) - __timezone;
+	if (z==a) __dst_offset = -3600;
+
+	if (dstrule(&__dst_start, &z) || dstrule(&__dst_end, &z))
+		__daylight = 0;
+}
+
+void __tzset(void)
+{
+	static int lock, init;
+	if (init) return;
+	LOCK(&lock);
+	if (!init) tzset();
+	init=1;
+	UNLOCK(&lock);
+}
+
+static int is_leap(int year)
+{
+	year -= 100;
+	return !(year&3) && ((year%100) || !(year%400));
+}
+
+static int cutoff_yday(struct tm *tm, struct rule *rule)
+{
+	static const char days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
+	static const int first_day[] = {0,31,59,90,120,151,181,212,243,273,304,335};
+	int yday, mday, leap;
+	
+	switch (rule->month) {
+	case 'J':
+		return rule->day + (tm->tm_mon > 1 && is_leap(tm->tm_year));
+	case 'L':
+		return rule->day;
+	default:
+		yday = first_day[rule->month];
+		leap = is_leap(tm->tm_year);
+		if (rule->month > 1 && leap) yday++;
+		mday = (rule->day - (yday + tm->tm_wday - tm->tm_yday) + 1400)%7 + 7*rule->week;
+		if (mday >= days_in_month[rule->month] + (leap && rule->month == 1))
+			mday -= 7;
+		return mday + yday;
+	}
+}
+
+struct tm *__dst_adjust(struct tm *tm)
+{
+	time_t t;
+	int start, end, secs;
+	int after_start, before_end;
+
+	if (tm->tm_isdst >= 0) return tm;
+	if (!__daylight) {
+		tm->tm_isdst = 0;
+		return tm;
+	}
+	
+	secs = tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec;
+	start = cutoff_yday(tm, &__dst_start);
+	end = cutoff_yday(tm, &__dst_end);
+
+	after_start = (tm->tm_yday > start || (tm->tm_yday == start && secs >= __dst_start.time));
+	before_end = (tm->tm_yday < end || (tm->tm_yday == end && secs < __dst_end.time));
+
+	if ((after_start && before_end) || ((end < start) && (after_start || before_end))) {
+		tm->tm_sec -= __dst_offset;
+		tm->tm_isdst = 1;
+		t = __tm_to_time(tm);
+		return __time_to_tm(t, tm);
+	} else tm->tm_isdst = 0;
+
+	return tm;
+}
diff --git a/src/time/utime.c b/src/time/utime.c
new file mode 100644
index 00000000..56e9e13a
--- /dev/null
+++ b/src/time/utime.c
@@ -0,0 +1,12 @@
+#include <utime.h>
+#include "syscall.h"
+
+int utime(const char *path, const struct utimbuf *times)
+{
+	long ktimes[2];
+	if (times) {
+		ktimes[0] = times->actime;
+		ktimes[1] = times->modtime;
+	}
+	return syscall2(__NR_utime, (long)path, times ? (long)ktimes : 0);
+}