From f35a04866f298aa4b0fa6846da0c0187751ce9b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Teichgr=C3=A4ber?= Date: Wed, 9 Jul 2008 08:27:22 -0400 Subject: lib9: rewrite date routines to use /usr/share/zoneinfo directly --- src/lib9/ctime.c | 157 +++++++++++++++++++++++++++++++++----- src/lib9/date.c | 100 ------------------------ src/lib9/mkfile | 4 +- src/lib9/opentemp.c | 16 ++-- src/lib9/tm2sec.c | 110 +++++++++++++++++++++++++++ src/lib9/zoneinfo.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib9/zoneinfo.h | 19 +++++ 7 files changed, 498 insertions(+), 123 deletions(-) delete mode 100644 src/lib9/date.c create mode 100644 src/lib9/tm2sec.c create mode 100644 src/lib9/zoneinfo.c create mode 100644 src/lib9/zoneinfo.h (limited to 'src/lib9') diff --git a/src/lib9/ctime.c b/src/lib9/ctime.c index 0782d099..c5e11569 100644 --- a/src/lib9/ctime.c +++ b/src/lib9/ctime.c @@ -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 #include -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]; @@ -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; @@ -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 deleted file mode 100644 index 8509bb48..00000000 --- a/src/lib9/date.c +++ /dev/null @@ -1,100 +0,0 @@ -#define NOPLAN9DEFINES -#include -#include -#include /* setenv etc. */ -#include - -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 index 782dd274..4bbd0f8b 100644 --- a/src/lib9/mkfile +++ b/src/lib9/mkfile @@ -88,7 +88,6 @@ LIB9OFILES=\ create.$O\ crypt.$O\ ctime.$O\ - date.$O\ dial.$O\ dirfstat.$O\ dirfwstat.$O\ @@ -149,6 +148,7 @@ LIB9OFILES=\ syslog.$O\ sysname.$O\ time.$O\ + tm2sec.$O\ tokenize.$O\ truerand.$O\ u16.$O\ @@ -158,6 +158,7 @@ LIB9OFILES=\ wait.$O\ waitpid.$O\ write.$O\ + zoneinfo.$O\ OFILES=\ $LIB9OFILES\ @@ -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 index f90bf771..0af154de 100644 --- a/src/lib9/opentemp.c +++ b/src/lib9/opentemp.c @@ -2,14 +2,20 @@ #include 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 new file mode 100644 index 00000000..5edf439d --- /dev/null +++ b/src/lib9/tm2sec.c @@ -0,0 +1,110 @@ +#include +#include + +#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; imon; 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 transition + */ + if (ti->t+ti->tzoff > secs) + if (zonetinfo(&ti1, i-1)!=-1) + ti = &ti1; + } else + /* + * west of GMT; check next local time transition + */ + 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, (long)secs); + secs -= ti->tzoff; + } + } + + if(secs < 0) + secs = 0; + return secs; +} diff --git a/src/lib9/zoneinfo.c b/src/lib9/zoneinfo.c new file mode 100644 index 00000000..f49d8120 --- /dev/null +++ b/src/lib9/zoneinfo.c @@ -0,0 +1,215 @@ +#include +#include + +/* + * 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 abbreviation strings */ + + 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(itzoff = get4(p); + ti->dlflag = p[4]; + assert(p[5]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; i0) { + ttinfo(ti, i-1); + ti->t = oldtt; +// fprint(2, "t:%ld off:%d dflag:%d %s\n", (long)ti->t, ti->tzoff, ti->dlflag, ti->zone); + 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