τau / time /

time.tau

source
/Users/niconex/Documents/tau/stdlib/time/time.tau
1# time - clocks and pauses.2#3# Durations are milliseconds, whole numbers. Wall clock time is what a clock4# on the wall says and can jump backwards, the monotonic one only moves5# forward and is the one to measure with.67syscall = import("syscall")89Millisecond = 110Second = 100011Minute = 6000012Hour = 36000001314# Now returns the wall clock time in milliseconds since the Unix epoch.15Now = fn() { syscall.TimeMillis() }1617# Unix returns the wall clock time in whole seconds since the Unix epoch.18Unix = fn() { syscall.TimeUnix() }1920# Mono returns a monotonic timestamp in milliseconds. Only differences21# between two of these have a meaning.22Mono = fn() { syscall.TimeMono() }2324# Since returns the milliseconds elapsed since the monotonic timestamp t.25Since = fn(t) { Mono() - t }2627# Sleep pauses the current tau routine for ms milliseconds.28Sleep = fn(ms) { syscall.SleepMillis(ms) }2930# Measure returns the milliseconds taken by f().31Measure = fn(f) {32	start = Mono()33	f()34	return Since(start)35}3637# ========== Dates ==========38#39# A date is UTC unless an offset says otherwise. Local takes the offset from40# the timezone database of the system, daylight saving included, so an hour41# in Rome is the hour in Rome.4243Days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]44Months = ["January", "February", "March", "April", "May", "June", "July",45	"August", "September", "October", "November", "December"]4647# div is the integer division that rounds towards minus infinity, the one the48# calendar arithmetic below is written in terms of. Dividing two integers49# rounds towards zero, which is a different answer on the negative side, so50# the quotient is pushed down by one where the two disagree.51div = fn(a, b) {52	q = a / b53	if a % b != 0 && (a < 0) != (b < 0) {54		--q55	}56	return q57}5859mod = fn(a, b) { a - div(a, b) * b }6061# Date returns what the wall clock time sec, in seconds since the Unix epoch,62# stands for: Year, Month (1 to 12), Day, Hour, Minute, Second, the Weekday63# with Sunday as 0, and the YearDay starting at 1.64#65# The calendar arithmetic is the one Howard Hinnant wrote for chrono: days66# are counted from the first of March so that the leap day falls at the end67# of the year, where it does no harm.68Date = fn(sec) { DateIn(sec, 0) }6970# DateIn is Date with a zone: offset is the seconds between that zone and71# UTC, 3600 for Rome in winter and 7200 in summer. The fields read as the72# clock on that wall reads, while Unix stays the instant it is.73DateIn = fn(sec, offset) {74	if offset == null {75		offset = 076	}7778	local = sec + offset79	days = div(local, 86400)80	rem = local - days * 864008182	z = days + 71946883	era = div(z, 146097)84	doe = z - era * 14609785	yoe = div(doe - div(doe, 1460) + div(doe, 36524) - div(doe, 146096), 365)86	y = yoe + era * 40087	doy = doe - (365 * yoe + div(yoe, 4) - div(yoe, 100))88	mp = div(5 * doy + 2, 153)89	d = doy - div(153 * mp + 2, 5) + 190	m = if mp < 10 { mp + 3 } else { mp - 9 }91	if m <= 2 {92		++y93	}9495	t = new()96	t.Unix = sec97	t.Offset = offset98	t.Year = y99	t.Month = m100	t.Day = d101	t.Hour = div(rem, 3600)102	t.Minute = mod(div(rem, 60), 60)103	t.Second = mod(rem, 60)104	t.Weekday = mod(days + 4, 7)105	t.YearDay = days - daysFromCivil(y, 1, 1) + 1106	return t107}108109# LocalOffset is the offset of the local zone at the instant sec, or right110# now when sec is null.111LocalOffset = fn(sec) {112	if sec == null {113		sec = Unix()114	}115	return syscall.TzOffset(sec)116}117118# LocalZone is the abbreviation of the local zone at that instant: CET, CEST.119LocalZone = fn(sec) {120	if sec == null {121		sec = Unix()122	}123	return syscall.TzName(sec)124}125126# Local returns the date sec stands for where this machine is.127Local = fn(sec) { DateIn(sec, LocalOffset(sec)) }128129# Now returns the local date of this instant.130NowLocal = fn() { Local(Unix()) }131132# daysFromCivil is the number of days between the Unix epoch and the given133# date, the way back from Date.134daysFromCivil = fn(y, m, d) {135	if m <= 2 {136		--y137	}138	era = div(y, 400)139	yoe = y - era * 400140	mp = if m > 2 { m - 3 } else { m + 9 }141	doy = div(153 * mp + 2, 5) + d - 1142	doe = yoe * 365 + div(yoe, 4) - div(yoe, 100) + doy143	return era * 146097 + doe - 719468144}145146# FromDate returns the seconds since the epoch of the given UTC date, the way147# back from Date.148FromDate = fn(year, month, day, hour, minute, second) {149	return daysFromCivil(year, month, day) * 86400 + hour * 3600 + minute * 60 + second150}151152# FromDateIn is FromDate in a zone: the date is read as the clock of that153# zone reads it, and the instant comes back in UTC.154FromDateIn = fn(year, month, day, hour, minute, second, offset) {155	if offset == null {156		offset = 0157	}158	return FromDate(year, month, day, hour, minute, second) - offset159}160161# ========== Weeks ==========162#163# Weekday counts from Sunday, the way Go does. Where the week starts is a164# question of where you are: in Italy it is Monday, which is what ISOWeekday165# and the ISO week number below are about.166167Sunday = 0168Monday = 1169Tuesday = 2170Wednesday = 3171Thursday = 4172Friday = 5173Saturday = 6174175# ISOWeekday numbers the days from Monday as 1 to Sunday as 7.176ISOWeekday = fn(t) { if t.Weekday == 0 { 7 } else { t.Weekday } }177178# IsWeekend reports whether the date falls on a Saturday or a Sunday.179IsWeekend = fn(t) { t.Weekday == Sunday || t.Weekday == Saturday }180181# StartOfDay returns the instant the day of t begins, in its own zone.182StartOfDay = fn(t) {183	return FromDateIn(t.Year, t.Month, t.Day, 0, 0, 0, t.Offset)184}185186# StartOfWeek returns the instant the week holding t begins, with first187# saying which day that is: Monday in most of Europe, Sunday elsewhere. With188# no first, Monday.189StartOfWeek = fn(t, first) {190	if first == null {191		first = Monday192	}193194	back = mod(t.Weekday - first, 7)195	return StartOfDay(t) - back * 86400196}197198# Week returns the number of the week holding t, counting from the first week199# that starts on a first day, with the days before it in week 0. It is what a200# calendar on a wall shows.201Week = fn(t, first) {202	if first == null {203		first = Monday204	}205206	jan1 = DateIn(FromDateIn(t.Year, 1, 1, 0, 0, 0, t.Offset), t.Offset)207	lead = mod(first - jan1.Weekday, 7)208	if t.YearDay <= lead {209		return 0210	}211	return div(t.YearDay - lead - 1, 7) + 1212}213214# ISOWeek returns the ISO 8601 year and week of t: weeks start on Monday and215# week 1 is the one holding the 4th of January, so the first days of January216# can belong to the last week of the year before.217ISOWeek = fn(t) {218	# The Thursday of this week decides which year the week belongs to.219	thursday = StartOfDay(t) + (4 - ISOWeekday(t)) * 86400220	th = DateIn(thursday, t.Offset)221222	jan4 = DateIn(FromDateIn(th.Year, 1, 4, 0, 0, 0, t.Offset), t.Offset)223	firstWeek = StartOfWeek(jan4, Monday)224225	return [th.Year, div(thursday - firstWeek, 604800) + 1]226}227228# IsLeap reports whether the year has a 29th of February.229IsLeap = fn(y) { y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) }230231# pad writes n with at least width digits.232pad = fn(n, width) {233	s = string(n)234	for len(s) < width {235		s = "0" + s236	}237	return s238}239240# Format writes t, a date from Date, in the given layout. The verbs are the241# ones of strftime, the handful that are worth having:242#243#	%Y year      %m month     %d day     %j day of the year244#	%H hour      %M minute    %S second245#	%b month name, short      %B month name246#	%a day name, short        %A day name247#	%z offset from UTC        %% a per cent sign248#249# With no layout the date is written as RFC 3339: 2006-01-02T15:04:05Z.250Format = fn(t, layout) {251	if layout == null {252		# RFC 3339: a Z for UTC, the offset for anywhere else.253		layout = if t.Offset == null || t.Offset == 0 { "%Y-%m-%dT%H:%M:%SZ" } else { "%Y-%m-%dT%H:%M:%S%z" }254	}255256	out = ""257	for i = 0; i < len(layout); ++i {258		if layout[i] != "%" || i == len(layout) - 1 {259			out = out + layout[i]260			continue261		}262263		++i264		out = out + verb(t, layout[i])265	}266	return out267}268269# verb writes the one field of t a format verb stands for. An unknown verb is270# left as it was written, so that a mistake shows up in the output instead of271# disappearing.272verb = fn(t, c) {273	if c == "Y" { return pad(t.Year, 4) }274	if c == "m" { return pad(t.Month, 2) }275	if c == "d" { return pad(t.Day, 2) }276	if c == "H" { return pad(t.Hour, 2) }277	if c == "M" { return pad(t.Minute, 2) }278	if c == "S" { return pad(t.Second, 2) }279	if c == "j" { return pad(t.YearDay, 3) }280	if c == "B" { return Months[t.Month - 1] }281	if c == "b" { return slice(Months[t.Month - 1], 0, 3) }282	if c == "A" { return Days[t.Weekday] }283	if c == "a" { return slice(Days[t.Weekday], 0, 3) }284	if c == "z" { return offsetString(t.Offset) }285	if c == "%" { return "%" }286	return "%" + c287}288289# offsetString writes a zone offset as RFC 3339 wants it: +01:00.290offsetString = fn(off) {291	if off == null {292		off = 0293	}294295	sign = "+"296	if off < 0 {297		sign = "-"298		off = -off299	}300	return sign + pad(div(off, 3600), 2) + ":" + pad(mod(div(off, 60), 60), 2)301}302303# Parse reads a date written as RFC 3339, "2006-01-02T15:04:05Z", and returns304# the seconds since the epoch. The time part may be left out.305Parse = fn(s) {306	if len(s) < 10 {307		return error("time: {s} is not a date")308	}309310	num = fn(from, to) {311		part = slice(s, from, to)312		for i = 0; i < len(part); ++i {313			if part[i] < "0" || part[i] > "9" {314				return error("time: {s} is not a date")315			}316		}317		return int(part)318	}319320	if failed(year = num(0, 4)) {321		return year322	}323	if failed(month = num(5, 7)) {324		return month325	}326	if failed(day = num(8, 10)) {327		return day328	}329330	if len(s) < 19 {331		return FromDate(year, month, day, 0, 0, 0)332	}333334	if failed(hour = num(11, 13)) {335		return hour336	}337	if failed(minute = num(14, 16)) {338		return minute339	}340	if failed(second = num(17, 19)) {341		return second342	}343	return FromDate(year, month, day, hour, minute, second)344}345346# String writes the date of the wall clock time sec as RFC 3339.347String = fn(sec) { Format(Date(sec), null) }