Open dkhalanskyjb opened 12 months ago
One practical use case for such functionality: the OCPP specification. It mandates that ISO date/time strings don't have more than 3 decimal points:
number of decimal places SHALL NOT exceed the maximum of 3.
In other words: no more than millisecond precision.
While working with kotlinx-datetime
, we actually ran into compatibility problems with vendors that will flat out reject the nanosecond-precise ISO date/time strings that kotlinx.datetime.Instant
gets serialized to by kotlinx.serialization
.
We could use JSR-310 (java.time
) instead, so we could solve it with truncateTo
, or we can write a helper function as suggested on StackOverflow, but I was kind of hoping that there would be an equally convenient built-in multiplatform solution for this.
Update:
I found a single-line workaround for forcibly reducing the precision to millisecond-level in serialized ISO date/time strings, at least for timestamps originating from the system clock:
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
// Will have 6 digits behind the decimal point on Kotlin/JVM, but 3 digits on Kotlin/JS and Kotlin/Native
println(Clock.System.now())
// Will have 3 digits behind the decimal point on Kotlin/JVM, Kotlin/JS and Kotlin/Native
println(Instant.fromEpochMilliseconds(Clock.System.now().toEpochMilliseconds()))
So Kotlin/JVM appears to appears to be the only Kotlin platform that creates Instant
s from the system clock at nanosecond precision, although I haven't tried it with Kotlin for Android yet.
@volkert-fastned, since your problem is with system interoperability, the upcoming API for parsing and formatting may solve it: https://github.com/Kotlin/kotlinx-datetime/pull/343
I've the use case where I want to check whether a day (LocalDate
) is in current week or current month. I don't think there's support for this yet, and I think this issue would help - correct me if I'm wrong. Is there any chance this will be implemented soon?
LE: here's the equivalent code using Java API (in Kotlin) of what I'm trying to do
fun isCurrentDay(timestamp: Long): Boolean {
val instant = Instant.ofEpochMilli(timestamp)
val currentLocalDate = LocalDate.now(ZoneId.systemDefault())
val timestampLocalDate = instant.atZone(ZoneId.systemDefault()).toLocalDate()
return currentLocalDate == timestampLocalDate
}
fun isCurrentWeek(timestamp: Long): Boolean {
val instant = Instant.ofEpochMilli(timestamp)
val currentLocalDate = LocalDate.now(ZoneId.systemDefault())
val timestampLocalDate = instant.atZone(ZoneId.systemDefault()).toLocalDate()
val currentWeekStart = currentLocalDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
val currentWeekEnd = currentWeekStart.plusDays(6)
return timestampLocalDate in currentWeekStart..currentWeekEnd
}
fun isCurrentMonth(timestamp: Long): Boolean {
val instant = Instant.ofEpochMilli(timestamp)
val currentLocalDate = LocalDate.now(ZoneId.systemDefault())
val timestampLocalDate = instant.atZone(ZoneId.systemDefault()).toLocalDate()
val currentMonthStart = currentLocalDate.with(TemporalAdjusters.firstDayOfMonth())
val currentMonthEnd = currentMonthStart.plusDays(
(currentLocalDate.lengthOfMonth() - 1).toLong()
)
return timestampLocalDate in currentMonthStart..currentMonthEnd
}
@alghe-global, here are some kotlinx-datetime implementations:
import kotlinx.datetime.*
fun LocalDate.isSameDay(timestamp: Long): Boolean =
this == timestampToDateInSystemTimeZone(timestamp)
fun LocalDate.isSameWeek(timestamp: Long): Boolean =
previousOrSame(DayOfWeek.MONDAY) ==
timestampToDateInSystemTimeZone(timestamp).previousOrSame(DayOfWeek.MONDAY)
fun LocalDate.isSameMonth(timestamp: Long): Boolean {
val timestampLocalDate = timestampToDateInSystemTimeZone(timestamp)
return year == timestampLocalDate.year && month == timestampLocalDate.month
}
/*
// After https://github.com/Kotlin/kotlinx-datetime/pull/457:
fun LocalDate.isSameMonth(timestamp: Long): Boolean =
yearMonth == timestampToDateInSystemTimeZone(timestamp).yearMonth
*/
private fun timestampToDateInSystemTimeZone(timestampMillis: Long) =
Instant.fromEpochMilliseconds(timestampMillis).toLocalDateTime(TimeZone.currentSystemDefault()).date
// https://github.com/Kotlin/kotlinx-datetime/issues/129#issuecomment-1152301045
private fun LocalDate.previousOrSame(requiredDayOfWeek: DayOfWeek): LocalDate =
minus((dayOfWeek.isoDayNumber - requiredDayOfWeek.isoDayNumber).mod(7), DateTimeUnit.DAY)
The "current week" use case would indeed benefit from temporal-adjuster-style functionality, but as a workaround, you could copy this implementation of previousOrSame
to your code.
"I have a date or a time, and I want to round/adjust it."
Examples:
Duration
: https://github.com/Kotlin/kotlinx-datetime/issues/225#issuecomment-1240884960 (can also be solved by tweaking the formatter).