lib9: rewrite date routines to use /usr/share/zoneinfo directly

This commit is contained in:
Michael Teichgräber 2008-07-09 08:27:22 -04:00
parent 1cccddd6b3
commit f35a04866f
7 changed files with 499 additions and 124 deletions

View file

@ -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"
cp[0] = ' ';
if(n >= 10)
cp[0] = (n/10)%10 + '0';
cp[1] = n%10 + '0';
static char dmsize[12] =
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
#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';
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

110
src/lib9/tm2sec.c Normal file
View file

@ -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 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;
}

215
src/lib9/zoneinfo.c Normal file
View file

@ -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 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(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->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<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, ti.zone);
p += 4;
}
}

19
src/lib9/zoneinfo.h Normal file
View file

@ -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);