> ## Content Index
> Fetch the complete content index at: https://inquisitivedeveloper.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Learn With Me: Elixir - Dates and Times (#37)
- URL: https://inquisitivedeveloper.com/lwm-elixir-37/
- Published: 2019-02-23T16:00:00.000Z
- Updated: 2019-02-23T16:00:00.000Z
- Description: We learn about how dates and times are handled in Elixir.
- Author: Kevin Peter
- Tags: Learn With Me: Elixir, Elixir, #Import 2026-10-23 20:03

Elixir does have native functionality that handles dates and times, but since it is a more recent addition to Elixir, so it's still in the process of maturing. Elixir 1.3 added standard data structures to support dates and times and Elixir 1.5 added some functionality to manipulate dates and times. I expect that this functionality will be slowly improved upon as Elixir evolves.

The `Calendar` module contains the rules that are used to manipulate dates, but there's only one set of rules in `Calendar.ISO`, which represents the Gregorian calendar. If you wanted your software to use the Julian calendar, for example, you'd either have to find a library that has these calendar rules or implement the rules yourself.

## Dates

A `Date` type represents a date on the calendar: there is no time component. It holds a day, month, and year as well as a reference to the calendar that it applies to. A `Date`, like many Elixir data types, is actually a struct that is packaged with functions that operate on that struct.

We can use the `~D` sigil to create a date literal that represents October 11, 2018 on the Gregorian calendar.

```elixir
iex> date = ~D[2018-10-11]
~D[2018-10-11]
iex> date = ~D[2018-20-11]
** (ArgumentError) cannot parse "2018-20-11" as date, reason: :invalid_date
	(elixir) lib/calendar/date.ex:312: Date.from_iso8601!/2
	(elixir) expanding macro: Kernel.sigil_D/2
	iex:8: (file)		

```

As you can see, we get an ArgumentError when we attempt to create a date literal using an invalid date. When IEx sees a `Date` struct, is displays it with `~D[]`. I wonder if there's some sort of function in the `Date` struct module that tells IEx how to display dates. That would certainly be useful for displaying the values of any data types I create in the future.

Besides using a sigil, we can also use `Date.new/3` to create a new data

```elixir
iex> date = Date.new(2018, 10, 11)
{:ok, ~D[2018-10-11]}

```

Notice that the above example actually returns a tuple indicating that the date was constructed successfully along with the data structure representing the date.

Let's see what happens when we try constructing an invalid date. I predict that I'll either get an `:error` or a tuple containing that same atom.

```elixir
iex> date = Date.new(2018, 10, 33)
{:error, :invalid_date}

```

Yep, since there's no October 33, 2018, we got a tuple with `:error` and an `:invalid_date` atom indicating what the error is.

So let's do pattern matching to extract the date structure from the tuple.

```elixir
iex> {:ok, date} = Date.new(2018, 10, 11)
{:ok, ~D[2018-10-11]}
iex> date
~D[2018-10-11]

```

We can compare dates to each other using comparison operators

```elixir
iex> easter = ~D[2018-04-01]
~D[2018-04-01]
iex> christmas = ~D[2018-12-25]
~D[2018-12-25]
iex> today = ~D[2018-12-25]
~D[2018-12-25]
iex> today == easter
false
iex> today == christmas
true	
iex> easter < christmas
true
iex> christmas > easter
true
iex> christmas < easter
false		

```

### Inspecting the Date

While learning about dates, I learned that we can also use the `inspect` command, a command I wasn't previously familiar with, to look at the internals of the structure.

```elixir
iex> inspect date
"~D[2018-10-11]"
iex> inspect date, structs: false
"%{__struct__: Date, calendar: Calendar.ISO, day: 11, month: 10, year: 2018}"

```

It's interesting to see what's in a data structure, and I think that `inspect` will prove useful in the future.

### Useful Date Functions

The `Date` module has a variety of functions, which I may or may not cover in detail in a future post. I'm going to go over a few of them here.

The [Date.add/2](https://hexdocs.pm/elixir/Date.html?ref=inquisitivedeveloper.com#add/2) function allows us to add days to a particular date.

```elixir
iex> Date.add(~D[2018-12-11], 10)
~D[2018-12-21]
iex> Date.add(~D[2018-12-11], -10)
~D[2018-12-01]
iex> Date.add(~D[2018-12-11], -20)
~D[2018-11-21]
iex> Date.add(~D[2018-12-11], 42)
~D[2019-01-22]

```

The `Date.day_of_week/1` returns the day of the week associated with a date. `1` is Monday, `2` is a Tuesday, and so forth until we get to `7`, which is a Sunday.

```elixir
iex> Date.day_of_week(~D[2018-12-11])
2
iex> Date.day_of_week(~D[2018-12-09])
7
iex> Date.day_of_week(~D[2018-12-14])
5

```

The `Date.days_in_month/1` function returns the number of days in the month that the date is in.

```elixir
iex> Date.days_in_month(~D[2018-12-11])
31
iex> Date.days_in_month(~D[2018-11-11])
30
iex> Date.days_in_month(~D[2019-02-11])
28

```

### Date ranges

We can construct a date range using `Date.range/2` and passing two dates

```elixir
iex> Date.range(~D[2018-04-01], ~D[2018-12-25])
#DateRange<~D[2018-04-01], ~D[2018-12-25]>

```

It's also possible to have a descending date range.

```elixir
iex> Date.range(~D[2018-12-25], ~D[2018-04-01])
#DateRange<~D[2018-12-25], ~D[2018-04-01]>

```

Date ranges appear to be enumerables, which I just love. This means that we can use any function in the `Enum` module on date ranges. For example, we can use `Enum.count/1` to count the number of days in the date range

```elixir
iex> Enum.count(Date.range(~D[2018-04-01], ~D[2018-12-25]))
269
iex> Enum.count(Date.range(~D[2018-04-01], ~D[2018-04-01]))
1
iex> Enum.count(Date.range(~D[2018-04-01], ~D[2018-03-25]))
8

```

It looks like it will also count descending date ranges without a problem. It looks like date ranges work just like integer ranges.

As with other enumerables, you can use the `in` keyword to see if a date is within the range, just like with integer ranges

```elixir
iex> ~D[2018-05-06] in Date.range(~D[2018-04-01], ~D[2018-12-25])
true
iex> ~D[2019-01-16] in Date.range(~D[2018-04-01], ~D[2018-12-25])
false

```

Like with integer ranges, I can look at all the individual dates in a date range by enumerating the date range with `Enum.to_list/1`.

```elixir
iex> date_range = Date.range(~D[2018-11-28], ~D[2018-12-25])
#DateRange<~D[2018-11-28], ~D[2018-12-25]>
iex> Enum.to_list(date_range)
[~D[2018-11-28], ~D[2018-11-29], ~D[2018-11-30], ~D[2018-12-01], ~D[2018-12-02],
 ~D[2018-12-03], ~D[2018-12-04], ~D[2018-12-05], ~D[2018-12-06], ~D[2018-12-07],
 ~D[2018-12-08], ~D[2018-12-09], ~D[2018-12-10], ~D[2018-12-11], ~D[2018-12-12],
 ~D[2018-12-13], ~D[2018-12-14], ~D[2018-12-15], ~D[2018-12-16], ~D[2018-12-17],
 ~D[2018-12-18], ~D[2018-12-19], ~D[2018-12-20], ~D[2018-12-21], ~D[2018-12-22],
 ~D[2018-12-23], ~D[2018-12-24], ~D[2018-12-25]]

```

I can even get a list containing all Mondays within the date range

```elixir
iex> Enum.filter(date_range, fn date -> Date.day_of_week(date) == 1 end)
[~D[2018-12-03], ~D[2018-12-10], ~D[2018-12-17], ~D[2018-12-24]]

```

That was a lot of fun!

## Times

The `Time` struct contains an hour, minute, second, and fractions of a second. The fraction of a second is stored as a tuple containing a number of microseconds and the number of significant digits.

Like dates, there is a sigil for defining time literals.

```elixir
#This is the time 20:31:14 with 234 microseconds added on
iex> ~T[20:31:14.234]
~T[20:31:14.234]
#Invalid time
iex> ~T[35:100:31]
** (ArgumentError) cannot parse "35:100:31" as time, reason: :invalid_format
    (elixir) lib/calendar/time.ex:268: Time.from_iso8601!/2
    (elixir) expanding macro: Kernel.sigil_T/2
    iex:25: (file)
#The seconds are required
iex> ~T[04:32]
** (ArgumentError) cannot parse "04:32" as time, reason: :invalid_format
    (elixir) lib/calendar/time.ex:268: Time.from_iso8601!/2
    (elixir) expanding macro: Kernel.sigil_T/2
    iex:25: (file)
#Valid time without the fractional part
iex> ~T[04:32:00]
~T[04:32:00]

```

Based on my experiments, the time in the time sigil is always 24-hour time. So 8:31 PM is represented as 20:31\. It also appears that a time needs the hour, minute, and second, although the fractional second part appears to be optional.

Like with dates, there is a `new` function for creating times as well.

```elixir
iex> Time.new(11, 55, 31)
{:ok, ~T[11:55:31]}
iex> Time.new(35, 100, 31)
{:error, :invalid_time}

```

Times can be compared just like dates and the `Time` module contains functions for manipulating time structures.

## DateTime Structures

There are also `DateTime` and `NaiveDateTime` structures. The difference between them is that `DateTime` is associated with a timezone, whereas `NaiveDateTime` is not. I have not yet seen examples of these two structures being used. The documentation for the two modules indicates that these modules are mostly used for conversion purposes, converting to and from various formats.

## Third-Party Libraries

Those of you who've dealt with dates, times, calendars, and time zones know that this is a very complex subject matter. Elixir's native date and time functionality may be good enough for many applications, but if you want more sophisticated functionality, the standard third-party library to use seems to be [Lau Taarnskov's Calendar library](https://github.com/lau/calendar?ref=inquisitivedeveloper.com), which implements much more than is available in core Elixir.