Seems like Kotlinx datatime has some issue initial...
# kotlinx-datetime
s
Seems like Kotlinx datatime has some issue initializing on distroless container with native target. Getting the following error even though the image has the tz details.
Copy code
Uncaught Kotlin exception: kotlin.native.internal.FileFailedToInitializeException: There was an error during file or class initialization
    at 0   app                                 0x2a7ee7           ThrowFileFailedToInitializeException + 311
    at 1   app                                 0x55f0a3           CallInitGlobalPossiblyLock + 527
    at 2   app                                 0x4e8cab           kfun:dev.suresh.Platform#<get-info>(){}kotlin.collections.Map<kotlin.String,kotlin.collections.Map<kotlin.String,kotlin.String?>> + 395
    at 3   app                                 0x51f4eb           kfun:$main$lambda$0COROUTINE$0.invokeSuspend#internal + 715
    at 4   app                                 0x522933           kfun:$main$lambda$0$FUNCTION_REFERENCE$0.invoke#internal + 339
    at 5   app                                 0x2969db           kfun:kotlin.coroutines.intrinsics.object-2.invokeSuspend#internal + 251
    at 6   app                                 0x29548b           kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 171
    at 7   app                                 0x35ddff           kfun:kotlinx.coroutines.DispatchedTask#run(){} + 623
    at 8   app                                 0x33bae7           kfun:kotlinx.coroutines.EventLoopImplBase#processNextEvent(){}kotlin.Long + 839
    at 9   app                                 0x369a93           kfun:kotlinx.coroutines#runBlocking(kotlin.coroutines.CoroutineContext;kotlin.coroutines.SuspendFunction1<kotlinx.coroutines.CoroutineScope,0:0>){0§<kotlin.Any?>}0:0 + 1171
    at 10  app                                 0x51f0f3           kfun:#main(kotlin.Array<kotlin.String>){} + 291
    at 11  app                                 0x51f17f           Konan_start + 79
    at 12  app                                 0x54babb           Init_and_run_start + 1027
    at 13  app                                 0x54bbd3           Konan_main + 15
    at 14  libc.so.6                           0xffff8678777f     0x0 + 281472937785215
    at 15  libc.so.6                           0xffff86787857     __libc_start_main + 151
    at 16  app                                 0x28a637           0x0 + 2663991
Caused by: kotlin.IllegalStateException: Failed to get the system timezone
    at 0   app                                 0x4cef13           kfun:kotlinx.datetime.TimeZone.Companion#currentSystemDefault(){}kotlinx.datetime.TimeZone + 643
    at 1   app                                 0x4e6533           kfun:BuildConfig.$init_global#internal + 5267
    at 2   app                                 0x55ef67           CallInitGlobalPossiblyLock + 211
    at 3   app                                 0x4e8cab           kfun:dev.suresh.Platform#<get-info>(){}kotlin.collections.Map<kotlin.String,kotlin.collections.Map<kotlin.String,kotlin.String?>> + 395
    ... and 14 more common stack frames skipped
Does this library support running on distroless images (gcr.io/distroless/cc-debian12) ?
d
The image may have the tz details, but is the system time zone correctly set? For example, via
/etc/localtime
.
s
Copy code
docker run -it --entrypoint=sh <http://gcr.io/distroless/cc-debian12:debug|gcr.io/distroless/cc-debian12:debug>
/ # cat /etc/localtime
cat: can't open '/etc/localtime': No such file or directory
Is that mandatory? Can we fallback to UTC if it’s not set like in Go or Rust programs ?
d
We can make the error a normal exception, then you'll be able to implement it yourself via
try { TimeZone.currentSystemDefault() } catch (e: Throwable) { TimeZone.UTC }
Would that be a good solution?
s
Yeah we can do that in our code, might be an issue with dependencies.
Right now i need to wrap all the calls to
TimeZone.currentSystemDefault()
d
It's a good idea to do that anyway for testability. Though please let me know if you have any dependencies you can't control that call currentSystemDefault, then we'll make UTC the default.
r
Making UTC the default seems like it would follow the principle of least surprise. I wouldn't expect that call to throw.
2
d
I'm mostly joking, but: why UTC? Why not UTC+012345 or a randomly chosen zone among the available ones? UTC is an important time zone, but
currentSystemDefault()
is the timezone in which the user expects to see times displayed, and not that many people in the world expect to see UTC time. Also, semantically, when there is no system time zone, this just means this function can not return the value that it should, that's it. So not having a system time zone set is a failure mode. I'm Kotlin, this means throwing an exception.
r
I'm mostly joking, but: why UTC? Why not UTC+012345 or a randomly chosen zone among the available ones? UTC is an important time zone,
To be accurate, UTC is not a time zone. Its a standard upon which all civil time and all time zones are based.
currentSystemDefault() is the timezone in which the user expects to see times displayed, and not that many people in the world expect to see UTC time.
At least some people would expect UTC. I would. Nobody expects it to error :-)
So not having a system time zone set is a failure mode.
Not if we can fall back to a value that is not a time zone. i.e. UTC. Using a value that is not a time zone directly matches not having a system time zone.
d
> To be accurate, UTC is not a time zone. Its a standard upon which all civil time and all time zones are based. > If we believe that, then it's invalid to return
UTC
from functions that promise to return time zones. Sort of undermines your entire position. However,
Etc/UTC
(or, colloquially, just UTC) is also a time zone, according to the timezone database https://en.m.wikipedia.org/wiki/Tz_database > At least some people would expect UTC. I would. > I'm not talking about software developers, I'm talking about users. > Not if we can fall back to a value that is not a time zone. i.e. UTC. > By that logic, all functions that can't compute a valid result should return zeros, or empty strings, or other "default" values. This is idiomatic Go, and some people enjoy writing code this way, but it's not idiomatic Kotlin. If you port
kotlinx-datetime
to Go, then yes, I think returning UTC would be appropriate.
r
> If we believe that, then it's invalid to return UTC from functions that promise to return time zones. It serves us as programmers to treat UTC as a time zone technically, which is why UTC is a "time zone" in our APIs and in the tzdb. However, in actuality it isn't one. See what is the difference between UTC and GMT (https://www.timeanddate.com/time/gmt-utc-time.html). This is a case in which our software models compromise on representing the real world accurately because it is pragmatic to do so. > I'm not talking about software developers, I'm talking about users. A user expects to see their log with times attached. Not an error message or an app crash. With Java-style checked Exceptions I could agree with
currentSystemDefault
throwing, as the programmer would be forced to deal with it and provide their own fallback. You might argue that it is a programmer's job to know and understand the APIs they are using but lets be honest -- many don't. Furthermore,
currentSystemDefault
is not a new API for anybody who has used Java -- it has a long history and lots of use in existing code and none of it checks for it throwing. Give the API a new name like
currentSystemDefaultOrThrow
and I can jump on board. > By that logic, all functions that can't compute a valid result should return zeros, or empty strings, or other "default" values... it's not idiomatic Kotlin I think this the main point. I would argue it is idiomatic Kotlin, because in this case UTC isn't a random default with no meaning, as "zero" or an "empty string" would be. UTC directly represents the concept of a time before a zone is applied to it to change the offset. This exactly matches the idea of "this system does not have a time zone defined, therefore do not apply any offset to my time".
d
If we go so deeply, then it's worth highlighting that we don't even support the UTC standard. The time scale chosen by the library is defined to be UTC-SLS.
👍 1
I think this the main point. I would argue it is idiomatic Kotlin, because in this case UTC isn't a random default with no meaning, as "zero" or an "empty string" would be. UTC directly represents the concept of a time before a zone is applied to it to change the offset. This exactly matches the idea of "this system does not have a time zone defined, therefore do not apply any offset to my time".
Nope, I completely disagree. Exactly the same logic can be used to justify returning 0 dollars as the price of an item when you can't calculate it: well, since the price couldn't be calculated, we can't ask the customer to pay anything, and we don't want the application to crash, so 0 makes sense. It's also the same as returning an empty string when the name of the item is not available: well, we can't print anything, but we don't expect the cash register to output an error.
You are referring to the idea that the default function with the type
(A) -> A
is the function that returns its argument unchanged. If your software for tax declaration decides it can't calculate your tax correctly, do you think it's appropriate to consider taxation as a function converting one amount of money to another, so that your software tells you that you don't have to pay any taxes at all this year?
Turns out, we will have to use UTC by default! The contract on /etc/localtime explicitly states that an absent symlink is the same as UTC: https://www.freedesktop.org/software/systemd/man/latest/localtime.html This means that it's not a question of preferences: we are simply wrong to disallow this.
👍 2
r
Turns out, we will have to use UTC by default! The contract on /etc/localtime explicitly states that an absent symlink is the same as UTC
Good! I'm not at all surprised that is the required behavior.
> Exactly the same logic can be used to justify returning 0 dollars as the price of an item when you can't calculate it No. Not being able to calculate a price is not the same thing in any world as saying the price is free / 0! It's not analogous to a programmer saying "I don't know what timezone to use here, what is normal for this system?", and the response being "I don't know either, so use UTC". If the programmer is explicitly asking, that means they have no insight themselves, so forcing them to deal with an exception adds no value. There isn't anything they can do with it. But if they ask to calculate a price and it cannot be done, that is definitely something the programmer can and must deal with.
d
No, the dialogue is different. — What timezone did the user set? — They didn't set any time zone, so I will pretend that they used the UTC time zone, don't worry. We are not providing a
bestTimeZoneToUse
function, we are providing
currentSystemDefault
. When you are asking for the current system time zone, you should receive that, not some arbitrary guess made by us. If you want a
bestTimeZoneToUse
, sure, the logic you described can be a good implementation for that.
r
> — They didn't set any time zone, so I will pretend that they used the UTC time zone, don't worry. You're pretending UTC is a time zone again. The response is actually: > — They didn't set any time zone, so therefore the valid zone to use for computation purposes is UTC, which means "no zone"
BTW, I agree that the name
currentSystemDefault
is interesting. Going just by that name, in this case that function should return null i.e. there is no default. It is not an error to not have a default set. But unfortunately the return type is not nullable. So the function name by itself is not sufficient to inform our next steps here.