Ash.Type.Range (ash v3.35.0)

Copy Markdown View Source

A range of values of an inner type, held as an Ash.Range.

attribute :stay, Ash.Type.Range, constraints: [inner_type: :date]

Inner types

inner typeprecisionresolutionwithout a resolution
:integer1a positive integercast to [)
:datea daya Duration of days or longercast to [)
:naive_datetimea seconda Durationkept as given
:datetimea second, or a microsecond with precision: :microseconda Durationkept as given

The precision is the smallest difference between two values. Cast to inclusive-exclusive, [), [1, 3] becomes [1, 4); kept as given, (a, b] stays (a, b]. With a resolution, every range is cast to [).

Constraints

  • :inner_type - Required. The type of the range's values: one of :integer, :date, :naive_datetime or :datetime, or an Ash.Type.NewType of one, such as :utc_datetime_usec. Values are ordered the same way in Elixir and in the data layer. Each has a precision: see Inner types, above.

  • :inner_constraints (keyword/0) - Constraints of the inner type, applied to each bound, limit and anchor. The default value is [].

  • :lower (keyword/0) - Constraints on the range's lower bound, which is nil when the range is unbounded below. The default value is [].

    • :required? (boolean/0) - The range must not be unbounded below. The default value is false.

    • :inclusive? (boolean/0) - true requires an inclusive lower bound, false an exclusive one. A range unbounded below passes either. An :integer or :date range, or one with a resolution, is cast to [), so only true is allowed.

    • :limit (term/0) - The least value the lower bound may take: limit: ~D[2025-07-01] keeps a range from starting before 1 July 2025. The range cannot then be unbounded below. Written as the lower bound, :start is this limit, or unbounded without one, so [:start, :end) is every value between the limits. With a resolution, it must be on the grid.

  • :upper (keyword/0) - Constraints on the range's upper bound, which is nil when the range is unbounded above. The default value is [].

    • :required? (boolean/0) - The range must not be unbounded above. The default value is false.

    • :inclusive? (boolean/0) - true requires an inclusive upper bound, false an exclusive one. A range unbounded above passes either. An :integer or :date range, or one with a resolution, is cast to [), so only false is allowed.

    • :limit (term/0) - The greatest value the upper bound may take: limit: ~D[2026-07-01] keeps a range from ending after 1 July 2026. An inclusive upper bound must be below it. The range cannot then be unbounded above. Written as the upper bound, :end is this limit, or unbounded without one. With a resolution, it must be on the grid.

  • :allow_empty? (boolean/0) - Allow an empty range, one holding no values (see Ash.Range.empty/0). The default value is false.

  • :resolution (term/0) - The length of a period: a positive Duration, or a positive integer for an :integer range. Duration.new!(week: 2) divides a :date range into fortnights. It is a whole number of the precision, in months and years or in weeks and shorter, not both.
    The periods' boundaries are the grid, and the range's bounds and limits must be on it. A range with a resolution is cast to [). Without one, a period is one unit of the precision.

  • :anchor (term/0) - A value on the grid, aligning the periods, within the limits or not: ~D[2025-06-23] starts fortnights on that Monday. Without one, the grid starts at 0 or at 0001-01-01T00:00:00Z, so weeks start on Monday. Required for a resolution in months or years, on the 28th of a month or earlier.
    A DateTime in a time zone aligns periods of a day or longer to its time of day in that zone, so a day is 23, 24 or 25 hours long. Its time of day must fall in no gap or overlap of the zone.

PostgreSQL

ash_postgres stores a range as a native range: :integer as int8range, :date as daterange, :naive_datetime as tsrange and :datetime as tstzrange.

Summary

Functions

The inner type, looking through any Ash.Type.NewType.

Returns the period containing value. Takes constraints from Ash.Type.init/2.

Returns the length of a period: the resolution, or one unit of the precision without one. Takes constraints from Ash.Type.init/2.

Functions

base_type(type)

@spec base_type(Ash.Type.t()) :: Ash.Type.t()

The inner type, looking through any Ash.Type.NewType.

handle_change?()

period(value, constraints)

@spec period(term(), Keyword.t()) :: {:ok, Ash.Range.t()} | {:error, String.t()}

Returns the period containing value. Takes constraints from Ash.Type.init/2.

iex> {:ok, constraints} =
...>   Ash.Type.init(Ash.Type.Range,
...>     inner_type: :date,
...>     resolution: Duration.new!(week: 2),
...>     anchor: ~D[2025-06-23]
...>   )
iex> Ash.Type.Range.period(~D[2025-06-25], constraints)
{:ok, %Ash.Range{lower: ~D[2025-06-23], upper: ~D[2025-07-07], bounds: :"[)"}}

prepare_change?()

resolution(constraints)

@spec resolution(Keyword.t()) :: Duration.t() | pos_integer()

Returns the length of a period: the resolution, or one unit of the precision without one. Takes constraints from Ash.Type.init/2.

iex> {:ok, constraints} = Ash.Type.init(Ash.Type.Range, inner_type: :utc_datetime)
iex> Ash.Type.Range.resolution(constraints)
%Duration{second: 1}