diff options
author | Ori Bernstein <ori@eigenstate.org> | 2014-08-29 20:37:34 -0400 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2014-08-29 20:37:34 -0400 |
commit | 33f429bcdd5beeed24a8650320e9d3a83ce2d6f5 (patch) | |
tree | 8570bc936b2301fa0aafab40395f1f33c8779ba2 /lib/date | |
parent | 9befecb14dfadec7f097d54065793d209cc6eb92 (diff) | |
parent | 456959812209570826cc92614f4aca8b21c13e5b (diff) | |
download | mc-33f429bcdd5beeed24a8650320e9d3a83ce2d6f5.tar.gz |
Merge branch 'master' of git+ssh://git.eigenstate.org/git/ori/libdate
Conflicts:
date.myr
Diffstat (limited to 'lib/date')
-rw-r--r-- | lib/date/Makefile | 1 | ||||
-rw-r--r-- | lib/date/date.myr | 119 | ||||
-rw-r--r-- | lib/date/fmt.myr | 115 | ||||
-rw-r--r-- | lib/date/parse.myr | 11 | ||||
-rw-r--r-- | lib/date/test/Makefile | 20 | ||||
-rw-r--r-- | lib/date/test/data/ftime-test-expected | 1 | ||||
-rw-r--r-- | lib/date/test/ftime-test.myr | 13 | ||||
-rwxr-xr-x | lib/date/test/runtest.sh | 124 | ||||
-rw-r--r-- | lib/date/test/tests | 23 | ||||
-rw-r--r-- | lib/date/types.myr | 13 | ||||
-rw-r--r-- | lib/date/zoneinfo.myr | 27 |
11 files changed, 393 insertions, 74 deletions
diff --git a/lib/date/Makefile b/lib/date/Makefile index 8cc557d..9467a12 100644 --- a/lib/date/Makefile +++ b/lib/date/Makefile @@ -1,6 +1,7 @@ MYRLIB=date MYRSRC= \ date.myr \ + fmt.myr \ types.myr \ zoneinfo.myr \ diff --git a/lib/date/date.myr b/lib/date/date.myr index fa57ff1..fe75a58 100644 --- a/lib/date/date.myr +++ b/lib/date/date.myr @@ -3,67 +3,59 @@ use "types.use" 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 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 utcnow = { + -> mkdate(std.now(), "") } 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 @@ const mkdate = {tm, off /* 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 @@ const mkdate = {tm, off 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 @@ const mkdate = {tm, off -> 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 - - 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 isleap = {d + -> d.year % 4 == 0 && (d.year % 100 != 0 || d.year % 400 == 0) } -const ndays = {y - if y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) - -> 366 - else - -> 365 - ;; +const add = {d, dt + -> mkdate(d.actual + (dt castto(std.time)), d.tzname) } + +const sub = {d, dt + -> mkdate(d.actual - (dt castto(std.time)), d.tzname) +} + +const diff = {a, b + -> (b.actual - a.actual) castto(diff) +} + diff --git a/lib/date/fmt.myr b/lib/date/fmt.myr new file mode 100644 index 0000000..722280c --- /dev/null +++ b/lib/date/fmt.myr @@ -0,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) +} diff --git a/lib/date/parse.myr b/lib/date/parse.myr new file mode 100644 index 0000000..e989719 --- /dev/null +++ b/lib/date/parse.myr @@ -0,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) +;; + + diff --git a/lib/date/test/Makefile b/lib/date/test/Makefile new file mode 100644 index 0000000..e559327 --- /dev/null +++ b/lib/date/test/Makefile @@ -0,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: diff --git a/lib/date/test/data/ftime-test-expected b/lib/date/test/data/ftime-test-expected new file mode 100644 index 0000000..c45fbe0 --- /dev/null +++ b/lib/date/test/data/ftime-test-expected @@ -0,0 +1 @@ +2014-8-29 19:47:43 +0000 diff --git a/lib/date/test/ftime-test.myr b/lib/date/test/ftime-test.myr new file mode 100644 index 0000000..453a977 --- /dev/null +++ b/lib/date/test/ftime-test.myr @@ -0,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]) +} diff --git a/lib/date/test/runtest.sh b/lib/date/test/runtest.sh new file mode 100755 index 0000000..95f548f --- /dev/null +++ b/lib/date/test/runtest.sh @@ -0,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 diff --git a/lib/date/test/tests b/lib/date/test/tests new file mode 100644 index 0000000..a1b3ddf --- /dev/null +++ b/lib/date/test/tests @@ -0,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 diff --git a/lib/date/types.myr b/lib/date/types.myr index eb60a67..76bba30 100644 --- 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 @@ pkg date = 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 + ;; ;; diff --git a/lib/date/zoneinfo.myr b/lib/date/zoneinfo.myr index 0307b4f..e7d274c 100644 --- a/lib/date/zoneinfo.myr +++ b/lib/date/zoneinfo.myr @@ -24,19 +24,39 @@ type ttinfo = struct 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 @@ const fetchttinfo = {sl, dst : ttinfo# (dst.abbrind, sl) = fetchbe8(sl) -> sl } - |