shithub: riscv

Download patch

ref: c834f535b799e0087d74103e96ddb03df457c597
parent: 6d3a2e6531bf784d433a39ac93b96beb0a0c8025
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Aug 10 15:02:09 EDT 2020

seconds: use new libc date api

--- a/sys/src/cmd/seconds.c
+++ b/sys/src/cmd/seconds.c
@@ -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;
+char *knownfmt[] = {
+	/* asctime */
+	"WW 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,
+};
 
-enum {
-	AM, PM, HR24,
+char *datefmt[] = {
+	/* RFC5322 */
+	"?W ?DD ?MMM ?YYYY",
+	"?W, DD-?MM-YY",
+	/* RFC822/RFC2822 */
+	"DD MMM YYYY",
+	"DD MMM YY",
+	/* RFC850 */
+	"WW, DD-MMM-YY",
+	/* RFC1123 */
+	"WWW, 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
+};
 
-	/* token types */
-	Month = 1,
-	Year,
-	Day,
-	Timetok,
-	Tz,
-	Dtz,
-	Ignore,
-	Ampm,
-
-	Maxtok		= 6, /* only this many chars are stored in datetktbl */
-	Maxdateflds	= 25,
+char *timefmt[] = {
+	" hh:mm:ss",
+	" hh:mm",
+	" hh",
+	" hh:mm:ss ?A",
+	" hh:mm ?A",
+	" hh ?A",
+	"",
+	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 */
-
-/* keep this struct small since we have an array of them */
-typedef struct {
-	char	token[Maxtok];
-	char	type;
-	schar	value;
-} Datetok;
-
-int dtok_numparsed;
-
-/* 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;
-}
+char *zonefmt[] = {
+	" ?Z",
+	"",
+	nil,
+};
 
-/* 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;
 
-/*
- * 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((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 (cmp < 0)
-			last = pos - 1;
+		if(*ep == 0)
+			sysfatal("tmparse: %r");
 		else
-			base = pos + 1;
+			sysfatal("tmparse: trailing junk");
+Found:
+		print("%lld\n", tmnorm(&tm));
 	}
-	return 0;
+	exits(nil);
 }
-
-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);