shithub: mc

ref: f7228d204a9cbe40f7b6286ba8987ef65691bfbd
dir: /lib/date/parse.myr/

View raw version
use std

use "types.use"
use "names.use"

pkg date =
	/* date i/o */
	const parsefmt	: (f : byte[:], s: byte[:]	-> std.option(instant))
	const parsefmtz	: (f : byte[:], s: byte[:], tz : byte[:]	-> std.option(instant))
;;

const UnixJulianDiff	= 719468

const parsefmt	= {f, s;	-> parsefmtz(f, s, "")}
const parsefmtz = {f, s, tz
	var d
	var err

	d = [.year = 0]
	err = false
	s = filldate(&d, f, s, tz, &err)
	if err || s.len > 0
		-> `std.None
	;;
	-> `std.Some d
}

generic intval = {dst : @a::(numeric,integral)#, s : byte[:], min : @a::(numeric,integral), max : @a::(numeric,integral), err : bool# -> byte[:]
	var i
	var c
	var num

	num = s
	for i = 0; i < min; i++
		(c, s) = std.striter(s)
		if !std.isdigit(c)
			err# = true
			-> s
		;;
	;;

	for i = min ; i < max; i++
		(c, s) = std.striter(s)
		if !std.isdigit(c)
			break
		;;
	;;

	num = num[:i]
	match std.intparse(num)
	| `std.Some v:
		dst# = v
		-> s
	| `std.None:
		err# = true
		-> s
	;;
}


const filldate = {d, f, s, tz, err -> byte[:]
	var fc, sc

	while f.len != 0
		(fc, f) = std.striter(f)
		if fc == '%'
			(fc, f) = std.striter(f)
			match fc
			/* named things */
			| 'a':	s = indexof(&d.day, s, _names.abbrevday, err)
			| 'A':	s = indexof(&d.day, s, _names.fullday, err)
			| 'b':	s = indexof(&d.mon, s, _names.abbrevmon, err)
			| 'B':	s = indexof(&d.mon, s, _names.fullmon, err)
			| 'c':	s = filldate(d, "%Y-%m-%d", s, tz, err)
			| 'C':	
				s = intval(&d.year, s, 2, 2, err)
				d.year += 1900
			| 'd':	s = intval(&d.day, s, 2, 2, err)
			| 'D':	s = filldate(d, "%m/%d/%y", s, tz, err)
			| 'e':	s = intval(&d.day, s, 1, 2, err)
			| 'F':	s = filldate(d, "%y-%m-%d", s, tz, err)
			/*
			| 'G':	o += std.bfmt(buf[o:], ...?
			| 'g':
			*/
			| 'h':	s = indexof(&d.day, s, _names.abbrevmon, err)
			| 'H':	s = intval(&d.h, s, 2, 2, err)
			| 'I':	s = intval(&d.h, s, 2, 2, err)
			| 'j':	std.fatal("unsupported '%j'\n")
			| 'k':	s = intval(&d.h, s, 1, 2, err)
			| 'l':	s = intval(&d.h, s, 1, 2, err)
			| 'm':	s = intval(&d.mon, s, 1, 2, err)
			| 'M':	s = intval(&d.m, s, 1, 2, err)
			| 'n':	s = matchstr(s, "\n", err)
			| 'O':	std.fatal("unsupported %O\n")
			| 'p':	s = matchampm(d, s, err)
			| 'P':	s = matchampm(d, s, err)
			| 'r':	s = filldate(d, "%H:%M:%S %P", s, tz, err) 
			| 'R':	s = filldate(d, "%H:%M %P", s, tz, err)
			| 's':	s = intval(&d.actual, s, 1, 64, err)
			| 'S':	s = intval(&d.s, s, 1, 2, err)
			| 't':	s = eatspace(s)
			| 'u':	s = intval(&d.wday, s, 1, 1, err)
			| 'U':	std.fatal("unsupported '%U'\n")
				/*
			| 'x':	o += bftime(buf[o:], Datefmt, d)
			| 'X':	o += bftime(buf[o:], Timefmt, d)
				*/
			| 'y':	s = intval(&d.year, s, 1, 2, err)
				d.year += 1900
			| 'Y':	
				s = intval(&d.year, s, 1, 4, err)
			| 'z':	s = timezone(&d.tzoff, s, err)
				/*
			| 'Z':	o += std.bfmt(buf[o:], "%s", d.tzname)
				*/
			| '%':	s = matchstr(s, "%", err)
			| _:	std.fatal("unknown format character %c\n", fc)
			;;
		else
			(sc, s) = std.striter(s)
			if std.isspace(sc)
				s = eatspace(s)
			elif (sc != fc)
				err# = true
				-> s
			;;
		;;
		if err#
			-> s
		;;
	;;
	d.actual = time(d)
	-> s
}

const eatspace = {s
	var c

	while std.isspace(std.decode(s))
		(c, s) = std.striter(s)
	;;
	-> s
}

const indexof = {dst, s, set, err
	var i
	for i = 0; i < set.len; i++
		if s.len >= set[i].len && std.streq(s, set[i])
			dst# = i
			-> s
		;;
	;;
	err# = true
	dst# = 0
	-> s
}

const timezone = {dst, s, err
	var isneg
	var tzoff

	if s.len < 1
		err# = true
		-> ""
	;;
	if std.sleq(s[:1], "-")
		isneg = true
	elif std.sleq(s[:1], "+") 
		isneg = false
	else
		err# = true
		-> ""
	;;
	s = intval(&tzoff, s[1:], 2, 4, err) 
	dst# = (tzoff / 100) * 3600 * 1_000_000 + (tzoff % 100) * 60 * 1_000_000
	-> s
}


const matchstr = {s, str, err
	if s.len <= str.len || !std.sleq(s[:str.len], str)
		err# = true
		-> ""
	;;
	-> s[str.len:]
}

const matchampm = {d, s, err
	if s.len < 2
		err# = true
		-> s
	;;
	if std.sleq(s[:2], "am") || std.sleq(s[:2], "AM")
		-> s[2:]
	elif std.sleq(s[:2], "pm") || std.sleq(s[:2], "PM")
		d.h += 12
		-> s[2:]
	else
		err# = true
		-> s
	;;
}

const time = {inst
	var t
	var c, y, ya, m, u

	t = 0

	if inst.mon > 2
		m = (inst.mon - 3) castto(std.time)
	else
		m = (inst.mon + 9) castto(std.time)
		y = (inst.year - 1) castto(std.time)
	;;
	
	c = y / 100
	ya = y - 100 * c
	u = (146097 * c) / 4 + \
		(1461 * ya) / 4 + \
		(153 * m + 2) / 5 + \
		(inst.day castto(std.time)) + \
		UnixJulianDiff

	t += (u * 24*60*60*1_000_000)
	t += (inst.h castto(std.time)) * 60*60*1_000_000
	t += (inst.m castto(std.time)) * 60*1_000_000
	t += (inst.s castto(std.time)) * 1_000_000
	t += inst.us castto(std.time)
	-> t
}