Kotlin / kotlinx-datetime

KotlinX multiplatform date/time library
Apache License 2.0
2.39k stars 99 forks source link

Implement TimeZone raw offset #296

Closed Sternbach-Software closed 10 months ago

Sternbach-Software commented 1 year ago

I am porting a very popular (used by several large airlines) astronomical calculator library to Kotlin Multiplatform, and it heavily relies on java.util.TimeZone.getRawOffset() (e.g. when calculating sunrise). It doesn’t seem like there is an equivalent in kotlinx-datetime. If I am wrong, how can I achieve equivalent results? If I am right, consider this a feature request. The javadoc for getRawOffset:

 /**
     * Returns the amount of time in milliseconds to add to UTC to get
     * standard time in this time zone. Because this value is not
     * affected by daylight saving time, it is called <I>raw
     * offset</I>.
     * <p>
     * If an underlying {@code TimeZone} implementation subclass
     * supports historical GMT offset changes, the method returns the
     * raw offset value of the current date. In Honolulu, for example,
     * its raw offset changed from GMT-10:30 to GMT-10:00 in 1947, and
     * this method always returns -36000000 milliseconds (i.e., -10
     * hours).
     *
     * @return the amount of raw offset time in milliseconds to add to UTC.
     * @see Calendar#ZONE_OFFSET
     */
    public abstract int getRawOffset();
dkhalanskyjb commented 1 year ago

After looking at the codebase, I don't understand how rawOffset helps with finding the time of the sunrise. Could you clarify this?

What usages of getRawOffset I can see in the original Java codebase are regarding the https://en.wikipedia.org/wiki/Local_mean_time There are various methods that return dates in this representation (relative to the "fixed local chatzos", whose definition involves the standard time without the UTC offset). Are those useful? The top links for "chatzos" are all by KosherJava itself, so I'm not sure if this functionality is popular.

Sternbach-Software commented 1 year ago

@dkhalanskyjb Can you clarify your question?

dkhalanskyjb commented 1 year ago

Could you clarify this?

I don't understand why rawOffset is useful for finding the time of the sunrise. There is a function there that finds the time in UTC (which is effectively the same as just having an Instant), which shouldn't require knowing the time zone, and maybe there's use in knowing the LocalDateTime of a sunrise, but neither of these is solved via raw offsets.

Are those useful?

Does anyone actually use the "fixed local chatzos" representations? You say the library is used by some airlines—do these airlines want to know the local-mean-time representation of dates? If yes, do you know why?

Sternbach-Software commented 1 year ago

Regarding your second question, yes. It is part of a feature being rolled out on some airlines (e.g. El Al) for religious prayer times. There is a significant population which needs that time to know when to pray.

dkhalanskyjb commented 1 year ago

Presumably, the people want to know their prayer times in terms of the time they have on their clocks (which are affected by the DST), not in terms of local-mean time. I can see why "chatzos" is useful to someone (and it's an Instant, completely oblivious to time zones or raw offsets), but what's the use case for "fixed local chatzos" (which does require the raw offset)?

Sternbach-Software commented 1 year ago

It is a method for calculating certain prayer times according to one of the primary religious authorities (Rabbi Moshe Feinstein). It is also used in calculating other religious times.

dkhalanskyjb commented 1 year ago

Well, what is that method? What are the specific words by that Rabbi regarding the matter? You won't get anywhere in this discussion without showing us some code that doesn't work without knowing the raw offsets and explaining who would find this code useful.

Example:

fun prayerTime(date: LocalDate, location: Location, timezone: TimeZone): Instant {
  TODO("what goes here? which part of this uses raw offsets?")
}
Sternbach-Software commented 1 year ago

Before going down that rabbit hole, can I ask if historical accuracy is important enough of a reason?

This library is also used for historical calculations, including before DST was introduced. The OpenJDK implementation of the default timezone's java.util.TimeZone.getRawOffset uniquely guarantees historical accuracy. Is historical accuracy enough of a reason to add getRawOffset to Kotlinx-datetime?

dkhalanskyjb commented 1 year ago

can I ask if historical accuracy is important enough of a reason?

It would be, but I don't think rawOffset is relevant to historical accuracy.

uniquely guarantees historical accuracy

Why do you believe so?

This library is also used for historical calculations, including before DST was introduced.

For any calculations, the TimeZone class queries the timezone database https://www.iana.org/time-zones (which is fairly standard practice, you'll observe the same on all major platforms), and there, not only the latest rules are available, but also the historic ones. If there were no DST transitions at some point in time, TimeZone will not apply the DST transitions then. Internally, it uses an equivalent of rawOffset to calculate the offset in effect.

dkhalanskyjb commented 10 months ago

@Sternbach-Software, did you manage to find any justification for providing access to the raw offset?

Sternbach-Software commented 10 months ago

I talked with the original author of the library, and they said that they wrote the code 20 years ago and didn't remember why the decision was made. I couldn't find anything.

Sternbach-Software commented 10 months ago

I know that using java.util.TimeZone.getOffset() gives different prayer times than using getRawOffset, but I don't know why the latter is the correct time.

dkhalanskyjb commented 10 months ago

Since seemingly no one knows the exact rules behind that logic, maybe it's simply not the correct time and there is a bug in the original code.

I'll close this issue for now, but if you discover anything specific, please let us know, and we'll reopen it.