shithub: mc

ref: 74d91a0021de012908cfdb35fb61a1473a376130
dir: /lib/date/zoneinfo+posixy.myr/

View raw version
use std
use sys

use "types"

pkg _zoneinfo =
	type zifile
	const findtzoff : (tz : byte[:], tm : std.time -> std.option(date.duration))
	const load	: (file : byte[:] -> zifile#)
	const free	: (f : zifile# -> void)
;;

type zifile = struct
	time	: int32[:]
	timetype: byte[:]
	ttinfo 	: ttinfo[:]
	abbrev	: byte[:]
	leap	: int32[2][:]
	isstd	: byte[:]
	isgmt	: byte[:]
;;

type ttinfo = struct
	gmtoff	: int32
	isdst	: byte
	abbrind	: byte
;;

const zonepath = [
	"/usr/share/zoneinfo",
	"/share/zoneinfo",
	"/etc/zoneinfo"
]

var zonecache	: std.htab(byte[:], zifile#)#
const __init__ = {
	zonecache = std.mkht()
}

const findtzoff = {tz, tm -> std.option(date.duration)
	var path
	var zone
	var cur
	var sb
	var ds
	var i

	match std.htget(zonecache, tz)
	| `std.Some z:
		zone = z
	| `std.None:
		/* load zone */
		if std.eq(tz, "") || std.eq(tz, "UTC")
			-> `std.Some 0
		elif std.eq(tz, "local")
			path = std.sldup("/etc/localtime")
		else
			path = ""
			for z : zonepath
				path = std.pathcat(z, tz)
				if sys.stat(path, &sb) == 0
					goto found
				;;
				std.slfree(path)
			;;
			std.slfree(path)
			-> `std.None
		;;
	:found
		zone = load(path)
		std.slfree(path)
		std.htput(zonecache, std.sldup(tz), zone)
	;;

	/* find applicable gmt offset */
	cur = (tm / 1_000_000 : int32)
	if zone.time.len == 0
		-> `std.None
	;;
	for i = 0; i < zone.time.len && cur < zone.time[i]; i++
		/* nothing */
	;;
	ds = zone.ttinfo[zone.timetype[i]].gmtoff
	->  `std.Some ((ds : date.duration) * 1_000_000)
}

const load = {file
	var nisgmt, nisstd, nleap, ntime, ntype, nchar
	var i, f, p, data

	/* check magic */
	match std.slurp(file)
	| `std.Ok d:	data = d
	| `std.Err m:	-> std.zalloc()
	;;

	if !std.eq(data[:4], "TZif")
		std.put("{} is not a zone info file\n", file)
		-> std.zalloc()
	;;

	/* skip to data */
	p = data[20:]
	(nisgmt, p) = fetchbe32(p)
	(nisstd, p) = fetchbe32(p)
	(nleap, p) = fetchbe32(p)
	(ntime, p) = fetchbe32(p)
	(ntype, p) = fetchbe32(p)
	(nchar, p) = fetchbe32(p)


	f = std.alloc()
	f.time = std.slalloc((ntime : std.size))
	for i = 0; i < ntime; i++
		(f.time[i], p) = fetchbe32(p)
	;;

	f.timetype = std.slalloc((ntime : std.size))
	for i = 0; i < ntime; i++
		(f.timetype[i], p) = fetchbe8(p)
	;;

	f.ttinfo = std.slalloc((ntype : std.size))
	for i = 0; i < ntype; i++
		p = fetchttinfo(p, &f.ttinfo[i])
	;;

	f.abbrev = std.slalloc((nchar : std.size))
	for i = 0; i < nchar; i++
		(f.abbrev[i], p) = fetchbe8(p)
	;;

	f.leap = std.slalloc((nleap : std.size))
	for i = 0; i < nleap; i++
		(f.leap[i][0], p) = fetchbe32(p)
		(f.leap[i][1], p) = fetchbe32(p)
	;;

	f.isstd = std.slalloc((nisstd : std.size))
	for i = 0; i < nisstd; i++
		(f.isstd[i], p) = fetchbe8(p)
	;;

	f.isgmt = std.slalloc((nisgmt : std.size))
	for i = 0; i < nisgmt; i++
		(f.isgmt[i], p) = fetchbe8(p)
	;;
	std.slfree(data)

	-> f
}

const free = {zi
	std.slfree(zi.time)
	std.slfree(zi.timetype)
	std.slfree(zi.ttinfo)
	std.slfree(zi.abbrev)
	std.slfree(zi.leap)
	std.slfree(zi.isstd)
	std.slfree(zi.isgmt)
	std.free(zi)
}

const fetchbe32 = {sl
	var v

	std.assert(sl.len >= 4, "Slice too small to fetch int32 from")
	v = std.getbe32(sl[:4])
	-> (v, sl[4:])
}

const fetchbe8 = {sl
	var v

	std.assert(sl.len >= 1, "Slice too small to fetch int8 from")
	v = sl[0]
	-> (v, sl[1:])
}


const fetchttinfo = {sl, dst : ttinfo#
	(dst.gmtoff, sl) = fetchbe32(sl)
	(dst.isdst, sl) = fetchbe8(sl)
	(dst.abbrind, sl) = fetchbe8(sl)
	-> sl
}