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:
75 Answers
Date.utc_today() |> Timex.shift(months: -1) 1You 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.
1ex_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