Make Duration and FiniteDuration general?

Perhaps changing the comment would be better than doing anything else. What I’ve seen at work is using Scala’s Durations as timestamps, which is obviously wrong, but often works.

Example where abusing Scala’s Duration doesn’t lead to an exception https://scastie.scala-lang.org/ItsiCnvYQLipxuAxWdTXDA

import scala.concurrent.duration._

val date1 = java.time.LocalDateTime.of(2018, 3, 14, 13, 58, 58)
val date2 = java.time.LocalDateTime.of(2019, 6, 11, 1, 5, 8)

val abusedDuration1 = date1.toInstant(java.time.ZoneOffset.UTC).toEpochMilli().millis
val abusedDuration2 = date2.toInstant(java.time.ZoneOffset.UTC).toEpochMilli().millis

val duration = abusedDuration2 - abusedDuration1
println(duration.toCoarsest)

If we shift dates by 1000 years to the past then code will fail despite the final duration being the same

import scala.concurrent.duration._

// dates shifted back by 1000 years
val date1 = java.time.LocalDateTime.of(1018, 3, 14, 13, 58, 58)
val date2 = java.time.LocalDateTime.of(1019, 6, 11, 1, 5, 8)

val abusedDuration1 = date1.toInstant(java.time.ZoneOffset.UTC).toEpochMilli().millis
val abusedDuration2 = date2.toInstant(java.time.ZoneOffset.UTC).toEpochMilli().millis

val duration = abusedDuration2 - abusedDuration1
println(duration.toCoarsest)

The solution is to not abuse Scala’s Duration and instead use dedicated method for computing durations https://scastie.scala-lang.org/SKURqgjERNef5ripAnXmcw

import scala.concurrent.duration._
import scala.jdk.DurationConverters._

val date1 = java.time.LocalDateTime.of(1018, 3, 14, 13, 58, 58)
val date2 = java.time.LocalDateTime.of(1019, 6, 11, 1, 5, 8)

val javaDuration = java.time.Duration.between(date1, date2)

val duration = javaDuration.toScala
println(duration.toCoarsest)

Personally I’ve been using scala Duration for most of my career but recently I try to not use it anymore. java.time.Duration is more reliable, more general and comes with broader set of functionalities. I wish there was more scala-like api for java.time but thats all.

I think trying to have fully-blown scala equivalent of java.time.Duration is a tremendous effort and also one that is not really needed.

Just wanted to say, sorry I was being a bit flippant when I wrote that. The DSL that allows 20.millis isn’t of much value to me personally but as @lloydmeta says there are other operations as well that would be useful on top of java.time.Duration: namely arithmetic ops (like +) and comparison ops (like >=). (I wonder if there’s anymore.) I think if we could transition towards Scala duration wrapping java.time.Duration with extension methods and maybe some additional DSL for things like creation that make it nice to use in Scala that would be amazing.

I know @sjrd is concerned at the maintenance of java.time.Duration in javalib in Scala.JS but the way I see it, it’s no different than anything else of Java’s that is supported in Scala.JS. In the same way that some Java classes are partially supported in Scala.JS – and by that I mean method 1 links and method 2 doesn’t – we could do the same thing with java.time.Duration: have a very simple implementation for the easy duration stuff, and then anything larger especially timezone-related could just not be implemented and fail to link if used. Then if people want a fuller implementation they can use one of the existing scalajs-java-time libraries that allow java.time stuff to link properly.

1 Like

This was already suggested above by @lihaoyi, and I replied that this is not possible. If someone adds the opt-in extension, they’ll end up with 2 java.time.Durations on their classpath: one correct and one broken. But which one will come first on the classpath will be pretty much random, so you’ll randomly get linking errors or not.

If someone adds the opt-in extension, they’ll end up with 2 java.time.Duration s on their classpath: one correct and one broken. But which one will come first on the classpath will be pretty much random, so you’ll randomly get linking errors or not.

That sounds like a really, really easy problem to solve. There’s been a general trend towards reproducible builds and build determinism. If SBT literally doesn’t have a means of maintaining, or allowing specification of classpath order, that’s a bigger problem independent of our discussion that should be solved regardless of whatever we decide to do with Durations. It shouldn’t be considered a blocker, that’s for sure. If anything, I think it should be made a pillar in the strategy for how Scala.JS maintains javalib in that the Scala.JS team should be able to provide minimal implementations and know that the ability exists for users to beef them up (and relieve the SJS team of the burden) if desirable.

1 Like

From what I can see Duration only has 2 dependencies: Temporal and TemporalUnit, both of which are interfaces. For TemporalUnit it seems doable to implement its ChronoUnit implementation. For Temporal scala.js could just provide the interface and leave it to third party libraries to provide the implementations.