tlib9: rewrite date routines to use /usr/share/zoneinfo directly - plan9port - … | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit f35a04866f298aa4b0fa6846da0c0187751ce9b2 | |
parent 1cccddd6b3fb4a90641b8d05dc0bed618380074c | |
Author: Michael Teichgräber <devnull@localhost> | |
Date: Wed, 9 Jul 2008 08:27:22 -0400 | |
lib9: rewrite date routines to use /usr/share/zoneinfo directly | |
Diffstat: | |
M src/lib9/ctime.c | 157 +++++++++++++++++++++++++++--… | |
D src/lib9/date.c | 100 -----------------------------… | |
M src/lib9/mkfile | 4 +++- | |
M src/lib9/opentemp.c | 16 +++++++++++----- | |
A src/lib9/tm2sec.c | 110 +++++++++++++++++++++++++++++… | |
A src/lib9/zoneinfo.c | 215 +++++++++++++++++++++++++++++… | |
A src/lib9/zoneinfo.h | 19 +++++++++++++++++++ | |
7 files changed, 498 insertions(+), 123 deletions(-) | |
--- | |
diff --git a/src/lib9/ctime.c b/src/lib9/ctime.c | |
t@@ -1,21 +1,135 @@ | |
+/* | |
+ * This routine converts time as follows. | |
+ * The epoch is 0000 Jan 1 1970 GMT. | |
+ * The argument time is in seconds since then. | |
+ * The localtime(t) entry returns a pointer to an array | |
+ * containing | |
+ * | |
+ * seconds (0-59) | |
+ * minutes (0-59) | |
+ * hours (0-23) | |
+ * day of month (1-31) | |
+ * month (0-11) | |
+ * year-1970 | |
+ * weekday (0-6, Sun is 0) | |
+ * day of the year | |
+ * daylight savings flag | |
+ * | |
+ * The routine gets the daylight savings time from the environment. | |
+ * | |
+ * asctime(tvec)) | |
+ * where tvec is produced by localtime | |
+ * returns a ptr to a character string | |
+ * that has the ascii time in the form | |
+ * | |
+ * \\ | |
+ * Thu Jan 01 00:00:00 GMT 1970n0 | |
+ * 012345678901234567890123456789 | |
+ * 0 1 2 | |
+ * | |
+ * ctime(t) just calls localtime, then asctime. | |
+ */ | |
+ | |
#include <u.h> | |
#include <libc.h> | |
-static | |
-void | |
-ct_numb(char *cp, int n) | |
+#include "zoneinfo.h" | |
+ | |
+static char dmsize[12] = | |
{ | |
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | |
+}; | |
- cp[0] = ' '; | |
- if(n >= 10) | |
- cp[0] = (n/10)%10 + '0'; | |
- cp[1] = n%10 + '0'; | |
+#define dysize ctimedysize | |
+static int dysize(int); | |
+static void ct_numb(char*, int); | |
+ | |
+char* | |
+ctime(long t) | |
+{ | |
+ return asctime(localtime(t)); | |
+} | |
+ | |
+Tm* | |
+localtime(long tim) | |
+{ | |
+ Tinfo ti; | |
+ Tm *ct; | |
+ | |
+ if (zonelookuptinfo(&ti, tim)!=-1) { | |
+ ct = gmtime(tim+ti.tzoff); | |
+ strncpy(ct->zone, ti.zone, sizeof ct->zone); | |
+ ct->zone[sizeof ct->zone-1] = 0; | |
+ ct->tzoff = ti.tzoff; | |
+ return ct; | |
+ } | |
+ return gmtime(tim); | |
+} | |
+ | |
+Tm* | |
+gmtime(long tim) | |
+{ | |
+ int d0, d1; | |
+ long hms, day; | |
+ static Tm xtime; | |
+ | |
+ /* | |
+ * break initial number into days | |
+ */ | |
+ hms = tim % 86400L; | |
+ day = tim / 86400L; | |
+ if(hms < 0) { | |
+ hms += 86400L; | |
+ day -= 1; | |
+ } | |
+ | |
+ /* | |
+ * generate hours:minutes:seconds | |
+ */ | |
+ xtime.sec = hms % 60; | |
+ d1 = hms / 60; | |
+ xtime.min = d1 % 60; | |
+ d1 /= 60; | |
+ xtime.hour = d1; | |
+ | |
+ /* | |
+ * day is the day number. | |
+ * generate day of the week. | |
+ * The addend is 4 mod 7 (1/1/1970 was Thursday) | |
+ */ | |
+ | |
+ xtime.wday = (day + 7340036L) % 7; | |
+ | |
+ /* | |
+ * year number | |
+ */ | |
+ if(day >= 0) | |
+ for(d1 = 1970; day >= dysize(d1); d1++) | |
+ day -= dysize(d1); | |
+ else | |
+ for (d1 = 1970; day < 0; d1--) | |
+ day += dysize(d1-1); | |
+ xtime.year = d1-1900; | |
+ xtime.yday = d0 = day; | |
+ | |
+ /* | |
+ * generate month | |
+ */ | |
+ | |
+ if(dysize(d1) == 366) | |
+ dmsize[1] = 29; | |
+ for(d1 = 0; d0 >= dmsize[d1]; d1++) | |
+ d0 -= dmsize[d1]; | |
+ dmsize[1] = 28; | |
+ xtime.mday = d0 + 1; | |
+ xtime.mon = d1; | |
+ strcpy(xtime.zone, "GMT"); | |
+ return &xtime; | |
} | |
char* | |
asctime(Tm *t) | |
{ | |
- int i; | |
char *ncp; | |
static char cbuf[30]; | |
t@@ -33,12 +147,6 @@ asctime(Tm *t) | |
ct_numb(cbuf+14, t->min+100); | |
ct_numb(cbuf+17, t->sec+100); | |
ncp = t->zone; | |
- for(i=0; i<3; i++) | |
- if(ncp[i] == 0) | |
- break; | |
- for(; i<3; i++) | |
- ncp[i] = '?'; | |
- ncp = t->zone; | |
cbuf[20] = *ncp++; | |
cbuf[21] = *ncp++; | |
cbuf[22] = *ncp; | |
t@@ -50,9 +158,24 @@ asctime(Tm *t) | |
return cbuf; | |
} | |
-char* | |
-ctime(long t) | |
+static | |
+int | |
+dysize(int y) | |
{ | |
- return asctime(localtime(t)); | |
+ | |
+ if(y%4 == 0 && (y%100 != 0 || y%400 == 0)) | |
+ return 366; | |
+ return 365; | |
+} | |
+ | |
+static | |
+void | |
+ct_numb(char *cp, int n) | |
+{ | |
+ | |
+ cp[0] = ' '; | |
+ if(n >= 10) | |
+ cp[0] = (n/10)%10 + '0'; | |
+ cp[1] = n%10 + '0'; | |
} | |
diff --git a/src/lib9/date.c b/src/lib9/date.c | |
t@@ -1,100 +0,0 @@ | |
-#define NOPLAN9DEFINES | |
-#include <u.h> | |
-#include <libc.h> | |
-#include <stdlib.h> /* setenv etc. */ | |
-#include <time.h> | |
- | |
-static int | |
-dotz(time_t t, char *tzone) | |
-{ | |
- struct tm *gtm; | |
- struct tm tm; | |
- | |
- strftime(tzone, 32, "%Z", localtime(&t)); | |
- tm = *localtime(&t); /* set local time zone field */ | |
- gtm = gmtime(&t); | |
- tm.tm_sec = gtm->tm_sec; | |
- tm.tm_min = gtm->tm_min; | |
- tm.tm_hour = gtm->tm_hour; | |
- tm.tm_mday = gtm->tm_mday; | |
- tm.tm_mon = gtm->tm_mon; | |
- tm.tm_year = gtm->tm_year; | |
- tm.tm_wday = gtm->tm_wday; | |
- return t - mktime(&tm); | |
-} | |
- | |
-static void | |
-tm2Tm(struct tm *tm, Tm *bigtm, int tzoff, char *zone) | |
-{ | |
- memset(bigtm, 0, sizeof *bigtm); | |
- bigtm->sec = tm->tm_sec; | |
- bigtm->min = tm->tm_min; | |
- bigtm->hour = tm->tm_hour; | |
- bigtm->mday = tm->tm_mday; | |
- bigtm->mon = tm->tm_mon; | |
- bigtm->year = tm->tm_year; | |
- bigtm->wday = tm->tm_wday; | |
- bigtm->tzoff = tzoff; | |
- strncpy(bigtm->zone, zone, 3); | |
- bigtm->zone[3] = 0; | |
-} | |
- | |
-static void | |
-Tm2tm(Tm *bigtm, struct tm *tm) | |
-{ | |
- /* initialize with current time to get local time zone! (tm_isdst) */ | |
- time_t t; | |
- time(&t); | |
- *tm = *localtime(&t); | |
- | |
- tm->tm_sec = bigtm->sec; | |
- tm->tm_min = bigtm->min; | |
- tm->tm_hour = bigtm->hour; | |
- tm->tm_mday = bigtm->mday; | |
- tm->tm_mon = bigtm->mon; | |
- tm->tm_year = bigtm->year; | |
- tm->tm_wday = bigtm->wday; | |
-} | |
- | |
-Tm* | |
-p9gmtime(long x) | |
-{ | |
- time_t t; | |
- struct tm tm; | |
- static Tm bigtm; | |
- | |
- t = (time_t)x; | |
- tm = *gmtime(&t); | |
- tm2Tm(&tm, &bigtm, 0, "GMT"); | |
- return &bigtm; | |
-} | |
- | |
-Tm* | |
-p9localtime(long x) | |
-{ | |
- time_t t; | |
- struct tm tm; | |
- static Tm bigtm; | |
- char tzone[32]; | |
- | |
- t = (time_t)x; | |
- tm = *localtime(&t); | |
- tm2Tm(&tm, &bigtm, dotz(t, tzone), tzone); | |
- return &bigtm; | |
-} | |
- | |
-long | |
-p9tm2sec(Tm *bigtm) | |
-{ | |
- time_t t; | |
- struct tm tm; | |
- char tzone[32]; | |
- | |
- Tm2tm(bigtm, &tm); | |
- t = mktime(&tm); | |
- if(strcmp(bigtm->zone, "GMT") == 0 || strcmp(bigtm->zone, "UCT") == 0){ | |
- t += dotz(t, tzone); | |
- } | |
- return t; | |
-} | |
- | |
diff --git a/src/lib9/mkfile b/src/lib9/mkfile | |
t@@ -88,7 +88,6 @@ LIB9OFILES=\ | |
create.$O\ | |
crypt.$O\ | |
ctime.$O\ | |
- date.$O\ | |
dial.$O\ | |
dirfstat.$O\ | |
dirfwstat.$O\ | |
t@@ -149,6 +148,7 @@ LIB9OFILES=\ | |
syslog.$O\ | |
sysname.$O\ | |
time.$O\ | |
+ tm2sec.$O\ | |
tokenize.$O\ | |
truerand.$O\ | |
u16.$O\ | |
t@@ -158,6 +158,7 @@ LIB9OFILES=\ | |
wait.$O\ | |
waitpid.$O\ | |
write.$O\ | |
+ zoneinfo.$O\ | |
OFILES=\ | |
$LIB9OFILES\ | |
t@@ -193,3 +194,4 @@ testgoogfmt: testfltfmt.$O googfmt.$O $XLIB | |
testgoogprint: testprint.$O googfmt.$O $XLIB | |
$LD -o $target testprint.$O googfmt.$O | |
+ctime.$O tm2sec.$O zoneinfo.$O: zoneinfo.h | |
diff --git a/src/lib9/opentemp.c b/src/lib9/opentemp.c | |
t@@ -2,14 +2,20 @@ | |
#include <libc.h> | |
int | |
-opentemp(char *template) | |
+opentemp(char *template, int mode) | |
{ | |
- int fd; | |
+ int fd, fd1; | |
fd = mkstemp(template); | |
if(fd < 0) | |
return -1; | |
- remove(template); | |
- return fd; | |
+ /* reopen for mode */ | |
+ fd1 = open(template, mode); | |
+ if(fd1 < 0){ | |
+ close(fd); | |
+ remove(template); | |
+ return -1; | |
+ } | |
+ close(fd); | |
+ return fd1; | |
} | |
- | |
diff --git a/src/lib9/tm2sec.c b/src/lib9/tm2sec.c | |
t@@ -0,0 +1,110 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+ | |
+#include "zoneinfo.h" | |
+ | |
+#define SEC2MIN 60L | |
+#define SEC2HOUR (60L*SEC2MIN) | |
+#define SEC2DAY (24L*SEC2HOUR) | |
+ | |
+/* | |
+ * days per month plus days/year | |
+ */ | |
+static int dmsize[] = | |
+{ | |
+ 365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | |
+}; | |
+static int ldmsize[] = | |
+{ | |
+ 366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | |
+}; | |
+ | |
+/* | |
+ * return the days/month for the given year | |
+ */ | |
+static int * | |
+yrsize(int y) | |
+{ | |
+ if((y%4) == 0 && ((y%100) != 0 || (y%400) == 0)) | |
+ return ldmsize; | |
+ else | |
+ return dmsize; | |
+} | |
+ | |
+/* | |
+ * compute seconds since Jan 1 1970 GMT | |
+ * and convert to our timezone. | |
+ */ | |
+long | |
+tm2sec(Tm *tm) | |
+{ | |
+ Tinfo ti0, ti1, *ti; | |
+ long secs; | |
+ int i, yday, year, *d2m; | |
+ | |
+ secs = 0; | |
+ | |
+ /* | |
+ * seconds per year | |
+ */ | |
+ year = tm->year + 1900; | |
+ for(i = 1970; i < year; i++){ | |
+ d2m = yrsize(i); | |
+ secs += d2m[0] * SEC2DAY; | |
+ } | |
+ | |
+ /* | |
+ * if mday is set, use mon and mday to compute yday | |
+ */ | |
+ if(tm->mday){ | |
+ yday = 0; | |
+ d2m = yrsize(year); | |
+ for(i=0; i<tm->mon; i++) | |
+ yday += d2m[i+1]; | |
+ yday += tm->mday-1; | |
+ }else{ | |
+ yday = tm->yday; | |
+ } | |
+ secs += yday * SEC2DAY; | |
+ | |
+ /* | |
+ * hours, minutes, seconds | |
+ */ | |
+ secs += tm->hour * SEC2HOUR; | |
+ secs += tm->min * SEC2MIN; | |
+ secs += tm->sec; | |
+ | |
+ /* | |
+ * Assume the local time zone if zone is not GMT | |
+ */ | |
+ if(strcmp(tm->zone, "GMT") != 0) { | |
+ i = zonelookuptinfo(&ti0, secs); | |
+ ti = &ti0; | |
+ if (i != -1) | |
+ if (ti->tzoff!=0) { | |
+ /* | |
+ * to what local time period `secs' belongs? | |
+ */ | |
+ if (ti->tzoff>0) { | |
+ /* | |
+ * east of GMT; check previous local time tran… | |
+ */ | |
+ if (ti->t+ti->tzoff > secs) | |
+ if (zonetinfo(&ti1, i-1)!=-1) | |
+ ti = &ti1; | |
+ } else | |
+ /* | |
+ * west of GMT; check next local time transiti… | |
+ */ | |
+ if (zonetinfo(&ti1, i+1)) | |
+ if (ti1.t+ti->tzoff < secs) | |
+ ti = &ti1; | |
+// fprint(2, "tt: %ld+%d %ld\n", (long)ti->t, ti->tzoff… | |
+ secs -= ti->tzoff; | |
+ } | |
+ } | |
+ | |
+ if(secs < 0) | |
+ secs = 0; | |
+ return secs; | |
+} | |
diff --git a/src/lib9/zoneinfo.c b/src/lib9/zoneinfo.c | |
t@@ -0,0 +1,215 @@ | |
+#include <u.h> | |
+#include <libc.h> | |
+ | |
+/* | |
+ * Access local time entries of zoneinfo files. | |
+ * Formats 0 and 2 are supported, and 4-byte timestamps | |
+ * | |
+ * Copyright © 2008 M. Teichgräber | |
+ * Contributed under the terms of the Lucent Public License 1.02. | |
+ */ | |
+#include "zoneinfo.h" | |
+ | |
+static | |
+struct Zoneinfo | |
+{ | |
+ int timecnt; /* # of transition times */ | |
+ int typecnt; /* # of local time types */ | |
+ int charcnt; /* # of characters of time zone abb… | |
+ | |
+ uchar *ptime; | |
+ uchar *ptype; | |
+ uchar *ptt; | |
+ uchar *pzone; | |
+} z; | |
+ | |
+static uchar *tzdata; | |
+ | |
+static | |
+uchar* | |
+readtzfile(char *file) | |
+{ | |
+ uchar *p; | |
+ int fd; | |
+ Dir *d; | |
+ | |
+ fd = open(file, OREAD); | |
+ if (fd<0) | |
+ return nil; | |
+ d = dirfstat(fd); | |
+ if (d==nil) | |
+ return nil; | |
+ p = malloc(d->length); | |
+ if (p!=nil) | |
+ readn(fd, p, d->length); | |
+ free(d); | |
+ close(fd); | |
+ return p; | |
+} | |
+static char *zonefile; | |
+void | |
+tzfile(char *f) | |
+{ | |
+ if (tzdata!=nil) { | |
+ free(tzdata); | |
+ tzdata = nil; | |
+ } | |
+ z.timecnt = 0; | |
+ zonefile = f; | |
+} | |
+ | |
+static | |
+long | |
+get4(uchar *p) | |
+{ | |
+ return (p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]; | |
+} | |
+ | |
+enum { | |
+ TTinfosz = 4+1+1, | |
+}; | |
+ | |
+static | |
+int | |
+parsehead(void) | |
+{ | |
+ uchar *p; | |
+ int ver; | |
+ | |
+ ver = tzdata[4]; | |
+ if (ver!=0) | |
+ if (ver!='2') | |
+ return -1; | |
+ | |
+ p = tzdata + 4 + 1 + 15; | |
+ | |
+ z.timecnt = get4(p+3*4); | |
+ z.typecnt = get4(p+4*4); | |
+ if (z.typecnt==0) | |
+ return -1; | |
+ z.charcnt = get4(p+5*4); | |
+ z.ptime = p+6*4; | |
+ z.ptype = z.ptime + z.timecnt*4; | |
+ z.ptt = z.ptype + z.timecnt; | |
+ z.pzone = z.ptt + z.typecnt*TTinfosz; | |
+ return 0; | |
+} | |
+ | |
+static | |
+void | |
+ttinfo(Tinfo *ti, int tti) | |
+{ | |
+ uchar *p; | |
+ int i; | |
+ | |
+ i = z.ptype[tti]; | |
+ assert(i<z.typecnt); | |
+ p = z.ptt + i*TTinfosz; | |
+ ti->tzoff = get4(p); | |
+ ti->dlflag = p[4]; | |
+ assert(p[5]<z.charcnt); | |
+ ti->zone = (char*)z.pzone + p[5]; | |
+} | |
+ | |
+static | |
+void | |
+readtimezone(void) | |
+{ | |
+ char *tmp; | |
+ | |
+ z.timecnt = 0; | |
+ switch (zonefile==nil) { | |
+ default: | |
+ if ((tmp=getenv("timezone"))!=nil) { | |
+ tzdata = readtzfile(tmp); | |
+ free(tmp); | |
+ break; | |
+ } | |
+ zonefile = "/etc/localtime"; | |
+ /* fall through */ | |
+ case 0: | |
+ tzdata = readtzfile(zonefile); | |
+ } | |
+ if (tzdata==nil) | |
+ return; | |
+ | |
+ if (strncmp("TZif", (char*)tzdata, 4)!=0) | |
+ goto errfree; | |
+ | |
+ if (parsehead()==-1) { | |
+ errfree: | |
+ free(tzdata); | |
+ tzdata = nil; | |
+ z.timecnt = 0; | |
+ return; | |
+ } | |
+} | |
+ | |
+static | |
+tlong | |
+gett4(uchar *p) | |
+{ | |
+ long l; | |
+ | |
+ l = get4(p); | |
+ if (l<0) | |
+ return 0; | |
+ return l; | |
+} | |
+int | |
+zonetinfo(Tinfo *ti, int i) | |
+{ | |
+ if (tzdata==nil) | |
+ readtimezone(); | |
+ if (i<0 || i>=z.timecnt) | |
+ return -1; | |
+ ti->t = gett4(z.ptime + 4*i); | |
+ ttinfo(ti, i); | |
+ return i; | |
+} | |
+ | |
+int | |
+zonelookuptinfo(Tinfo *ti, tlong t) | |
+{ | |
+ uchar *p; | |
+ int i; | |
+ tlong oldtt, tt; | |
+ | |
+ if (tzdata==nil) | |
+ readtimezone(); | |
+ oldtt = 0; | |
+ p = z.ptime; | |
+ for (i=0; i<z.timecnt; i++) { | |
+ tt = gett4(p); | |
+ if (t<tt) | |
+ break; | |
+ oldtt = tt; | |
+ p += 4; | |
+ } | |
+ if (i>0) { | |
+ ttinfo(ti, i-1); | |
+ ti->t = oldtt; | |
+// fprint(2, "t:%ld off:%d dflag:%d %s\n", (long)ti->t, ti->tzo… | |
+ return i-1; | |
+ } | |
+ return -1; | |
+} | |
+ | |
+void | |
+zonedump(int fd) | |
+{ | |
+ int i; | |
+ uchar *p; | |
+ tlong t; | |
+ Tinfo ti; | |
+ | |
+ if (tzdata==nil) | |
+ readtimezone(); | |
+ p = z.ptime; | |
+ for (i=0; i<z.timecnt; i++) { | |
+ t = gett4(p); | |
+ ttinfo(&ti, i); | |
+ fprint(fd, "%ld\t%d\t%d\t%s\n", (long)t, ti.tzoff, ti.dlflag, … | |
+ p += 4; | |
+ } | |
+} | |
diff --git a/src/lib9/zoneinfo.h b/src/lib9/zoneinfo.h | |
t@@ -0,0 +1,19 @@ | |
+#define zonetinfo _p9zonetinfo | |
+#define zonedump _p9zonedump | |
+#define zonelookuptinfo _p9zonelookuptinfo | |
+ | |
+typedef long tlong; | |
+ | |
+typedef | |
+struct Tinfo | |
+{ | |
+ long t; | |
+ int tzoff; | |
+ int dlflag; | |
+ char *zone; | |
+} Tinfo; | |
+ | |
+extern int zonelookuptinfo(Tinfo*, tlong); | |
+extern int zonetinfo(Tinfo*, int); | |
+extern void zonedump(int fd); | |
+ |