kotlinx-datetime icon indicating copy to clipboard operation
kotlinx-datetime copied to clipboard

LocalTime.MAX is internal (get datetime in end of the day)

Open mtrakal opened this issue 10 months ago • 6 comments

Hello,

we have: LocalDate.atStartOfDayIn but we don't have endOfDay. It would be nice to add that as well.

We would like to use LocalDate.atTime(LocalTime.MAX) but LocalTime.MAX is marked as internal, so it's impossible.

Other way is use create custom extension like: LocalDate.plus(1.days).atStartOfDayIn().minus(1.nano/micro) etc, but it's not straightforward. It would be nice to have it inside kotlinx-datetime natively.

mtrakal avatar Feb 19 '25 13:02 mtrakal

Hi!

Other way is use create custom extension

Yes, that's the way to go.

Logically, there is no such thing as the last moment of the day. Your post already shows this:

1.nano/micro

Which one is it and why? 23:59:59.999999 is not the last moment of the day, because there's 23:59:59.999999999 after that, but that is also not the last moment of the day, because there's 23:59:59.999999999999 after that. kotlinx-datetime can't represent a local time with such precision, so we need to round this number. Normal rounding (that is, rounding to the nearest number that is representable) gives us 00:00 of the next day, which is exactly LocalDate.plus(1, DateTimeUnit.DAY).atStartOfDayIn(timeZone).

dkhalanskyjb avatar Feb 19 '25 14:02 dkhalanskyjb

Yes we go with custom extension, we used LocalDate.plus(1, DateTimeUnit.DAY).atStartOfDayIn(timeZone) but it can cause some issues (because it's next day and when someone trims the time later he gets different day than expected). We could minus(millis/micro), but it didn't look like a straightforward solution.

JavaTime has java.time.LocalTime.MAX which is defined like (at least in my JDK used on android):

    /**
     * The maximum supported {@code LocalTime}, '23:59:59.999999999'.
     * This is the time just before midnight at the end of the day.
     */
    public static final LocalTime MAX;

So I want to use this library LocalTime.MAX because I don't like to relate on java classes directly except UI layer / formating.

localDate.atTime(java.time.LocalTime.MAX.toKotlinLocalTime()) is solution for me in extension now.

mtrakal avatar Feb 19 '25 14:02 mtrakal

We could minus(millis/micro), but it didn't look like a straightforward solution.

It's the only correct solution I know of, unfortunately.

localDate.atTime(java.time.LocalTime.MAX.toKotlinLocalTime())

If because of a daylight saving time transition, clocks are shifted someday in some time zone from 23:00 to 00:00 of the following day directly, the time 23:59:59.999999999 may not even exist in that time zone on that day, so this solution is less robust than subtracting a nanosecond from atStartOfDayIn.

dkhalanskyjb avatar Feb 19 '25 15:02 dkhalanskyjb

Which one is it and why? 23:59:59.999999 is not the last moment of the day, because there's 23:59:59.999999999 after that, but that is also not the last moment of the day, because there's 23:59:59.999999999999 after that. kotlinx-datetime can't represent a local time with such precision, so we need to round this number.

I have a slightly different use case where I don't want to use as an exclusive bound as is suggested above. It's currently impossible to represent an exclusive bound for something like [00:00:00, 24:00:00) because the 24 denotation for local time was removed from the ISO standard, and because the range [00:00:00, 00:00:000) is simply an empty range. We cope with this on our side by specifying an inclusive range where the end boundary is the maximum value of a local time, so currently that's[00:00:00, 23:59:59.999_999_999].

We know this isn't perfect, but it does specify every single discrete nanosecond of 0 - 24 hours in local time that LocalTime can currently represent. In the event that LocalTime is adjusted to represent more granular picoseconds, I imagine that any LocalTime.MAX value would then be adjusted to represent 23:59:59.999_999_999_999 in tandem.

In essence, I don't want the max possible value of a LocalTime, which is ill defined because you can always subdivide the seconds component further for a slightly larger value, but I want the maximum possibly representable value that kotlinx-datetime can currently support for LocalTime. Right now that's with 999,999,999 nanoseconds, but in the future it might be 999,999,999,999 picoseconds.

kevincianfarini avatar Apr 29 '25 15:04 kevincianfarini

Imagine a user of your code wants to represent [17:00:00; 18:00:00). If your abstraction does not allow representing [00:00:00, 24:00:00) without resorting to workarounds like 23:59:59.999999999, then it also can't represent the exclusive upper bound of 18:00:00 and has to resort to 17:59:59.999999999. 24:00:00 is not special in that regard. This simply means that upper bounds are not LocalTime values.

For example, here we had to use a separate entity to deal with exactly this problem (upper bounds of local times before a timezone transition): https://github.com/Kotlin/kotlinx-datetime/blob/875e64de936c61773384985041f6708a7d07f833/core/commonKotlin/src/internal/MonthDayTime.kt#L164-L172

dkhalanskyjb avatar Apr 29 '25 16:04 dkhalanskyjb

Btw Instant already has a resolution to it, so maybe choose that? (start of next day minus the instant quantum)

ursusursus avatar Jul 13 '25 01:07 ursusursus