# `Calendrical.Interval`

Constructs and compares date intervals for any Calendrical calendar.

An interval is represented as an Elixir `t:Date.Range.t/0` whose `:first` and
`:last` dates are in the desired calendar. Ranges produced by this module
are enumerable and have the day as their unit of precision.

The constructor functions cover the standard calendrical units: `year/2`,
`quarter/3`, `month/3`, `week/3`, and `day/3`. Each one also accepts a
single `t:Calendar.date/0` argument and returns the interval that contains
that date.

`compare/2` and the corresponding `relation/2` (re-exported from
`Calendrical.IntervalRelation`) implement
[Allen's interval algebra](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra),
returning one of 13 atoms describing how two ranges relate.

# `compare`

```elixir
@spec compare(range_1 :: Date.Range.t(), range_2 :: Date.Range.t()) ::
  Calendrical.interval_relation()
```

Compares two date ranges and returns the Allen-algebra relation between
them.

Uses [Allen's Interval Algebra](https://en.wikipedia.org/wiki/Allen%27s_interval_algebra)
to return one of 13 different relationships:

Relation       | Converse
-------------- | --------------
`:precedes`    | `:preceded_by`
`:meets`       | `:met_by`
`:overlaps`    | `:overlapped_by`
`:finished_by` | `:finishes`
`:contains`    | `:during`
`:starts`      | `:started_by`
`:equals`      | `:equals`

### Arguments

* `range_1` is a `t:Date.Range.t/0`.

* `range_2` is a `t:Date.Range.t/0`.

### Returns

* An atom representing the relationship of `range_1` to `range_2`.

### Examples

    iex> Calendrical.Interval.compare Calendrical.Interval.day(~D[2019-01-01]),
    ...> Calendrical.Interval.day(~D[2019-01-02])
    :meets

    iex> Calendrical.Interval.compare Calendrical.Interval.day(~D[2019-01-01]),
    ...> Calendrical.Interval.day(~D[2019-01-03])
    :precedes

    iex> Calendrical.Interval.compare Calendrical.Interval.day(~D[2019-01-03]),
    ...> Calendrical.Interval.day(~D[2019-01-01])
    :preceded_by

    iex> Calendrical.Interval.compare Calendrical.Interval.day(~D[2019-01-02]),
    ...> Calendrical.Interval.day(~D[2019-01-01])
    :met_by

    iex> Calendrical.Interval.compare Calendrical.Interval.day(~D[2019-01-02]),
    ...> Calendrical.Interval.day(~D[2019-01-02])
    :equals

# `day`

```elixir
@spec day(Date.t()) :: Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the day containing
`date`.

The range is enumerable.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  day containing `date`, in `date`'s calendar, or

* `{:error, %Calendrical.InvalidDateError{}}` for a date the calendar does not have, or

* `{:error, %Calendrical.MissingFieldsError{}}` for a date missing a
  field.

### Examples

    iex> Calendrical.Interval.day(~D[2019-06-15])
    Date.range(~D[2019-06-15], ~D[2019-06-15])

# `day`

```elixir
@spec day(Calendar.year(), Calendar.day(), Calendrical.calendar()) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` containing a single `day`.

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `day` is any `day` in the `year` for `calendar` (the ordinal day of year).

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` containing the single requested day, or

* `{:error, %Calendrical.InvalidDateError{}}` if `day` is greater than
  the number of days in the year.

### Examples

    iex> {:ok, _} =
    ...>   Calendrical.new(MyApp.FiscalUS, :month,
    ...>     year_begins: {10, 1},
    ...>     year_number: :majority,
    ...>     weeks: [first_day: 7, min_days_in_first_week: 4]
    ...>   )
    iex> Calendrical.Interval.day 2019, 52, MyApp.FiscalUS
    Date.range(Date.new!(2019, 2, 21, MyApp.FiscalUS), Date.new!(2019, 2, 21, MyApp.FiscalUS))

    iex> Calendrical.Interval.day 2019, 92, Calendrical.NRF
    Date.range(~D[2019-W14-1 Calendrical.NRF], ~D[2019-W14-1 Calendrical.NRF])

    iex> Calendrical.Interval.day 2019, 8, Calendrical.ISOWeek
    Date.range(~D[2019-W02-1 Calendrical.ISOWeek], ~D[2019-W02-1 Calendrical.ISOWeek])

# `month`

```elixir
@spec month(Date.t()) :: Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the month containing
`date`.

The range is enumerable.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  month containing `date`, in `date`'s calendar.

* `{:error, %Calendrical.InvalidDateError{}}` for a date the calendar does not have, or

* `{:error, %Calendrical.MissingFieldsError{}}` for a date missing a
  field.

### Examples

    iex> Calendrical.Interval.month(~D[2019-06-15])
    Date.range(~D[2019-06-01], ~D[2019-06-30])

# `month`

```elixir
@spec month(Calendar.year(), Calendar.month(), Calendrical.calendar()) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the `month`.

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `month` is any `month` in the `year` for `calendar`.

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the `month`.

### Examples

    iex> {:ok, _} = Calendrical.new(MyApp.FiscalUK, :month, year_begins: {4, 1}, year_number: :majority)
    iex> Calendrical.Interval.month 2019, 3, MyApp.FiscalUK
    Date.range(Date.new!(2019, 3, 1, MyApp.FiscalUK), Date.new!(2019, 3, 30, MyApp.FiscalUK))

    iex> {:ok, _} =
    ...>   Calendrical.new(MyApp.FiscalUS, :month,
    ...>     year_begins: {10, 1},
    ...>     year_number: :majority,
    ...>     weeks: [first_day: 7, min_days_in_first_week: 4]
    ...>   )
    iex> Calendrical.Interval.month 2019, 3, MyApp.FiscalUS
    Date.range(Date.new!(2019, 3, 1, MyApp.FiscalUS), Date.new!(2019, 3, 31, MyApp.FiscalUS))

# `quadrimester`

```elixir
@spec quadrimester(
  Calendar.year(),
  Calendrical.quadrimester(),
  Calendrical.calendar()
) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the `quadrimester`
(third of the year).

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `quadrimester` is `1`, `2` or `3`.

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  `quadrimester`, or

* `{:error, exception}` for a quadrimester the year does not have.

### Examples

    iex> Calendrical.Interval.quadrimester 2026, 2
    Date.range(~D[2026-05-01 Calendrical.Gregorian], ~D[2026-08-31 Calendrical.Gregorian])

# `quarter`

```elixir
@spec quarter(Date.t()) :: Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the quarter containing
`date`.

The range is enumerable.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  quarter containing `date`, in `date`'s calendar.

* `{:error, exception}` for a date the calendar does not have.

### Examples

    iex> Calendrical.Interval.quarter(~D[2019-06-15])
    Date.range(~D[2019-04-01], ~D[2019-06-30])

# `quarter`

```elixir
@spec quarter(Calendar.year(), Calendrical.quarter(), Calendrical.calendar()) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the `quarter`.

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `quarter` is any `quarter` in the `year` for `calendar`.

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the `quarter`.

### Examples

    iex> {:ok, _} = Calendrical.new(MyApp.FiscalUK, :month, year_begins: {4, 1}, year_number: :majority)
    iex> Calendrical.Interval.quarter 2019, 2, MyApp.FiscalUK
    Date.range(Date.new!(2019, 4, 1, MyApp.FiscalUK), Date.new!(2019, 6, 30, MyApp.FiscalUK))

    iex> Calendrical.Interval.quarter 2019, 2, Calendrical.ISOWeek
    Date.range(~D[2019-W14-1 Calendrical.ISOWeek], ~D[2019-W26-7 Calendrical.ISOWeek])

# `semester`

```elixir
@spec semester(Calendar.year(), Calendrical.semester(), Calendrical.calendar()) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the `semester`
(half of the year).

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `semester` is `1` or `2`.

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  `semester`, or

* `{:error, exception}` for a semester the year does not have.

### Examples

    iex> Calendrical.Interval.semester 2026, 2
    Date.range(~D[2026-07-01 Calendrical.Gregorian], ~D[2026-12-31 Calendrical.Gregorian])

# `week`

```elixir
@spec week(Date.t()) :: Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the week containing
`date`.

The range is enumerable.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  week containing `date`, in `date`'s calendar.

* `{:error, exception}` for a date the calendar does not have.

### Examples

    iex> Calendrical.Interval.week(~D[2019-06-15])
    Date.range(~D[2019-06-10], ~D[2019-06-16])

# `week`

```elixir
@spec week(Calendar.year(), Calendrical.week(), Calendrical.calendar()) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the `week`.

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `week` is any `week` in the `year` for `calendar`.

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the `week`, or

* `{:error, exception}` for a week the year does not have.

### Examples

    iex> {:ok, _} =
    ...>   Calendrical.new(MyApp.FiscalUS, :month,
    ...>     year_begins: {10, 1},
    ...>     year_number: :majority,
    ...>     weeks: [first_day: 7, min_days_in_first_week: 4]
    ...>   )
    iex> Calendrical.Interval.week 2019, 52, MyApp.FiscalUS
    Date.range(Date.new!(2019, 12, 22, MyApp.FiscalUS), Date.new!(2019, 12, 28, MyApp.FiscalUS))

    iex> Calendrical.Interval.week 2019, 52, Calendrical.NRF
    Date.range(~D[2019-W52-1 Calendrical.NRF], ~D[2019-W52-7 Calendrical.NRF])

    iex> Calendrical.Interval.week 2019, 52, Calendrical.ISOWeek
    Date.range(~D[2019-W52-1 Calendrical.ISOWeek], ~D[2019-W52-7 Calendrical.ISOWeek])

    iex> Calendrical.Interval.week 2019, 52, Calendrical.Julian
    Date.range(~D[2019-12-24 Calendrical.Julian], ~D[2019-12-30 Calendrical.Julian])

# `year`

```elixir
@spec year(Date.t()) :: Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the year containing
`date`.

The range is enumerable.

### Arguments

* `date` is any `t:Calendar.date/0`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the
  year containing `date`, in `date`'s calendar, or

* `{:error, %Calendrical.InvalidDateError{}}` for a date the calendar does not have, or

* `{:error, %Calendrical.MissingFieldsError{}}` for a date with no
  year.

### Examples

    iex> Calendrical.Interval.year(~D[2019-06-15])
    Date.range(~D[2019-01-01], ~D[2019-12-31])

# `year`

```elixir
@spec year(Calendar.year(), Calendrical.calendar()) ::
  Date.Range.t() | Calendrical.date_error()
```

Returns a `t:Date.Range.t/0` that represents the `year`.

The range is enumerable.

### Arguments

* `year` is any `year` for `calendar`.

* `calendar` is any module that implements the `Calendar` and `Calendrical`
  behaviours, or `Calendar.ISO`, which answers as `Calendrical.Gregorian`
  with dates in `Calendar.ISO`. The default is `Calendrical.Gregorian`.

### Returns

* A `t:Date.Range.t/0` representing the enumerable days in the `year`.

### Examples

    iex> {:ok, _} = Calendrical.new(MyApp.FiscalUK, :month, year_begins: {4, 1}, year_number: :majority)
    iex> Calendrical.Interval.year 2019, MyApp.FiscalUK
    Date.range(Date.new!(2019, 1, 1, MyApp.FiscalUK), Date.new!(2019, 12, 31, MyApp.FiscalUK))

    iex> Calendrical.Interval.year 2019, Calendrical.NRF
    Date.range(~D[2019-W01-1 Calendrical.NRF], ~D[2019-W52-7 Calendrical.NRF])

---

*Consult [api-reference.md](api-reference.md) for complete listing*
