# `Tempo.Duration`
[🔗](https://github.com/kipcole9/tempo/blob/v0.20.0/lib/tempo/duration.ex#L1)

A calendar-relative duration — a list of `{unit, amount}`
pairs such as `[year: 1, month: 6]`. Produced by the ISO 8601
parser (`P1Y6M`), the RRULE encoder (as the `FREQ + INTERVAL`
cadence), and arithmetic helpers in `Tempo.Math`.

# `t`

```elixir
@type t() :: %Tempo.Duration{time: [{unit(), integer() | Tempo.Microsecond.t()}]}
```

# `unit`

```elixir
@type unit() ::
  :year
  | :month
  | :week
  | :day
  | :hour
  | :minute
  | :second
  | :microsecond
  | :day_of_year
  | :day_of_week
```

# `new`

```elixir
@spec new(keyword()) :: {:ok, t()} | {:error, Exception.t()}
```

Construct a `t:Tempo.Duration.t/0` from a keyword list of
`{unit, amount}` pairs.

Components can be passed in any order; `new/1` reorders them
coarse-to-fine before building the struct.

### Arguments

* `components` is a keyword list of duration units.

### Options

Every value must be an integer. Negative values are permitted
(reverse-direction duration).

* `:year` is the year count.

* `:month` is the month count.

* `:week` is the week count.

* `:day` is the day count.

* `:day_of_year` is a day-of-year offset (used by RRULE expansion).

* `:day_of_week` is a day-of-week offset (used by RRULE expansion).

* `:hour` is the hour count.

* `:minute` is the minute count.

* `:second` is the second count.

### Returns

* `{:ok, t()}` on success.

* `{:error, reason}` when a key is unknown or a value is not an
  integer.

### Examples

    iex> {:ok, d} = Tempo.Duration.new(year: 1, month: 6)
    iex> d.time
    [year: 1, month: 6]

    iex> {:ok, d} = Tempo.Duration.new(month: 6, year: 1)
    iex> d.time
    [year: 1, month: 6]

# `new!`

```elixir
@spec new!(keyword()) :: t()
```

Bang variant of `new/1`.

# `to_unit`

```elixir
@spec to_unit(t(), unit(), keyword()) :: {:ok, float()} | {:error, Exception.t()}
```

Express a duration as a single magnitude in `unit`, as a float.

For a duration built only from fixed-length units (microsecond
through week, with `day = 24 h` and `week = 7 d`), the conversion
is exact and needs no context. A duration carrying `:month` or
`:year` has no fixed length, so it converts only against a
reference date supplied as `:relative_to` — the duration is applied
to that date and the elapsed time measured on the UTC time line
(DST-exact when the reference is zoned). Tempo never assumes a
nominal month or year; it returns an error instead.

### Arguments

* `duration` is a `t:t/0`.

* `unit` is the target unit — one of `:microsecond`, `:second`,
  `:minute`, `:hour`, `:day`, `:week`. (`:month`/`:year` are not
  fixed magnitudes and cannot be a target.)

### Options

* `:relative_to` is a `t:Tempo.t/0` reference date. Required to
  convert a duration containing `:month` or `:year`; optional
  otherwise, where a zoned reference makes `:day`/`:week` DST-exact.

### Returns

* `{:ok, magnitude}` where `magnitude` is a `float()`.

* `{:error, reason}` when the duration needs a `:relative_to` it
  was not given, the target unit is not fixed-length, or the
  reference is invalid.

### Examples

    iex> Tempo.Duration.to_unit(~o"PT90M", :hour)
    {:ok, 1.5}

    iex> Tempo.Duration.to_unit(~o"P2D", :hour)
    {:ok, 48.0}

    iex> Tempo.Duration.to_unit(~o"P1M", :day, relative_to: ~o"2026-02-01")
    {:ok, 28.0}

# `to_unit!`

```elixir
@spec to_unit!(t(), unit(), keyword()) :: float()
```

Bang variant of `to_unit/3` — returns the float or raises.

### Examples

    iex> Tempo.Duration.to_unit!(~o"PT8H", :hour)
    8.0

---

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