Skip to content

Commit

Permalink
Duration converters #85
Browse files Browse the repository at this point in the history
  • Loading branch information
johanandren committed Jan 22, 2017
1 parent 2030bfa commit 6ecc420
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 0 deletions.
61 changes: 61 additions & 0 deletions src/main/scala/scala/compat/java8/DurationConverters.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2012-2017 Typesafe Inc. <http://www.typesafe.com>
*/
package scala.compat.java8

import java.time.temporal.ChronoUnit
import java.util.concurrent.TimeUnit
import java.time.{Duration => JavaDuration}

import scala.concurrent.duration.{FiniteDuration, Duration => ScalaDuration}


/**
* This class contains static methods which convert between Java Durations
* and the durations from the Scala concurrency package. This is useful when mediating between Scala and Java
* libraries with asynchronous APIs where timeouts for example are often expressed as durations.
*/
object DurationConverters {

/**
* Transform a Java duration into a Scala duration. If the nanosecond part of the Java duration is zero the returned
* duration will have a time unit of seconds and if there is a nanoseconds part the Scala duration will have a time
* unit of nanoseconds.
*
* @throws IllegalArgumentException If the given Java Duration is out of bounds of what can be expressed with the
* Scala Durations.
*/
final def toScala(duration: java.time.Duration): scala.concurrent.duration.Duration = {
if (duration.getNano == 0) {
if (duration.getSeconds == 0) ScalaDuration.Zero
else FiniteDuration(duration.getSeconds, TimeUnit.SECONDS)
} else {
FiniteDuration(
duration.getSeconds * 1000000000 + duration.getNano,
TimeUnit.NANOSECONDS
)
}
}

/**
* Transform a Scala duration into a Java duration. Note that the Scala duration keeps the time unit it was created
* with while a Java duration always is a pair of seconds and nanos, so the unit it lost.
*
* @throws IllegalArgumentException If the Scala duration express a amount of time for the time unit that
* a Java Duration can not express (infinite durations and undefined durations)
*/
final def toJava(duration: scala.concurrent.duration.Duration): java.time.Duration = {
require(duration.isFinite(), s"Got [$duration] but only finite Scala durations can be expressed as a Java Durations")
if (duration.length == 0) JavaDuration.ZERO
else duration.unit match {
case TimeUnit.NANOSECONDS => JavaDuration.ofNanos(duration.length)
case TimeUnit.MICROSECONDS => JavaDuration.of(duration.length, ChronoUnit.MICROS)
case TimeUnit.MILLISECONDS => JavaDuration.ofMillis(duration.length)
case TimeUnit.SECONDS => JavaDuration.ofSeconds(duration.length)
case TimeUnit.MINUTES => JavaDuration.ofMinutes(duration.length)
case TimeUnit.HOURS => JavaDuration.ofHours(duration.length)
case TimeUnit.DAYS => JavaDuration.ofDays(duration.length)
}
}

}
112 changes: 112 additions & 0 deletions src/test/scala/scala/compat/java8/DurationConvertersTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package scala.compat.java8

import org.junit.Test
import org.junit.Assert._
import java.time.{Duration => JavaDuration}

import scala.util.Try

class DurationConvertersTest {

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

@Test
def scalaNanosToJavaDuration(): Unit = {
Seq[(Long, (Long, Int))](
(Long.MinValue + 1) -> (-9223372037L, 145224193), // because java duration nanos are offset from the "wrong" direction
-1000000001L -> (-2, 999999999),
-1L -> (-1, 999999999),
0L -> (0, 0),
1L -> (0, 1),
1000000001L -> (1,1),
Long.MaxValue -> (9223372036L, 854775807)
).foreach { case (n, (expSecs, expNanos)) =>
val result = toJava(n.nanos)
assertEquals(s"toJava($n nanos) -> $expSecs s)", expSecs, result.getSeconds)
assertEquals(s"toJava($n nanos) -> $expNanos n)", expNanos, result.getNano)
}
}

@Test
def scalaMilliSecondsToJavaDuration(): Unit = {
Seq[(Long, (Long, Int))](
-9223372036854L -> (-9223372037L, 146000000),
-1L -> (-1L, 999000000),
0L -> (0L, 0),
1L -> (0L, 1000000),
9223372036854L -> (9223372036L, 854000000)
).foreach { case (n, (expSecs, expNanos)) =>
val result = toJava(n.millis)
assertEquals(s"toJava($n millis) -> $expSecs s)", expSecs, result.getSeconds)
assertEquals(s"toJava($n millis) -> $expNanos n)", expNanos, result.getNano)
}
}

@Test
def scalaMicroSecondsToJavaDuration(): Unit = {
Seq[(Long, (Long, Int))](
-9223372036854775L -> (-9223372037L, 145225000),
-1L -> (-1L, 999999000),
0L -> (0L, 0),
1L -> (0L, 1000),
9223372036854775L -> (9223372036L, 854775000)
).foreach { case (n, (expSecs, expNanos)) =>
val result = toJava(n.micros)
assertEquals(s"toJava($n micros) -> $expSecs s)", expSecs, result.getSeconds)
assertEquals(s"toJava($n micros) -> $expNanos n)", expNanos, result.getNano)
}
}

@Test
def scalaSecondsToJavaDuration(): Unit = {
Seq[(Long, (Long, Int))](
-9223372036L -> (-9223372036L, 0),
-1L -> (-1L, 0),
0L -> (0L, 0),
1L -> (1L, 0),
9223372036L -> (9223372036L, 0)
).foreach { case (n, (expSecs, expNanos)) =>
val result = toJava(n.seconds)
assertEquals(expSecs, result.getSeconds)
assertEquals(expNanos, result.getNano)
}
}


@Test
def javaSecondsToScalaDuration(): Unit = {
Seq[Long](-9223372036L, -1L, 0L, 1L, 9223372036L).foreach { n =>
assertEquals(n, toScala(JavaDuration.ofSeconds(n)).toSeconds)
}
}


@Test
def javaNanosPartToScalaDuration(): Unit = {
Seq[Long](Long.MinValue + 1L, -1L, 0L, 1L, Long.MaxValue).foreach { n =>
assertEquals(n, toScala(JavaDuration.ofNanos(n)).toNanos)
}
}

@Test
def unsupportedScalaDurationThrows(): Unit = {
Seq(Duration.Inf, Duration.MinusInf, Duration.Undefined).foreach { d =>
val res = Try { toJava(d) }
assertTrue(s"Expected exception for $d but got success", res.isFailure)
}
}

@Test
def unsupportedJavaDurationThrows(): Unit = {
Seq(JavaDuration.ofSeconds(-9223372037L), JavaDuration.ofSeconds(9223372037L)).foreach { d =>
val res = Try { toScala(d) }
assertTrue(s"Expected exception for $d but got success", res.isFailure)
}
}


}

0 comments on commit 6ecc420

Please sign in to comment.