shithub: mc

Download patch

ref: 33f429bcdd5beeed24a8650320e9d3a83ce2d6f5
parent: 9befecb14dfadec7f097d54065793d209cc6eb92
parent: 456959812209570826cc92614f4aca8b21c13e5b
author: Ori Bernstein <ori@eigenstate.org>
date: Fri Aug 29 16:37:34 EDT 2014

Merge branch 'master' of git+ssh://git.eigenstate.org/git/ori/libdate

Conflicts:
	date.myr

--- a/lib/date/Makefile
+++ b/lib/date/Makefile
@@ -1,6 +1,7 @@
 MYRLIB=date
 MYRSRC= \
 	date.myr \
+	fmt.myr \
 	types.myr \
 	zoneinfo.myr \
 
--- a/lib/date/date.myr
+++ b/lib/date/date.myr
@@ -3,67 +3,59 @@
 use "zoneinfo.use"
 
 pkg date =
-	/* date i/o */
-	const parse	: (d : byte[:]	-> date)
-	const parsefmt	: (fmt : byte[:], d : byte[:]	-> date)
-	const parsez	: (d : byte[:], tz : byte[:]	-> date)
-	const fmt	: (d : date	-> byte[:])
-	const fmtymd	: (d : date	-> byte[:])
-	const bfmt	: (buf : byte[:], d : date	-> std.size)
-	const bfmtymd	: (buf : byte[:], d : date	-> std.size)
-
 	/* useful constructors */
-	const mkdate	: (tm : std.time, tz : diff -> date)
-	const now	: (tz : byte[:] -> date)
-	const utcnow	: (-> date)
-	const localoff	: (-> diff)
-	const tzoff	: (tzname : byte[:]	-> diff)
-	const time	: (d : date	-> std.time)
+	const utcnow	: (-> instant)
+	const now	: (tz : byte[:] -> instant)
+	const tozone	: (d : instant, zone : byte[:]	-> instant)
+	const mkdate	: (tm : std.time, zone : byte[:]	-> instant)
 
+	const localoff	: (tm : std.time -> diff)
+	const tzoff	: (tzname : byte[:], tm : std.time	-> diff)
+	const isleap	: (d : instant	-> bool)
+
 	/* date differences */
-	const add	: (d : date, dt : diff	-> date)
-	const diff	: (a : date, b : date -> diff)
+	const add	: (d : instant, dt : diff	-> instant)
+	const sub	: (d : instant, dt : diff	-> instant)
+	const diff	: (a : instant, b : instant	-> diff)
+	const duradd	: (d : instant, dt : duration	-> instant)
+	const dursub	: (d : instant, dt : duration	-> instant)
 ;;
 
-const UnixJulianDiff	= 719468
+const Unix2Julian	= 719468
 const Days400y	= 365*400 + 4*25 - 3
 const Days4y	= 365*4 + 1
 
-const fmt = {d
-	-> std.fmt("%04i-%02i-%02i %i:%i:%i", d.year, d.mon, d.day, d.h, d.m, d.s)
+const utcnow = {
+	-> mkdate(std.now(), "")
 }
 
-const fmtymd = {d
-	-> std.fmt("%04i-%02i-%02i", d.year, d.mon, d.day)
-}
-
-const bfmt = {buf, d
-	-> std.bfmt(buf, "%04i-%02i-%02i %i:%i:%i", d.year, d.mon, d.day, d.h, d.m, d.s)
-}
-
-const bfmtymd = {buf, d
-	-> std.bfmt(buf, "%04i-%02i-%02i", d.year, d.mon, d.day)
-}
-
 const now = {tz : byte[:]
 	var tm
 
 	tm = std.now()
-	-> mkdate(tm, _zoneinfo.findtzoff(tz, tm))
+	-> mkdate(tm, tz)
 }
 
-const utcnow = {
-	-> mkdate(std.now(), 0)
+const tozone = {d, tz
+	-> mkdate(d.actual, tz)
 }
 
-const mkdate = {tm, off
+const mkdate = {tm, tz 
 	var j, y, m, d
 	var t, e
 	var date
+	var off
 
 	date.actual = tm
+	/* time zones */
+	std.assert(tz.len <= date._tzbuf.len, "time zone name too long\n")
+	off =_zoneinfo.findtzoff(tz, tm) 
 	date.tzoff = off
+	std.slcp(date._tzbuf[:tz.len], tz)
+	date.tzname = date._tzbuf[:tz.len]
 	tm += off castto(std.time)
+
+	/* break up time */
 	t = tm % (24*60*60*1_000_000)	/* time */
 	e = tm / (24*60*60*1_000_000)	/* epoch days */
 
@@ -79,8 +71,9 @@
 
 	/* weekday */
 	date.wday = ((e + 4) % 7) castto(int)	/* the world started on Thursday */
+
 	/*
-	year, month, day: 
+	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
@@ -88,7 +81,7 @@
 
 	Lots of magic. Yer a wizard, 'arry.
 	*/
-	j = e + UnixJulianDiff
+	j = e + Unix2Julian
 	y = (4 * j - 1) / Days400y
 	j = 4 * j - 1 - Days400y * y
 	d = j / 4
@@ -111,39 +104,27 @@
 	-> date
 }
 
-const time = {date
-	var t
-	var c, y, ya, m, u
+const localoff = {tm
+	-> _zoneinfo.findtzoff("local", tm)
+}
 
-	t = 0
+const tzoff = {tz, tm
+	-> _zoneinfo.findtzoff(tz, tm)
+}
 
-	if date.mon > 2
-		m = (date.mon - 3) castto(std.time)
-	else
-		m = (date.mon + 9) castto(std.time)
-		y = (date.year - 1) castto(std.time)
-	;;
-	
-	c = y / 100
-	ya = y - 100 * c
-	u = (146097 * c) / 4 + \
-		(1461 * ya) / 4 + \
-		(153 * m + 2) / 5 + \
-		(date.day castto(std.time)) + \
-		UnixJulianDiff
+const isleap = {d
+	-> d.year % 4 == 0 && (d.year % 100 != 0 || d.year % 400 == 0)
+}
 
-	t += (u * 24*60*60*1_000_000)
-	t += (date.h castto(std.time)) * 60*60*1_000_000
-	t += (date.m castto(std.time)) * 60*1_000_000
-	t += (date.s castto(std.time)) * 1_000_000
-	t += date.us castto(std.time)
-	-> t
+const add  = {d, dt
+	-> mkdate(d.actual + (dt castto(std.time)), d.tzname)
 }
 
-const ndays = {y
-	if y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)
-		-> 366
-	else
-		-> 365
-	;;
+const sub  = {d, dt
+	-> mkdate(d.actual - (dt castto(std.time)), d.tzname)
 }
+
+const diff = {a, b
+	-> (b.actual - a.actual) castto(diff)
+}
+
--- /dev/null
+++ b/lib/date/fmt.myr
@@ -1,0 +1,115 @@
+use std
+use "types.use"
+
+pkg date = 
+	const fmt	: (d : instant, time : bool	-> byte[:])
+	const bfmt	: (buf : byte[:], d : instant, time : bool	-> std.size)
+	const ftime	: (f : byte[:], d : instant	-> byte[:])
+	const bftime	: (buf : byte[:], f : byte[:], d : instant	-> std.size)
+;;
+
+const Datetimefmt	= "%Y-%m-%d %H:%M:%S %z"
+const Timefmt	= "%h:%m:%s %z"
+const Datefmt	= "%Y-%m-%d %z"
+
+const fmt = {d, time
+	if time
+		-> ftime(Datetimefmt, d)
+	else
+		-> ftime(Datefmt, d)
+	;;
+}
+
+const bfmt = {buf, d, time
+	if time
+		-> bftime(buf, Datetimefmt, d)
+	else
+		-> bftime(buf, Datefmt, d)
+	;;
+}
+
+const ftime = {f, d
+	var buf
+	var sz
+
+	buf = std.slalloc(2048)
+	sz = bftime(buf, f, d)
+	-> buf[:sz]
+}
+
+const abbrevday = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
+const fullday = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
+const abbrevmon = ["NONE", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+const fullmon = ["NONE", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
+
+const bftime = {buf, f, d
+	var c
+	var o
+	
+	o = 0
+	while f.len != 0
+		(c, f) = std.striter(f)
+		if c == '%'
+			(c, f) = std.striter(f)
+			match c
+			| 'a':	o += std.bfmt(buf[o:], "%s", abbrevday[d.day])
+			| 'A':	o += std.bfmt(buf[o:], "%s", fullday[d.day])
+			| 'b':	o += std.bfmt(buf[o:], "%s", abbrevmon[d.mon])
+			| 'B':	o += std.bfmt(buf[o:], "%s", fullmon[d.mon])
+			| 'c':	o += bftime(buf[o:], "%Y-%m-%d", d)
+			| 'C':	o += std.bfmt(buf[o:], "%02i", d.year % 100)
+			| 'd':	o += std.bfmt(buf[o:], "%02i", d.day)
+			| 'D':	o += std.bfmt(buf[o:], "%m/%d/%y (wtf america)", d.mon, d.day, d.year)
+			| 'e':	o += std.bfmt(buf[o:], "%2i", d.day)
+			| 'F':	o += std.bfmt(buf[o:], "%y-%m-%d", d.year, d.mon, d.day)
+			/*
+			| 'G':	o += std.bfmt(buf[o:], ...?
+			| 'g':
+			*/
+			| 'h':	o += std.bfmt(buf[o:], "%s", abbrevmon[d.mon])
+			| 'H':	o += std.bfmt(buf[o:], "%02i", d.h)
+			| 'I':	o += std.bfmt(buf[o:], "%02i", d.h % 12)
+			| 'j':	o += std.bfmt(buf[o:], "year day... unimplemented.")
+			| 'k':	o += std.bfmt(buf[o:], "%i", d.h)
+			| 'l':	o += std.bfmt(buf[o:], "%i", d.h % 12)
+			| 'm':	o += std.bfmt(buf[o:], "%i", d.mon)
+			| 'M':	o += std.bfmt(buf[o:], "%i", d.m)
+			| 'n':	o += std.bfmt(buf[o:], "\n")
+			| 'O':	o += std.bfmt(buf[o:], "unsupported %O")
+			| 'p':	o += std.bfmt(buf[o:], "%s", ["AM", "PM"][d.h/12])
+			| 'P':	o += std.bfmt(buf[o:], "%s", ["am", "pm"][d.h/12])
+			| 'r':	o += bftime(buf[o:], "%H:%M:%S %P", d) 
+			| 'R':	o += bftime(buf[o:], "%H:%M %P", d)
+			| 's':	o += std.bfmt(buf[o:], "%l", d.actual)
+			| 'S':	o += std.bfmt(buf[o:], "%i", d.s)
+			| 't':	o += std.bfmt(buf[o:], "\t")
+			| 'u':	o += std.bfmt(buf[o:], "%i", d.wday)
+			| 'U':	o += std.bfmt(buf[o:], "week number... unimplemented.")
+			| 'x':	o += bftime(buf[o:], Datefmt, d)
+			| 'X':	o += bftime(buf[o:], Timefmt, d)
+			| 'y':	o += std.bfmt(buf[o:], "%i", d.year % 100)
+			| 'Y':	o += std.bfmt(buf[o:], "%i", d.year)
+			| 'z':	o += timezone(buf[o:], d.tzoff)
+			| 'Z':	o += std.bfmt(buf[o:], "%s", d.tzname)
+			| '%':	o += std.bfmt(buf[o:], "%%")
+			;;
+		else
+			o += std.bfmt(buf[o:], "%c", c)
+		;;
+	;;
+	-> o
+}
+
+const timezone = {buf, off
+	var h, m
+	var sep
+
+	sep = "+"
+	if off < 0
+		off = -off
+		sep = "-"
+	;;
+	h = off % 3600
+	m = off / 3600
+	-> std.bfmt(buf, "%s%02i%02i", sep, h, m)
+}
--- /dev/null
+++ b/lib/date/parse.myr
@@ -1,0 +1,11 @@
+use std
+use "date.use"
+
+pkg date =
+	/* date i/o */
+	const parse	: (d : byte[:]	-> instant)
+	const parsefmt	: (fmt : byte[:], d : byte[:]	-> instant)
+	const parsez	: (d : byte[:], tz : byte[:]	-> instant)
+;;
+
+
--- /dev/null
+++ b/lib/date/test/Makefile
@@ -1,0 +1,20 @@
+# don't build anything for 'all'
+all: 
+	$(MAKE) -C ..
+
+check:
+	./runtest.sh
+
+.PHONY: %
+%:
+	./runtest.sh $@
+
+.PHONY: clean
+clean:
+	rm -f testmatch.use testmatch.o
+	@for i in `awk '/^[A-Z]/{print $$2}' tests`; do \
+	    echo rm -f $$i; \
+	    rm -f $$i; \
+	done
+
+install:
--- /dev/null
+++ b/lib/date/test/data/ftime-test-expected
@@ -1,0 +1,1 @@
+2014-8-29 19:47:43 +0000
--- /dev/null
+++ b/lib/date/test/ftime-test.myr
@@ -1,0 +1,13 @@
+use std
+use date
+
+const main = {
+	var buf : byte[1024]
+	var d
+	var n
+
+	/*Fri 29 Aug 2014 07:47:43 PM UTC*/
+	d = date.mkdate(1_409_341_663*1_000_000, "")
+	n = date.bfmt(buf[:], d, true)
+	std.put("%s\n", buf[:n])
+}
--- /dev/null
+++ b/lib/date/test/runtest.sh
@@ -1,0 +1,124 @@
+#!/bin/bash
+NFAILURES=0
+NPASSES=0
+
+function build {
+    rm -f $1 $1.o $1.s $1.use
+    myrbuild $FLAGS -b $1 $1.myr $EXTRA_SRC
+}
+
+function pass {
+    PASSED="$PASSED $1"
+    NPASSED=$[$NPASSED + 1]
+}
+
+function fail {
+    echo "FAIL: $1"
+    FAILED="$FAILED $1"
+    NFAILED=$[$NFAILED + 1]
+}
+
+function expectstatus {
+    ./$1 $3
+    if [ $? -eq $2 ]; then
+        pass $1
+        return
+    else
+        fail $1
+    fi
+}
+
+function expectprint {
+    if [ "`./$1 $3`" != "$2" ]; then
+        fail $1
+    else
+        pass $1
+    fi
+}
+
+
+function expectcompare {
+    if [ x"" !=  x"$TMPDIR" ]; then 
+        t=$TMPDIR/myrtest-$1-$RANDOM
+    else
+        t=/tmp/myrtest-$1-$RANDOM
+    fi
+    ./$1 $3 > $t
+    if cmp $t data/$1-expected; then
+        pass $1
+    else
+        fail $1
+    fi
+    rm -f $t
+}
+
+function expectfcompare {
+    ./$1 $3
+    if cmp data/$1-expected $2; then
+        pass $1
+    else
+        fail $1
+    fi
+}
+
+function shouldskip {
+  if [ -z $ARGS ]; then
+      return 1
+  fi
+
+  for i in $ARGS; do
+      if [ $i = $1 ]; then
+          return 1
+      fi
+  done
+  return 0
+}
+
+
+# Should build and run
+function B {
+    if shouldskip $1; then
+        return
+    fi
+
+    test="$1"; shift
+    type="$1"; shift
+    res="$1"; shift
+    if [ $# > 0 ]; then
+        args="$1"; shift
+    fi
+    build $test
+    case $type in
+    "E")  expectstatus "$test" "$res" "$input";;
+    "P")  expectprint "$test" "$res" "$input";;
+    "C")  expectcompare "$test" "$res" "$input";;
+    "F")  expectfcompare "$test" "$res" "$args";;
+    esac
+}
+
+# Should fail
+function F {
+    if shouldskip $1; then
+        return
+    fi
+    (build $1) > /dev/null
+    if [ $? -eq '1' ]; then
+        pass $1
+    else
+        fail $1
+    fi
+}
+
+# Should generate a usefile
+function U {
+    return
+}
+
+source tests
+
+echo "PASSED ($NPASSED): $PASSED"
+if [ -z "$NFAILED" ]; then
+    echo "SUCCESS"
+else
+    echo "FAILURES ($NFAILED): $FAILED"
+fi
--- /dev/null
+++ b/lib/date/test/tests
@@ -1,0 +1,23 @@
+FLAGS=-I../
+mkdir -p tmpout
+# Format:
+# [B|F] testname [E|P] result
+#    [B|F]: Compiler outcome.
+#	B: Expect that this test will build.
+#	F: Expect that this test will not build.
+#    testname: Test case
+#	The test that will run. We will try to
+#	compile 'testname.myr' to 'testname',
+#	and then execute it, verifying the result
+#    [E|P|C]: Result type
+#	E tells us that the result is an exit status
+#	P tells us that the result is on stdout,
+#         and should be compared to the value on the
+#         line
+#	C tells us that the result is on stdout,
+#         and should be compared to the contents of
+#         the file passed on the line.
+#    result: Result value
+#	What we compare with. This should be self-
+#	evident.
+B ftime-test   C
--- a/lib/date/types.myr
+++ b/lib/date/types.myr
@@ -1,7 +1,7 @@
 use std
 
 pkg date = 
-	type date = struct
+	type instant = struct
 		actual	: std.time	/* epoch time in microseconds */
 		tzoff	: diff	/* timezone offset in microseconds */
 		year	: int	/* year, starting at 0 (ie, 1 BCE) */
@@ -12,7 +12,18 @@
 		m	: int	/* minute: [0..59] */
 		s	: int	/* second: [0..59] */
 		us	: int	/* microsecond: [0..999,999] */
+		tzname	: byte[:]	/* current time zone name */
+		_tzbuf	: byte[32]	/* current time zone name storage */
 	;;
 
 	type diff = std.time
+
+	type duration = union
+		`Day	int
+		`Month	int
+		`Year	int
+		`Hour	int
+		`Minute	int
+		`Second	int
+	;;
 ;;
--- a/lib/date/zoneinfo.myr
+++ b/lib/date/zoneinfo.myr
@@ -24,19 +24,39 @@
 	abbrind	: byte
 ;;
 
+const zonepath = [
+	"/usr/share/zoneinfo",
+	"/share/zoneinfo",
+	"/etc/zoneinfo"
+]
+
 const findtzoff = {tz, tm
 	var path
 	var zone
 	var cur
+	var sb
 	var ds
 	var i
 
 	/* load zone */
-	match std.getenv("ZI")
-	| `std.Some zi:	path = zi
-	| `std.None:	path = "/etc/localtime"
+	if std.sleq(tz, "") || std.sleq(tz, "UTC")
+		-> 0
+	elif std.sleq(tz, "local")
+		path = std.sldup("/etc/localtime")
+	else
+		for z in zonepath
+			path = std.pathcat(z, tz)
+			if std.stat(path, &sb) == 0
+				goto found
+			;;
+			std.slfree(path)
+		;;
+		std.slfree(path)
+		-> 0
 	;;
+:found
 	zone = load(path)
+	std.slfree(path)
 
 	/* find applicable gmt offset */
 	cur = (tm / 1_000_000) castto(int32)
@@ -154,4 +174,3 @@
 	(dst.abbrind, sl) = fetchbe8(sl)
 	-> sl
 }
-