9front - general discussion about 9front
 help / color / mirror / Atom feed
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\



  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).