211 lines
7.5 KiB
Markdown
211 lines
7.5 KiB
Markdown
# Lyng time functions
|
|
|
|
Lyng date and time support requires importing `lyng.time` packages. Lyng uses simple yet modern time object models:
|
|
|
|
- `Instant` class for time stamps with platform-dependent resolution
|
|
- `Duration` to represent amount of time not depending on the calendar, e.g. in absolute units (milliseconds, seconds,
|
|
hours, days)
|
|
|
|
## Time instant: `Instant`
|
|
|
|
Represent some moment of time not depending on the calendar (calendar for example may b e changed, daylight saving time
|
|
can be for example introduced or dropped). It is similar to `TIMESTAMP` in SQL or `Instant` in Kotlin. Some moment of
|
|
time; not the calendar date.
|
|
|
|
Instant is comparable to other Instant. Subtracting instants produce `Duration`, period in time that is not dependent on
|
|
the calendar, e.g. absolute time period.
|
|
|
|
It is possible to add or subtract `Duration` to and from `Instant`, that gives another `Instant`.
|
|
|
|
Instants are converted to and from `Real` number of seconds before or after Unix Epoch, 01.01.1970. Constructor with
|
|
single number parameter constructs from such number of seconds,
|
|
and any instance provide `.epochSeconds` member:
|
|
|
|
import lyng.time
|
|
|
|
// default constructor returns time now:
|
|
val t1 = Instant()
|
|
val t2 = Instant()
|
|
assert( t2 - t1 < 1.millisecond )
|
|
assert( t2.epochSeconds - t1.epochSeconds < 0.001 )
|
|
>>> void
|
|
|
|
## Constructing
|
|
|
|
import lyng.time
|
|
|
|
// empty constructor gives current time instant using system clock:
|
|
val now = Instant()
|
|
|
|
// constructor with Instant instance makes a copy:
|
|
assertEquals( now, Instant(now) )
|
|
|
|
// constructing from a number is trated as seconds since unix epoch:
|
|
val copyOfNow = Instant( now.epochSeconds )
|
|
|
|
// note that instant resolution is higher that Real can hold
|
|
// so reconstructed from real slightly differs:
|
|
assert( abs( (copyOfNow - now).milliseconds ) < 0.01 )
|
|
>>> void
|
|
|
|
The resolution of system clock could be more precise and double precision real number of `Real`, keep it in mind.
|
|
|
|
## Comparing and calculating periods
|
|
|
|
import lyng.time
|
|
|
|
val now = Instant()
|
|
|
|
// you cam add or subtract periods, and compare
|
|
assert( now - 5.minutes < now )
|
|
val oneHourAgo = now - 1.hour
|
|
assertEquals( now, oneHourAgo + 1.hour)
|
|
|
|
>>> void
|
|
|
|
## Getting the max precision
|
|
|
|
Normally, subtracting instants gives precision to microseconds, which is well inside the jitter
|
|
the language VM adds. Still `Instant()` or `Instant.now()` capture most precise system timer at hand and provide inner
|
|
value of 12 bytes, up to nanoseconds (hopefully). To access it use:
|
|
|
|
import lyng.time
|
|
|
|
// capture time
|
|
val now = Instant.now()
|
|
|
|
// this is Int value, number of whole epoch
|
|
// milliseconds to the moment, it fits 8 bytes Int well
|
|
val seconds = now.epochWholeSeconds
|
|
assert(seconds is Int)
|
|
|
|
// and this is Int value of nanoseconds _since_ the epochMillis,
|
|
// it effectively add 4 more mytes int:
|
|
val nanos = now.nanosecondsOfSecond
|
|
assert(nanos is Int)
|
|
assert( nanos in 0..999_999_999 )
|
|
|
|
// we can construct epochSeconds from these parts:
|
|
assertEquals( now.epochSeconds, nanos * 1e-9 + seconds )
|
|
>>> void
|
|
|
|
## Truncating to more realistic precision
|
|
|
|
Full precision Instant is way too long and impractical to store, especially when serializing,
|
|
so it is possible to truncate it to milliseconds, microseconds or seconds:
|
|
|
|
import lyng.time
|
|
import lyng.serialization
|
|
|
|
// max supported size (now microseconds for serialized value):
|
|
assert( Lynon.encode(Instant.now()).size in [8,9] )
|
|
// shorter: milliseconds only
|
|
assertEquals( 7, Lynon.encode(Instant.now().truncateToMillisecond()).size )
|
|
// truncated to seconds, good for file mtime, etc:
|
|
assertEquals( 6, Lynon.encode(Instant.now().truncateToSecond()).size )
|
|
>>> void
|
|
|
|
## Formatting instants
|
|
|
|
You can freely use `Instant` in string formatting. It supports usual sprintf-style formats:
|
|
|
|
import lyng.time
|
|
val now = Instant()
|
|
|
|
// will be something like "now: 12:10:05"
|
|
val currentTimeOnly24 = "now: %tT"(now)
|
|
|
|
// we can extract epoch second with formatting too,
|
|
// this was since early C time
|
|
|
|
// get epoch while seconds from formatting
|
|
val unixEpoch = "Now is %ts since unix epoch"(now)
|
|
|
|
// and it is the same as now.epochSeconds, int part:
|
|
assertEquals( unixEpoch, "Now is %d since unix epoch"(now.epochSeconds.toInt()) )
|
|
>>> void
|
|
|
|
See
|
|
the [complete list of available formats](https://github.com/sergeych/mp_stools?tab=readme-ov-file#datetime-formatting)
|
|
and the [formatting reference](https://github.com/sergeych/mp_stools?tab=readme-ov-file#printf--sprintf): it all works
|
|
in Lyng as `"format"(args...)`!
|
|
|
|
## Instant members
|
|
|
|
| member | description |
|
|
|--------------------------------|---------------------------------------------------------|
|
|
| epochSeconds: Real | positive or negative offset in seconds since Unix epoch |
|
|
| epochWholeSeconds: Int | same, but in _whole seconds_. Slightly faster |
|
|
| nanosecondsOfSecond: Int | offset from epochWholeSeconds in nanos (1) |
|
|
| isDistantFuture: Bool | true if it `Instant.distantFuture` |
|
|
| isDistantPast: Bool | true if it `Instant.distantPast` |
|
|
| truncateToSecond: Intant | create new instnce truncated to second |
|
|
| truncateToMillisecond: Instant | truncate new instance with to millisecond |
|
|
| truncateToMicrosecond: Instant | truncate new instance to microsecond |
|
|
|
|
(1)
|
|
: The value of nanoseconds is to be added to `epochWholeSeconds` to get exact time point. It is in 0..999_999_999 range.
|
|
The precise time instant value therefore needs as for now 12 bytes integer; we might use bigint later (it is planned to
|
|
be added)
|
|
|
|
## Class members
|
|
|
|
| member | description |
|
|
|--------------------------------|----------------------------------------------|
|
|
| Instant.now() | create new instance with current system time |
|
|
| Instant.distantPast: Instant | most distant instant in past |
|
|
| Instant.distantFuture: Instant | most distant instant in future |
|
|
|
|
# `Duraion` class
|
|
|
|
Represent absolute time distance between two `Instant`.
|
|
|
|
import lyng.time
|
|
val t1 = Instant()
|
|
|
|
// yes we can delay to period, and it is not blocking. is suspends!
|
|
delay(1.millisecond)
|
|
|
|
val t2 = Instant()
|
|
// be suspend, so actual time may vary:
|
|
assert( t2 - t1 >= 1.millisecond)
|
|
assert( t2 - t1 < 100.millisecond)
|
|
>>> void
|
|
|
|
Duration can be converted from numbers, like `5.minutes` and so on. Extensions are created for
|
|
`Int` and `Real`, so for n as Real or Int it is possible to create durations::
|
|
|
|
- `n.millisecond`, `n.milliseconds`
|
|
- `n.second`, `n.seconds`
|
|
- `n.minute`, `n.minutes`
|
|
- `n.hour`, `n.hours`
|
|
- `n.day`, `n.days`
|
|
|
|
The bigger time units like months or years are calendar-dependent and can't be used with `Duration`.
|
|
|
|
Each duration instance can be converted to number of any of these time units, as `Real` number, if `d` is a `Duration`
|
|
instance:
|
|
|
|
- `d.microseconds`
|
|
- `d.milliseconds`
|
|
- `d.seconds`
|
|
- `d.minutes`
|
|
- `d.hours`
|
|
- `d.days`
|
|
|
|
for example
|
|
|
|
import lyng.time
|
|
assertEquals( 60, 1.minute.seconds )
|
|
assertEquals( 10.milliseconds, 0.01.seconds )
|
|
|
|
>>> void
|
|
|
|
# Utility functions
|
|
|
|
## delay(duration: Duration)
|
|
|
|
Suspends current coroutine for at least the specified duration.
|
|
|
|
|