URI: 
       tlib9: rewrite date routines to use /usr/share/zoneinfo directly - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit f35a04866f298aa4b0fa6846da0c0187751ce9b2
   DIR parent 1cccddd6b3fb4a90641b8d05dc0bed618380074c
  HTML 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(-)
       ---
   DIR 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';
        }
        
   DIR 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;
       -}
       -
   DIR 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
   DIR 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;
        }
       -
   DIR 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 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;
       +}
   DIR 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 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;
       +        }
       +}
   DIR 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);
       +