ref: c7f1a4d7d10daa2f561535ab68df000e200d0e5b
parent: 7e02923fe7e9abd65a9b039a832ca593075f8132
author: Ori Bernstein <ori@eigenstate.org>
date: Fri Aug 29 11:38:31 EDT 2014
Add time formatting support.
--- 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,25 +3,21 @@
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 utcnow : (-> instant)
+ const now : (tz : 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 Unix2Julian = 719468
@@ -28,41 +24,33 @@
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 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 */
@@ -78,8 +66,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
@@ -110,10 +99,28 @@
-> date
}
-const ndays = {y
- if y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)
- -> 366
- else
- -> 365
- ;;
+const localoff = {tm
+ -> _zoneinfo.findtzoff("local", tm)
}
+
+const tzoff = {tz, tm
+ -> _zoneinfo.findtzoff(tz, tm)
+}
+
+
+const isleap = {d
+ -> d.year % 4 == 0 && (d.year % 100 != 0 || d.year % 400 == 0)
+}
+
+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)
+}
+
--- /dev/null
+++ b/lib/date/fmt.myr
@@ -1,0 +1,112 @@
+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)
+ | 'Y': o += std.bfmt(buf[o:], "%i", d.year % 100)
+ | '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
+
+ h = off % 3600
+ m = off / 3600
+ if off < 0
+ off = -off
+ ;;
+ -> std.bfmt(buf, "%i:%i", 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)
+;;
+
+
--- 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
}
-