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
}
-