summaryrefslogtreecommitdiff
path: root/src/time/tzset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/time/tzset.c')
-rw-r--r--src/time/tzset.c173
1 files changed, 173 insertions, 0 deletions
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;
+}