Hello, (referred here from <#C0B8RC352|server>): I...
# kotlinx-datetime
d
Hello, (referred here from #server): Is there a way to define the resolution that Kotlin uses for `Instant`s? Reason: Our Kotlin application talks to a PostgreSQL database and saves `Instant`s in a
timestamp
column. PostgreSQL has microsecond resolution for that column. On MacOS, this works because
Clock.System.now()
also returns an
Instant
with microsecond resolution, so comparing an object before persisting it and after retrieving it from the database works. On Linux (which we use for our pipeline) this same comparison fails because
2023-06-09T07:34:27.965341799Z != 2023-06-09T07:34:27.965342Z
I found similar issues for e.g. H2, but there it seems possible to simply store nanoseconds in the database. It is not necessarily an issue to change the tests, we can simply set fixed instants (without nanosecond precision), but the question remains nevertheless: Is there a way to force Kotlin to always use a specific precision?
d
Hello! No, there is no such mechanism.
d
OK, thanks for the answer! That's at least good to know 🙂
j
I use this extension to force milliseconds precision on all platforms:
Copy code
fun Clock.nowMillis() = Instant.fromEpochMilliseconds(now().toEpochMilliseconds())
m
It looks like
Instant
does not have a
truncatedTo()
function, like
java.time.Instant
. I have used that in JVM projects that needed to behave the same on macOS and Linux. I try to ensure better than millisecond precision in most cases, because even basic desktop machines run faster than that these days.
Someone requested that functionality, unsuccessfully: https://github.com/Kotlin/kotlinx-datetime/issues/62
d
Note that the request was "unsuccessful" mainly because the person requesting it closed the issue. We are willing to add new functionality if there is some demand. The requested APIs are not always a good fit for the library, so first we collect the use cases so that we know how to "rephrase" the need more idiomatically. So far, I don't remember anyone telling us about any use cases for this except https://github.com/Kotlin/kotlinx-datetime/issues/223#issuecomment-1484976078, and for that, I'm not sure
Instant.truncateTo
is the answer. The bottom line: if someone thinks the functionality is worth having, they will have to spell it out for us with clear examples of why it would be useful.
m
Thanks for clarifying that situation, @Dmitry Khalanskiy [JB]. The example you showed is not a good use for a
truncatedTo()
function. I think aligning values between systems of different precisions might be. When I used it on the JVM I had tests that passed on Mac but failed on Linux because the JVM uses the system clock, which has microsecond precision on macOS and nanosecond precision on Linux. If I want that in kotlinx-datetime I will probably write an extension function first.
d
What are some examples of needing to align between systems? So far, both the topic starter and you only mention tests, and if I understood their form correctly, they could be rewritten without using
truncateTo
in a way similar to this:
Copy code
val instant1 = Clock.System.now()
val instant2 = sendToServerAndReceiveBack(instant1)
assertEquals(instant1.toEpochMilliseconds(), instant2.toEpochMilliseconds())
I think this reflects the intention even better than
Copy code
val instant1 = Clock.System.now().roundTo(DateTimeUnit.MILLISECOND)
val instant2 = sendToServerAndReceiveBack(instant1)
assertEquals(instant1, instant2)
After all, the second operation potentially loses precision, and we want to check precisely that the millisecond portion is always preserved, not that this operation preserves an
Instant
as long as it's rounded to lose precision.
If, for example, some database refuses to accept a value with more than millisecond precision, then yeah, it's certainly required to be able to round the values, at least during serialization.
k
The OP's requirement was microsecond resolution.
d
Thanks all for the interesting discussions and pointers. Indeed, the main usecase is testing where comparing before and after fails if there is a "truncating" system in the middle (PostgreSQL in my case). Any
.truncate(...)
method or the alternative
.with(...)
sounds like a good idea. I would then put it into the
set()
or
get()
part of the class which has
Instant
fields, making sure to always have a certain precision. For now that should also be possible by converting the provided
Instant
from
Clock.now()
, e.g. via
Clock.System.now()._toJavaInstant_().with(ChronoField._NANO_OF_SECOND_, 0L)._toKotlinInstant_()
k
My random thoughts are that a
truncateTo
might be useful, but Java's version of
truncateTo
is limited to truncating to single units. What if you wanted the time to the nearest quarter of an hour? Perhaps that could be done with a
round(Duration, RoundingMode)
which allows you to round down, up, half up etc. But that sounds too specialized and can be user-created per application.