/*---------------------------------------------------------------------------- Copyright 2012-2021, Microsoft Research, Daan Leijen Licensed under the Apache License, Version 2.0 ("The License"). You may not use this file except in compliance with the License. A copy of the License can be found in the LICENSE file at the root of this distribution. ----------------------------------------------------------------------------*/ /* Date and time functionality. # 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 `:ddouble` giving it a very high range and precision (up to 31 decimal digits, see the `module 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: * A `:duration` is a span of time in SI seconds (as measured on the earth's geoid). * An `:instant` represents an instant in time. Instants can be substracted to get the `:duration` between them. Instants are usually represented as the number of fractional SI seconds since the `epoch` (2000-01-01Z), but can be represented in different time scales for efficiency. For example, there is the TAI time scale (`ts-tai`), standard UTC (`ts-ti`), or astronomical time scales like terrestrial time (`module std/time/astro`). * A `:time` is a human readable `:date` (year, month, day), and `:clock` (hour, minutes, seconds) in a particular `:calendar` (usually the standard ISO calendar) and time zone. Instants can be converted to time values and back. We can add logical years, months, or days to a `:time`, or get the difference between times in days etc. See the `module std/time/time` module for more information. The current time, in the UTC timezone (`tz-utc`) using the ISO calendar (`cal-iso`): ``` > 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)" ``` We can also get the current instant in time as the number of SI seconds since the `epoch`: ``` > now() 539106063.980999947s ``` See `module 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-utc`) and calendar (`cal-iso`): ``` > time(2000,1,1).show "2000-01-01T00:00:00Z" ``` Instants can be constructed similarly, and can be converted to and from `:time` 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" ``` The `cal-tai` calendar is the standard ISO calendar but uses the TAI time scale (`ts-tai`) instead of UTC time. ## 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 ``` 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) ``` See the `std/time/utc` module for more information on leap seconds. ## Time zones If calculating with timezones besides the UTC (`tz-utc`) and local timezone (`tz-local`), one needs to use the `module 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" } ``` ## Intervals Using the `now` 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 `module std/time/timer` module should be used with functions like `ticks` or `elapsed`: ```koka 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" } ``` ## UTC The library uses by default the International Time (TI) time scale (`ts-ti`) 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`, `:time`, 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: - `ts-ti-sls` smooths leap seconds over the last 1000s of a day with a leap second so they never appear in any displayed time or timestamp. - `ts-utc-create`/`cal-utc-load` creates a proper UTC timescale from a leap second table. This ensures future leap seconds are fully accounted for and that duration calculations work properly up to 6 months into the future. . */ module std/timestd/time pub import std/time/durationstd/time/duration pub import std/time/instantstd/time/instant pub import std/time/utcstd/time/utc pub import std/time/chronostd/time/chrono pub import std/time/timerstd/time/timer // For now not supported until we move to async // pub import std/time/download pub import std/time/datestd/time/date pub import std/time/calendarstd/time/calendar pub import std/time/calendarsstd/time/calendars pub import std/time/timestd/time/time pub import std/time/localestd/time/locale pub import std/time/formatstd/time/format pub import std/time/parsestd/time/parse import std/num/ddoublestd/num/ddouble // for documentation