I want to use java.time.LocalDate
and java.time.LocalDateTime
with an implicit Ordered
like:
val date1 = java.time.LocalDate.of(2000, 1, 1)
val date2 = java.time.LocalDate.of(2010, 10, 10)
if (date1 < date2) ...
import scala.math.Ordering.Implicits._
doesn't work, because LocalDate
inherits from Comparable<ChronoLocalDate>
instead of Comparable<LocalDate>
.
How can I write my own imlicit Orderd to use <, <=, >, >= operators/methods to compare LocalDate
's?
Edit:
I found a way with use of an implicit class:
import java.time.{LocalDate}
object MyDateTimeUtils {
implicit class MyLocalDateImprovements(val ld: LocalDate)
extends Ordered[LocalDate] {
def compare(that: LocalDate): Int = ld.compareTo(that)
}
}
// Test
import MyDateTimeUtils._
val d1 = LocalDate.of(2016, 1, 1)
val d2 = LocalDate.of(2017, 2, 3)
if (d1 < d2) println("d1 is less than d2")
But I would prefer a way like Scala is doing for all Java classes which implements Comparable<T>
. You just have to
import scala.math.Ordering.Implicits._
in your code. Scala implements it like:
implicit def ordered[A <% Comparable[A]]: Ordering[A] = new Ordering[A] {
def compare(x: A, y: A): Int = x compareTo y
}
But unfortunately LocalDate
implements Comparable<ChronoLocalDate>
instead of Comparable<LocalDate>
. I couldn't find a way to modify the above implicit ordered method to fit with LocalDate
/Comparable<ChronoLocalDate>
. Any idea?
Here's my solution for
java.time.LocalDateTime
Here is the solution that I use:
define two implicits. The first one for making an
Ordering[LocalDate]
available. And a second one for givingLocalDate
acompare
method which comes in very handy. I typically put these in package objects in a library I can just include where I need them.with both of these defined you can now do things like:
Alternatively... you could just use my library (v0.4.3) directly. see: https://github.com/sfosdal/oslo
Comparing by
LocalDate.toEpochDay
is clear, though maybe relatively slow...The answer by @tzach-zohar is great, in that it's most obvious what is going on; you're ordering by Epoch Day:
However, if you look at the implementation of
toEpochDay
you'll see that it's relatively involved - 18 lines of code, with 4 divisions, 3 conditionals and a call toisLeapYear()
- and the resulting value isn't cached, so it gets recalculated with each comparison, which might be expensive if there were a large number ofLocalDate
s to be sorted....making use of
LocalDate.compareTo
is probably more performant...The implementation of
LocalDate.compareTo
is simpler - just 2 conditionals, no division - and it's what you'd be getting with that implicit conversion ofjava.lang.Comparable
toscala.math.Ordering
thatscala.math.Ordering.Implicits._
offers, if only it worked! But as you said, it doesn't, becauseLocalDate
inherits fromComparable<ChronoLocalDate>
instead ofComparable<LocalDate>
. One way to take advantage of it might be......which lets you order
LocalDate
s by casting them toChronoLocalDate
s, and using theOrdering[ChronoLocalDate]
thatscala.math.Ordering.Implicits._
gives you!...and in the end it looks best with the lambda syntax for SAM types
The lambda syntax for SAM types, introduced with Scala 2.12, can make really short work of constructing a
new Ordering
:...and I think this ends up being my personal favourite! Concise, still fairly clear, and using (I think) the best-performing comparison method.
You can use
Ordering.by
to create ordering for any type, given a function from that type to something that already has an Ordering - in this case, toLong
:A slight modification to the implicit
ordered
should do the trick.Now every type which is comparable to itself or to a supertype of itself should have an
Ordering
.