ref #34 time formatting and precision time access
This commit is contained in:
parent
732d8f3877
commit
230cb0a067
58
docs/time.md
58
docs/time.md
@ -58,14 +58,67 @@ The resolution of system clock could be more precise and double precision real n
|
|||||||
|
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
|
## Getting the max precision
|
||||||
|
|
||||||
|
Normally, subtracting instants gives precision to microseconds, which is well inside the jitter
|
||||||
|
the language VM adds. Still `Instant()` captures 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()
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
## 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
|
## Instant members
|
||||||
|
|
||||||
| member | description |
|
| member | description |
|
||||||
|-----------------------|---------------------------------------------------------|
|
|--------------------------|---------------------------------------------------------|
|
||||||
| epochSeconds: Real | positive or negative offset in seconds since Unix epoch |
|
| 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` |
|
| isDistantFuture: Bool | true if it `Instant.distantFuture` |
|
||||||
| isDistantPast: Bool | true if it `Instant.distantPast` |
|
| isDistantPast: Bool | true if it `Instant.distantPast` |
|
||||||
|
|
||||||
|
(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
|
## Class members
|
||||||
|
|
||||||
| member | description |
|
| member | description |
|
||||||
@ -102,6 +155,7 @@ The bigger time units like months or years are calendar-dependent and can't be u
|
|||||||
|
|
||||||
Each duration instance can be converted to number of any of these time units, as `Real` number, if `d` is a `Duration` instance:
|
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.milliseconds`
|
||||||
- `d.seconds`
|
- `d.seconds`
|
||||||
- `d.minutes`
|
- `d.minutes`
|
||||||
@ -112,6 +166,8 @@ for example
|
|||||||
|
|
||||||
import lyng.time
|
import lyng.time
|
||||||
assertEquals( 60, 1.minute.seconds )
|
assertEquals( 60, 1.minute.seconds )
|
||||||
|
assertEquals( 10.milliseconds, 0.01.seconds )
|
||||||
|
|
||||||
>>> void
|
>>> void
|
||||||
|
|
||||||
# Utility functions
|
# Utility functions
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.5.2"
|
agp = "8.5.2"
|
||||||
clikt = "5.0.3"
|
clikt = "5.0.3"
|
||||||
kotlin = "2.2.0"
|
kotlin = "2.1.21"
|
||||||
android-minSdk = "24"
|
android-minSdk = "24"
|
||||||
android-compileSdk = "34"
|
android-compileSdk = "34"
|
||||||
kotlinx-coroutines = "1.10.1"
|
kotlinx-coroutines = "1.10.1"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("multiplatform") version "2.2.0"
|
kotlin("multiplatform") version "2.1.21"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "net.sergeych"
|
group = "net.sergeych"
|
||||||
|
@ -33,10 +33,6 @@ open class ObjClass(
|
|||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun defaultInstance(): Obj = object : Obj() {
|
|
||||||
// override val objClass: ObjClass = this@ObjClass
|
|
||||||
// }
|
|
||||||
|
|
||||||
fun createField(
|
fun createField(
|
||||||
name: String,
|
name: String,
|
||||||
initialValue: Obj,
|
initialValue: Obj,
|
||||||
|
@ -56,6 +56,11 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
addFn("milliseconds") {
|
addFn("milliseconds") {
|
||||||
thisAs<ObjDuration>().duration.toDouble(DurationUnit.MILLISECONDS).toObj()
|
thisAs<ObjDuration>().duration.toDouble(DurationUnit.MILLISECONDS).toObj()
|
||||||
}
|
}
|
||||||
|
addFn("microseconds") {
|
||||||
|
thisAs<ObjDuration>().duration.toDouble(DurationUnit.MICROSECONDS).toObj()
|
||||||
|
}
|
||||||
|
// extensions
|
||||||
|
|
||||||
ObjInt.type.addFn("seconds") {
|
ObjInt.type.addFn("seconds") {
|
||||||
ObjDuration(thisAs<ObjInt>().value.seconds)
|
ObjDuration(thisAs<ObjInt>().value.seconds)
|
||||||
}
|
}
|
||||||
@ -63,7 +68,6 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
ObjInt.type.addFn("second") {
|
ObjInt.type.addFn("second") {
|
||||||
ObjDuration(thisAs<ObjInt>().value.seconds)
|
ObjDuration(thisAs<ObjInt>().value.seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjInt.type.addFn("milliseconds") {
|
ObjInt.type.addFn("milliseconds") {
|
||||||
ObjDuration(thisAs<ObjInt>().value.milliseconds)
|
ObjDuration(thisAs<ObjInt>().value.milliseconds)
|
||||||
}
|
}
|
||||||
@ -71,7 +75,6 @@ class ObjDuration(val duration: Duration) : Obj() {
|
|||||||
ObjInt.type.addFn("millisecond") {
|
ObjInt.type.addFn("millisecond") {
|
||||||
ObjDuration(thisAs<ObjInt>().value.milliseconds)
|
ObjDuration(thisAs<ObjInt>().value.milliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjReal.type.addFn("seconds") {
|
ObjReal.type.addFn("seconds") {
|
||||||
ObjDuration(thisAs<ObjReal>().value.seconds)
|
ObjDuration(thisAs<ObjReal>().value.seconds)
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,10 @@ class ObjInstant(val instant: Instant) : Obj() {
|
|||||||
return super.compareTo(scope, other)
|
return super.compareTo(scope, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun toKotlin(scope: Scope): Any {
|
||||||
|
return instant
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val distantFuture by lazy {
|
val distantFuture by lazy {
|
||||||
ObjInstant(Instant.DISTANT_FUTURE)
|
ObjInstant(Instant.DISTANT_FUTURE)
|
||||||
@ -51,7 +55,7 @@ class ObjInstant(val instant: Instant) : Obj() {
|
|||||||
when (a0) {
|
when (a0) {
|
||||||
null -> {
|
null -> {
|
||||||
val t = Clock.System.now()
|
val t = Clock.System.now()
|
||||||
Instant.fromEpochSeconds(t.epochSeconds, (t.nanosecondsOfSecond / 1_000_000).toLong()*1_000_000)
|
Instant.fromEpochSeconds(t.epochSeconds, t.nanosecondsOfSecond)
|
||||||
}
|
}
|
||||||
is ObjInt -> Instant.fromEpochSeconds(a0.value)
|
is ObjInt -> Instant.fromEpochSeconds(a0.value)
|
||||||
is ObjReal -> {
|
is ObjReal -> {
|
||||||
@ -78,6 +82,14 @@ class ObjInstant(val instant: Instant) : Obj() {
|
|||||||
addFn("isDistantPast") {
|
addFn("isDistantPast") {
|
||||||
thisAs<ObjInstant>().instant.isDistantPast.toObj()
|
thisAs<ObjInstant>().instant.isDistantPast.toObj()
|
||||||
}
|
}
|
||||||
|
addFn("epochWholeSeconds") {
|
||||||
|
ObjInt(thisAs<ObjInstant>().instant.epochSeconds)
|
||||||
|
}
|
||||||
|
addFn("nanosecondsOfSecond") {
|
||||||
|
ObjInt(thisAs<ObjInstant>().instant.nanosecondsOfSecond.toLong())
|
||||||
|
}
|
||||||
|
// class members
|
||||||
|
|
||||||
addClassConst("distantFuture", distantFuture)
|
addClassConst("distantFuture", distantFuture)
|
||||||
addClassConst("distantPast", distantPast)
|
addClassConst("distantPast", distantPast)
|
||||||
// addFn("epochMilliseconds") {
|
// addFn("epochMilliseconds") {
|
||||||
|
@ -2560,4 +2560,18 @@ class ScriptTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInstantFormatting() = runTest {
|
||||||
|
eval(
|
||||||
|
"""
|
||||||
|
import lyng.time
|
||||||
|
val now = Instant()
|
||||||
|
val unixEpoch = "%ts"(now)
|
||||||
|
println("current seconds is %s"(unixEpoch))
|
||||||
|
println("current time is %tT"(now))
|
||||||
|
assertEquals( unixEpoch.toInt(), now.epochSeconds.toInt() )
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ import junit.framework.TestCase.assertEquals
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.sergeych.lyng.ModuleScope
|
import net.sergeych.lyng.ModuleScope
|
||||||
import net.sergeych.lyng.Source
|
import net.sergeych.lyng.Source
|
||||||
|
import net.sergeych.lyng.eval
|
||||||
import net.sergeych.lyng.pacman.InlineSourcesImportProvider
|
import net.sergeych.lyng.pacman.InlineSourcesImportProvider
|
||||||
import net.sergeych.lyng.toSource
|
import net.sergeych.lyng.toSource
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
@ -37,4 +38,16 @@ class OtherTests {
|
|||||||
assertEquals("foo1 / bar1", scope.eval(src).toString())
|
assertEquals("foo1 / bar1", scope.eval(src).toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInstantTruncation() = runBlocking {
|
||||||
|
eval("""
|
||||||
|
import lyng.time
|
||||||
|
val t1 = Instant()
|
||||||
|
val t2 = Instant()
|
||||||
|
// assert( t1 != t2 )
|
||||||
|
println(t1 - t2)
|
||||||
|
""".trimIndent())
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user