# SPDX-FileCopyrightText: 2019 ash contributors # # SPDX-License-Identifier: MIT defmodule Ash.Query.Function.Ago do @moduledoc """ Subtracts the given interval or Duration from the current time in UTC. For example: deleted_at > ago(7, :day) deleted_at > ago(Duration.new!(day: 7)) Documentation + available intervals inspired by the corresponding ecto interval implementation """ use Ash.Query.Function, name: :ago, eager_evaluate?: false def args, do: [[:integer, :duration_name], [:duration]] def returns, do: [:utc_datetime_usec] def evaluate(%{arguments: [factor, interval]}) do now = DateTime.utc_now() shifted = datetime_add(now, -factor, interval) {:known, shifted} end def evaluate(%{arguments: [duration]}) do now = DateTime.utc_now() shifted = DateTime.shift(now, Duration.negate(duration)) {:known, shifted} end @doc false def datetime_add(datetime, amount_to_add, unit) when unit in [:microsecond, :millisecond, :second] do DateTime.add(datetime, amount_to_add, unit) end def datetime_add(datetime, amount_to_add, :minute) do DateTime.add(datetime, amount_to_add * 60, :second) end def datetime_add(datetime, amount_to_add, :hour) do DateTime.add(datetime, amount_to_add * 60 * 60, :second) end def datetime_add(datetime, amount_to_add, :day) do date = Date.add(datetime, amount_to_add) %{datetime | year: date.year, month: date.month, day: date.day} end def datetime_add(datetime, amount_to_add, :week) do datetime_add(datetime, 7 * amount_to_add, :day) end def datetime_add(datetime, amount_to_add, :month) do months_since_zero = datetime.year * 12 + datetime.month - 1 + amount_to_add year = div(months_since_zero, 12) {year, month} = case rem(months_since_zero, 12) do x when x < 0 -> {year - 1, 12 + x + 1} x -> {year, x + 1} end # clamp to last day of month last_day = Calendar.ISO.days_in_month(year, month) day = min(datetime.day, last_day) %{datetime | year: year, month: month, day: day} end def datetime_add(%{month: month, day: day} = datetime, amount_to_add, :year) do result = %{datetime | year: datetime.year + amount_to_add} # Adjust 29-Feb to 01-March when not a leap year if month == 2 and day == 29 and not Calendar.ISO.leap_year?(result.year) do %{result | month: 3, day: 1} else result end end def datetime_add(datetime, duration) when is_struct(duration, Duration) do DateTime.shift(datetime, duration) end def can_return_nil?(_), do: false end