Compare date and time in Elixir with protocols
The Calendar library has functionality for comparing dates and datetimes.
The Calendar.Date
module has a function for getting the difference
between two dates. You can for instance pass date tuples
or Calendar structs to the Calendar.Date.diff/2
funciton. This will return the difference in days
between the two dates.
Ecto is another library that has structs for dates and datetimes.
To get an Ecto date struct you can use the Ecto.Date.from_erl
function like this:
What if you could use the same function in the calendar library to compare
Ecto.Date
structs? You can!
The Calecto library contains a protocol implementation that makes this possible. It looks like this:
This means that if you have added Calecto to your Elixir project, you can pass Ecto.Date structs to the same function:
In the above example an Ecto.Date struct is compared to a tuple.
Whatever implements the ContainsDate
protocol can be used as parameters.
We can also compare two Ecto.Date structs:
All the other functions
But that is not all. diff/2
is just one of the functions in the Calendar.Date
module.
Any place where a date is expected you can use Ecto.Date
structs, Calendar.Date
structs
or tuples as you please. In addition to that you can implement the Calendar.ContainsDate
for your own custom
date types.
Those are just a few examples of the available functionality.
Comparing Ecto.DateTime structs
Because Ecto.DateTime
structs have datetimes, but no timezone information
they are equivalent to Calendars NaiveDateTime
structs.
Like Calendar.Date
functions take arguments that implement the Calendar.ContainsDate
protocol, Calendar.NaiveDateTime
functions take arguments that implement the
Calendar.ContainsNaiveDateTime
protocol. Out of the box Calendar has implemented
these protocols for Calendar.NaiveDateTime
structs and erlang style tuples.
Calecto implements the protocol for Ecto.DateTime
structs.
This means we can use the Calendar.NaiveDateTime.diff/2
function to compare
Ecto.DateTime
structs too.
The difference between the two structs is 603 seconds and 0 microseconds.
You can pattern match on the fourth tuple element to get :before
:after
or :same_time
.
Alternatively if you do not want to know how big the difference is, but just want
to know if the first datetime is before the second one or not, there is a function for that:
And as with the Date
module you can also use Ecto.DateTime
s in the other functions
in the NaiveDateTime
module:
Comparing just the date part of a datetime
Because Ecto.Datetime
contains a date - the
ContainsDate
protocol is also implemented for Ecto.Datetime
. As with a simple date we can also use
the functions in the Calendar.Date
module with an Ecto.DateTime
as an argument.
What if you have two datetimes and you want to know if they are on the same date - not
if they are on the exact same time, just if they are on the same date. There is not a special
function for that made for date-times. Instead we can simply use the same function as before:
Date.diff/2
. When passing datetimes, the protocols simply discards the time and just
uses the date part of the date-time:
How to get all of this goodness
Just add Calendar (and Calecto if you use Ecto) to your mix file. Then you can start using the features of Calendar without changing anything else in your existing code.
The whole way
So far we have seen how the protocols effortlessly allow using date and time types from Ecto with Calendar modules.
If you go a bit further and use Calecto types e.g.
Calecto.DateTimeUTC
instead of Ecto.DateTime
, you get more functionality,
convenience and protection from bugs.
Protocols
With Elixir protocols it is possible to build functionality once in one library and then use it with data from other libraries without changing a single line of code in either! All you have to do is implement the necessary protocols. In this example those libraries are Calendar and Ecto. And Calecto provides the protocol implementation.
Even within one library the protocols provide a clean way of making one function usable with different data-types.
If you liked this post you might want to follow me on twitter for updates on new posts and more. Twitter handle: @laut