-
Notifications
You must be signed in to change notification settings - Fork 361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Duration and time measurement API #190
Comments
Can you detail why the stdlib is the target for this API rather than a standalone kotlinx library? I don't think there's a clear delineation between when something should target the stdlib vs. kotlinx. Coroutines were split between the two, but leaned aggressively away from the stdlib in everything but the fundamental building blocks. Looking ahead, the multiplatform file/fs abstraction seems to be targeting kotlinx entirely. Nothing about the proposal makes it obvious why the stdlib is where this should live. I'm not opposed to it being there, to be clear, but I think it's worth capturing the arguments as to why something should live in the stdlib vs. kotlinx, applying them to this proposal, and seeing whether it still makes sense. |
As far as I understand this KEEP is mostly about duration and timing related to measurement, timeouts etc. and not for real-world date/time application. I suggest to be careful with the naming (e.g. A For a date/time library it could for example be decided that using |
Well some possible names to solve this kind of conflict would be |
FWIW I made a little lib a while ago that implements a large part of this proposal for the The major difference is that I chose the internal value of the inline class to be a Also I like |
@JakeWharton We believe that the API for working with date and times is a cornerstone thing and ideally it should be provided in the standard library. Unfortunately, the full blown date-time support is too heavy to be included in the stdlib, so we're trying to find a balance here. Duration and time measurement seem to have a moderate API surface and cover a lot of time use cases, so placing them in the stdlib looks reasonable. @fluidsonic When considering the naming of kotlin.time entities we keep in mind their potential integration with a kotlinx date/time library. If you have concrete examples of how the date/time concepts can conflict with the proposed interfaces/classes, let's discuss 'em here. |
I think the proposal needs bit more details on how
Maybe the API should include its own |
Given that they both represent the same
We do not have functions/properties to construct a duration from Float, but if we had, they would be equal.
The value will be clamped to the Long range, same as
We prohibit having Most of these questions are answered in the API docs of the corresponding members, see Duration.kt. |
It would seem extremely useful to add |
I would really, really, strongly suggest reviewing the C++
It's not really a very good compromise (IMHO). The problem is that once you go from just durations, to also time points, these are all measured from the epoch, which is some 50 years ago. So if you want to get the duration between two different time points today, you are basically getting some catastrophic cancellation. You can end up with durations of 10 nanoseconds, that are actually mostly noise. I would actually suggest making Duration generic on the underlying stored value type. Rather than using an enum to express the units, all you really need is some way to express the ratio of stored ticks, to seconds (this could either be a generic parameter, or it could be a stored value, depending on priorities). Check out the approach in
Wouldn't you expect that a Clock would actually be able to tell you the time? If you instead have a Clock that returns time points, you can call But again, this interface works much better with integers, rather than floating point, because of catastrophic cancellation issues. This is just one example IMHO of a more general thing, which is that durations, time points, dates, timezones, etc, need to be designed together. If you try to just design durations on their own, when you introduce time points you may see that your duration design does not play well with it.
This is can be solved (again, in chrono/date: https://en.cppreference.com/w/cpp/chrono/time_point). Basically, a TimeStamp can be regarded as being generic on a Duration type (which it stores), and an Epoch type. The Epoch type is really little more than a "tag". But basically functions are defined so that TimeStamp's of different epochs cannot be subtracted. If you want to do that, you need to explicitly convert the TimePoint to a duration (by calling |
A quick example of catastrophic cancellation; I wrote the following trivial C++ program to show what happens if you had two durations 10 nanoseconds apart, today:
This program just printed 0 (for me): https://coliru.stacked-crooked.com/a/2d5c2492ca14464f. So basically, a duration based on double is not a duration that can serve as the basis for good TimePoint's, but this already means that the design of everything is compromised (IMHO). |
The fact that duration value is stored as a double doesn't imply that time points will be stored as doubles too. I believe this cancels catastrophic cancellation issues.
|
You obviously can do it this way, but it's just making things harder on yourself (and less performant). One way or another, a time point is going to be a duration from a fixed moment (the epoch). If you think you've designed a good Duration type, it makes sense to use your own Duration type for that, does it not? It seems problematic that a time point essentially stores a duration and you can't use your own duration type for that. Your time point will have to use something else to store the duration, and then it will raise the question of why you can't simply use whatever the time point stores as your duration type. Etc. A TimePoint being a Duration + epoch is a very clean and logical design, and you should be able to use the same Duration inside the TimePoint, as generally throughout the library. Btw, note that neither Java nor C# use floating point for their duration, and C++ keeps it generic. So, of all the reviewed libraries, Klock is the only one that decided to back Duration with floating point (exclusively). The first 3 are standard libraries for some of the most used languages in the world, subject to unbelievable amounts of criticism, design discussion, etc. Klock obviously is not playing in that ballpark. So I think this is something you should consider.
You can do it this way, but what's the advantage? You now have two classes, and two interfaces. VS, you could have a single interface, and an extension method that will work with all Clocks. Note that Clock's returning a time_point is the interface that is actually more representative of how system clocks work; at the system level usually a clock is something that you ask for a time, and it gives you a time. You don't communicate with the system clock by asking it to "mark" the start, etc. Again, I would really urge you to review chrono/date. I know there's "always another" library, but it's a modern take on this stuff in a major language (with a very critical standardization process), it deserves to be on the short list of prior art. |
For people who mostly just use Kotlin as a better Java, is it maybe possible to put this duplicative stuff into kotlinx or other modules? I get that when targeted JavaScript or LLVM there are almost no usable libraries, but, the |
I believe this can be achieved without such radical measure as evicting
Since Kotlin 1.3 we have |
@quicknir, Could you tell from your experience working with |
I doubt such confusions would be reported, as such reports would be clearly non-actionable. And Kotlin 1.3 is quite new. My project still hasn't upgraded to it yet :( |
Ilya, I'm not sure exactly what you are asking. How can you define a point in time other than an epoch and duration? It's like defining a point in space; you have to pick coordinate axes and state your distances in those coordinates. Are you asking what the advantage is in using the same specific duration type inside of the time point class, instead of something else? I'll post later with more details but some of the things accomplished by Chrono sadly can't be done in kotlin because of type system limitations. That said i still think using a floating point type for duration is a huge misstep. |
Hello from the GoodTime team at Google! We work on libraries and static analysis to fight date- and time-related bugs in Java, and we see Kotlin as an opportunity to provide our users the same safety right out of the box. (Thanks to @JakeWharton for looping us in on this proposal.) Most bugs we find are unit mismatches, and the main way we prevent mismatches is to make APIs accept a One obstacle that we've hit in the most performance-sensitive code (like core concurrency and RPC tracing) is that those systems don't want their elapsed-time calculations to allocate. To be clear, this is a rare concern: Most users of those APIs are happy to use overloads or wrapper APIs that do allocate. But the implementations themselves avoid allocating. From what I understand, this often works as well as a "proper" But of course it would be nice to have a My concern is with I can see going in any of a few directions:
Or maybe there's a clever way to get everything we could want? If we're stuck choosing between these options, maybe it would help to define the scope of this proposal even more precisely. For example, should it be usable for nanobenchmarks that check whether one arithmetic operator outperforms another? for high-performance RPC systems? etc. |
|
Just saw this KEEP recently and figured I'd weigh in as I've been working on a pure Kotlin time library that draws heavily from java.time.
Anyway, while it's pretty early in development and certainly not ready for anyone to consume, this is the source code for my library: https://github.com/erikc5000/island-time. I figure it may at least provide some ideas on possible ways of dealing with durations, periods, and unit granularity in Kotlin to help inform discussion here -- and perhaps, get a picture of how a date-time library might build on the standard library primitives covered by the proposal. I'm also open to collaborating on or using it as a test bed for a future kotlinx library that offers more complete date-time functionality. Writing and maintaining a time library is a pretty big time commitment (see what I did there :-) ), so while a fun hobby project right now, ultimately, I'd very much like to offload some of that. |
I just came across this KEEP after reading the Kotlin 1.3.50 released Blog post. When I saw In the comments section "Marcel" suggested to use e.g. But if the Kotlin team doesn't plan to implement it this way, it's worth taking a look at the implementation in Golang. package time
[...]
// A Duration represents the elapsed time between two instants
// as an int64 nanosecond count. The representation limits the
// largest representable duration to approximately 290 years.
type Duration int64
const (
minDuration Duration = -1 << 63
maxDuration Duration = 1<<63 - 1
)
// Common durations. There is no definition for units of Day or larger
// to avoid confusion across daylight savings time zone transitions.
//
// To count the number of units in a Duration, divide:
// second := time.Second
// fmt.Print(int64(second/time.Millisecond)) // prints 1000
//
// To convert an integer number of units to a Duration, multiply:
// seconds := 10
// fmt.Print(time.Duration(seconds)*time.Second) // prints 10s
//
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
) Source: https://github.com/golang/go/blob/master/src/time/time.go |
I played a bit with the current implementation. I focused on some use case where duration are constants in the code: withTimeout(30.seconds) the actual So this is a use case for a further Kotlin enhancement. |
Why? Any running time concerns would, by my estimate, be taken care of by JIT at the latest. Inline classes are there to provide zero-cost abstractions, after all. |
I think it shouldn't be in Standard Library, because 1) it doesn't require any special compiler features or inspections, 2) this feauture is implemented on the platforms (java.time for JVM, Date for JS, chrono.h for C++). |
|
I like the Time being in the standard-library, as it gives a standard and hopefully more cross-platform libraries. I don't like it to have extension-functions on the Number-class as it polutes the Number-type. By combining this with a kotlin-library that is a non-standard library, the types can still be extension-functions for the developers who want that. By making this additional library an official Kotlin-library, the API will be the standard way to do this. Below are a couple of libraries:
I chose these as these overlap the extension-vals in this proposal a lot. |
@ilya-g. Indeed, there is a minor problem with that. Additionally, it is not always obvious how to construct that duration. When I don't know how to construct something, I type its name (like |
I did love the extension versions! It was short, human readable and easy to understand, even for non developers without a deep knowledge of val timeToFix = 2.days
val timeNow = Duration.days(2) |
Yeah we really miss them too. Usage of time in apps is so common that we really do not care about the namespace pollution. |
Wouldn't 2.toDuration(...) type of syntax give us the best of both worlds?
It doesn't pollute the namespace, is similar to toInt, toLong, .... and it's easily discoverable via the IDE
2.toDuration(days = true) or 2.toDuration(Duration.DAYS) or something similar alongside Duration.days which is not a bad syntax either
…On Apr 10 2021, at 11:04 am, Philip Wedemann ***@***.***> wrote:
I did love the extension versions! It was short, human readable and easy to understand, even for non developers without a deep knowledge of Duration. I will miss it.
val timeToFix = 2.days
val timeNow = Duration.days(2)
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub ***@***.***/0?redirect=https%3A%2F%2Fgithub.com%2FKotlin%2FKEEP%2Fissues%2F190%23issuecomment-817104477&recipient=cmVwbHkrQUFSTEM1U1c0WU1aSjRUUDJCNlZRUjU2UFZGUUxFVkJOSEhCV0FGTzdRQHJlcGx5LmdpdGh1Yi5jb20%3D), or unsubscribe ***@***.***/1?redirect=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAARLC5UJDCSCBIABSUMWFU3TIAIALANCNFSM4HVB6OYA&recipient=cmVwbHkrQUFSTEM1U1c0WU1aSjRUUDJCNlZRUjU2UFZGUUxFVkJOSEhCV0FGTzdRQHJlcGx5LmdpdGh1Yi5jb20%3D).
|
Or maybe all of the extension functions can be put into separate package or even just the |
While storing a duration value as a To counter that, we've decided to change the internal representation to a But by doing so, we had to sacrifice the sub-nanosecond precision of very short durations, and some implementation efficiency, especially in Kotlin/JS, which doesn't have an efficient Given that the internal value is now represented with a See the changes in the proposal text in this PR: #249 |
I 👍 on this, but in last weeks I've changed my mind. I like the current API, the new one seems too much verbose and reads really bad ("Duration (missing "in") seconds (missing "from") two" instead of very nice "two seconds" ). The namespace pollution wasn't ever a problem for me. |
Is the type designed to have its backing implementation changed to the |
@JakeWharton No, we don't have such plans. That Duration type has vastly different semantics, it's more like |
@ilya-g adding my voice to the negative feedback chorus regarding the extension properties on numeric types for Duration. I've found using them to be particularly Are there any plans to reconsider? I know this could be easily provided by a 3rd party or even Kotlin itself as a separate artifact, but I am curious about what the response is to the feedback from the community. |
I'd be happy to import the extension functions via an extra dependency, this is something I use a lot
…On Apr 21 2021, at 3:25 am, Eliezer Graber ***@***.***> wrote:
@ilya-g ***@***.***/0?redirect=https%3A%2F%2Fgithub.com%2Filya-g&recipient=cmVwbHkrQUFSTEM1VE81VEwyUU43UkpBNUlBQU42Uk5RQlBFVkJOSEhCV0FGTzdRQHJlcGx5LmdpdGh1Yi5jb20%3D) adding my voice to the negative feedback chorus regarding the extension properties on numeric types for Duration. I've found using them to be particularly fun, and they really help readability (delay(1.seconds) vs delay(Duration.seconds(1))).
Are there any plans to reconsider? I know this could be easily provided by a 3rd party or even Kotlin itself as a separate artifact, but I am curious about what the response is to the feedback from the community.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub ***@***.***/1?redirect=https%3A%2F%2Fgithub.com%2FKotlin%2FKEEP%2Fissues%2F190%23issuecomment-823707762&recipient=cmVwbHkrQUFSTEM1VE81VEwyUU43UkpBNUlBQU42Uk5RQlBFVkJOSEhCV0FGTzdRQHJlcGx5LmdpdGh1Yi5jb20%3D), or unsubscribe ***@***.***/2?redirect=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAARLC5T56EXYR4SJG7PPFL3TJYSRRANCNFSM4HVB6OYA&recipient=cmVwbHkrQUFSTEM1VE81VEwyUU43UkpBNUlBQU42Uk5RQlBFVkJOSEhCV0FGTzdRQHJlcGx5LmdpdGh1Yi5jb20%3D).
|
Just wanted to note my agreement with @eygraber— I would love to see it return, even as an independent dependency. :-) |
We can consider returning these extensions in a more limited scope form, for example in a companion object or in another nested object in Duration, as @KarelPeeters has proposed:
After that we can introduce a helper function for bringing this object into context, e.g.
|
For anyone that is interested, I made a library to provide the factory extensions (as well as simple extension properties to retrieve the Duration value as a Double). https://github.com/eygraber/kotlin-duration-extensions |
There is an issue on YouTrack about keeping the extension properties on numeric types. |
The compiler/linter can just show a warning/error in this case, no? |
This is a broader problem for wherever these literal suffix style extensions are used (CSS, Compose, etc), it might be a good idea to have an annotation that would limit them to being used only on literals (properties and whatnot could have a |
We have explored this option a bit more, and it turned out that even if we put the extension properties into the Duration companion object or into a nested object, the IDE is still smart enough that it finds them anyway and proposes them in completion, inserting an import of something like However, we could still pursue this approach if we bet on context sensitive resolution, KT-16768. If it was implemented, the compiler would resolve an expression like |
Thanks @ilya-g for the update! IMO the problem addressed here is the weaker of the 2. Autocomplete imprecision is a write-time issue, while reading the boilerplate The other problem (limiting such extensions to number literals) is a valid concern. But while we're waiting for such a feature (if it is ever implemented at all), it's always nice to give the option to the developer to choose between |
And this is also something which could probably be fixed in the IDE. The IDE should only show the completions when the import is there. |
I kinda disagree here. In Kotlin, a lot of things are extensions from other packages, and I rely a LOT on being able to auto-complete and auto-import from packages that I haven't imported yet. This would be quite a big inconvenience (at least to me) if only imported stuff were auto-completed. |
valid point. |
During the pre-stabilization review of the |
PSA: it seems the extensions have been moved to the companion object and the previously new ones were deprecated in 1.6 JetBrains/kotlin@9cbbc35#diff-a192d12cd63b9223cbb5ee2c2aadf51aa0e07cbdc057346f87643703a3984121 |
Wait, so I thought the whole point was to provide extension properties for literal numbers and factory functions for variables. The initial point that drove the change from extension properties to factory functions was that |
@joffrey-bion We thought that having both would be an overkill and bloat the API surface. |
This PR updates the proposal text to include a section about value class-based time marks returned by the default monotonic time source: #310 This change of the |
I would like to ask about serialization. The KEEP says:
This kind of makes sense to me - since this may depend on the I wonder how to deal with situations where serialization is needed. Especially on Android again, where an app process may get killed and recreated while the app is in the background. Without serialization there may be no way to keep a |
The proposal is updated with the description of comparable time marks introduced in Kotlin 1.8.0 in the experimental part of We're considering to stabilize the most of the remaining experimental API in |
This issue is to discuss the proposal to introduce
Duration
,TimeSource
,TimeMark
and time measurement API.The text was updated successfully, but these errors were encountered: