How to implement Date.add(date, n, :month) in Elixir

Would be nice to have this in the standard Elixir library, but we don't.

Date.add(date, n, :month) # where n could be +/- 

How would you implement this?

This looks like a good starting point:

7

5 Answers

Date.utc_today() |> Timex.shift(months: -1) 
1

You could use the Timex implementation:

 defp shift_by(%NaiveDateTime{:year => year, :month => month} = datetime, value, :months) do m = month + value shifted = cond do m > 0 -> years = div(m - 1, 12) month = rem(m - 1, 12) + 1 %{datetime | :year => year + years, :month => month} m <= 0 -> years = div(m, 12) - 1 month = 12 + rem(m, 12) %{datetime | :year => year + years, :month => month} end # If the shift fails, it's because it's a high day number, and the month # shifted to does not have that many days. This will be handled by always # shifting to the last day of the month shifted to. case :calendar.valid_date({shifted.year,shifted.month,shifted.day}) do false -> last_day = :calendar.last_day_of_the_month(shifted.year, shifted.month) cond do shifted.day <= last_day -> shifted :else -> %{shifted | :day => last_day} end true -> shifted end end 

Timex uses the MIT license, so you should be able to incorporate this in pretty much any project.

1

ex_cldr_calendars can also do basic date math for adding and subtracting years, quarters, months, weeks and days for any calendar that implements the Calendar behaviour.

iex> Cldr.Calendar.plus ~D[2019-03-31], :months, -1 ~D[2019-02-28] # The :coerce option determines whether to force an end # of month date when the result of the operation is an invalid date iex> Cldr.Calendar.plus ~D[2019-03-31], :months, -1, coerce: false {:error, :invalid_date} 

Without adding a dependency like Timex, the following works for adding/subtracting Gregorian months without too much trouble - assuming you only need the first of each month. Shifting to a day of the month directly may be best served through a library, given how many calendrical fallacies there are.

defmodule DateUtils @doc """ Shift a given date forward or back n months """ def shift_n_months(date, n) when n < 0, do: subtract_n_months(date, -1 * n) def shift_n_months(date, n), do: add_n_months(date, n) def add_n_months(date, 0), do: Date.beginning_of_month(date) def add_n_months(date, n) do date |> Date.end_of_month() |> Date.add(1) |> add_n_months(n - 1) end def subtract_n_months(date, 0), do: Date.beginning_of_month(date) def subtract_n_months(date, n) do date |> Date.beginning_of_month() |> Date.add(-1) |> subtract_n_months(n - 1) end end 

There is an elixir function . Give it any date and it will add the dates for you.

iex>Date.add(~D[2000-01-03], -2) ~D[2000-01-01] 

If you want to create the date to add to then i suggest you use the

iex>{:ok, date} = Date.new(year, month, day) iex>date |> Date.add(n) 
2

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like