shithub: scc

Download patch

ref: 059ee8d668654371daeca95d67fc0c8626cddef1
parent: e24a46183972b00f59f0ce9b35a3ddcf78faf142
author: Naveen Narayanan <zerous@simple-cc.org>
date: Tue Oct 13 16:11:07 EDT 2020

libc: Implement tzset in time

Supported format: stdoffset[dst[offset]],start,end

This implementation of tzset supports Julian and Gregorian formats for
the start and end dates. The default time when daylight savings time
starts and ends is 02:00:00 AM.

Thanks k0ga for the parser!

--- a/src/libc/arch/posix/_tzone.c
+++ b/src/libc/arch/posix/_tzone.c
@@ -1,27 +1,247 @@
 #include <stdlib.h>
+#include <string.h>
 #include <time.h>
+
 #include "../../libc.h"
 
-struct tzone *
-_tzone(struct tm *tm)
+#define TOKENSIZ 10
+
+enum {
+	EOS,
+	NUM,
+	STR,
+};
+
+enum {
+	GREGORIAN,
+	JULIAN,
+};
+
+static char st[TOKENSIZ], ds[TOKENSIZ], tokstr[TOKENSIZ];
+static int tok;
+
+char *_tzname[2] = { st, ds };
+time_t _tzstdoff, _tzdstoff;
+time_t _tzstart, _tzend;
+int _tzjulian;
+
+static int
+next(char *str)
 {
-	static struct tzone tz;
-	static int first = 1;
+	int n, t;
+	static char *s;
 
-	if (!first)
-		return &tz;
+	if (str)
+		s = str;
 
-	tz.name = getenv("TZ");
-	if (!tz.name || *tz.name == '\0') {
-		tz.name = NULL;
-		tz.gmtoff = 0;
-		tz.isdst = 0;
-	} else {
-		/* TODO: parse TZ string */
-		tz.gmtoff = 0;
-		tz.isdst = 0;
+	switch (*s) {
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+		n = strspn(s, "0123456789");
+		t = NUM;
+		break;
+	case '+':
+	case '-':
+	case ':':
+	case ',':
+		n = 1;
+		t = *s;
+		break;
+	case '\0':
+		n = 0;
+		t = EOS;
+		break;
+	default:
+		n = strcspn(s, "+-0123456789");
+		t = STR;
+		break;
 	}
-	first = 0;
 
-	return &tz;
+	if (n >= TOKENSIZ-1)
+		return -1;
+	memcpy(tokstr, s, n);
+	tokstr[n] = '\0';
+	s += n;
+
+	return tok = t;
+}
+
+static int
+accept(int c)
+{
+	if (tok != c)
+		return 0;
+	return next(NULL);
+}
+
+static int
+num(int max)
+{
+	int n;
+
+	if (tok == EOS)
+		return 0;
+	if (tok != NUM)
+		return -1;
+	n = atoi(tokstr);
+	if (n < 0 || n > max)
+		return -1;
+	return n;
+}
+
+static long
+offset(void)
+{
+	int sign = 1;
+	int n;
+	long off;
+
+	if (tok == EOS)
+		return -1;
+
+	switch (tok) {
+	case '+':
+		sign = -1;
+	case '-':
+		next(NULL);
+		break;
+	default:
+		return -1;
+	}
+
+	if ((n = num(24)) < 0)
+		return -1;
+	off = n * SECHOUR;
+	next(NULL);
+	if (tok == EOS)
+		goto ret;
+
+	if (!accept(':'))
+		return -1;
+	if ((n = num(60)) < 0)
+		return -1;
+	off += n * SECMIN;
+	next(NULL);
+	if (tok == EOS)
+		goto ret;
+
+	if (!accept(':'))
+		return -1;
+	if ((n = num(60)) < 0)
+		return -1;
+	off += n;
+	next(NULL);
+
+ ret:
+	return sign * off;
+}
+
+static int
+std(void)
+{
+	time_t off;
+
+	if (tok != STR)
+		return 0;
+	strcpy(st, tokstr);
+	next(NULL);
+
+	if ((off = offset()) == -1)
+		return 0;
+	_tzstdoff = off;
+
+	return 1;
+}
+
+static int
+dst(void)
+{
+	time_t off;
+
+	if (tok != STR)
+		return 0;
+	strcpy(ds, tokstr);
+	next(NULL);
+
+	if ((off = offset()) == -1)
+		_tzdstoff = off;
+	else
+		_tzdstoff = _tzstdoff + SECHOUR;
+
+	return 1;
+}
+
+static int
+yday(void)
+{
+	int type, n;
+
+	if (tok == STR && !strcmp(tokstr, "J"))
+		type = JULIAN;
+	else if (tok == NUM)
+		type = GREGORIAN;
+	else
+		return -1;
+
+	switch (type) {
+	case JULIAN:
+		next(NULL);
+		n = num(365);
+		next(NULL);
+		if (n == 0)
+			return -1;
+		_tzjulian = 1;
+		break;
+	case GREGORIAN:
+		n = num(365);
+		next(NULL);
+		break;
+	}
+
+	return n;
+}
+
+static int
+rule(void)
+{
+	if (tok == EOS)
+		return 0;
+	if (!accept(','))
+		return 0;
+	if ((_tzstart = yday()) == -1)
+		return 0;
+	if (!accept(','))
+		return 0;
+	if ((_tzend = yday()) == -1)
+		return 0;
+}
+
+void
+_tzset(void)
+{
+	char *tz = getenv("TZ");
+	int i;
+
+	_tzstdoff = _tzdstoff = (time_t) -1;
+	_tzstart = _tzend = (time_t) -1;
+	_tzjulian = 0;
+
+	if (!tz)
+		return;
+	next(tz);
+
+	if (!std())
+		return;
+	if (!dst())
+		return;
+	if (!rule())
+		return;
 }
--- a/src/libc/libc.h
+++ b/src/libc/libc.h
@@ -24,11 +24,19 @@
 
 struct tm;
 
-extern struct tzone *_tzone(struct tm *tm);
+extern void _tzset(void);
 extern int _daysyear(int year);
 extern int _newyear(int year);
 extern void *_getheap(void);
 extern int _dtoi(char);
+
+#ifdef _TIME_H
+extern char *_tzname[2];
+extern time_t _tzstdoff, _tzdstoff;
+extern time_t _tzstart, _tzend;
+extern int _tzjulian;
+extern struct tzone tzones[];
+#endif
 
 #ifdef stdin
 extern int _flsbuf(FILE *fp);
--- a/src/libc/time/Makefile
+++ b/src/libc/time/Makefile
@@ -12,11 +12,17 @@
 	localtime.$O\
 	mktime.$O\
 	strftime.$O\
+	tz.$O\
 
 all: $(LIBC)
 
 $(LIBC): $(OBJS)
 	$(MKLST)
+
+$(OBJS): tz.c
+
+tz.c: timezone.lst
+	awk -f gentz.awk $< >$@
 
 dep: inc-dep
 
--- /dev/null
+++ b/src/libc/time/gentz.awk
@@ -1,0 +1,9 @@
+BEGIN { print "#include \"../libc.h\"\nstruct tzone tzones[] = {" }
+{
+    split($2,a,":")
+    min = a[1] * 60
+    min = min + a[2]
+    sec = min * 60
+    printf "\t{\"%s\", %d},\n", $1, sec
+}
+END { print "\t{0,\t0}\n};\n" }
--- a/src/libc/time/localtime.c
+++ b/src/libc/time/localtime.c
@@ -1,22 +1,58 @@
+#include <string.h>
 #include <time.h>
 
 #include "../libc.h"
 #undef localtime
 
+static time_t
+gmtoff(char *tz)
+{
+	for (struct tzone *t = tzones; t->name; t++)
+		if (!strcmp(t->name, tz))
+			return t->gmtoff;
+	return 0;
+}
+
 struct tm *
 localtime(const time_t *timep)
 {
-	struct tzone *tz;
 	struct tm *tm;
-	time_t t = *timep;
+	time_t t;
+	int yday;
+	static int first = 1;
 
-	tz = _tzone(gmtime(timep));
-	t += tz->gmtoff * 60;
-	t += tz->isdst * 60;
+	t = *timep;
 	tm = gmtime(&t);
-	tm->tm_zone = tz->name;
-	tm->tm_isdst = tz->isdst;
-	tm->tm_gmtoff = tz->gmtoff;
+	yday = tm->tm_yday;
+
+	if (first) {
+		_tzset();
+		if (_tzstdoff == -1)
+			_tzstdoff = gmtoff(_tzname[0]);
+		if (_tzdstoff == -1)
+			_tzdstoff = gmtoff(_tzname[1]);
+	}
+	first = 0;
+
+	tm->tm_gmtoff = _tzstdoff;
+	tm->tm_zone = _tzname[0];
+	tm->tm_isdst = 0;
+
+	if (_tzjulian	       &&
+	    ((yday + 1) < 60)  ||
+	    (FEBDAYS(tm->tm_year) < 29))
+		yday++;
+
+	if (yday >= _tzstart &&
+	    yday <= _tzend   &&
+	    tm->tm_hour >= 2) {
+		tm->tm_gmtoff = _tzdstoff;
+		tm->tm_zone = _tzname[1];
+		tm->tm_isdst = 1;
+	}
+
+	t += tm->tm_gmtoff;
+	tm = gmtime(&t);
 
 	return tm;
 }
--- a/src/libc/time/mktime.c
+++ b/src/libc/time/mktime.c
@@ -109,13 +109,7 @@
 
 	aux = localtime(&t);
 
-	dst = 0;
-	if (tm->tm_isdst == 0 && aux->tm_isdst == 1)
-		dst = -SECHOUR;
-	else if (tm->tm_isdst == 1 && aux->tm_isdst == 0)
-		dst = +SECHOUR;
-
-	t += aux->tm_gmtoff + dst;
+	t += aux->tm_gmtoff;
 
 	return t;
 }
--- /dev/null
+++ b/src/libc/time/timezone.lst
@@ -1,0 +1,332 @@
+ACDT	+10:30
+ACST	+09:30
+ACT	-05
+ACT	+09
+ACWST	+08:45
+ADT	-03
+AEDT	+11
+AEST	+10
+AFT	+04:30
+AKDT	-08
+AKST	-09
+ALMT	+06
+AMST	-03
+AMT	-04
+AMT	+04
+ANAT	+12
+AQTT	+05
+ART	-03
+AST	+03
+AST	-04
+AWST	+08
+AZOST	+00
+AZOT	-01
+AZT	+04
+BDT	+08
+BIOT	+06
+BIT	-12
+BOT	-04
+BRST	-02
+BRT	-03
+BST	+06
+BST	+11
+BST	+01
+BTT	+06
+CAT	+02
+CCT	+06:30
+CDT	-05
+CDT	-04
+CEST	+02
+CET	+01
+CHADT	+13:45
+CHAST	+12:45
+CHOT	+08
+CHOST	+09
+CHST	+10
+CHUT	+10
+CIST	-08
+CIT	+08
+CKT	-10
+CLST	-03
+CLT	-04
+COST	-04
+COT	-05
+CST	-06
+CST	+08
+CST	-05
+CT	+08
+CVT	-01
+CWST	+08:45
+CXT	+07
+DAVT	+07
+DDUT	+10
+DFT	+01
+EASST	-05
+EAST	-06
+EAT	+03
+ECT	-04
+ECT	-05
+EDT	-04
+EEST	+03
+EET	+02
+EGST	+00
+EGT	-01
+EIT	+09
+EST	-05
+FET	+03
+vFJT	+12
+FKST	-03
+FKT	-04
+FNT	-02
+GALT	-06
+GAMT	-09
+GET	+04
+GFT	-03
+GILT	+12
+GIT	-09
+GMT	+00
+GST	-02
+GST	+04
+GYT	-04
+HDT	-09
+HAEC	+02
+HST	-10
+HKT	+08
+HMT	+05
+HOVST	+08
+HOVT	+07
+ICT	+07
+IDLW	-12
+IDT	+03
+IOT	+03
+IRDT	+04:30
+IRKT	+08
+IRST	+03:30
+IST	+05:30
+IST	+01
+IST	+02
+JST	+09
+KALT	+02
+KGT	+06
+KOST	+11
+KRAT	+07
+KST	+09
+LHST	+10:30
+LHST	+11
+LINT	+14
+MAGT	+12
+MART	-09:30
+MAWT	+05
+MDT	-06
+MET	+01
+MEST	+02
+MHT	+12
+MIST	+11
+MIT	-09:30
+MMT	+06:30
+MSK	+03
+MST	+08
+MST	-07
+MUT	+04
+MVT	+05
+MYT	+08
+NCT	+11
+NDT	-02:30
+NFT	+11
+NOVT	+07
+NPT	+05:45
+NST	-03:30
+NT	-03:30
+NUT	-11
+NZDT	+13
+NZST	+12
+OMST	+06
+ORAT	+05
+PDT	-07
+PET	-05
+PETT	+12
+PGT	+10
+PHOT	+13
+PHT	+08
+PKT	+05
+PMDT	-02
+PMST	-03
+PONT	+11
+PST	-08
+PST	+08
+PYST	-03
+PYT	-04
+RET	+04
+ROTT	-03
+SAKT	+11
+SAMT	+04
+SAST	+02
+SBT	+11
+SCT	+04
+SDT	-10
+SGT	+08
+SLST	+05:30
+SRET	+11
+SRT	-03
+SST	-11
+SST	+08
+SYOT	+03
+TAHT	-10
+THA	+07
+TFT	+05
+TJT	+05
+TKT	+13
+TLT	+09
+TMT	+05
+TRT	+03
+TOT	+13
+TVT	+12
+ULAST	+09
+ULAT	+08
+UTC	+00
+UYST	-02
+UYT	-03
+UZT	+05
+VET	-04
+VLAT	+10
+VOLT	+04
+VOST	+06
+VUT	+11
+WAKT	+12
+WAST	+02
+WAT	+01
+WEST	+01
+WET	+00
+WIT	+07
+WGST	-02
+WGT	-03
+WST	+08
+YAKT	+09
+YEKT	+12
+FKST	-03
+FKT	-04
+FNT	-02
+GALT	-06
+GAMT	-09
+GET	+04
+GFT	-03
+GILT	+12
+GIT	-09
+GMT	+00
+GST	-02
+GST	+04
+GYT	-04
+HDT	-09
+HAEC	+02
+HST	-10
+HKT	+08
+HMT	+05
+HOVST	+08
+HOVT	+07
+ICT	+07
+IDLW	-12
+IDT	+03
+IOT	+03
+IRDT	+04:30
+IRKT	+08
+IRST	+03:30
+IST	+05:30
+IST	+01
+IST	+02
+JST	+09
+KALT	+02
+KGT	+06
+KOST	+11
+KRAT	+07
+KST	+09
+LHST	+10:30
+LHST	+11
+LINT	+14
+MAGT	+12
+MART	-09:30
+MAWT	+05
+MDT	-06
+MET	+01
+MEST	+02
+MHT	+12
+MIST	+11
+MIT	-09:30
+MMT	+06:30
+MSK	+03
+MST	+08
+MST	-07
+MUT	+04
+MVT	+05
+MYT	+08
+NCT	+11
+NDT	-02:30
+NFT	+11
+NOVT	+07
+NPT	+05:45
+NST	-03:30
+NT	-03:30
+NUT	-11
+NZDT	+13
+NZST	+12
+OMST	+06
+ORAT	+05
+PDT	-07
+PET	-05
+PETT	+12
+PGT	+10
+PHOT	+13
+PHT	+08
+PKT	+05
+PMDT	-02
+PMST	-03
+PONT	+11
+PST	-08
+PST	+08
+PYST	-03
+PYT	-04
+RET	+04
+ROTT	-03
+SAKT	+11
+SAMT	+04
+SAST	+02
+SBT	+11
+SCT	+04
+SDT	-10
+SGT	+08
+SLST	+05:30
+SRET	+11
+SRT	-03
+SST	-11
+SST	+08
+SYOT	+03
+TAHT	-10
+THA	+07
+TFT	+05
+TJT	+05
+TKT	+13
+TLT	+09
+TMT	+05
+TRT	+03
+TOT	+13
+TVT	+12
+ULAST	+09
+ULAT	+08
+UYST	-02
+UYT	-03
+UZT	+05
+VET	-04
+VLAT	+10
+VOLT	+04
+VOST	+06
+VUT	+11
+WAKT	+12
+WAST	+02
+WAT	+01
+WEST	+01
+WET	+00
+WIT	+07
+WGST	-02
+WGT	-03
+WST	+08
+YAKT	+09
+YEKT	+05