summaryrefslogtreecommitdiff
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);
+}