I want to use java.time.LocalDate
and java.time.LocalDateTime
with an implicit Ordered
like:
val date1 = java.time.Loc
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:
implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
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 to isLeapYear()
- and the resulting value isn't cached, so it gets recalculated with each comparison, which might be expensive if there were a large number of LocalDate
s to be sorted.
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 of java.lang.Comparable to scala.math.Ordering that scala.math.Ordering.Implicits._
offers, if only it worked! But as you said, it doesn't, because LocalDate
inherits from Comparable<ChronoLocalDate>
instead of Comparable<LocalDate>
. One way to take advantage of it might be...
import scala.math.Ordering.Implicits._
implicit val localDateOrdering: Ordering[LocalDate] =
Ordering.by(identity[ChronoLocalDate])
...which lets you order LocalDate
s by casting them to ChronoLocalDate
s, and using the Ordering[ChronoLocalDate]
that scala.math.Ordering.Implicits._
gives you!
The lambda syntax for SAM types, introduced with Scala 2.12, can make really short work of constructing a new Ordering
:
implicit val localDateOrdering: Ordering[LocalDate] = _ compareTo _
...and I think this ends up being my personal favourite! Concise, still fairly clear, and using (I think) the best-performing comparison method.
Here's my solution for java.time.LocalDateTime
implicit val localDateTimeOrdering: Ordering[LocalDateTime] =
Ordering.by(x => x.atZone(ZoneId.of("UTC")).toEpochSecond)
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, to Long
:
implicit val localDateOrdering: Ordering[LocalDate] = Ordering.by(_.toEpochDay)
Here is the solution that I use:
define two implicits. The first one for making an Ordering[LocalDate]
available. And a second one for giving LocalDate
a compare
method which comes in very handy. I typically put these in package objects in a library I can just include where I need them.
package object net.fosdal.oslo.odatetime {
implicit val orderingLocalDate: Ordering[LocalDate] = Ordering.by(d => (d.getYear, d.getDayOfYear))
implicit class LocalDateOps(private val localDate: LocalDate) extends AnyVal with Ordered[LocalDate] {
override def compare(that: LocalDate): Int = Ordering[LocalDate].compare(localDate, that)
}
}
with both of these defined you can now do things like:
import net.fosdal.oslo.odatetime._
val bool: Boolean = localDate1 < localDate1
val localDates: Seq[LocalDate] = ...
val sortedSeq = localDates.sorted
Alternatively... you could just use my library (v0.4.3) directly. see: https://github.com/sfosdal/oslo
A slight modification to the implicit ordered
should do the trick.
type AsComparable[A] = A => Comparable[_ >: A]
implicit def ordered[A: AsComparable]: Ordering[A] = new Ordering[A] {
def compare(x: A, y: A): Int = x compareTo y
}
Now every type which is comparable to itself or to a supertype of itself should have an Ordering
.