ref: 059ee8d668654371daeca95d67fc0c8626cddef1
parent: e24a46183972b00f59f0ce9b35a3ddcf78faf142
author: Naveen Narayanan <[email protected]>
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