1. Overview
Duration and FiniteDuration both represent a time frame. Duration is an abstract class, and FiniteDuration is a final implementation of Duration. In this article, we’ll demonstrate use cases, transformations, and utility functions for these classes.
2. Initialization, Operators, and Comparisons
2.1. Initialization
We can instantiate a Duration object in many ways:
val duration1 = Duration("5 seconds")
val duration2 = Duration(5, TimeUnit.SECONDS)
val duration3 = 5.seconds
val duration4 = 250.millis
2.2. Operators
Duration supports the +, –**,** and * operators:
val addition = Duration("5 seconds") + Duration(5, TimeUnit.SECONDS) // 10 seconds
val subtraction = 5.seconds - 5.seconds // 0 seconds
val product = 5.seconds * 3 // 15 seconds
We can also use division, which has two variants:
val division1 = 5.seconds / 2 // 2500 milliseconds
val division2 = 5.seconds / Duration("5 seconds") // 1.0
A duration divided by a number results in a new duration that is essentially the dividend divided by the given number. On the other hand, a duration divided by a duration results in a number that describes how many times the divisor should be multiplied to result in the dividend.
2.3. Comparisons
Comparisons are also supported via the operators <, <=, >, and >=:
val result1 = 5.seconds < 1000.millis // false
val result2 = 5.seconds <= 1000.millis // false
val result3 = 5.seconds > 1000.millis // true
val result4 = 5.seconds >= 1000.millis // true
Another way to compare two given duration objects is the compare() function:
val result5 = 5.seconds.compare(1000.millis) // 1
The compare() function returns a positive number if the compared Duration is greater than the argument, negative if the compared Duration is less than the argument, and zero if the two Durations are equal.
3. Conversions and Utilities
3.1. TimeUnit Conversions
We can extract a Duration‘s time unit with the related helper functions:
20.days.toMillis // 1728000000L
20.days.toSeconds // 1728000L
20.days.toHours // 480L
20.days.toMinutes // 28800L
3.2. Deadline Conversion
The Deadline describes a time-based countdown. Function fromNow converts a Duration into a Deadline:
val deadline = 100.millis.fromNow // Deadline(1302934436354298 nanoseconds)
val hasTimeLeft = deadline.hasTimeLeft() // true
val isOverdue = deadline.isOverdue() // false
Thread.sleep(100L) // sleeping for the duration of the deadline
val hasTimeLeft2 = deadline.hasTimeLeft() // false
val isOverdue2 = deadline.isOverdue() // true
3.3. Coarsest
Function toCoarsest returns the Duration to the coarsest unit possible:
5000.millis.toCoarsest // 5 seconds
5.seconds.toCoarsest // 5 seconds
4. Converting Java Duration Into Scala FiniteDuration
Since both Java and Scala code compiles to the same bytecode and runs on the JVM, we can use Java classes and libraries in our Scala code and vice versa. In this section, we’ll learn how to transform the Java Duration object into a Scala FiniteDuration object.
4.1. Using FiniteDuration.apply()
Let’s start by creating the javaDuration object with a duration of one hour:
val javaDuration = Duration.of(1, ChronoUnit.HOURS)
Now, let’s see how we can use the apply(length, unit) method to create the FiniteDuration object from javaDuration:
val expected = scala.concurrent.duration.Duration(1, HOURS)
val actual = FiniteDuration.apply(javaDuration.toHours, TimeUnit.HOURS)
assert(expected == actual)
We must note that the apply() method internally creates the object using the FiniteDuration(length, unit) constructor.
4.2. Using Duration.fromNanos()
Now, we’ll learn another convenient approach where we convert the Java Duration object to nanoseconds and then use the fromNanos method to construct a FiniteDuration object.
Let’s begin by writing the asFiniteDuration() method that accepts an argument of java.time.Duration type :
def asFiniteDuration(javaDuration: java.time.Duration) =
scala.concurrent.duration.Duration.fromNanos(javaDuration.toNanos)
Further, let’s verify that the asFiniteDuration() method is working as expected:
val javaDuration = Duration.of(1, ChronoUnit.HOURS)
val actual = asFiniteDuration(javaDuration)
val expected = scala.concurrent.duration.Duration(1, HOURS)
assert(expected == actual)
It looks like we got this right.
4.3. Using the scala-java8-compat Library
Moving on, let’s explore the toScala() method from the scala-java8-compat library that makes it quite convenient to convert a Java Duration object to Scala’s FiniteDuration object.
Let’s start by adding the dependency for the scala-java8-compat library in the project’s build.sbt file:
libraryDependencies += "org.scala-lang.modules" %% "scala-java8-compat" % "1.0.2"
Now, we’re ready to use the toScala method to convert a Java Duration to a Scala FiniteDuration object:
val javaDuration = Duration.of(1, ChronoUnit.HOURS)
val actual = javaDuration.toScala
val expected = scala.concurrent.duration.Duration(1, HOURS)
assert(expected == actual)
That’s it. We’ve successfully learned one more approach to solving our use case.
4.4. Using the Typesafe Config Library
Applications often rely upon configuration values to govern their behavior. So, let’s explore how to read the duration value from a configuration file and then transform it to a Scala Duration.
First, we’ll require the ConfigFactory class from the typesafe configuration library to read the duration value from a configuration file. So, let’s add the dependency for the typesafe configuration library in the project’s build.sbt file:
libraryDependencies += "com.typesafe" % "config" % "1.2.0"
Next, let’s go ahead to define the timeout.value property in the resources/application.conf configuration file:
$ cat resources/application.conf
timeout {
value = 60m
}
Now, we can write the asFiniteDurationFromConf() method that loads the timeout.value property from the application.conf file and then transforms it into a FiniteDuration object:
def asFiniteDurationFromConf() = {
Duration.fromNanos(
ConfigFactory
.load()
.getDuration("timeout.value", TimeUnit.NANOSECONDS)
)
}
We must note that we used the ConfigFactory.load() method to load the timeout.value property. Then, we used the getDuration() method to get the Java Duration object. Lastly, we used the fromNanos() method to convert it into a FiniteDuration object.
Finally, let’s see the asFiniteDurationFromConf() method in action:
val actual = asFiniteDurationFromConf()
val expected = scala.concurrent.duration.Duration(1, TimeUnit.HOURS)
assert(expected == actual)
Perfect! It looks like we’ve got this right.
5. Conclusion
In this article, we presented basic usage, transformations, and utility functions for the Duration and FiniteDuration classes. Additionally, we learned several ways to convert the Java Duration object to the Scala FiniteDuration object.
As always, the complete source code for the tutorial is available over on GitHub.