ref #34 time formatting and precision time access
This commit is contained in:
		
							parent
							
								
									732d8f3877
								
							
						
					
					
						commit
						230cb0a067
					
				
							
								
								
									
										66
									
								
								docs/time.md
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								docs/time.md
									
									
									
									
									
								
							@ -58,13 +58,66 @@ 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 |
 | 
				
			||||||
| isDistantFuture: Bool | true if it `Instant.distantFuture`                      |
 | 
					| epochWholeSeconds: Int   | same, but in _whole seconds_. Slightly faster           |
 | 
				
			||||||
| isDistantPast: Bool   | true if it `Instant.distantPast`                        |
 | 
					| nanosecondsOfSecond: Int | offset from epochWholeSeconds in nanos (1)              |
 | 
				
			||||||
 | 
					| isDistantFuture: Bool    | true if it `Instant.distantFuture`                      |
 | 
				
			||||||
 | 
					| 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -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