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..02a7332914d --- /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 minus infinite 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()) + } + + } +} 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..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 @@ -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 } @@ -23,13 +25,22 @@ 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) } 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..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 @@ -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 } @@ -27,13 +28,23 @@ 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) } 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() + } } }