# `Tempo.Format`
[🔗](https://github.com/kipcole9/tempo/blob/v0.5.0/lib/tempo/format.ex#L1)

Locale-aware formatting for `Tempo` values, dispatching to the
Localize library.

`Tempo.to_string/1,2` and the `String.Chars` implementation for
`Tempo`, `Tempo.Interval`, and `Tempo.IntervalSet` route through
this module. Callers don't need to use it directly.

### Rendering rule — Tempo values are intervals

A `%Tempo{}` at year or month resolution is a bounded span, and
its user-visible rendering reflects that span. Rule:

* `~o"2026"` (year resolution) → `"Jan – Dec 2026"`.
  Iteration over a year yields months; the first and last months
  are shown.

* `~o"2026-06"` (month resolution) → `"Jun 1 – 30, 2026"`.
  Iteration over a month yields days; first and last day.

* `~o"2026-06-15"` (day resolution) → `"Jun 15, 2026"`. A day is
  atomic at human display granularity; collapse to a single
  value.

* `~o"2026-06-15T14:30"` (minute or finer) → `"Jun 15, 2026, 2:30 PM"`.
  Same collapse rationale.

The cutoff between "expand as closed interval" and "collapse as
single value" lives at **day granularity** by design — matching
how people talk: "2026" is January-through-December; "June 2026"
is June 1 to 30; "June 15" is just June 15.

### Closed vs half-open at the display boundary

The underlying interval is always half-open `[from, to)`. For
display we compute the **closed** last member — `to - 1
iteration_unit` — so users see `"Jan – Dec 2026"`, not
`"Jan – Jan 2026/2027"`. The closure happens purely at
the display layer; the internal representation is unchanged.

### Calendar awareness

The map passed to Localize carries the Tempo's `:calendar`
field, so Localize selects the appropriate CLDR data when
available for that calendar. Coverage of non-Gregorian calendars
depends on Localize's CLDR data and may fall back to
Gregorian-equivalent formatting where calendar-specific data is
absent.

# `to_relative_string`

```elixir
@spec to_relative_string(
  Tempo.t() | Tempo.Interval.t(),
  keyword()
) :: String.t()
```

Format a Tempo or Tempo.Interval as a locale-aware relative
time string, like `"3 hours ago"` or `"in 2 days"`.

Delegated from `Tempo.to_relative_string/1,2`.

# `to_string`

```elixir
@spec to_string(
  Tempo.t() | Tempo.Interval.t() | Tempo.IntervalSet.t() | Tempo.Duration.t(),
  keyword()
) :: String.t()
```

Format a Tempo, Interval, or IntervalSet as a locale-aware
string.

Delegated from `Tempo.to_string/1,2`.

---

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