# `Tempo.Interval.Steps`
[🔗](https://github.com/kipcole9/tempo/blob/v0.20.0/lib/tempo/interval/steps.ex#L1)

Closed-form step arithmetic for interval enumeration.

Backs the `Enumerable` protocol implementation for
`t:Tempo.Interval.t/0` — specifically its `count/1`, `slice/1`,
and `member?/2` callbacks — with O(1) (or near-O(1))
implementations driven by the calendar's date algebra rather than
walking the interval one step at a time.

Each function takes the iteration unit and the calendar in addition
to the endpoints:

* `count_steps/4` — how many `unit`-wide steps fit in
  `[from, to)`.

* `nth_step/4` — the `n`-th element from `from` at `unit`
  granularity.

* `on_step?/4` — whether `element` falls on a `unit`-step
  boundary anchored at `from`.

Phase 1 covers `:year`, `:month`, and `:day`. Sub-day units
(`:hour`, `:minute`, `:second`, `:microsecond`) are added in
subsequent phases.

# `count_steps`

```elixir
@spec count_steps(Tempo.t(), Tempo.t(), atom(), module()) ::
  non_neg_integer() | :not_supported
```

Count the number of `unit`-wide steps in the half-open span
`[from, to)`.

### Arguments

* `from` is the lower-bound `t:Tempo.t/0`.

* `to` is the exclusive upper-bound `t:Tempo.t/0`.

* `unit` is one of `:year`, `:month`, `:day` (Phase 1 scope).

* `calendar` is the shared calendar module of both endpoints.

### Returns

* A non-negative integer count, or

* `:not_supported` when the unit / calendar combination has no
  closed-form path. Callers should fall back to walking.

### Examples

    iex> from = Tempo.from_iso8601!("2026Y")
    iex> to = Tempo.from_iso8601!("2030Y")
    iex> Tempo.Interval.Steps.count_steps(from, to, :year, Calendrical.Gregorian)
    4

    iex> from = Tempo.from_iso8601!("2026-01")
    iex> to = Tempo.from_iso8601!("2027-03")
    iex> Tempo.Interval.Steps.count_steps(from, to, :month, Calendrical.Gregorian)
    14

    iex> from = Tempo.from_iso8601!("2026-01-01")
    iex> to = Tempo.from_iso8601!("2026-02-01")
    iex> Tempo.Interval.Steps.count_steps(from, to, :day, Calendrical.Gregorian)
    31

# `nth_step`

```elixir
@spec nth_step(Tempo.t(), non_neg_integer(), atom(), module()) ::
  Tempo.t() | :not_supported
```

Return the Tempo at step `n` from `from` at `unit` granularity.

### Arguments

* `from` is the anchor `t:Tempo.t/0`.

* `n` is a non-negative integer step count (0 returns `from`).

* `unit` is one of `:year`, `:month`, `:day` (Phase 1 scope).

* `calendar` is the calendar module.

### Returns

* The Tempo at step `n`, or

* `:not_supported` for unhandled units.

### Examples

    iex> from = Tempo.from_iso8601!("2026-01-01")
    iex> Tempo.Interval.Steps.nth_step(from, 30, :day, Calendrical.Gregorian)
    ~o"2026Y1M31D"

    iex> from = Tempo.from_iso8601!("2026-01")
    iex> Tempo.Interval.Steps.nth_step(from, 14, :month, Calendrical.Gregorian)
    ~o"2027Y3M"

# `on_step?`

```elixir
@spec on_step?(Tempo.t(), Tempo.t(), atom(), module()) :: boolean() | :not_supported
```

Return `true` when `element` falls on a `unit`-step boundary
anchored at `from`.

### Arguments

* `element` is any `t:Tempo.t/0`.

* `from` is the anchor `t:Tempo.t/0`.

* `unit` is one of `:year`, `:month`, `:day` (Phase 1 scope).

* `calendar` is the calendar module.

### Returns

* `true` if `element` equals `from` advanced by some non-negative
  integer number of `unit` steps, `false` otherwise, or
  `:not_supported`.

### Examples

    iex> from = Tempo.from_iso8601!("2026-01-01")
    iex> elt = Tempo.from_iso8601!("2026-01-31")
    iex> Tempo.Interval.Steps.on_step?(elt, from, :day, Calendrical.Gregorian)
    true

---

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