A Kotlin Time DSL for Java 8 Time

A Watch

Dealing with time is often hard. Managing time in Java however became a lot easier with the introduction of Joda Time, and later the port of Joda Time to Java 8. One of the effects this has is that as a result, we can do easy time manipulation using Duration methods. And now things can be even easier when using a Kotlin DSL consisting of Extension Methods and Operator Overloading. In this blogpost I’ll explain how to create such a DSL.

A history of time in Java

Historically, many countries have invented their own solution to measuring time. A lot of these choices have been affected by political and religious reasons. To solve this challenge, the ISO-8601 standard was created.

The ISO standard defines a set of rules for handling dates and times between computer systems in a standardised way, and includes standards for formatting time, dates and timezones.

While the ISO-8601 is pretty rich, the standard Java Calendar and Date classes are quite poor in functionality and tricky to use. As an alternative, the Joda Time library was created and the first release was published in November 2005. The Joda Time adoption resulted in the integration of the library into the standard Java SDK. With a few changes it was ported over into the standard, replacing the old way of handling date and time in the SDK.

Java 8 Time

In Java 8, and example of the Java 8 Time API can be seen in the following code:

LocalDate past = LocalDate.now().minus(1, ChronoUnit.HOURS).plus(2, ChronoUnit.SECONDS);

In this code, we’re using the LocalDate class to remove 1 hour from the current time and adds two seconds. Other options, such as days, months, weeks are possible. They allow us to more easily work with and manipulate dates and times.

However, when using Kotlin, we can do even better. The above code is slightly verbose, and requires a bit of mental processing to understand. Wouldn’t it be nicer if we could rewrite the above with something like:

val past = now - 1 hour plus 2 seconds

Well, it turns out that with Kotlin, we can’t. But we can get very close!

Kotlin extension methods and operator overloading

To implement our simple time DSL, we’re going to use two Kotlin features: extension methods and operator overloading. Extension methods are a way to add functionality to classes without extending them. Operator overloading allows you to delegate operators such as plus (+), minus (-) and many others to methods which are similarly named. The combination of the two allows us to create a simple Kotlin Time DSL, which looks like this:

val past = now() - 1.hour + 2.seconds

The numbers in the above example are instances of Kotlin’s Integer, or Int. Using extension methods however, we get the option to add the hours and seconds property to the Int class. Note that hour here is not a typo; we’ll use method aliases to rename the method and make things even more readable.

// time/TimeExtension.kt
package time

val Int.seconds: Duration
    get() {
        return Duration.ofSeconds(this.toLong())
    }

val Int.hours: Duration
    get() {
        return Duration.ofHours(this.toLong())
    }    

The above block of code defines two extension methods: seconds and hours. By extending Int with a property of seconds, it’s possible to convert the Intto a duration using the ofSeconds method. The this in the above example refers to the number (in this case 2) the extension method is called on.

To use the above code, you can do the following:

import time.*                                 // import the time DSL
import java.time.LocalDate.now                // normal static import
import time.hours as hour                     // method alias

fun main() {
    val past = now() - 1.hour + 2.seconds
}

This imports our DSL from the time package. However, since we want to use 1 hour instead of 1 hours, we use an import alias to rename the hours to hour.

Note: Kotlin still allows us to import both time.hour as well as time.hours, but due to a bug in IntelliJ, be aware that pressing Optimise Imports might remove one of the imports. This is especially dangerous when doing an Optimise Import on commit (which is a great way to break your code!).

I hope the above gives a bit more information on how to start writing Kotlin DSL. The complete code can be found on Github, and as always, comments welcome!

Update: After writing this blogpost, one of my colleagues (Jake) informed me of a much bigger collection of extension methods for Kotlin. I haven’t gone through all of them, but this Github repository seems to have a wide range of examples, and includes something like the above DSL too.

Leave a Reply

Your email address will not be published. Required fields are marked *