From: ori@eigenstate.org
To: ori@eigenstate.org, 9front@9front.org
Subject: Re: [9front] libc: date handling improvements
Date: Sun Jun 14 23:03:28 PDT 2020 [thread overview]
Message-ID: <BBADDAB4CED66A16CA14D7B468D4D9D0@eigenstate.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 929 bytes --]
>> I implemented libdate -- but the right place to put
>> it is in libc, replacing our ctime and gmtime apis.
>>
>> Follow ups will replace seconds(1), upas time parsing
>> code, and whatever other instances of hand rolled date
>> parsing and formatting can be found.
>>
>> Here's a patch that does this. Testing appreciated,
>> and follow ups that I haven't done yet would also be
>> great :)
>>
>
> Committed -- please let me know if you notice any
> breakage.
Now, with a whole bunch of changes through the system.
I'm going to run these for a while and test a bit harder
to see if there's any fallout before committing, but
while I do that, I'll put them up for other people to.
test and comment.
I don't personally use httpd or imap4d, so while I'm
going to try to give them a good shake, if anyone does
use them, I'd appreciate reports on that.
All patches depend on libc-date, but should otherwise
be independent.
[-- Attachment #2: Type: text/plain, Size: 32082 bytes --]
diff -r e168590166a5 sys/include/libc.h
--- a/sys/include/libc.h Sun Jul 19 14:14:14 2020 -0700
+++ b/sys/include/libc.h Sun Jul 19 21:34:16 2020 -0700
@@ -314,22 +314,44 @@
/*
* Time-of-day
*/
+typedef struct Tzone Tzone;
+#pragma incomplete Tzone
+
typedef
struct Tm
{
- int sec;
- int min;
- int hour;
- int mday;
- int mon;
- int year;
- int wday;
- int yday;
- char zone[4];
- int tzoff;
+ int nsec; /* nseconds (range 0...1e9) */
+ int sec; /* seconds (range 0..60) */
+ int min; /* minutes (0..59) */
+ int hour; /* hours (0..23) */
+ int mday; /* day of the month (1..31) */
+ int mon; /* month of the year (0..11) */
+ int year; /* year A.D. */
+ int wday; /* day of week (0..6, Sunday = 0) */
+ int yday; /* day of year (0..365) */
+ char zone[16]; /* time zone name */
+ int tzoff; /* time zone delta from GMT */
+ Tzone *tz; /* time zone associated with this date */
} Tm;
+typedef
+struct Tmfmt {
+ char *fmt;
+ Tm *tm;
+} Tmfmt;
+
+#pragma varargck type "τ" Tmfmt
+
+extern Tzone* tzload(char *name);
+extern Tm* tmnow(Tm*, Tzone*);
+extern Tm* tmtime(Tm*, vlong, Tzone*);
+extern Tm* tmtimens(Tm*, vlong, int, Tzone*);
+extern Tm* tmparse(Tm*, char*, char*, Tzone*, char **ep);
+extern vlong tmnorm(Tm*);
+extern Tmfmt tmfmt(Tm*, char*);
+extern void tmfmtinstall(void);
+
extern Tm* gmtime(long);
extern Tm* localtime(long);
extern char* asctime(Tm*);
diff -r e168590166a5 sys/src/libc/9sys/ctime.c
--- a/sys/src/libc/9sys/ctime.c Sun Jul 19 14:14:14 2020 -0700
+++ b/sys/src/libc/9sys/ctime.c Sun Jul 19 21:34:16 2020 -0700
@@ -1,301 +1,39 @@
-/*
- * 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 char dmsize[12] =
-{
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
-
-/*
- * The following table is used for 1974 and 1975 and
- * gives the day number of the first day after the Sunday of the
- * change.
- */
-
-static int dysize(int);
-static void ct_numb(char*, int);
-
-#define TZSIZE 150
-static void readtimezone(void);
-static int rd_name(char**, char*);
-static int rd_long(char**, long*);
-static
-struct
-{
- char stname[4];
- char dlname[4];
- long stdiff;
- long dldiff;
- long dlpairs[TZSIZE];
-} timezone;
-
-char*
-ctime(long t)
-{
- return asctime(localtime(t));
-}
-
Tm*
localtime(long tim)
{
- Tm *ct;
- long t, *p;
- int dlflag;
+ static Tm tm;
+ Tzone *tz;
- if(timezone.stname[0] == 0)
- readtimezone();
- t = tim + timezone.stdiff;
- dlflag = 0;
- for(p = timezone.dlpairs; *p; p += 2)
- if(t >= p[0])
- if(t < p[1]) {
- t = tim + timezone.dldiff;
- dlflag++;
- break;
- }
- ct = gmtime(t);
- if(dlflag){
- strcpy(ct->zone, timezone.dlname);
- ct->tzoff = timezone.dldiff;
- } else {
- strcpy(ct->zone, timezone.stname);
- ct->tzoff = timezone.stdiff;
- }
- return ct;
+ /*
+ * We have no way to report errors,
+ * so we just ignore them here.
+ */
+ tz = tzload("local");
+ tmtime(&tm, tim, tz);
+ return &tm;
}
Tm*
-gmtime(long tim)
+gmtime(long abs)
{
- 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;
+ static Tm tm;
+ return tmtime(&tm, abs, nil);
}
char*
-asctime(Tm *t)
+ctime(long abs)
{
- char *ncp;
- static char cbuf[30];
+ Tzone *tz;
+ Tm tm;
- strcpy(cbuf, "Thu Jan 01 00:00:00 GMT 1970\n");
- ncp = &"SunMonTueWedThuFriSat"[t->wday*3];
- cbuf[0] = *ncp++;
- cbuf[1] = *ncp++;
- cbuf[2] = *ncp;
- ncp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[t->mon*3];
- cbuf[4] = *ncp++;
- cbuf[5] = *ncp++;
- cbuf[6] = *ncp;
- ct_numb(cbuf+8, t->mday);
- ct_numb(cbuf+11, t->hour+100);
- ct_numb(cbuf+14, t->min+100);
- ct_numb(cbuf+17, t->sec+100);
- ncp = t->zone;
- cbuf[20] = *ncp++;
- cbuf[21] = *ncp++;
- cbuf[22] = *ncp;
- ct_numb(cbuf+24, (t->year+1900) / 100 + 100);
- ct_numb(cbuf+26, t->year+100);
- return cbuf;
+ /*
+ * We have no way to report errors,
+ * so we just ignore them here.
+ */
+ tz = tzload("local");
+ tmtime(&tm, abs, tz);
+ return asctime(&tm);
}
-
-static
-dysize(int y)
-{
-
- 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';
-}
-
-static
-void
-readtimezone(void)
-{
- char buf[TZSIZE*11+30], *p;
- int i;
-
- memset(buf, 0, sizeof(buf));
- i = open("/env/timezone", 0);
- if(i < 0)
- goto error;
- if(read(i, buf, sizeof(buf)) >= sizeof(buf)){
- close(i);
- goto error;
- }
- close(i);
- p = buf;
- if(rd_name(&p, timezone.stname))
- goto error;
- if(rd_long(&p, &timezone.stdiff))
- goto error;
- if(rd_name(&p, timezone.dlname))
- goto error;
- if(rd_long(&p, &timezone.dldiff))
- goto error;
- for(i=0; i<TZSIZE; i++) {
- if(rd_long(&p, &timezone.dlpairs[i]))
- goto error;
- if(timezone.dlpairs[i] == 0)
- return;
- }
-
-error:
- timezone.stdiff = 0;
- strcpy(timezone.stname, "GMT");
- timezone.dlpairs[0] = 0;
-}
-
-static
-rd_name(char **f, char *p)
-{
- int c, i;
-
- for(;;) {
- c = *(*f)++;
- if(c != ' ' && c != '\n')
- break;
- }
- for(i=0; i<3; i++) {
- if(c == ' ' || c == '\n')
- return 1;
- *p++ = c;
- c = *(*f)++;
- }
- if(c != ' ' && c != '\n')
- return 1;
- *p = 0;
- return 0;
-}
-
-static
-rd_long(char **f, long *p)
-{
- int c, s;
- long l;
-
- s = 0;
- for(;;) {
- c = *(*f)++;
- if(c == '-') {
- s++;
- continue;
- }
- if(c != ' ' && c != '\n')
- break;
- }
- if(c == 0) {
- *p = 0;
- return 0;
- }
- l = 0;
- for(;;) {
- if(c == ' ' || c == '\n')
- break;
- if(c < '0' || c > '9')
- return 1;
- l = l*10 + c-'0';
- c = *(*f)++;
- }
- if(s)
- l = -l;
- *p = l;
- return 0;
-}
diff -r e168590166a5 sys/src/libc/9sys/tm2sec.c
--- a/sys/src/libc/9sys/tm2sec.c Sun Jul 19 14:14:14 2020 -0700
+++ b/sys/src/libc/9sys/tm2sec.c Sun Jul 19 21:34:16 2020 -0700
@@ -1,202 +1,11 @@
#include <u.h>
#include <libc.h>
-#define TZSIZE 150
-static void readtimezone(void);
-static int rd_name(char**, char*);
-static int rd_long(char**, long*);
-static
-struct
-{
- char stname[4];
- char dlname[4];
- long stdiff;
- long dldiff;
- long dlpairs[TZSIZE];
-} timezone;
-
-#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)
{
- long secs, *p;
- int i, yday, year, *d2m;
+ Tm tt;
- if(strcmp(tm->zone, "GMT") != 0 && timezone.stname[0] == 0)
- readtimezone();
- 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;
-
- /*
- * Only handles zones mentioned in /env/timezone,
- * but things get too ambiguous otherwise.
- */
- if(strcmp(tm->zone, timezone.stname) == 0)
- secs -= timezone.stdiff;
- else if(strcmp(tm->zone, timezone.dlname) == 0)
- secs -= timezone.dldiff;
- else if(tm->zone[0] == 0){
- secs -= timezone.dldiff;
- for(p = timezone.dlpairs; *p; p += 2)
- if(secs >= p[0] && secs < p[1])
- break;
- if(*p == 0){
- secs += timezone.dldiff;
- secs -= timezone.stdiff;
- }
- }
- return secs;
+ tt = *tm;
+ return tmnorm(&tt);
}
-
-static
-void
-readtimezone(void)
-{
- char buf[TZSIZE*11+30], *p;
- int i;
-
- memset(buf, 0, sizeof(buf));
- i = open("/env/timezone", 0);
- if(i < 0)
- goto error;
- if(read(i, buf, sizeof(buf)) >= sizeof(buf))
- goto error;
- close(i);
- p = buf;
- if(rd_name(&p, timezone.stname))
- goto error;
- if(rd_long(&p, &timezone.stdiff))
- goto error;
- if(rd_name(&p, timezone.dlname))
- goto error;
- if(rd_long(&p, &timezone.dldiff))
- goto error;
- for(i=0; i<TZSIZE; i++) {
- if(rd_long(&p, &timezone.dlpairs[i]))
- goto error;
- if(timezone.dlpairs[i] == 0)
- return;
- }
-
-error:
- timezone.stdiff = 0;
- strcpy(timezone.stname, "GMT");
- timezone.dlpairs[0] = 0;
-}
-
-static int
-rd_name(char **f, char *p)
-{
- int c, i;
-
- for(;;) {
- c = *(*f)++;
- if(c != ' ' && c != '\n')
- break;
- }
- for(i=0; i<3; i++) {
- if(c == ' ' || c == '\n')
- return 1;
- *p++ = c;
- c = *(*f)++;
- }
- if(c != ' ' && c != '\n')
- return 1;
- *p = 0;
- return 0;
-}
-
-static int
-rd_long(char **f, long *p)
-{
- int c, s;
- long l;
-
- s = 0;
- for(;;) {
- c = *(*f)++;
- if(c == '-') {
- s++;
- continue;
- }
- if(c != ' ' && c != '\n')
- break;
- }
- if(c == 0) {
- *p = 0;
- return 0;
- }
- l = 0;
- for(;;) {
- if(c == ' ' || c == '\n')
- break;
- if(c < '0' || c > '9')
- return 1;
- l = l*10 + c-'0';
- c = *(*f)++;
- }
- if(s)
- l = -l;
- *p = l;
- return 0;
-}
diff -r e168590166a5 sys/src/libc/port/date.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/src/libc/port/date.c Sun Jul 19 21:34:16 2020 -0700
@@ -0,0 +1,992 @@
+#include <u.h>
+#include <libc.h>
+
+typedef struct Tzabbrev Tzabbrev;
+typedef struct Tzoffpair Tzoffpair;
+
+#define Ctimefmt "WW MMM _D hh:mm:ss ZZZ YYYY"
+#define P(pad, w) ((pad) < (w) ? 0 : pad - w)
+
+enum {
+ Tzsize = 150,
+ Nsec = 1000*1000*1000,
+ Usec = 1000*1000,
+ Msec = 1000,
+ Daysec = (vlong)24*3600,
+ Days400y = 365*400 + 4*25 - 3,
+ Days4y = 365*4 + 1,
+};
+
+enum {
+ Cend,
+ Cspace,
+ Cnum,
+ Cletter,
+ Cpunct,
+};
+
+struct Tzone {
+ char tzname[32];
+ char stname[16];
+ char dlname[16];
+ long stdiff;
+ long dldiff;
+ long dlpairs[150];
+};
+
+static QLock zlock;
+static int nzones;
+static Tzone **zones;
+static int mdays[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+static char *wday[] = {
+ "Sunday","Monday","Tuesday",
+ "Wednesday","Thursday","Friday",
+ "Saturday", nil,
+};
+static char *month[] = {
+ "January", "February", "March",
+ "April", "May", "June", "July",
+ "August", "September", "October",
+ "November", "December", nil
+};
+
+struct Tzabbrev {
+ char *abbr;
+ char *name;
+};
+
+struct Tzoffpair {
+ char *abbr;
+ int off;
+};
+
+#define isalpha(c)\
+ (((c)|0x60) >= 'a' && ((c)|0x60) <= 'z')
+
+/* Obsolete time zone names. Hardcoded to match RFC5322 */
+static Tzabbrev tzabbrev[] = {
+ {"UT", "GMT"}, {"GMT", "GMT"}, {"UTC", "GMT"},
+ {"EST", "US_Eastern"}, {"EDT", "US_Eastern"},
+ {"CST", "US_Central"}, {"CDT", "US_Central"},
+ {"MST", "US_Mountain"}, {"MDT", "US_Mountain"},
+ {"PST", "US_Pacific"}, {"PDT", "US_Pacific"},
+ {nil},
+};
+
+/* Military timezone names */
+static Tzoffpair milabbrev[] = {
+ {"A", -1*3600}, {"B", -2*3600}, {"C", -3*3600},
+ {"D", -4*3600}, {"E", -5*3600}, {"F", -6*3600},
+ {"G", -7*3600}, {"H", -8*3600}, {"I", -9*3600},
+ {"K", -10*3600}, {"L", -11*3600}, {"M", -12*3600},
+ {"N", +1*3600}, {"O", +2*3600}, {"P", +3*3600},
+ {"Q", +4*3600}, {"R", +5*3600}, {"S", +6*3600},
+ {"T", +7*3600}, {"U", +8*3600}, {"V", +9*3600},
+ {"W", +10*3600}, {"X", +11*3600}, {"Y", +12*3600},
+ {"Z", 0}, {nil, 0}
+};
+
+static vlong
+mod(vlong a, vlong b)
+{
+ vlong r;
+
+ r = a % b;
+ if(r < 0)
+ r += b;
+ return r;
+}
+
+static int
+isleap(int y)
+{
+ return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
+}
+
+static int
+rdname(char **f, char *p, int n)
+{
+ char *s, *e;
+
+ for(s = *f; *s; s++)
+ if(*s != ' ' && *s != '\t' && *s != '\n')
+ break;
+ e = s + n;
+ for(; *s && s != e; s++) {
+ if(*s == ' ' || *s == '\t' || *s == '\n')
+ break;
+ *p++ = *s;
+ }
+ *p = 0;
+ if(n - (e - s) < 3 || *s != ' ' && *s != '\t' && *s != '\n'){
+ werrstr("truncated name");
+ return -1;
+ }
+ *f = s;
+ return 0;
+}
+
+static int
+rdlong(char **f, long *p)
+{
+ int c, s;
+ long l;
+
+ s = 0;
+ while((c = *(*f)++) != 0){
+ if(c == '-')
+ s++;
+ else if(c != ' ' && c != '\n')
+ break;
+ }
+ if(c == 0) {
+ *p = 0;
+ return 0;
+ }
+ l = 0;
+ for(;;) {
+ if(c == ' ' || c == '\n')
+ break;
+ if(c < '0' || c > '9'){
+ werrstr("non-number %c in name", c);
+ return -1;
+ }
+ l = l*10 + c-'0';
+ c = *(*f)++;
+ }
+ if(s)
+ l = -l;
+ *p = l;
+ return 0;
+}
+
+static int
+loadzone(Tzone *tz, char *name)
+{
+ char buf[Tzsize*11+30], path[128], *p;
+ int i, f, r;
+
+ memset(tz, 0, sizeof(Tzone));
+ if(strcmp(name, "local") == 0)
+ snprint(path, sizeof(path), "/env/timezone");
+ else
+ snprint(path, sizeof(path), "/adm/timezone/%s", name);
+ memset(buf, 0, sizeof(buf));
+ if((f = open(path, 0)) == -1)
+ return -1;
+ r = read(f, buf, sizeof(buf));
+ close(f);
+ if(r == sizeof(buf) || r == -1)
+ return -1;
+ buf[r] = 0;
+ p = buf;
+ if(rdname(&p, tz->stname, sizeof(tz->stname)) == -1)
+ return -1;
+ if(rdlong(&p, &tz->stdiff) == -1)
+ return -1;
+ if(rdname(&p, tz->dlname, sizeof(tz->dlname)) == -1)
+ return -1;
+ if(rdlong(&p, &tz->dldiff) == -1)
+ return -1;
+ for(i=0; i < Tzsize; i++) {
+ if(rdlong(&p, &tz->dlpairs[i]) == -1){
+ werrstr("invalid transition time");
+ return -1;
+ }
+ if(tz->dlpairs[i] == 0)
+ return 0;
+ }
+ werrstr("invalid timezone %s", name);
+ return -1;
+}
+
+Tzone*
+tzload(char *tzname)
+{
+ Tzone *tz, **newzones;
+ int i;
+
+ if(tzname == nil)
+ tzname = "GMT";
+ qlock(&zlock);
+ for(i = 0; i < nzones; i++){
+ tz = zones[i];
+ if(strcmp(tz->stname, tzname) == 0)
+ goto found;
+ if(strcmp(tz->dlname, tzname) == 0)
+ goto found;
+ if(strcmp(tz->tzname, tzname) == 0)
+ goto found;
+ }
+
+ tz = malloc(sizeof(Tzone));
+ if(tz == nil)
+ goto error;
+ newzones = realloc(zones, (nzones + 1) * sizeof(Tzone*));
+ if(newzones == nil)
+ goto error;
+ if(loadzone(tz, tzname) != 0)
+ goto error;
+ if(snprint(tz->tzname, sizeof(tz->tzname), tzname) >= sizeof(tz->tzname)){
+ werrstr("timezone name too long");
+ return nil;
+ }
+ zones = newzones;
+ zones[nzones] = tz;
+ nzones++;
+found:
+ qunlock(&zlock);
+ return tz;
+error:
+ free(tz);
+ qunlock(&zlock);
+ return nil;
+}
+
+static void
+tzoffset(Tzone *tz, vlong abs, Tm *tm)
+{
+ long dl, *p;
+ dl = 0;
+ if(tz == nil){
+ snprint(tm->zone, sizeof(tm->zone), "GMT");
+ tm->tzoff = 0;
+ return;
+ }
+ for(p = tz->dlpairs; *p; p += 2)
+ if(abs > p[0] && abs <= p[1]){
+ dl = 1;
+ break;
+ }
+ if(dl){
+ snprint(tm->zone, sizeof(tm->zone), tz->dlname);
+ tm->tzoff = tz->dldiff;
+ }else{
+ snprint(tm->zone, sizeof(tm->zone), tz->stname);
+ tm->tzoff = tz->stdiff;
+ }
+}
+
+static Tm*
+tmfill(Tm *tm, vlong abs, vlong nsec)
+{
+ vlong zrel, j, y, m, d, t, e;
+ int i;
+
+ zrel = abs + tm->tzoff;
+ t = zrel % Daysec;
+ e = zrel / Daysec;
+ if(t < 0){
+ t += Daysec;
+ e -= 1;
+ }
+
+ t += nsec/Nsec;
+ tm->sec = mod(t, 60);
+ t /= 60;
+ tm->min = mod(t, 60);
+ t /= 60;
+ tm->hour = mod(t, 24);
+ tm->wday = mod((e + 4), 7);
+
+ /*
+ * Split up year, month, day.
+ *
+ * Implemented according to "Algorithm 199,
+ * conversions between calendar date and
+ * Julian day number", Robert G. Tantzen,
+ * Air Force Missile Development
+ * Center, Holloman AFB, New Mex.
+ *
+ * Lots of magic.
+ */
+ j = (zrel + 2440588 * Daysec) / (Daysec) - 1721119;
+ y = (4 * j - 1) / Days400y;
+ j = 4 * j - 1 - Days400y * y;
+ d = j / 4;
+ j = (4 * d + 3) / Days4y;
+ d = 4 * d + 3 - Days4y * j;
+ d = (d + 4) / 4 ;
+ m = (5 * d - 3) / 153;
+ d = 5 * d - 3 - 153 * m;
+ d = (d + 5) / 5;
+ y = 100 * y + j;
+
+ if(m < 10)
+ m += 3;
+ else{
+ m -= 9;
+ y++;
+ }
+
+ /* there's no year 0 */
+ if(y <= 0)
+ y--;
+ /* and if j negative, the day and month are also negative */
+ if(m < 0)
+ m += 12;
+ if(d < 0)
+ d += mdays[m - 1];
+
+ tm->yday = d;
+ for(i = 0; i < m - 1; i++)
+ tm->yday += mdays[i];
+ if(m > 1 && isleap(y))
+ tm->yday++;
+ tm->year = y - 1900;
+ tm->mon = m - 1;
+ tm->mday = d;
+ tm->nsec = mod(nsec, Nsec);
+ return tm;
+}
+
+
+Tm*
+tmtime(Tm *tm, vlong abs, Tzone *tz)
+{
+ return tmtimens(tm, abs, 0, tz);
+}
+
+Tm*
+tmtimens(Tm *tm, vlong abs, int ns, Tzone *tz)
+{
+ tm->tz = tz;
+ tzoffset(tz, abs, tm);
+ return tmfill(tm, abs, ns);
+}
+
+Tm*
+tmnow(Tm *tm, Tzone *tz)
+{
+ vlong ns;
+
+ ns = nsec();
+ return tmtimens(tm, nsec()/Nsec, mod(ns, Nsec), tz);
+}
+
+vlong
+tmnorm(Tm *tm)
+{
+ vlong c, yadj, j, abs, y, m, d;
+
+ if(tm->mon > 1){
+ m = tm->mon - 2;
+ y = tm->year + 1900;
+ }else{
+ m = tm->mon + 10;
+ y = tm->year + 1899;
+ }
+ d = tm->mday;
+ c = y / 100;
+ yadj = y - 100 * c;
+ j = (c * Days400y / 4 +
+ Days4y * yadj / 4 +
+ (153 * m + 2)/5 + d -
+ 719469);
+ abs = j * Daysec;
+ abs += tm->hour * 3600;
+ abs += tm->min * 60;
+ abs += tm->sec;
+ if(tm->tz){
+ tzoffset(tm->tz, abs - tm->tzoff, tm);
+ tzoffset(tm->tz, abs - tm->tzoff, tm);
+ }
+ abs -= tm->tzoff;
+ tmfill(tm, abs, tm->nsec);
+ return abs;
+}
+
+static int
+τconv(Fmt *f)
+{
+ int depth, n, v, w, h, m, c0, sgn, pad, off;
+ char *p, *am;
+ Tmfmt tf;
+ Tm *tm;
+
+ n = 0;
+ tf = va_arg(f->args, Tmfmt);
+ tm = tf.tm;
+ p = tf.fmt;
+ if(p == nil)
+ p = Ctimefmt;
+ while(*p){
+ w = 1;
+ pad = 0;
+ while(*p == '_'){
+ pad++;
+ p++;
+ }
+ c0 = *p++;
+ while(c0 && *p == c0){
+ w++;
+ p++;
+ }
+ pad += w;
+ switch(c0){
+ case 0:
+ break;
+ case 'Y':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->year + 1900); break;
+ case 2: n += fmtprint(f, "%*d", pad, tm->year % 100); break;
+ case 4: n += fmtprint(f, "%*d", pad, tm->year + 1900); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'M':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->mon + 1); break;
+ case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->mon + 1); break;
+ case 3: n += fmtprint(f, "%*.3s", pad, month[tm->mon]); break;
+ case 4: n += fmtprint(f, "%*s", pad, month[tm->mon]); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'D':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->mday); break;
+ case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->mday); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'W':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->wday + 1); break;
+ case 2: n += fmtprint(f, "%*.3s", pad, wday[tm->wday]); break;
+ case 3: n += fmtprint(f, "%*s", pad, wday[tm->wday]); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'H':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->hour % 12); break;
+ case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->hour % 12); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'h':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->hour); break;
+ case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->hour); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'm':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->min); break;
+ case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->min); break;
+ default: goto badfmt;
+ }
+ break;
+ case 's':
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, tm->sec); break;
+ case 2: n += fmtprint(f, "%*s%02d", pad-2, "", tm->sec); break;
+ default: goto badfmt;
+ }
+ break;
+ case 't':
+ v = tm->nsec / (1000*1000);
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, v % 1000); break;
+ case 2:
+ case 3: n += fmtprint(f, "%*s%03d", P(pad, 3), "", v % 1000); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'u':
+ v = tm->nsec / 1000;
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, v % 1000); break;
+ case 2: n += fmtprint(f, "%*s%03d", P(pad, 3), "", v % 1000); break;
+ case 3: n += fmtprint(f, "%*d", P(pad, 6), v); break;
+ case 4: n += fmtprint(f, "%*s%06d", P(pad, 6), "", v); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'n':
+ v = tm->nsec;
+ switch(w){
+ case 1: n += fmtprint(f, "%*d", pad, v%1000); break;
+ case 2: n += fmtprint(f, "%*s%03d", P(pad, 3), "", v % 1000); break;
+ case 3: n += fmtprint(f, "%*d", pad , v%(1000*1000)); break;
+ case 4: n += fmtprint(f, "%*s%06d", P(pad, 6), "", v%(1000000)); break;
+ case 5: n += fmtprint(f, "%*d", pad, v); break;
+ case 6: n += fmtprint(f, "%*s%09d", P(pad, 9), "", v); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'z':
+ if(w != 1)
+ goto badfmt;
+ case 'Z':
+ sgn = (tm->tzoff < 0) ? '-' : '+';
+ off = (tm->tzoff < 0) ? -tm->tzoff : tm->tzoff;
+ h = off/3600;
+ m = (off/60)%60;
+ if(w < 3 && pad < 5)
+ pad = 5;
+ switch(w){
+ case 1: n += fmtprint(f, "%*s%c%02d%02d", pad-5, "", sgn, h, m); break;
+ case 2: n += fmtprint(f, "%*s%c%02d:%02d", pad-5, "", sgn, h, m); break;
+ case 3: n += fmtprint(f, "%*s", pad, tm->zone); break;
+ }
+ break;
+ case 'A':
+ case 'a':
+ if(w != 1)
+ goto badfmt;
+ if(c0 == 'a')
+ am = (tm->hour < 12) ? "am" : "pm";
+ else
+ am = (tm->hour < 12) ? "AM" : "PM";
+ n += fmtprint(f, "%*s", pad, am);
+ break;
+ case '[':
+ depth = 1;
+ while(*p){
+ if(*p == '[')
+ depth++;
+ if(*p == ']')
+ depth--;
+ if(*p == '\\')
+ p++;
+ if(depth == 0)
+ break;
+ fmtrune(f, *p++);
+ }
+ if(*p++ != ']')
+ goto badfmt;
+ break;
+ default:
+ while(w-- > 0)
+ n += fmtrune(f, c0);
+ break;
+ }
+ }
+ return n;
+badfmt:
+ werrstr("garbled format %s", tf.fmt);
+ return -1;
+}
+
+static int
+getnum(char **ps, int maxw, int *ok)
+{
+ char *s, *e;
+ int n;
+
+ n = 0;
+ e = *ps + maxw;
+ for(s = *ps; s != e && *s >= '0' && *s <= '9'; s++){
+ n *= 10;
+ n += *s - '0';
+ }
+ *ok = s != *ps;
+ *ps = s;
+ return n;
+}
+
+static int
+lookup(char **s, char **tab, int len, int *ok)
+{
+ int nc, i;
+
+ *ok = 0;
+ for(i = 0; *tab; tab++){
+ nc = (len != -1) ? len : strlen(*tab);
+ if(cistrncmp(*s, *tab, nc) == 0){
+ *s += nc;
+ *ok = 1;
+ return i;
+ }
+ i++;
+ }
+ *ok = 0;
+ return -1;
+}
+
+Tm*
+tmparse(Tm *tm, char *fmt, char *str, Tzone *tz, char **ep)
+{
+ int depth, n, w, c0, zs, z0, z1, md, ampm, zoned, sloppy, tzo, ok;
+ vlong abs;
+ char *s, *p, *q;
+ Tzone *zparsed;
+ Tzabbrev *a;
+ Tzoffpair *m;
+
+ p = fmt;
+ s = str;
+ tzo = 0;
+ ampm = -1;
+ zoned = 0;
+ zparsed = nil;
+ sloppy = 0;
+ /* Default all fields */
+ tmtime(tm, 0, nil);
+ if(*p == '~'){
+ sloppy = 1;
+ p++;
+ }
+ while(*p){
+ w = 1;
+ c0 = *p++;
+ if(c0 == '?'){
+ w = -1;
+ c0 = *p++;
+ }
+ while(*p == c0){
+ if(w != -1) w++;
+ p++;
+ }
+ ok = 1;
+ switch(c0){
+ case 'Y':
+ switch(w){
+ case -1:
+ tm->year = getnum(&s, 4, &ok);
+ if(tm->year > 100) tm->year -= 1900;
+ break;
+ case 1: tm->year = getnum(&s, 4, &ok) - 1900; break;
+ case 2: tm->year = getnum(&s, 2, &ok); break;
+ case 3:
+ case 4: tm->year = getnum(&s, 4, &ok) - 1900; break;
+ default: goto badfmt;
+ }
+ break;
+ case 'M':
+ switch(w){
+ case -1:
+ tm->mon = getnum(&s, 2, &ok) - 1;
+ if(!ok) tm->mon = lookup(&s, month, -1, &ok);
+ if(!ok) tm->mon = lookup(&s, month, 3, &ok);
+ break;
+ case 1:
+ case 2: tm->mon = getnum(&s, 2, &ok) - 1; break;
+ case 3: tm->mon = lookup(&s, month, 3, &ok); break;
+ case 4: tm->mon = lookup(&s, month, -1, &ok); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'D':
+ switch(w){
+ case -1:
+ case 1:
+ case 2: tm->mday = getnum(&s, 2, &ok); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'W':
+ switch(w){
+ case -1:
+ tm->wday = lookup(&s, wday, -1, &ok);
+ if(!ok) tm->wday = lookup(&s, wday, 3, &ok);
+ break;
+ case 1: tm->wday = lookup(&s, wday, 3, &ok); break;
+ case 2: tm->wday = lookup(&s, wday, -1, &ok); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'h':
+ switch(w){
+ case -1:
+ case 1:
+ case 2: tm->hour = getnum(&s, 2, &ok); break;
+ default: goto badfmt;
+ }
+ break;
+ case 'm':
+ switch(w){
+ case -1:
+ case 1:
+ case 2: tm->min = getnum(&s, 2, &ok); break;
+ default: goto badfmt;
+ }
+ break;
+ case 's':
+ switch(w){
+ case -1:
+ case 1:
+ case 2: tm->sec = getnum(&s, 2, &ok); break;
+ default: goto badfmt;
+ }
+ break;
+ case 't':
+ switch(w){
+ case -1:
+ case 1:
+ case 2:
+ case 3: tm->nsec += getnum(&s, 3, &ok)*1000000; break;
+ }
+ break;
+ case 'u':
+ switch(w){
+ case -1:
+ case 1:
+ case 2: tm->nsec += getnum(&s, 3, &ok)*1000; break;
+ case 3:
+ case 4: tm->nsec += getnum(&s, 6, &ok)*1000; break;
+ }
+ break;
+ case 'n':
+ switch(w){
+ case 1:
+ case 2: tm->nsec += getnum(&s, 3, &ok); break;
+ case 3:
+ case 4: tm->nsec += getnum(&s, 6, &ok); break;
+ case -1:
+ case 5:
+ case 6: tm->nsec += getnum(&s, 9, &ok); break;
+ }
+ break;
+ case 'z':
+ if(w != 1)
+ goto badfmt;
+ case 'Z':
+ zs = 0;
+ zoned = 1;
+ switch(*s++){
+ case '+': zs = 1; break;
+ case '-': zs = -1; break;
+ default: s--; break;
+ }
+ q = s;
+ switch(w){
+ case -1:
+ case 3:
+ for(a = tzabbrev; a->abbr; a++){
+ n = strlen(a->abbr);
+ if(cistrncmp(s, a->abbr, n) == 0 && !isalpha(s[n]))
+ break;
+ }
+ if(a->abbr != nil){
+ s += strlen(a->abbr);
+ zparsed = tzload(a->name);
+ if(zparsed == nil){
+ werrstr("unloadable zone %s (%s)", a->abbr, a->name);
+ if(w != -1)
+ return nil;
+ }
+ goto Zoneparsed;
+ }
+ for(m = milabbrev; m->abbr != nil; m++){
+ n = strlen(m->abbr);
+ if(cistrncmp(s, m->abbr, n) == 0 && !isalpha(s[n]))
+ break;
+ }
+ if(m->abbr != nil){
+ snprint(tm->zone, sizeof(tm->zone), "%s", m->abbr);
+ tzo = m->off;
+ goto Zoneparsed;
+ }
+ if(w != -1)
+ break;
+ /* fall through */
+ case 1:
+ /* offset: [+-]hhmm */
+ z0 = getnum(&s, 4, &ok);
+ if(s - q == 4){
+ z1 = z0 % 100;
+ if(z0/100 > 13 || z1 >= 60)
+ goto baddate;
+ tzo = zs*(3600*(z0/100) + 60*z1);
+ snprint(tm->zone, sizeof(tm->zone), "%c%02d%02d", zs<0?'-':'+', z0/100, z1);
+ goto Zoneparsed;
+ }
+ if(w != -1)
+ goto baddate;
+ /* fall through */
+ case 2:
+ s = q;
+ /* offset: [+-]hh:mm */
+ z0 = getnum(&s, 2, &ok);
+ if(*s++ != ':')
+ break;
+ z1 = getnum(&s, 2, &ok);
+ if(z1 > 60)
+ break;
+ tzo = zs*(3600*z0 + 60*z1);
+ snprint(tm->zone, sizeof(tm->zone), "%c%d02:%02d", zs<0?'-':'+', z0, z1);
+ goto Zoneparsed;
+ }
+ if(w != -1)
+ goto baddate;
+ /*
+ * Final fuzzy fallback: If we have what looks like an
+ * unknown timezone abbreviation, keep the zone name,
+ * but give it a timezone offset of 0. This allows us
+ * to avoid rejecting zones outside of RFC5322.
+ */
+ for(s = q; *s; s++)
+ if(!isalpha(*s))
+ break;
+ if(s - q >= 3 && !isalpha(*s)){
+ strncpy(tm->zone, q, s - q);
+ tzo = 0;
+ ok = 1;
+ goto Zoneparsed;
+ }
+ goto baddate;
+Zoneparsed:
+ break;
+ case 'A':
+ case 'a':
+ if(cistrncmp(s, "am", 2) == 0)
+ ampm = 0;
+ else if(cistrncmp(s, "pm", 2) == 0)
+ ampm = 1;
+ else
+ goto baddate;
+ s += 2;
+ break;
+ case '[':
+ depth = 1;
+ while(*p){
+ if(*p == '[')
+ depth++;
+ if(*p == ']')
+ depth--;
+ if(*p == '\\')
+ p++;
+ if(depth == 0)
+ break;
+ if(*s == 0)
+ goto baddate;
+ if(*s++ != *p++)
+ goto baddate;
+ }
+ if(*p != ']')
+ goto badfmt;
+ p++;
+ break;
+ case '_':
+ case ',':
+ case ' ':
+
+ if(*s != ' ' && *s != '\t' && *s != ',' && *s != '\n' && *s != '\0')
+ goto baddate;
+ p += strspn(p, " ,_\t\n");
+ s += strspn(s, " ,\t\n");
+ break;
+ default:
+ if(*s == 0)
+ goto baddate;
+ if(*s++ != c0)
+ goto baddate;
+ break;
+ }
+ if(!ok)
+ goto baddate;
+ }
+ if(*p != '\0')
+ goto baddate;
+ if(ep != nil)
+ *ep = s;
+ if(!sloppy && ampm != -1 && (tm->hour < 1 || tm->hour > 12))
+ goto baddate;
+ if(ampm == 0 && tm->hour == 12)
+ tm->hour = 0;
+ else if(ampm == 1 && tm->hour < 12)
+ tm->hour += 12;
+ /*
+ * If we're allowing sloppy date ranges,
+ * we'll normalize out of range values.
+ */
+ if(!sloppy){
+ if(tm->yday < 0 || tm->yday > 365 + isleap(tm->year + 1900))
+ goto baddate;
+ if(tm->wday < 0 || tm->wday > 6)
+ goto baddate;
+ if(tm->mon < 0 || tm->mon > 11)
+ goto baddate;
+ md = mdays[tm->mon];
+ if(tm->mon == 1 && isleap(tm->year + 1900))
+ md++;
+ if(tm->mday < 0 || tm->mday > md)
+ goto baddate;
+ if(tm->hour < 0 || tm->hour > 24)
+ goto baddate;
+ if(tm->min < 0 || tm->min > 59)
+ goto baddate;
+ if(tm->sec < 0 || tm->sec > 60)
+ goto baddate;
+ if(tm->nsec < 0 || tm->nsec > Nsec)
+ goto baddate;
+ }
+
+ /*
+ * Normalizing gives us the local time,
+ * but because we havnen't applied the
+ * timezone, we think we're GMT. So, we
+ * need to shift backwards. Then, we move
+ * the "GMT that was local" back to local
+ * time.
+ */
+ abs = tmnorm(tm);
+ tm->tzoff = tzo;
+ if(!zoned)
+ tzoffset(tz, abs, tm);
+ else if(zparsed != nil){
+ tzoffset(zparsed, abs, tm);
+ tzoffset(zparsed, abs + tm->tzoff, tm);
+ }
+ abs -= tm->tzoff;
+ if(tz != nil || !zoned)
+ tmtimens(tm, abs, tm->nsec, tz);
+ return tm;
+baddate:
+ werrstr("invalid date %s", str);
+ return nil;
+badfmt:
+ werrstr("garbled format %s near '%s'", fmt, p);
+ return nil;
+}
+
+Tmfmt
+tmfmt(Tm *d, char *fmt)
+{
+ return (Tmfmt){fmt, d};
+}
+
+void
+tmfmtinstall(void)
+{
+ fmtinstall(L'τ', τconv);
+}
+
+/* These legacy functions need access to τconv */
+static char*
+dotmfmt(Fmt *f, ...)
+{
+ static char buf[30];
+ va_list ap;
+
+ va_start(ap, f);
+ f->runes = 0;
+ f->start = buf;
+ f->to = buf;
+ f->stop = buf + sizeof(buf) - 1;
+ f->flush = nil;
+ f->farg = nil;
+ f->nfmt = 0;
+ f->args = ap;
+ τconv(f);
+ va_end(ap);
+ buf[sizeof(buf) - 1] = 0;
+ return buf;
+}
+
+char*
+asctime(Tm* tm)
+{
+ Tmfmt tf;
+ Fmt f;
+
+ tf = tmfmt(tm, "WW MMM _D hh:mm:ss ZZZ YYYY\n");
+ return dotmfmt(&f, tf);
+}
+
diff -r e168590166a5 sys/src/libc/port/mkfile
--- a/sys/src/libc/port/mkfile Sun Jul 19 14:14:14 2020 -0700
+++ b/sys/src/libc/port/mkfile Sun Jul 19 21:34:16 2020 -0700
@@ -20,6 +20,7 @@
cleanname.c\
crypt.c\
ctype.c\
+ date.c\
encodefmt.c\
execl.c\
exits.c\
[-- Attachment #3: Type: text/plain, Size: 3094 bytes --]
diff -r f8f63e944375 sys/src/cmd/date.c
--- a/sys/src/cmd/date.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/date.c Sat Jul 18 22:37:01 2020 -0700
@@ -1,101 +1,68 @@
#include <u.h>
#include <libc.h>
-static char *day[] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+enum {
+ Nsec = 1000*1000*1000,
};
-static char *mon[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
- "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-int uflg, nflg, iflg, tflg, mflg;
-
-char*
-isodate(Tm *t)
+void
+usage(void)
{
- static char c[26]; /* leave room to append isotime */
- snprint(c, 11, "%04d-%02d-%02d",
- t->year + 1900, t->mon + 1, t->mday);
- return c;
-}
-
-char*
-isotime(Tm *t)
-{
- int tz;
- char *c, *d;
- d = isodate(t);
- c = d+10;
- snprint(c, 10, "T%02d:%02d:%02d",
- t->hour, t->min, t->sec); /* append to isodate */
- tz = t->tzoff / 60;
- if(t->tzoff) {
- /* localtime */
- if (t->tzoff > 0) {
- c[9] = '+';
- } else {
- c[9] = '-';
- tz = -tz;
- }
- snprint(c+10, 6, "%02d:%02d", tz / 60, tz % 60);
- } else {
- c[9] = 'Z';
- c[10] = 0;
- }
- return d;
-}
-
-char *
-mailtime(Tm *t)
-{
- static char c[64];
- char *sgn;
- int off;
-
- sgn = "+";
- if(t->tzoff < 0)
- sgn = "";
- off = (t->tzoff/3600)*100 + (t->tzoff/60)%60;
- snprint(c, sizeof(c), "%s, %.2d %s %.4d %.2d:%.2d:%.2d %s%.4d",
- day[t->wday], t->mday, mon[t->mon], t->year + 1900,
- t->hour, t->min, t->sec, sgn, off);
- return c;
+ fprint(2, "usage: date [-itunm] [-f fmt] [seconds]\n");
+ exits("usage");
}
void
main(int argc, char *argv[])
{
- ulong now;
- Tm *tm;
+ int nflg, uflg;
+ char *fmt;
+ vlong s, ns;
+ Tzone *tz;
+ Tm tm;
+
+ nflg = 0;
+ uflg = 0;
+ tz = nil;
+ fmt = "W MMM _D hh:mm:ss ZZZ YYYY";
+ tmfmtinstall();
+
ARGBEGIN{
- case 'n': nflg = 1; break;
- case 'u': uflg = 1; break;
- case 't': tflg = 1; /* implies -i */
- case 'i': iflg = 1; break;
- case 'm': mflg = 1; break;
- default: fprint(2, "usage: date [-itunm] [seconds]\n"); exits("usage");
+ case 'n': nflg = 1; break;
+ case 'u': uflg = 1; break;
+ case 't': fmt = "YYYY-MM-DDThh:mm:ssZZ"; break;
+ case 'i': fmt = "YYYY-MM-DD"; break;
+ case 'm': fmt = "W, DD MMM YYYY hh:mm:ss Z"; break;
+ case 'f': fmt = EARGF(usage()); break;
+ default: usage();
}ARGEND
- if(argc == 1)
- now = strtoul(*argv, 0, 0);
- else
- now = time(0);
+ s = 0;
+ ns = 0;
+ switch(argc) {
+ case 0:
+ ns = nsec();
+ s = ns/Nsec;
+ ns = ns%Nsec;
+ break;
+ case 1:
+ s = strtoll(argv[0], nil, 0);
+ ns = 0;
+ break;
+ default:
+ usage();
+ break;
+ }
+
+ if(!uflg && (tz = tzload("local")) == nil)
+ sysfatal("timezone: %r");
+ if(tmtimens(&tm, s, ns, tz) == nil)
+ sysfatal("now: %r");
if(nflg)
- print("%ld\n", now);
- else {
- tm = uflg ? gmtime(now) : localtime(now);
- if(iflg) {
- if(tflg)
- print("%s\n", isotime(tm));
- else
- print("%s\n", isodate(tm));
- } else if(mflg)
- print("%s\n", mailtime(tm));
- else
- print("%s", asctime(tm));
- }
+ print("%lld\n", tmnorm(&tm));
+ else
+ if(print("%τ\n", tmfmt(&tm, fmt)) == -1)
+ sysfatal("%r");
exits(0);
}
[-- Attachment #4: Type: text/plain, Size: 13722 bytes --]
diff -r f8f63e944375 sys/src/cmd/seconds.c
--- a/sys/src/cmd/seconds.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/seconds.c Sat Jul 18 22:37:07 2020 -0700
@@ -1,466 +1,114 @@
-/*
- * seconds absolute_date ... - convert absolute_date to seconds since epoch
- */
-
#include <u.h>
#include <libc.h>
-#include <ctype.h>
-typedef ulong Time;
-
-enum {
- AM, PM, HR24,
-
- /* token types */
- Month = 1,
- Year,
- Day,
- Timetok,
- Tz,
- Dtz,
- Ignore,
- Ampm,
-
- Maxtok = 6, /* only this many chars are stored in datetktbl */
- Maxdateflds = 25,
+char *knownfmt[] = {
+ /* asctime */
+ "W MMM DD hh:mm:ss ?Z YYYY",
+ /* RFC3339 */
+ "YYYY-MM-DD[T]hh:mm:ss[Z]?Z",
+ "YYYY-MM-DD[T]hh:mm:ss[Z]?Z",
+ "YYYY-MM-DD[T]hh:mm:ss ?Z",
+ "YYYY-MM-DD[T]hh:mm:ss?Z",
+ nil,
};
-/*
- * macros for squeezing values into low 7 bits of "value".
- * all timezones we care about are divisible by 10, and the largest value
- * (780) when divided is 78.
- */
-#define TOVAL(tp, v) ((tp)->value = (v) / 10)
-#define FROMVAL(tp) ((tp)->value * 10) /* uncompress */
+char *datefmt[] = {
+ /* RFC5322 */
+ "?W ?DD ?MMM ?YYYY",
+ "?W, DD-?MM-YY",
+ /* RFC822/RFC2822 */
+ "DD MMM YYYY",
+ "DD MMM YY",
+ /* RFC850 */
+ "W, DD-MMM-YY",
+ /* RFC1123 */
+ "WW, DD MMM YYYY",
+ /* RFC 3339 and human-readable variants */
+ "YYYY-MM-DD",
+ "YYYY-MM-DD [@] ",
+ /* random formats */
+ "?W ?MMM ?DD ?YYYY",
+ "?MMM ?DD ?YYYY",
+ "?DD ?MM ?YYYY",
+ "MMM ?DD ?YYYY",
+ "YYYY ?MM ?DD",
+ "YYYY ?DD ?MM",
+ "YYYY/MM?/DD?",
+ "MMM YYYY ?DD",
+ "?DD YYYY MMM",
+ "MM/DD/YYYY",
+ nil
+};
-/* keep this struct small since we have an array of them */
-typedef struct {
- char token[Maxtok];
- char type;
- schar value;
-} Datetok;
+char *timefmt[] = {
+ " hh:mm:ss",
+ " hh:mm",
+ " hh",
+ " hh:mm:ss ?A",
+ " hh:mm ?A",
+ " hh ?A",
+ "",
+ nil,
+};
-int dtok_numparsed;
+char *zonefmt[] = {
+ " ?Z",
+ "",
+ nil,
+};
-/* forwards */
-Datetok *datetoktype(char *s, int *bigvalp);
-
-static Datetok datetktbl[];
-static unsigned szdatetktbl;
-
-/* parse 1- or 2-digit number, advance *cpp past it */
-static int
-eatnum(char **cpp)
-{
- int c, x;
- char *cp;
-
- cp = *cpp;
- c = *cp;
- if (!isascii(c) || !isdigit(c))
- return -1;
- x = c - '0';
-
- c = *++cp;
- if (isascii(c) && isdigit(c)) {
- x = 10*x + c - '0';
- cp++;
- }
- *cpp = cp;
- return x;
-}
-
-/* return -1 on failure */
-int
-parsetime(char *time, Tm *tm)
-{
- tm->hour = eatnum(&time);
- if (tm->hour == -1 || *time++ != ':')
- return -1; /* only hour; too short */
-
- tm->min = eatnum(&time);
- if (tm->min == -1)
- return -1;
- if (*time++ != ':') {
- tm->sec = 0;
- return 0; /* no seconds; okay */
- }
-
- tm->sec = eatnum(&time);
- if (tm->sec == -1)
- return -1;
-
- /* this may be considered too strict. garbage at end of time? */
- return *time == '\0' || isascii(*time) && isspace(*time)? 0: -1;
-}
-
-/*
- * try to parse pre-split timestr in fields as an absolute date
- */
-int
-tryabsdate(char **fields, int nf, Tm *now, Tm *tm)
-{
- int i, mer = HR24, bigval = -1;
- long flg = 0, ty;
- Datetok *tp;
-
- now = localtime(time(0)); /* default to local time (zone) */
- tm->tzoff = now->tzoff;
- strncpy(tm->zone, now->zone, sizeof tm->zone);
-
- tm->mday = tm->mon = tm->year = -1; /* mandatory */
- tm->hour = tm->min = tm->sec = 0;
- dtok_numparsed = 0;
-
- for (i = 0; i < nf; i++) {
- if (fields[i][0] == '\0')
- continue;
- tp = datetoktype(fields[i], &bigval);
- ty = (1L << tp->type) & ~(1L << Ignore);
- if (flg & ty)
- return -1; /* repeated type */
- flg |= ty;
- switch (tp->type) {
- case Year:
- tm->year = bigval;
- if (tm->year < 1970 || tm->year > 2106)
- return -1; /* can't represent in ulong */
- /* convert 4-digit year to 1900 origin */
- if (tm->year >= 1900)
- tm->year -= 1900;
- break;
- case Day:
- tm->mday = bigval;
- break;
- case Month:
- tm->mon = tp->value - 1; /* convert to zero-origin */
- break;
- case Timetok:
- if (parsetime(fields[i], tm) < 0)
- return -1;
- break;
- case Dtz:
- case Tz:
- /* tm2sec mangles timezones, so we do our own handling */
- tm->tzoff = FROMVAL(tp);
- snprint(tm->zone, sizeof(tm->zone), "GMT");
- break;
- case Ignore:
- break;
- case Ampm:
- mer = tp->value;
- break;
- default:
- return -1; /* bad token type: CANTHAPPEN */
- }
- }
- if (tm->year == -1 || tm->mon == -1 || tm->mday == -1)
- return -1; /* missing component */
- if (mer == PM)
- tm->hour += 12;
- return 0;
-}
-
-int
-prsabsdate(char *timestr, Tm *now, Tm *tm)
-{
- int nf;
- char *fields[Maxdateflds];
- static char delims[] = "- \t\n/,";
-
- nf = gettokens(timestr, fields, nelem(fields), delims+1);
- if (nf > nelem(fields))
- return -1;
- if (tryabsdate(fields, nf, now, tm) < 0) {
- char *p = timestr;
-
- /*
- * could be a DEC-date; glue it all back together, split it
- * with dash as a delimiter and try again. Yes, this is a
- * hack, but so are DEC-dates.
- */
- while (--nf > 0) {
- while (*p++ != '\0')
- ;
- p[-1] = ' ';
- }
- nf = gettokens(timestr, fields, nelem(fields), delims);
- if (nf > nelem(fields) || tryabsdate(fields, nf, now, tm) < 0)
- return -1;
- }
- return 0;
-}
-
-int
-validtm(Tm *tm)
-{
- if (tm->year < 0 || tm->mon < 0 || tm->mon > 11 ||
- tm->mday < 1 || tm->hour < 0 || tm->hour >= 24 ||
- tm->min < 0 || tm->min > 59 ||
- tm->sec < 0 || tm->sec > 61) /* allow 2 leap seconds */
- return 0;
- return 1;
-}
-
-Time
-seconds(char *timestr)
-{
- Tm date;
-
- memset(&date, 0, sizeof date);
- if (prsabsdate(timestr, localtime(time(0)), &date) < 0)
- return -1;
- return validtm(&date)? tm2sec(&date) - 60*date.tzoff: -1;
-}
-
-int
-convert(char *timestr)
-{
- char *copy;
- Time tstime;
-
- copy = strdup(timestr);
- if (copy == nil)
- sysfatal("out of memory");
- tstime = seconds(copy);
- free(copy);
- if (tstime == -1) {
- fprint(2, "%s: `%s' not a valid date\n", argv0, timestr);
- return 1;
- }
- print("%lud\n", tstime);
- return 0;
-}
static void
usage(void)
{
- fprint(2, "usage: %s date-time ...\n", argv0);
+ fprint(2, "usage: %s [-f fmt] date-time/win m...\n", argv0);
exits("usage");
}
+/*
+ * seconds absolute_date ... - convert absolute_date to seconds since epoch
+ */
void
main(int argc, char **argv)
{
- int i, sts;
+ char **f, **df, **tf, **zf, *fmt, *ep, buf[256];
+ Tzone *tz;
+ Tm tm;
+ int i;
- sts = 0;
+ fmt = nil;
ARGBEGIN{
+ case 'f':
+ fmt = EARGF(usage());
+ break;
default:
usage();
- }ARGEND
- if (argc == 0)
- usage();
- for (i = 0; i < argc; i++)
- sts |= convert(argv[i]);
- exits(sts != 0? "bad": 0);
+ }ARGEND;
+
+ if((tz = tzload("local")) == nil)
+ sysfatal("bad local time: %r");
+ for(i = 0; i < argc; i++){
+ if(fmt != nil){
+ if(tmparse(&tm, fmt, argv[i], tz, &ep) != nil && *ep == 0)
+ goto Found;
+ }else{
+ for(f = knownfmt; *f != nil; f++)
+ if(tmparse(&tm, *f, argv[i], tz, &ep) != nil && *ep == 0)
+ goto Found;
+ for(df = datefmt; *df; df++)
+ for(tf = timefmt; *tf; tf++)
+ for(zf = zonefmt; *zf; zf++){
+ snprint(buf, sizeof(buf), "%s%s%s", *df, *tf, *zf);
+ if(tmparse(&tm, buf, argv[i], tz, &ep) != nil && *ep == 0)
+ goto Found;
+ }
+ }
+ if(*ep == 0)
+ sysfatal("tmparse: %r");
+ else
+ sysfatal("tmparse: trailing junk");
+Found:
+ print("%lld\n", tmnorm(&tm));
+ }
+ exits(nil);
}
-
-/*
- * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
- * is WAY faster than the generic bsearch().
- */
-Datetok *
-datebsearch(char *key, Datetok *base, unsigned nel)
-{
- int cmp;
- Datetok *last = base + nel - 1, *pos;
-
- while (last >= base) {
- pos = base + ((last - base) >> 1);
- cmp = key[0] - pos->token[0];
- if (cmp == 0) {
- cmp = strncmp(key, pos->token, Maxtok);
- if (cmp == 0)
- return pos;
- }
- if (cmp < 0)
- last = pos - 1;
- else
- base = pos + 1;
- }
- return 0;
-}
-
-Datetok *
-datetoktype(char *s, int *bigvalp)
-{
- char *cp = s;
- char c = *cp;
- static Datetok t;
- Datetok *tp = &t;
-
- if (isascii(c) && isdigit(c)) {
- int len = strlen(cp);
-
- if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
- tp->type = Timetok;
- else {
- if (bigvalp != nil)
- *bigvalp = atoi(cp); /* won't fit in tp->value */
- if (len == 4)
- tp->type = Year;
- else if (++dtok_numparsed == 1)
- tp->type = Day;
- else
- tp->type = Year;
- }
- } else if (c == '-' || c == '+') {
- int val = atoi(cp + 1);
- int hr = val / 100;
- int min = val % 100;
-
- val = hr*60 + min;
- TOVAL(tp, c == '-'? -val: val);
- tp->type = Tz;
- } else {
- char lowtoken[Maxtok+1];
- char *ltp = lowtoken, *endltp = lowtoken+Maxtok;
-
- /* copy to lowtoken to avoid modifying s */
- while ((c = *cp++) != '\0' && ltp < endltp)
- *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
- *ltp = '\0';
- tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
- if (tp == nil) {
- tp = &t;
- tp->type = Ignore;
- }
- }
- return tp;
-}
-
-
-/*
- * to keep this table reasonably small, we divide the lexval for Tz and Dtz
- * entries by 10 and truncate the text field at MAXTOKLEN characters.
- * the text field is not guaranteed to be NUL-terminated.
- */
-static Datetok datetktbl[] = {
-/* text token lexval */
- "acsst", Dtz, 63, /* Cent. Australia */
- "acst", Tz, 57, /* Cent. Australia */
- "adt", Dtz, -18, /* Atlantic Daylight Time */
- "aesst", Dtz, 66, /* E. Australia */
- "aest", Tz, 60, /* Australia Eastern Std Time */
- "ahst", Tz, 60, /* Alaska-Hawaii Std Time */
- "am", Ampm, AM,
- "apr", Month, 4,
- "april", Month, 4,
- "ast", Tz, -24, /* Atlantic Std Time (Canada) */
- "at", Ignore, 0, /* "at" (throwaway) */
- "aug", Month, 8,
- "august", Month, 8,
- "awsst", Dtz, 54, /* W. Australia */
- "awst", Tz, 48, /* W. Australia */
- "bst", Tz, 6, /* British Summer Time */
- "bt", Tz, 18, /* Baghdad Time */
- "cadt", Dtz, 63, /* Central Australian DST */
- "cast", Tz, 57, /* Central Australian ST */
- "cat", Tz, -60, /* Central Alaska Time */
- "cct", Tz, 48, /* China Coast */
- "cdt", Dtz, -30, /* Central Daylight Time */
- "cet", Tz, 6, /* Central European Time */
- "cetdst", Dtz, 12, /* Central European Dayl.Time */
- "cst", Tz, -36, /* Central Standard Time */
- "dec", Month, 12,
- "decemb", Month, 12,
- "dnt", Tz, 6, /* Dansk Normal Tid */
- "dst", Ignore, 0,
- "east", Tz, -60, /* East Australian Std Time */
- "edt", Dtz, -24, /* Eastern Daylight Time */
- "eet", Tz, 12, /* East. Europe, USSR Zone 1 */
- "eetdst", Dtz, 18, /* Eastern Europe */
- "est", Tz, -30, /* Eastern Standard Time */
- "feb", Month, 2,
- "februa", Month, 2,
- "fri", Ignore, 5,
- "friday", Ignore, 5,
- "fst", Tz, 6, /* French Summer Time */
- "fwt", Dtz, 12, /* French Winter Time */
- "gmt", Tz, 0, /* Greenwish Mean Time */
- "gst", Tz, 60, /* Guam Std Time, USSR Zone 9 */
- "hdt", Dtz, -54, /* Hawaii/Alaska */
- "hmt", Dtz, 18, /* Hellas ? ? */
- "hst", Tz, -60, /* Hawaii Std Time */
- "idle", Tz, 72, /* Intl. Date Line, East */
- "idlw", Tz, -72, /* Intl. Date Line, West */
- "ist", Tz, 12, /* Israel */
- "it", Tz, 22, /* Iran Time */
- "jan", Month, 1,
- "januar", Month, 1,
- "jst", Tz, 54, /* Japan Std Time,USSR Zone 8 */
- "jt", Tz, 45, /* Java Time */
- "jul", Month, 7,
- "july", Month, 7,
- "jun", Month, 6,
- "june", Month, 6,
- "kst", Tz, 54, /* Korea Standard Time */
- "ligt", Tz, 60, /* From Melbourne, Australia */
- "mar", Month, 3,
- "march", Month, 3,
- "may", Month, 5,
- "mdt", Dtz, -36, /* Mountain Daylight Time */
- "mest", Dtz, 12, /* Middle Europe Summer Time */
- "met", Tz, 6, /* Middle Europe Time */
- "metdst", Dtz, 12, /* Middle Europe Daylight Time*/
- "mewt", Tz, 6, /* Middle Europe Winter Time */
- "mez", Tz, 6, /* Middle Europe Zone */
- "mon", Ignore, 1,
- "monday", Ignore, 1,
- "mst", Tz, -42, /* Mountain Standard Time */
- "mt", Tz, 51, /* Moluccas Time */
- "ndt", Dtz, -15, /* Nfld. Daylight Time */
- "nft", Tz, -21, /* Newfoundland Standard Time */
- "nor", Tz, 6, /* Norway Standard Time */
- "nov", Month, 11,
- "novemb", Month, 11,
- "nst", Tz, -21, /* Nfld. Standard Time */
- "nt", Tz, -66, /* Nome Time */
- "nzdt", Dtz, 78, /* New Zealand Daylight Time */
- "nzst", Tz, 72, /* New Zealand Standard Time */
- "nzt", Tz, 72, /* New Zealand Time */
- "oct", Month, 10,
- "octobe", Month, 10,
- "on", Ignore, 0, /* "on" (throwaway) */
- "pdt", Dtz, -42, /* Pacific Daylight Time */
- "pm", Ampm, PM,
- "pst", Tz, -48, /* Pacific Standard Time */
- "sadt", Dtz, 63, /* S. Australian Dayl. Time */
- "sast", Tz, 57, /* South Australian Std Time */
- "sat", Ignore, 6,
- "saturd", Ignore, 6,
- "sep", Month, 9,
- "sept", Month, 9,
- "septem", Month, 9,
- "set", Tz, -6, /* Seychelles Time ?? */
- "sst", Dtz, 12, /* Swedish Summer Time */
- "sun", Ignore, 0,
- "sunday", Ignore, 0,
- "swt", Tz, 6, /* Swedish Winter Time */
- "thu", Ignore, 4,
- "thur", Ignore, 4,
- "thurs", Ignore, 4,
- "thursd", Ignore, 4,
- "tue", Ignore, 2,
- "tues", Ignore, 2,
- "tuesda", Ignore, 2,
- "ut", Tz, 0,
- "utc", Tz, 0,
- "wadt", Dtz, 48, /* West Australian DST */
- "wast", Tz, 42, /* West Australian Std Time */
- "wat", Tz, -6, /* West Africa Time */
- "wdt", Dtz, 54, /* West Australian DST */
- "wed", Ignore, 3,
- "wednes", Ignore, 3,
- "weds", Ignore, 3,
- "wet", Tz, 0, /* Western Europe */
- "wetdst", Dtz, 6, /* Western Europe */
- "wst", Tz, 48, /* West Australian Std Time */
- "ydt", Dtz, -48, /* Yukon Daylight Time */
- "yst", Tz, -54, /* Yukon Standard Time */
- "zp4", Tz, -24, /* GMT +4 hours. */
- "zp5", Tz, -30, /* GMT +5 hours. */
- "zp6", Tz, -36, /* GMT +6 hours. */
-};
-static unsigned szdatetktbl = nelem(datetktbl);
[-- Attachment #5: Type: text/plain, Size: 2450 bytes --]
diff -r f8f63e944375 sys/src/cmd/upas/common/common.h
--- a/sys/src/cmd/upas/common/common.h Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/common/common.h Sat Jul 18 23:30:20 2020 -0700
@@ -52,9 +52,6 @@
void mailfmtinstall(void); /* 'U' = 2047fmt */
#pragma varargck type "U" char*
-/* totm.c */
-int fromtotm(char*, Tm*);
-
/* a pipe between parent and child*/
typedef struct{
Biobuf bb;
diff -r f8f63e944375 sys/src/cmd/upas/common/folder.c
--- a/sys/src/cmd/upas/common/folder.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/common/folder.c Sat Jul 18 23:30:20 2020 -0700
@@ -1,5 +1,7 @@
#include "common.h"
+#define Ctimefmt "W MMM _D hh:mm:ss ZZZ YYYY"
+
enum{
Mbox = 1,
Mdir,
@@ -185,7 +187,7 @@
appendfolder(Biobuf *b, char *addr, int fd)
{
char *s;
- int r;
+ int r, n;
Biobuf bin;
Folder *f;
Tm tm;
@@ -194,9 +196,10 @@
Bseek(f->out, 0, 2);
Binit(&bin, fd, OREAD);
s = Brdstr(&bin, '\n', 0);
- if(!s || strncmp(s, "From ", 5))
+ n = strlen(s);
+ if(!s || strncmp(s, "From ", 5) != 0)
Bprint(f->out, "From %s %.28s\n", addr, ctime(f->t));
- else if(fromtotm(s, &tm) >= 0)
+ else if(n > 5 && tmparse(&tm, Ctimefmt, s + 5, nil, nil) != nil)
f->t = tm2sec(&tm);
if(s)
Bwrite(f->out, s, strlen(s));
diff -r f8f63e944375 sys/src/cmd/upas/common/mkfile
--- a/sys/src/cmd/upas/common/mkfile Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/common/mkfile Sat Jul 18 23:30:20 2020 -0700
@@ -10,7 +10,6 @@
fmt.$O\
libsys.$O\
process.$O\
- totm.$O\
HFILES=common.h\
sys.h\
diff -r f8f63e944375 sys/src/cmd/upas/common/totm.c
--- a/sys/src/cmd/upas/common/totm.c Fri Jul 17 16:53:20 2020 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#include <common.h>
-
-static char mtab[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-
-int
-ctimetotm(char *s, Tm *tm)
-{
- char buf[32];
-
- if(strlen(s) < 28)
- return -1;
- snprint(buf, sizeof buf, "%s", s);
- memset(tm, 0, sizeof *tm);
- buf[7] = 0;
- tm->mon = (strstr(mtab, buf+4) - mtab)/3;
- tm->mday = atoi(buf+8);
- tm->hour = atoi(buf+11);
- tm->min = atoi(buf+14);
- tm->sec = atoi(buf+17);
- tm->zone[0] = buf[20];
- tm->zone[1] = buf[21];
- tm->zone[2] = buf[22];
- tm->year = atoi(buf+24) - 1900;
- return 0;
-}
-
-int
-fromtotm(char *s, Tm *tm)
-{
- char buf[256], *f[3];
-
- snprint(buf, sizeof buf, "%s", s);
- if(getfields(buf, f, nelem(f), 0, " ") != 3)
- return -1;
- return ctimetotm(f[2], tm);
-}
[-- Attachment #6: Type: text/plain, Size: 14221 bytes --]
diff -r f8f63e944375 sys/src/cmd/upas/common/common.h
--- a/sys/src/cmd/upas/common/common.h Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/common/common.h Sat Jul 18 23:30:43 2020 -0700
@@ -52,9 +52,6 @@
void mailfmtinstall(void); /* 'U' = 2047fmt */
#pragma varargck type "U" char*
-/* totm.c */
-int fromtotm(char*, Tm*);
-
/* a pipe between parent and child*/
typedef struct{
Biobuf bb;
diff -r f8f63e944375 sys/src/cmd/upas/common/folder.c
--- a/sys/src/cmd/upas/common/folder.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/common/folder.c Sat Jul 18 23:30:43 2020 -0700
@@ -1,5 +1,7 @@
#include "common.h"
+#define Ctimefmt "W MMM _D hh:mm:ss ZZZ YYYY"
+
enum{
Mbox = 1,
Mdir,
@@ -185,7 +187,7 @@
appendfolder(Biobuf *b, char *addr, int fd)
{
char *s;
- int r;
+ int r, n;
Biobuf bin;
Folder *f;
Tm tm;
@@ -194,9 +196,10 @@
Bseek(f->out, 0, 2);
Binit(&bin, fd, OREAD);
s = Brdstr(&bin, '\n', 0);
- if(!s || strncmp(s, "From ", 5))
+ n = strlen(s);
+ if(!s || strncmp(s, "From ", 5) != 0)
Bprint(f->out, "From %s %.28s\n", addr, ctime(f->t));
- else if(fromtotm(s, &tm) >= 0)
+ else if(n > 5 && tmparse(&tm, Ctimefmt, s + 5, nil, nil) != nil)
f->t = tm2sec(&tm);
if(s)
Bwrite(f->out, s, strlen(s));
diff -r f8f63e944375 sys/src/cmd/upas/common/mkfile
--- a/sys/src/cmd/upas/common/mkfile Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/common/mkfile Sat Jul 18 23:30:43 2020 -0700
@@ -10,7 +10,6 @@
fmt.$O\
libsys.$O\
process.$O\
- totm.$O\
HFILES=common.h\
sys.h\
diff -r f8f63e944375 sys/src/cmd/upas/common/totm.c
--- a/sys/src/cmd/upas/common/totm.c Fri Jul 17 16:53:20 2020 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#include <common.h>
-
-static char mtab[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
-
-int
-ctimetotm(char *s, Tm *tm)
-{
- char buf[32];
-
- if(strlen(s) < 28)
- return -1;
- snprint(buf, sizeof buf, "%s", s);
- memset(tm, 0, sizeof *tm);
- buf[7] = 0;
- tm->mon = (strstr(mtab, buf+4) - mtab)/3;
- tm->mday = atoi(buf+8);
- tm->hour = atoi(buf+11);
- tm->min = atoi(buf+14);
- tm->sec = atoi(buf+17);
- tm->zone[0] = buf[20];
- tm->zone[1] = buf[21];
- tm->zone[2] = buf[22];
- tm->year = atoi(buf+24) - 1900;
- return 0;
-}
-
-int
-fromtotm(char *s, Tm *tm)
-{
- char buf[256], *f[3];
-
- snprint(buf, sizeof buf, "%s", s);
- if(getfields(buf, f, nelem(f), 0, " ") != 3)
- return -1;
- return ctimetotm(f[2], tm);
-}
diff -r f8f63e944375 sys/src/cmd/upas/fs/strtotm.c
--- a/sys/src/cmd/upas/fs/strtotm.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/fs/strtotm.c Sat Jul 18 23:30:43 2020 -0700
@@ -1,98 +1,25 @@
#include <u.h>
#include <libc.h>
-static char*
-skiptext(char *q)
+int
+strtotm(char *s, Tm *t)
{
- while(*q != '\0' && *q != ' ' && *q != '\t' && *q != '\r' && *q != '\n')
- q++;
- return q;
+ char **f, *fmt[] = {
+ "W MMM DD hh:mm:ss ?Z YYYY",
+ "?W ?DD ?MMM ?YYYY hh:mm:ss ?Z",
+ "?W ?DD ?MMM ?YYYY hh:mm:ss",
+ "?W, DD-?MM-YY",
+ "?DD ?MMM ?YYYY hh:mm:ss ?Z",
+ "?DD ?MMM ?YYYY hh:mm:ss",
+ "?DD-?MM-YY",
+ "?MMM/?DD/?YYYY hh:mm:ss ?Z",
+ "?MMM/?DD/?YYYY hh:mm:ss",
+ "?MMM/?DD/?YYYY",
+ nil,
+ };
+
+ for(f = fmt; *f; f++)
+ if(tmparse(t, *f, s, nil, nil) != nil)
+ return 0;
+ return -1;
}
-
-static char*
-skipwhite(char *q)
-{
- while(*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n')
- q++;
- return q;
-}
-
-static char* months[] = {
- "jan", "feb", "mar", "apr",
- "may", "jun", "jul", "aug",
- "sep", "oct", "nov", "dec"
-};
-
-int
-strtotm(char *p, Tm *t)
-{
- char *q, *r;
- int j;
- Tm tm;
- int delta;
-
- delta = 0;
- memset(&tm, 0, sizeof(tm));
- tm.mon = -1;
- tm.hour = -1;
- tm.min = -1;
- tm.year = -1;
- tm.mday = -1;
- memcpy(tm.zone, "GMT", 3);
- for(p = skipwhite(p); *p; p = skipwhite(q)){
- q = skiptext(p);
-
- /* look for time in hh:mm[:ss] */
- if(r = memchr(p, ':', q - p)){
- tm.hour = strtol(p, 0, 10);
- tm.min = strtol(r + 1, 0, 10);
- if(r = memchr(r + 1, ':', q - (r + 1)))
- tm.sec = strtol(r + 1, 0, 10);
- else
- tm.sec = 0;
- continue;
- }
-
- /* look for month */
- for(j = 0; j < 12; j++)
- if(cistrncmp(p, months[j], 3) == 0){
- tm.mon = j;
- break;
- }
- if(j != 12)
- continue;
-
- /* look for time zone [A-Z][A-Z]T */
- if(q - p == 3)
- if(p[0] >= 'A' && p[0] <= 'Z')
- if(p[1] >= 'A' && p[1] <= 'Z')
- if(p[2] == 'T'){
- strecpy(tm.zone, tm.zone + 4, p);
- continue;
- }
-
- if(p[0] == '+'||p[0] == '-')
- if(q - p == 5 && strspn(p + 1, "0123456789") == 4){
- delta = (((p[1] - '0')*10 + p[2] - '0')*60 + (p[3] - '0')*10 + p[4] - '0')*60;
- if(p[0] == '-')
- delta = -delta;
- continue;
- }
- if(strspn(p, "0123456789") == q - p){
- j = strtol(p, nil, 10);
- if(j >= 1 && j <= 31)
- tm.mday = j;
- if(j >= 1900)
- tm.year = j - 1900;
- continue;
- }
- //eprint("strtotm: garbage %.*s\n", utfnlen(p, q - p), p);
- }
- if(tm.mon < 0 || tm.year < 0
- || tm.hour < 0 || tm.min < 0
- || tm.mday < 0)
- return -1;
-
- *t = *localtime(tm2sec(&tm) - delta);
- return 0;
-}
diff -r f8f63e944375 sys/src/cmd/upas/imap4d/date.c
--- a/sys/src/cmd/upas/imap4d/date.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/imap4d/date.c Sat Jul 18 23:30:43 2020 -0700
@@ -1,142 +1,10 @@
#include "imap4d.h"
-static char *wdayname[] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-static char *monname[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-/*
- * zone : [A-Za-z][A-Za-z][A-Za-z] some time zone names
- * | [A-IK-Z] military time; rfc1123 says the rfc822 spec is wrong.
- * | "UT" universal time
- * | [+-][0-9][0-9][0-9][0-9]
- * zones is the rfc-822 list of time zone names
- */
-static Namedint zones[] =
-{
- {"A", -1 * 3600},
- {"B", -2 * 3600},
- {"C", -3 * 3600},
- {"CDT", -5 * 3600},
- {"CST", -6 * 3600},
- {"D", -4 * 3600},
- {"E", -5 * 3600},
- {"EDT", -4 * 3600},
- {"EST", -5 * 3600},
- {"F", -6 * 3600},
- {"G", -7 * 3600},
- {"GMT", 0},
- {"H", -8 * 3600},
- {"I", -9 * 3600},
- {"K", -10 * 3600},
- {"L", -11 * 3600},
- {"M", -12 * 3600},
- {"MDT", -6 * 3600},
- {"MST", -7 * 3600},
- {"N", +1 * 3600},
- {"O", +2 * 3600},
- {"P", +3 * 3600},
- {"PDT", -7 * 3600},
- {"PST", -8 * 3600},
- {"Q", +4 * 3600},
- {"R", +5 * 3600},
- {"S", +6 * 3600},
- {"T", +7 * 3600},
- {"U", +8 * 3600},
- {"UT", 0},
- {"V", +9 * 3600},
- {"W", +10 * 3600},
- {"X", +11 * 3600},
- {"Y", +12 * 3600},
- {"Z", 0},
-};
-
-static void
-zone2tm(Tm *tm, char *s)
-{
- int i;
- Tm aux, *atm;
-
- if(*s == '+' || *s == '-'){
- i = strtol(s, &s, 10);
- tm->tzoff = (i/100)*3600 + i%100;
- strncpy(tm->zone, "", 4);
- return;
- }
-
- /*
- * look it up in the standard rfc822 table
- */
- strncpy(tm->zone, s, 3);
- tm->zone[3] = 0;
- tm->tzoff = 0;
- for(i = 0; i < nelem(zones); i++){
- if(cistrcmp(zones[i].name, s) == 0){
- tm->tzoff = zones[i].v;
- return;
- }
- }
-
- /*
- * one last try: look it up in the current local timezone
- * probe a couple of times to get daylight/standard time change.
- */
- aux = *tm;
- memset(aux.zone, 0, 4);
- aux.hour--;
- for(i = 0; i < 2; i++){
- atm = localtime(tm2sec(&aux));
- if(cistrcmp(tm->zone, atm->zone) == 0){
- tm->tzoff = atm->tzoff;
- return;
- }
- aux.hour++;
- }
-
- strncpy(tm->zone, "GMT", 4);
- tm->tzoff = 0;
-}
-
-/*
- * hh[:mm[:ss]]
- */
-static void
-time2tm(Tm *tm, char *s)
-{
- tm->hour = strtoul(s, &s, 10);
- if(*s++ != ':')
- return;
- tm->min = strtoul(s, &s, 10);
- if(*s++ != ':')
- return;
- tm->sec = strtoul(s, &s, 10);
-}
-
-static int
-dateindex(char *d, char **tab, int n)
-{
- int i;
-
- for(i = 0; i < n; i++)
- if(cistrcmp(d, tab[i]) == 0)
- return i;
- return -1;
-}
-
int
imap4date(Tm *tm, char *date)
{
- char *flds[4];
-
- if(getfields(date, flds, 3, 0, "-") != 3)
+ if(tmparse(tm, "DD-?MM-YYYY hh:mm:ss ?Z", date, nil, nil) == nil)
return 0;
-
- tm->mday = strtol(flds[0], nil, 10);
- tm->mon = dateindex(flds[1], monname, 12);
- tm->year = strtol(flds[2], nil, 10) - 1900;
return 1;
}
@@ -146,29 +14,17 @@
ulong
imap4datetime(char *date)
{
- char *flds[4], *sflds[4];
- ulong t;
Tm tm;
+ vlong s;
- if(getfields(date, flds, 4, 0, " ") != 3)
- return ~0;
-
- if(!imap4date(&tm, flds[0]))
- return ~0;
-
- if(getfields(flds[1], sflds, 3, 0, ":") != 3)
- return ~0;
-
- tm.hour = strtol(sflds[0], nil, 10);
- tm.min = strtol(sflds[1], nil, 10);
- tm.sec = strtol(sflds[2], nil, 10);
-
- strcpy(tm.zone, "GMT");
- tm.yday = 0;
- t = tm2sec(&tm);
- zone2tm(&tm, flds[2]);
- t -= tm.tzoff;
- return t;
+ s = -1;
+ if(tmparse(&tm, "?DD-?MM-YYYY hh:mm:ss ?Z", date, nil, nil) != nil)
+ s = tmnorm(&tm);
+ else if(tmparse(&tm, "?W, ?DD-?MM-YYYY hh:mm:ss ?Z", date, nil, nil) != nil)
+ s = tmnorm(&tm);
+ if(s > 0 && s < (1ULL<<31))
+ return s;
+ return ~0;
}
/*
@@ -181,85 +37,18 @@
Tm*
date2tm(Tm *tm, char *date)
{
- char *flds[7], *s, dstr[64];
- int n;
- Tm gmt, *atm;
+ char **f, *fmts[] = {
+ "?W, ?DD ?MMM YYYY hh:mm:ss ?Z",
+ "?W ?M ?DD hh:mm:ss ?Z YYYY",
+ "?W, DD-?MM-YY hh:mm:ss ?Z",
+ "?DD ?MMM YYYY hh:mm:ss ?Z",
+ "?M ?DD hh:mm:ss ?Z YYYY",
+ "DD-?MM-YYYY hh:mm:ss ?Z",
+ nil,
+ };
- /*
- * default date is Thu Jan 1 00:00:00 GMT 1970
- */
- tm->wday = 4;
- tm->mday = 1;
- tm->mon = 1;
- tm->hour = 0;
- tm->min = 0;
- tm->sec = 0;
- tm->year = 70;
- strcpy(tm->zone, "GMT");
- tm->tzoff = 0;
-
- strncpy(dstr, date, sizeof dstr);
- dstr[sizeof dstr - 1] = 0;
- n = tokenize(dstr, flds, 7);
- if(n != 6 && n != 5)
- return nil;
-
- if(n == 5){
- for(n = 5; n >= 1; n--)
- flds[n] = flds[n - 1];
- n = 5;
- }else{
- /*
- * Wday[,]
- */
- s = strchr(flds[0], ',');
- if(s != nil)
- *s = 0;
- tm->wday = dateindex(flds[0], wdayname, 7);
- if(tm->wday < 0)
- return nil;
- }
-
- /*
- * check for the two major formats:
- * Month first or day first
- */
- tm->mon = dateindex(flds[1], monname, 12);
- if(tm->mon >= 0){
- tm->mday = strtoul(flds[2], nil, 10);
- time2tm(tm, flds[3]);
- zone2tm(tm, flds[4]);
- tm->year = strtoul(flds[5], nil, 10);
- if(strlen(flds[5]) > 2)
- tm->year -= 1900;
- }else{
- tm->mday = strtoul(flds[1], nil, 10);
- tm->mon = dateindex(flds[2], monname, 12);
- if(tm->mon < 0)
- return nil;
- tm->year = strtoul(flds[3], nil, 10);
- if(strlen(flds[3]) > 2)
- tm->year -= 1900;
- time2tm(tm, flds[4]);
- zone2tm(tm, flds[5]);
- }
-
- if(n == 5){
- gmt = *tm;
- strncpy(gmt.zone, "", 4);
- gmt.tzoff = 0;
- atm = gmtime(tm2sec(&gmt));
- tm->wday = atm->wday;
- }else{
- /*
- * Wday[,]
- */
- s = strchr(flds[0], ',');
- if(s != nil)
- *s = 0;
- tm->wday = dateindex(flds[0], wdayname, 7);
- if(tm->wday < 0)
- return nil;
- }
- return tm;
+ for(f = fmts; *f; f++)
+ if(tmparse(tm, *f, date, nil, nil) != nil)
+ return tm;
+ return nil;
}
diff -r f8f63e944375 sys/src/cmd/upas/imap4d/imap4d.c
--- a/sys/src/cmd/upas/imap4d/imap4d.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/imap4d/imap4d.c Sat Jul 18 23:30:43 2020 -0700
@@ -215,6 +215,7 @@
Binit(&bin, dup(0, -1), OREAD);
close(0);
Binit(&bout, 1, OWRITE);
+ tmfmtinstall();
quotefmtinstall();
fmtinstall('F', Ffmt);
fmtinstall('D', Dfmt); /* rfc822; # imap date %Z */
diff -r f8f63e944375 sys/src/cmd/upas/imap4d/print.c
--- a/sys/src/cmd/upas/imap4d/print.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/imap4d/print.c Sat Jul 18 23:30:43 2020 -0700
@@ -90,40 +90,24 @@
return fmtstrcpy(f, encfs(buf, sizeof buf, s));
}
-static char *day[] = {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
-};
-
-static char *mon[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
int
Dfmt(Fmt *f)
{
- char buf[128], *p, *e, *sgn, *fmt;
- int off;
- Tm *tm;
+ char buf[128], *fmt;
+ Tm *tm, t;
tm = va_arg(f->args, Tm*);
- if(tm == nil)
- tm = localtime(time(0));
- sgn = "+";
- if(tm->tzoff < 0)
- sgn = "";
- e = buf + sizeof buf;
- p = buf;
- off = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
+ if(tm == nil){
+ tz = tzload("local");
+ tm = tmtime(&t, time(0), tz);
+ }
if((f->flags & FmtSharp) == 0){
/* rfc822 style */
- fmt = "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d";
- p = seprint(p, e, "%s, ", day[tm->wday]);
+ fmt = "WW, DD MMM YYYY hh:mm:ss Z";
}else
- fmt = "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d";
- seprint(p, e, fmt,
- tm->mday, mon[tm->mon], tm->year + 1900, tm->hour, tm->min, tm->sec,
- sgn, off);
+ fmt = "DD-MMM-YYYY hh:mm:ss Z";
if(f->r == L'δ')
- return fmtstrcpy(f, buf);
+ return fmtprint(f, "%τ", tmfmt(tm, fmt));
+ snprint(buf, sizeof(buf), "%τ", tmfmt(tm, fmt));
return fmtprint(f, "%Z", buf);
}
diff -r f8f63e944375 sys/src/cmd/upas/marshal/marshal.c
--- a/sys/src/cmd/upas/marshal/marshal.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/upas/marshal/marshal.c Sat Jul 18 23:30:43 2020 -0700
@@ -140,6 +140,7 @@
char lastchar;
char *replymsg;
+#define Rfc822fmt "WW, DD MMM YYYY hh:mm:ss Z"
enum
{
Ok = 0,
@@ -208,6 +209,7 @@
hdrstring = nil;
ccargc = bccargc = 0;
+ tmfmtinstall();
quotefmtinstall();
fmtinstall('Z', doublequote);
fmtinstall('U', rfc2047fmt);
@@ -792,29 +794,13 @@
Bterm(f);
}
-char *ascwday[] =
-{
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-char *ascmon[] =
-{
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
int
printdate(Biobuf *b)
{
- int tz;
Tm *tm;
tm = localtime(time(0));
- tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
-
- return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n",
- ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900 + tm->year,
- tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz);
+ return Bprint(b, "Date: %τ\n", tmfmt(tm, Rfc822fmt));
}
int
@@ -1003,16 +989,10 @@
int
printunixfrom(int fd)
{
- int tz;
Tm *tm;
tm = localtime(time(0));
- tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
-
- return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n",
- user,
- ascwday[tm->wday], ascmon[tm->mon], tm->mday,
- tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900 + tm->year);
+ return fprint(fd, "From %s %τ\n", user, tmfmt(tm, Rfc822fmt));
}
char *specialfile[] =
[-- Attachment #7: Type: text/plain, Size: 3283 bytes --]
diff -r f8f63e944375 sys/src/cmd/webcookies.c
--- a/sys/src/cmd/webcookies.c Fri Jul 17 16:53:20 2020 +0200
+++ b/sys/src/cmd/webcookies.c Sun Jul 19 11:23:07 2020 -0700
@@ -606,143 +606,25 @@
}
/*
+ * Parse a date in one of these formats:
* Sunday, 25-Jan-2002 12:24:36 GMT
* Sunday, 25 Jan 2002 12:24:36 GMT
* Sun, 25 Jan 02 12:24:36 GMT
*/
-int
-isleap(int year)
-{
- return year%4==0 && (year%100!=0 || year%400==0);
-}
-
uint
strtotime(char *s)
{
- char *os;
- int i;
+ char **f, *fmts[] = {
+ "?WW, ?DD-?MM-?YYYY hh:mm:ss ?Z",
+ "?WW, ?DD ?MM ?YYYY hh:mm:ss ?Z",
+ nil,
+ };
Tm tm;
- static int mday[2][12] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
- 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
- };
- static char *wday[] = {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday",
- };
- static char *mon[] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- };
-
- memset(&tm, 0, sizeof(tm));
-
- os = s;
- /* Sunday, */
- for(i=0; i<nelem(wday); i++){
- if(cistrncmp(s, wday[i], strlen(wday[i])) == 0){
- s += strlen(wday[i]);
- break;
- }
- if(cistrncmp(s, wday[i], 3) == 0){
- s += 3;
- break;
- }
- }
- if(i==nelem(wday)){
- if(debug)
- fprint(2, "bad wday (%s)\n", os);
- return -1;
- }
- if(*s++ != ',' || *s++ != ' '){
- if(debug)
- fprint(2, "bad wday separator (%s)\n", os);
- return -1;
- }
-
- /* 25- */
- if(!isdigit(s[0]) || !isdigit(s[1]) || (s[2]!='-' && s[2]!=' ')){
- if(debug)
- fprint(2, "bad day of month (%s)\n", os);
- return -1;
- }
- tm.mday = strtol(s, 0, 10);
- s += 3;
-
- /* Jan- */
- for(i=0; i<nelem(mon); i++)
- if(cistrncmp(s, mon[i], 3) == 0){
- tm.mon = i;
- s += 3;
- break;
- }
- if(i==nelem(mon)){
- if(debug)
- fprint(2, "bad month (%s)\n", os);
- return -1;
- }
- if(s[0] != '-' && s[0] != ' '){
- if(debug)
- fprint(2, "bad month separator (%s)\n", os);
- return -1;
- }
- s++;
-
- /* 2002 */
- if(!isdigit(s[0]) || !isdigit(s[1])){
- if(debug)
- fprint(2, "bad year (%s)\n", os);
- return -1;
- }
- tm.year = strtol(s, 0, 10);
- s += 2;
- if(isdigit(s[0]) && isdigit(s[1]))
- s += 2;
- else{
- if(tm.year <= 68)
- tm.year += 2000;
- else
- tm.year += 1900;
- }
- if(tm.mday==0 || tm.mday > mday[isleap(tm.year)][tm.mon]){
- if(debug)
- fprint(2, "invalid day of month (%s)\n", os);
- return -1;
- }
- tm.year -= 1900;
- if(*s++ != ' '){
- if(debug)
- fprint(2, "bad year separator (%s)\n", os);
- return -1;
- }
-
- if(!isdigit(s[0]) || !isdigit(s[1]) || s[2]!=':'
- || !isdigit(s[3]) || !isdigit(s[4]) || s[5]!=':'
- || !isdigit(s[6]) || !isdigit(s[7]) || s[8]!=' '){
- if(debug)
- fprint(2, "bad time (%s)\n", os);
- return -1;
- }
-
- tm.hour = strtol(s, 0, 10);
- tm.min = strtol(s+3, 0, 10);
- tm.sec = strtol(s+6, 0, 10);
- if(tm.hour >= 24 || tm.min >= 60 || tm.sec >= 60){
- if(debug)
- fprint(2, "invalid time (%s)\n", os);
- return -1;
- }
- s += 9;
-
- if(cistrcmp(s, "GMT") != 0){
- if(debug)
- fprint(2, "time zone not GMT (%s)\n", os);
- return -1;
- }
- strcpy(tm.zone, "GMT");
- tm.yday = 0;
- return tm2sec(&tm);
+ for(f = fmts; *f != nil; f++)
+ if(tmparse(&tm, *f, s, nil, nil) != nil)
+ return tmnorm(&tm);
+ return -1;
}
/*
[-- Attachment #8: Type: text/plain, Size: 4793 bytes --]
diff -r e168590166a5 sys/src/cmd/ip/httpd/httpd.c
--- a/sys/src/cmd/ip/httpd/httpd.c Sun Jul 19 14:14:14 2020 -0700
+++ b/sys/src/cmd/ip/httpd/httpd.c Sun Jul 19 14:18:57 2020 -0700
@@ -51,6 +51,7 @@
address = nil;
hmydomain = nil;
netdir = "/net";
+ tmfmtinstall();
fmtinstall('D', hdatefmt);
fmtinstall('H', httpfmt);
fmtinstall('U', hurlfmt);
diff -r e168590166a5 sys/src/libhttpd/date.c
--- a/sys/src/libhttpd/date.c Sun Jul 19 14:14:14 2020 -0700
+++ b/sys/src/libhttpd/date.c Sun Jul 19 14:18:57 2020 -0700
@@ -4,212 +4,41 @@
/*
* print dates in the format
- * Wkd, DD Mon YYYY HH:MM:SS GMT
+ * Wkd, DD Mon YYYY HH:MM:SS +0000
+ */
+int
+hdatefmt(Fmt *f)
+{
+ Tm *tm, ts;
+ ulong t;
+
+ t = va_arg(f->args, ulong);
+ tm = tmtime(&ts, t, nil);
+ return fmtprint(f, "%τ", tmfmt(tm, "WW DD MMM YYYY hh:mm:ss Z"));
+}
+
+/*
* parse dates of formats
* Wkd, DD Mon YYYY HH:MM:SS GMT
* Weekday, DD-Mon-YY HH:MM:SS GMT
* Wkd Mon ( D|DD) HH:MM:SS YYYY
* plus anything similar
- */
-static char *
-weekdayname[7] =
-{
- "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
-};
-static char *
-wdayname[7] =
-{
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-static char *
-monname[12] =
-{
- "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static int dateindex(char*, char**, int);
-
-static int
-dtolower(int c)
-{
- if(c >= 'A' && c <= 'Z')
- return c - 'A' + 'a';
- return c;
-}
-
-static int
-disalpha(int c)
-{
- return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
-}
-
-static int
-disdig(int c)
-{
- return c >= '0' && c <= '9';
-}
-
-int
-hdatefmt(Fmt *f)
-{
- Tm *tm;
- ulong t;
-
- t = va_arg(f->args, ulong);
- tm = gmtime(t);
- return fmtprint(f, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
- wdayname[tm->wday], tm->mday, monname[tm->mon], tm->year+1900,
- tm->hour, tm->min, tm->sec);
-}
-
-static char*
-dateword(char *date, char *buf)
-{
- char *p;
- int c;
-
- p = buf;
- while(!disalpha(c = *date) && !disdig(c) && c)
- date++;
- while(disalpha(c = *date)){
- if(p - buf < 30)
- *p++ = dtolower(c);
- date++;
- }
- *p = 0;
- return date;
-}
-
-static int
-datenum(char **d)
-{
- char *date;
- int c, n;
-
- date = *d;
- while(!disdig(c = *date) && c)
- date++;
- if(c == 0){
- *d = date;
- return -1;
- }
- n = 0;
- while(disdig(c = *date)){
- n = n * 10 + c - '0';
- date++;
- }
- *d = date;
- return n;
-}
-
-/*
- * parse a date and return the seconds since the epoch
- * return 0 for a failure
+ * return seconds from epoch, 0 for a failure
*/
ulong
hdate2sec(char *date)
{
+ char **f, *fmt[] = {
+ "?W, ?DD ?MM ?YYYY hh:mm:ss ?Z",
+ "?W, ?DD-?MM-?YYYY-hh:mm:ss ?Z",
+ "?W, ?DD ?MM ?YYYY hh:mm:ss",
+ "?W, ?DD-?MM-?YYYY hh:mm:ss",
+ nil,
+ };
Tm tm;
- char buf[32];
- memset(&tm, 0, sizeof(tm));
-
- /*
- * Weekday|Wday
- */
- date = dateword(date, buf);
- tm.wday = dateindex(buf, wdayname, 7);
- if(tm.wday < 0)
- tm.wday = dateindex(buf, weekdayname, 7);
- if(tm.wday < 0)
- return 0;
-
- /*
- * check for the two major formats
- */
- date = dateword(date, buf);
- tm.mon = dateindex(buf, monname, 12);
- if(tm.mon >= 0){
- /*
- * MM
- */
- tm.mday = datenum(&date);
- if(tm.mday < 1 || tm.mday > 31)
- return 0;
-
- /*
- * HH:MM:SS
- */
- tm.hour = datenum(&date);
- if(tm.hour < 0 || tm.hour >= 24)
- return 0;
- tm.min = datenum(&date);
- if(tm.min < 0 || tm.min >= 60)
- return 0;
- tm.sec = datenum(&date);
- if(tm.sec < 0 || tm.sec >= 60)
- return 0;
-
- /*
- * YYYY
- */
- tm.year = datenum(&date);
- if(tm.year < 70 || tm.year > 99 && tm.year < 1970)
- return 0;
- if(tm.year >= 1970)
- tm.year -= 1900;
- }else{
- /*
- * MM-Mon-(YY|YYYY)
- */
- tm.mday = datenum(&date);
- if(tm.mday < 1 || tm.mday > 31)
- return 0;
- date = dateword(date, buf);
- tm.mon = dateindex(buf, monname, 12);
- if(tm.mon < 0 || tm.mon >= 12)
- return 0;
- tm.year = datenum(&date);
- if(tm.year < 70 || tm.year > 99 && tm.year < 1970)
- return 0;
- if(tm.year >= 1970)
- tm.year -= 1900;
-
- /*
- * HH:MM:SS
- */
- tm.hour = datenum(&date);
- if(tm.hour < 0 || tm.hour >= 24)
- return 0;
- tm.min = datenum(&date);
- if(tm.min < 0 || tm.min >= 60)
- return 0;
- tm.sec = datenum(&date);
- if(tm.sec < 0 || tm.sec >= 60)
- return 0;
-
- /*
- * timezone
- */
- dateword(date, buf);
- if(strncmp(buf, "gmt", 3) != 0)
- return 0;
- }
-
- strcpy(tm.zone, "GMT");
- tm.tzoff = 0;
- tm.yday = 0;
- return tm2sec(&tm);
-}
-
-static int
-dateindex(char *d, char **tab, int n)
-{
- int i;
-
- for(i = 0; i < n; i++)
- if(cistrcmp(d, tab[i]) == 0)
- return i;
+ for(f = fmt; *f != nil; f++)
+ if(tmparse(&tm, *f, date, nil, nil) != nil)
+ return tmnorm(&tm);
return -1;
}
next reply other threads:[~2020-07-20 15:04 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-06-15 6:03 ori [this message]
-- strict thread matches above, loose matches on Subject: below --
2020-05-31 3:39 ori
2020-05-31 4:57 ` [9front] " ori
2020-06-06 20:56 ` ori
2020-06-13 19:40 ` ori
2020-06-15 6:02 ` ori
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=BBADDAB4CED66A16CA14D7B468D4D9D0@eigenstate.org \
--to=ori@eigenstate.org \
--cc=9front@9front.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).