From 1780dca7f5c43aead8fa2432f7d819016b5a11af Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 14 Sep 2025 13:11:43 +0100 Subject: [PATCH 1/6] convert non-finite durations --- .../util/JavaDurationConvertersSpec.scala | 66 +++++++++++++++++++ .../pekko/util/JavaDurationConverters.scala | 9 ++- .../pekko/util/JavaDurationConverters.scala | 7 +- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala diff --git a/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala b/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala new file mode 100644 index 00000000000..f6bc9b270f4 --- /dev/null +++ b/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.pekko.util + +import java.time.temporal.ChronoUnit + +import org.apache.pekko +import pekko.util.JavaDurationConverters._ +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec + +import scala.concurrent.duration._ + +class JavaDurationConvertersSpec extends AnyWordSpec with Matchers { + + "JavaDurationConverters" must { + + "convert from java.time.Duration to scala.concurrent.duration.FiniteDuration" in { + val javaDuration = java.time.Duration.ofSeconds(5, 3) + val scalaDuration: FiniteDuration = javaDuration.asScala + scalaDuration should ===(5.seconds + 3.nanoseconds) + } + + "convert from scala.concurrent.duration.FiniteDuration to java.time.Duration" in { + val scalaDuration: FiniteDuration = 5.seconds + 3.nanoseconds + val javaDuration: java.time.Duration = scalaDuration.asJava + javaDuration should ===(java.time.Duration.ofSeconds(5, 3)) + } + + "convert from Duration.Zero to java.time.Duration" in { + val javaDuration: java.time.Duration = Duration.Zero.asJava + javaDuration should ===(java.time.Duration.ZERO) + } + + "convert infinite duration to java.time.Duration" in { + val scalaDuration: Duration = Duration.Inf + scalaDuration.asJava should ===(ChronoUnit.FOREVER.getDuration) + } + + "convert minfinite duration to java.time.Duration" in { + val scalaDuration: Duration = Duration.MinusInf + scalaDuration.asJava should ===(ChronoUnit.FOREVER.getDuration().negated()) + } + + "convert undefined duration to java.time.Duration" in { + val scalaDuration: Duration = Duration.Undefined + scalaDuration.asJava should ===(ChronoUnit.FOREVER.getDuration()) + } + + } +} \ No newline at end of file diff --git a/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala index 5588faa3897..c33559e9074 100644 --- a/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala @@ -12,7 +12,9 @@ */ package org.apache.pekko.util + import java.time.{ Duration => JDuration } +import java.time.temporal.ChronoUnit import scala.concurrent.duration.{ Duration, FiniteDuration } @@ -30,6 +32,11 @@ private[pekko] object JavaDurationConverters { } final implicit class ScalaDurationOps(val self: Duration) extends AnyVal { - @inline def asJava: JDuration = JDuration.ofNanos(self.toNanos) + @inline def asJava: JDuration = self match { + case fd: FiniteDuration => JDuration.ofNanos(fd.toNanos) + case Duration.Inf => ChronoUnit.FOREVER.getDuration() + case Duration.MinusInf => ChronoUnit.FOREVER.getDuration().negated() + case _ => ChronoUnit.FOREVER.getDuration() + } } } diff --git a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala index d8f941dcc3c..039f289f89b 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala @@ -34,6 +34,11 @@ private[pekko] object JavaDurationConverters { } final implicit class ScalaDurationOps(val self: Duration) extends AnyVal { - def asJava: JDuration = JDuration.ofNanos(self.toNanos) + def asJava: JDuration = self match { + case fd: FiniteDuration => JDuration.ofNanos(fd.toNanos) + case Duration.Inf => ChronoUnit.FOREVER.getDuration() + case Duration.MinusInf => ChronoUnit.FOREVER.getDuration().negated() + case _ => ChronoUnit.FOREVER.getDuration() + } } } From 8fc7855b64ed28dc0cba2a38db6eda08ebd0a42a Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 14 Sep 2025 13:16:28 +0100 Subject: [PATCH 2/6] Update JavaDurationConvertersSpec.scala --- .../org/apache/pekko/util/JavaDurationConvertersSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala b/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala index f6bc9b270f4..0e0ae8062d7 100644 --- a/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala +++ b/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala @@ -63,4 +63,4 @@ class JavaDurationConvertersSpec extends AnyWordSpec with Matchers { } } -} \ No newline at end of file +} From 7f28478bce554d8fb1c434ded7d2cf63d8ae7ef7 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 14 Sep 2025 13:21:23 +0100 Subject: [PATCH 3/6] scalafmt --- .../org/apache/pekko/util/JavaDurationConverters.scala | 6 +++--- .../org/apache/pekko/util/JavaDurationConverters.scala | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala index c33559e9074..696f00ef6eb 100644 --- a/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala @@ -34,9 +34,9 @@ private[pekko] object JavaDurationConverters { final implicit class ScalaDurationOps(val self: Duration) extends AnyVal { @inline def asJava: JDuration = self match { case fd: FiniteDuration => JDuration.ofNanos(fd.toNanos) - case Duration.Inf => ChronoUnit.FOREVER.getDuration() - case Duration.MinusInf => ChronoUnit.FOREVER.getDuration().negated() - case _ => ChronoUnit.FOREVER.getDuration() + case Duration.Inf => ChronoUnit.FOREVER.getDuration() + case Duration.MinusInf => ChronoUnit.FOREVER.getDuration().negated() + case _ => ChronoUnit.FOREVER.getDuration() } } } diff --git a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala index 039f289f89b..0edb4fda466 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala @@ -36,9 +36,9 @@ private[pekko] object JavaDurationConverters { final implicit class ScalaDurationOps(val self: Duration) extends AnyVal { def asJava: JDuration = self match { case fd: FiniteDuration => JDuration.ofNanos(fd.toNanos) - case Duration.Inf => ChronoUnit.FOREVER.getDuration() - case Duration.MinusInf => ChronoUnit.FOREVER.getDuration().negated() - case _ => ChronoUnit.FOREVER.getDuration() + case Duration.Inf => ChronoUnit.FOREVER.getDuration() + case Duration.MinusInf => ChronoUnit.FOREVER.getDuration().negated() + case _ => ChronoUnit.FOREVER.getDuration() } } } From 66b8f7263f09b2bbc1beead6e7860d60f746d060 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 14 Sep 2025 13:58:49 +0100 Subject: [PATCH 4/6] Update JavaDurationConverters.scala --- .../scala-3/org/apache/pekko/util/JavaDurationConverters.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala index 0edb4fda466..ff968809f4f 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala @@ -14,6 +14,7 @@ package org.apache.pekko.util import java.time.{ Duration => JDuration } +import java.time.temporal.ChronoUnit import scala.concurrent.duration.{ Duration, FiniteDuration } From 350db6ae556fb9c2b700741f77999ab124bfe0d2 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 15 Sep 2025 11:58:10 +0100 Subject: [PATCH 5/6] doc --- .../org/apache/pekko/util/JavaDurationConvertersSpec.scala | 2 +- .../org/apache/pekko/util/JavaDurationConverters.scala | 4 ++++ .../org/apache/pekko/util/JavaDurationConverters.scala | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala b/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala index 0e0ae8062d7..02a7332914d 100644 --- a/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala +++ b/actor-tests/src/test/scala/org/apache/pekko/util/JavaDurationConvertersSpec.scala @@ -52,7 +52,7 @@ class JavaDurationConvertersSpec extends AnyWordSpec with Matchers { scalaDuration.asJava should ===(ChronoUnit.FOREVER.getDuration) } - "convert minfinite duration to java.time.Duration" in { + "convert minus infinite duration to java.time.Duration" in { val scalaDuration: Duration = Duration.MinusInf scalaDuration.asJava should ===(ChronoUnit.FOREVER.getDuration().negated()) } diff --git a/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala index 696f00ef6eb..f257eae70a2 100644 --- a/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-2/org/apache/pekko/util/JavaDurationConverters.scala @@ -25,9 +25,13 @@ import org.apache.pekko.annotation.InternalStableApi */ @InternalStableApi private[pekko] object JavaDurationConverters { + // Scala FiniteDurations only support up to approx 252 years + // Java Durations support much larger durations + // this method will throw an java.lang.IllegalArgumentException if the Java Duration is too large @inline def asFiniteDuration(duration: JDuration): FiniteDuration = duration.asScala final implicit class JavaDurationOps(val self: JDuration) extends AnyVal { + // see note on asFiniteDuration @inline def asScala: FiniteDuration = Duration.fromNanos(self.toNanos) } diff --git a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala index ff968809f4f..796398f0545 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala @@ -28,9 +28,14 @@ private[pekko] object JavaDurationConverters { // Ideally this should have the Scala 3 inline keyword but then Java sources are // unable to call this method, see https://github.com/lampepfl/dotty/issues/19346 + + // Scala FiniteDurations only support up to approx 252 years + // Java Durations support much larger durations + // this method will throw an java.lang.IllegalArgumentException if the Java Duration is too large def asFiniteDuration(duration: JDuration): FiniteDuration = duration.asScala final implicit class JavaDurationOps(val self: JDuration) extends AnyVal { + // see note on asFiniteDuration def asScala: FiniteDuration = Duration.fromNanos(self.toNanos) } From e73804aa0faec815abebd31944bdba7d6d95eac1 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 15 Sep 2025 13:22:08 +0100 Subject: [PATCH 6/6] Update JavaDurationConverters.scala --- .../scala-3/org/apache/pekko/util/JavaDurationConverters.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala index 796398f0545..974684f42e2 100644 --- a/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala +++ b/actor/src/main/scala-3/org/apache/pekko/util/JavaDurationConverters.scala @@ -28,7 +28,7 @@ private[pekko] object JavaDurationConverters { // Ideally this should have the Scala 3 inline keyword but then Java sources are // unable to call this method, see https://github.com/lampepfl/dotty/issues/19346 - + // Scala FiniteDurations only support up to approx 252 years // Java Durations support much larger durations // this method will throw an java.lang.IllegalArgumentException if the Java Duration is too large