From: ori@eigenstate.org
To: ori@eigenstate.org, 9front@9front.org
Subject: Re: [9front] libc: date handling improvements
Date: Sun, 14 Jun 2020 23:02:36 -0700 [thread overview]
Message-ID: <93898413D555D17F598826842E67E234@eigenstate.org> (raw)
In-Reply-To: <364F2C1F2CDA34BA1E40FA14CDBA8767@eigenstate.org>
>> 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.
One more attempt -- and now, because this shit is annoying
enough to test, I started writing some regression tests.
Hoping to add other shit to the tests, too, as I start
touching other parts of the system.
https://git.sr.ht/~ori/regress
Attached, with a few bugfixes:
- Fix a missed case in the change from 0-based years
to 1900-based years.
- Fix a bug with negative modulo for years before 1970.
- Add a few more common formats to seconds(1), to match
the documentation.
- Make seconds(1) more flexible in the formats accepted:
when possible, build up dates from parts. (Perf is fine,
in spite of a lot more dates tried.)
diff -r 6055167dc76a sys/include/libc.h
--- a/sys/include/libc.h Mon Jun 15 00:12:57 2020 +0200
+++ b/sys/include/libc.h Sun Jun 14 23:01:58 2020 -0700
@@ -314,22 +314,45 @@
/*
* 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;
+ vlong abs; /* seconds since Jan 1 1970, GMT */
+ 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* tmgetzone(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*);
+extern Tm* 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 6055167dc76a sys/man/1/seconds
--- a/sys/man/1/seconds Mon Jun 15 00:12:57 2020 +0200
+++ b/sys/man/1/seconds Sun Jun 14 23:01:58 2020 -0700
@@ -3,6 +3,9 @@
seconds \- convert human-readable date (and time) to seconds since epoch
.SH SYNOPSIS
.B seconds
+[
+.I -f
+]
.I date
\&...
.SH DESCRIPTION
@@ -18,16 +21,15 @@
it will usually be necessary to enclose it in quotes.
.PP
.I Seconds
-accepts a somewhat wider range of input than just output from
+accepts a superset of a number of well known formats.
.IR date (1).
-The main requirement is that the date must be fully specified,
-with a day of month, month and year
-in any order.
-The month must be an English name (or abbreviation),
-not a number, and the year must contain 4 digits.
-Unambiguous time-zone names are understood (i.e., not
-.LR IST )
-or time zones may be written as
+Among these are RFC5322, RFC822, RFC2822, RFC850,
+RFC1123, and several other commmonly used formats.
+
+.PP
+The -f flag allows using a specific format, specified
+in the syntax of
+.IR tmdate (2).
.IR ±hhmm .
Case is ignored.
.SH EXAMPLES
diff -r 6055167dc76a sys/src/cmd/seconds.c
--- a/sys/src/cmd/seconds.c Mon Jun 15 00:12:57 2020 +0200
+++ b/sys/src/cmd/seconds.c Sun Jun 14 23:01:58 2020 -0700
@@ -1,236 +1,56 @@
-/*
- * 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",
};
-/*
- * 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 */
+ "?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",
+ "",
+ 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)
@@ -239,228 +59,44 @@
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, buf[256];
+ 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;
+
+ for(i = 0; i < argc; i++){
+ if(fmt != nil){
+ if(tmparse(&tm, fmt, argv[i], nil) != nil)
+ goto Found;
+ }else{
+ for(f = knownfmt; *f != nil; f++)
+ if(tmparse(&tm, *f, argv[i], nil) != nil)
+ 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], nil) != nil)
+ goto Found;
+ }
+ }
+ sysfatal("tmparse: %r");
+Found:
+ print("%lld\n", tm.abs);
+ }
+ 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);
diff -r 6055167dc76a sys/src/libc/9sys/ctime.c
--- a/sys/src/libc/9sys/ctime.c Mon Jun 15 00:12:57 2020 +0200
+++ b/sys/src/libc/9sys/ctime.c Sun Jun 14 23:01:58 2020 -0700
@@ -33,269 +33,34 @@
#include <u.h>
#include <libc.h>
-static char dmsize[12] =
+Tm*
+localtime(long tim)
{
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
-};
+ static Tm tm;
+ Tzone *tz;
-/*
- * The following table is used for 1974 and 1975 and
- * gives the day number of the first day after the Sunday of the
- * change.
- */
+ /* No error checking: the API doesn't allow it. */
+ tz = tmgetzone("local");
+ tmtime(&tm, tim, tz);
+ return &tm;
-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)
+gmtime(long abs)
{
- Tm *ct;
- long t, *p;
- int dlflag;
-
- 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;
-}
-
-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;
+ 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;
+ /* No error checking: the API doesn't allow it. */
+ tz = tmgetzone("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 6055167dc76a sys/src/libc/9sys/tm2sec.c
--- a/sys/src/libc/9sys/tm2sec.c Mon Jun 15 00:12:57 2020 +0200
+++ b/sys/src/libc/9sys/tm2sec.c Sun Jun 14 23:01:58 2020 -0700
@@ -1,202 +1,9 @@
#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;
-
- 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;
+ tmnorm(tm);
+ return tm->abs;
}
-
-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 6055167dc76a sys/src/libc/port/mkfile
--- a/sys/src/libc/port/mkfile Mon Jun 15 00:12:57 2020 +0200
+++ b/sys/src/libc/port/mkfile Sun Jun 14 23:01:58 2020 -0700
@@ -20,6 +20,7 @@
cleanname.c\
crypt.c\
ctype.c\
+ date.c\
encodefmt.c\
execl.c\
exits.c\
next prev parent reply other threads:[~2020-06-15 6:02 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
2020-06-15 6:03 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=93898413D555D17F598826842E67E234@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).