std/time▲toc

Date and time functionality.

1. Dates and time

This is an extensive library for managing time. By using careful defaults the library offers an easy to use interface for the common use cases, but uses accurate and precise time representations and algorithms. In particular, it handles accurate leap seconds, time zones, multiple calendars, and astronomical time scales. Internally time is represented by a 128-bit ddoublestd/num/ddouble/ddouble: V giving it a very high range and precision (up to 31 decimal digits, see the std/time/instant module for more information). As such, we believe this library is one of the most accurate time libraries around.

Core concepts are:

The current time, in the UTC timezone (tz-utcstd/time/calendar/tz-utc: timezone) using the ISO calendar (cal-isostd/time/calendar/cal-iso: calendar):

> time-now().show
"2017-01-30T15:40:25.367000103Z"
> time-now().show
"2017-01-30T15:40:25.367000103Z"

Or the current time in the local timezone:

> local-time-now().show
"2017-01-30T07:59:01.693000078-08:00 (PST)"
> local-time-now().show
"2017-01-30T07:59:01.693000078-08:00 (PST)"

We can also get the current instant in time as the number of SI seconds since the epochstd/time/instant/epoch: instant:

> now()
539106063.980999947s
> nowstd/time/chrono/now: () -> <utc,ndet> instant()
539106063.980999947s

See std/time/chrono for more information on system time.

Calendar times can be constructed by giving a year, month (1-based), and day of the month (1-based) and optional hour, minutes, seconds, fraction of the second, timezone (tz-utcstd/time/calendar/tz-utc: timezone) and calendar (cal-isostd/time/calendar/cal-iso: calendar):

> time(2000,1,1).show
"2000-01-01T00:00:00Z"
> time(2000,1,1).show
"2000-01-01T00:00:00Z"

Instants can be constructed similarly, and can be converted to and from timestd/time/time/time: V values. Here we construct a time value interpreted in the TAI time scale (which was 32 seconds ahead of UTC on 2000-01-01Z):

> instant(2000,1,1).time(cal=cal-tai).show
"2000-01-01T00:00:32Z TAI"
> instant(2000,1,1).time(cal=cal-tai).show
"2000-01-01T00:00:32Z TAI"

The cal-tai calendar is the standard ISO calendar but uses the TAI time scale (ts-taistd/time/instant/ts-tai: timescale) instead of UTC time.

1.1. Leap seconds

The library takes much care to correctly handle leap seconds and time zone transitions. For example, here are some durations between different times:

> (time(2016,1,1,0,0,0,0.5) - time(2015,12,31,23,59,59)).show
"1.5s"

> (time(2017,1,1,0,0,0,0.5) - time(2016,12,31,23,59,59)).show
"2.5s" // over a leap second

> (time(2017,1,1,0,0,0,0.5) - time(2017,1,1,2,59,59,tz=tz-fixed(3)).show
"2.5s" // UTC+3, and over a leap second
> (time(2016,1,1,0,0,0,0.5) - time(2015,12,31,23,59,59)).show
"1.5s"

> (time(2017,1,1,0,0,0,0.5) - time(2016,12,31,23,59,59)).show
"2.5s" // over a leap second

> (time(2017,1,1,0,0,0,0.5) - time(2017,1,1,2,59,59,tz=tz-fixedstd/time/calendar/tz-fixed: (hours : int, mins : ?int, name : ?string, abbrv : ?string, hourwidth : ?int) -> timezone(3)).show
"2.5s" // UTC+3, and over a leap second

Similarly, adding logical days (or weeks, months, or years) works over leap seconds and other time irregularties:

> time(2016,12,31,12).add-days(1).show
"2017-01-01T12:00:00Z"   // over a leap second

> time(1582,10,4,cal=cal-jg).add-days(1).show
"1582-10-15T00:00:00Z JG"  // transition from Julian (`cal-julian`) to Gregorian (`cal-gregorian`) calendar)
> time(2016,12,31,12).add-days(1).show
"2017-01-01T12:00:00Z"   // over a leap second

> time(1582,10,4,cal=cal-jgstd/time/calendars/cal-jg: calendar).add-days(1).show
"1582-10-15T00:00:00Z JG"  // transition from Julian (`cal-julian`) to Gregorian (`cal-gregorian`) calendar)

See the std/time/utc module for more information on leap seconds.

1.2. Time zones

If calculating with timezones besides the UTC (tz-utcstd/time/calendar/tz-utc: timezone) and local timezone (tz-localstd/time/calendar/tz-local: () -> ndet timezone), one needs to use the std/time/timezone module to load the latest time zone information (std/time/timezone/load-timezones).

import std/time
import std/time/timezone

fun test-timezones() {
  val tzs = load-timezones()
  val tz1 = tzs.timezone("America/New_York")
  val tz2 = tzs.timezone("Pacific/Kosrae")

  /* A flight of 12.5 hours from New York to Kosrae */
  val t1  = instant(1997,12,31,12,1,0,tz=tz1) + (12.hours + 30.minutes)
  t1.time(tz=tz2).show.println   // "1998-01-01T17:31:00+12:00"

  /* Again, but across DST & leap second */
  val t2  = instant(1998,12,31,12,1,0,tz=tz1) + (12.hours + 30.minutes)
  t2.time(tz=tz2).show.println   // "1999-01-01T16:30:59+11:00"
}
import std/time
import std/time/timezone

fun test-timezones() {
  val tzs = load-timezones()
  val tz1 = tzs.timezone("America/New_York")
  val tz2 = tzs.timezone("Pacific/Kosrae")

  /* A flight of 12.5 hours from New York to Kosrae */
  val t1  = instant(1997,12,31,12,1,0,tz=tz1) + (12.hours + 30.minutes)
  t1.time(tz=tz2).show.println   // "1998-01-01T17:31:00+12:00"

  /* Again, but across DST & leap second */
  val t2  = instant(1998,12,31,12,1,0,tz=tz1) + (12.hours + 30.minutes)
  t2.time(tz=tz2).show.println   // "1999-01-01T16:30:59+11:00"
}

1.3. Intervals

Using the nowstd/time/chrono/now: () -> <utc,ndet> instant or time-now for measuring time intervals is not appropriate since these functions are not guaranteed to be monotonic or of sufficiently high precision. For timing, the std/time/timer module should be used with functions like ticksstd/time/timer/ticks: () -> ndet duration or elapsedstd/time/timer/elapsed: forall<a,e> (action : () -> <ndet|e> a) -> <ndet|e> (duration, a):

import std/time/timer

fun nfib(n : int) : int {
  if (n <= 1) then 1 else 1 + nfib(n - 1) + nfib(n - 2)
}

fun test-nfib() {
  print-elapsed{ nfib(30) }   // "elapsed 0.111s"
}
import std/time/timer

fun nfib(n : intstd/core/int: V) : intstd/core/int: V {
  if (n <= 1) then 1 else 1 + nfib(n - 1) + nfib(n - 2)
}

fun test-nfib() {
  print-elapsedstd/time/timer/print-elapsed: forall<a,e> (action : () -> <ndet,console|e> a, msg : ?string) -> <ndet,console|e> a{ nfib(30) }   // "elapsed 0.111s"
}

1.4. UTC

The library uses by default the International Time (TI) time scale (ts-tistd/time/utc/ts-ti: timescale) which uses UTC time with leap seconds up to 2019, and ignores leap seconds after this. This is a nice default as it tracks historical leap seconds precisely while having deterministic future time calculations. Moreover, even if there are future leap seconds, only durations calculations between times will be off by the inserted leap seconds; time-now, timestd/time/time/time: V, and instants stay valid and designate correct times (as they use a day + seconds internal representation).

There are two other good UTC time scales though:

.

Time durations in SI seconds.

Instants in time.

UTC time scales and leap second support.

Get the system time.

High resolution timer.

Automatic downloading of time information.

Date, clock, and week days support.

Calendars.

Various calendars: TAI, GPS, ISO week, Julian, Coptic, etc.

Time represents instants as human readable calendar time.

Localization of time formats.

Formatting of time into a string.

Parsing of time strings.