# `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()
```

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. The default is `Calendrical.Gregorian`.

### Returns

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

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

### Examples

    iex> Calendrical.Interval.day 2019, 52, Calendrical.Fiscal.US
    Date.range(~D[2019-02-21 Calendrical.Fiscal.US], ~D[2019-02-21 Calendrical.Fiscal.US])

    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])

# `day`

```elixir
@spec day(Calendar.year(), Calendar.day(), Calendrical.calendar()) ::
  Date.Range.t() | {:error, :invalid_date}
```

# `month`

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

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. The default is `Calendrical.Gregorian`.

### Returns

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

### Examples

    iex> Calendrical.Interval.month 2019, 3, Calendrical.Fiscal.UK
    Date.range(~D[2019-03-01 Calendrical.Fiscal.UK], ~D[2019-03-30 Calendrical.Fiscal.UK])

    iex> Calendrical.Interval.month 2019, 3, Calendrical.Fiscal.US
    Date.range(~D[2019-03-01 Calendrical.Fiscal.US], ~D[2019-03-31 Calendrical.Fiscal.US])

# `month`

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

# `quarter`

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

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. The default is `Calendrical.Gregorian`.

### Returns

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

### Examples

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

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

# `quarter`

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

# `week`

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

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. The default is `Calendrical.Gregorian`.

### Returns

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

* `{:error, :not_defined}` if the calendar does not support the concept of
  weeks.

### Examples

    iex> Calendrical.Interval.week 2019, 52, Calendrical.Fiscal.US
    Date.range(~D[2019-12-22 Calendrical.Fiscal.US], ~D[2019-12-28 Calendrical.Fiscal.US])

    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
    {:error, :not_defined}

# `week`

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

# `year`

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

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. The default is `Calendrical.Gregorian`.

### Returns

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

### Examples

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

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

# `year`

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

---

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