【问题标题】:How to implement Date.add(date, n, :month) in Elixir如何在 Elixir 中实现 Date.add(date, and, :month)
【发布时间】:2021-10-06 03:38:55
【问题描述】:

如果在标准 Elixir 库中有这个功能会很好,但我们没有。

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

你将如何实现它?

这看起来是一个很好的起点:https://stackoverflow.com/a/53407676/44080

【问题讨论】:

  • 从链接的问题中获取我的答案并用Enum.reduce(1..n, date, fn _, acc -> previous_month(acc) end) 包装会有什么问题?
  • 如果@AlekseiMatiushkin 的答案对您不起作用,您可能应该在您的问题中添加一些细节来解释为什么您不能使用他的答案。我不清楚为什么他的回答不能满足你的需要。
  • @OnorioCatenacci 说您需要 14 个月前的日期?你要打多少电话到previous_month(acc)?我认为这个想法是只打一个电话
  • 嗯。对一个 Elixir 函数的一次调用会产生多少个处理器滴答声?只需像我上面展示的那样编写一个包装器,然后调用这个包装器。 @OnorioCatenacci 老实说,我不确定 OP 之后的解决方案是什么。
  • @AlekseiMatiushkin 也许我不明白您提出的解决方案:Enum.reduce(1..n, date, fn _, acc -> previous_month(acc) end)。你在做Enum.reduce(1..n, ...意味着你至少要调用previous_month(acc)n。这就是我所说的 n 次调用

标签: date elixir


【解决方案1】:

你可以使用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,因此您应该能够将其纳入几乎任何项目中。

【讨论】:

  • Timex 不支持 Calendar 行为,并假定每个日期都在预测的公历中。当然,这在很多国家都是很常见的用法,但并不普遍。例如,即使在美国,许多公司日历也使用 445 个日历,并在一周中的不同月份和日期开始。
【解决方案2】:
Date.utc_today() |> Timex.shift(months: -1) 

【讨论】:

  • 虽然这可能会回答问题,但请考虑添加一些解释。
【解决方案3】:

ex_cldr_calendars 还可以对任何实现Calendar 行为的日历进行基本的日期数学运算,以添加和减去年、季度、月、周和日。

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}

【讨论】:

    【解决方案4】:

    在不添加像 Timex 这样的依赖项的情况下,以下方法可以轻松地添加/减去公历月份 - 假设您只需要每个月的第一天。考虑到有多少日历谬误,最好通过图书馆直接转移到一个月中的某一天。

    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
    

    【讨论】:

      【解决方案5】:

      有一个长生不老药函数Date.add/2。给它任何日期,它会为您添加日期。

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

      如果您想创建要添加的日期,那么我建议您使用Date.new/4

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

      【讨论】:

      • 你想如何使用这个裸函数减去正好三个月
      • 在这种情况下,您可以找到这些月份的天数,然后进行减法。
      猜你喜欢
      • 2020-07-21
      • 2022-12-02
      • 1970-01-01
      • 2016-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-07
      • 1970-01-01
      相关资源
      最近更新 更多