# `Feel.Analysis.MomentArithmetic`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/analysis/moment_arithmetic.ex#L1)

The times and dates and times that a time or a date and time plus or minus a duration can be (D-109).

The run (`Feel.Runtime.Temporal`) moves a date and time in two ways, and a time in one:

| The duration | The date and time it gives |
| ------------ | -------------------------- |
| `years and months duration` | the months added to the date of the **clock**, the day cut to the length of the month, the time of the day and the zone kept; `null` when the year leaves the calendar (as for a date, `Feel.Analysis.DateArithmetic`) |
| `days and time duration` | the seconds added to the **instant**: elapsed time, so the clock follows by the offset the zone has then (a zone id can have another offset); `null` when the clock leaves the calendar |

| The duration | The time it gives |
| ------------ | ----------------- |
| `days and time duration` | the seconds added to the second of the day, which wraps round: `23:00:00` plus two hours is `01:00:00`; the zone is kept, and it is never `null` |

Of the three lines of a set (`Feel.Type.Moment`), the clock of the values with no zone moves with the clock and the
second, so it is worked out exactly: by months, the days of the clock as `DateArithmetic` does for a date with the
time of the day kept, and by seconds, as a number plus the seconds. A value with a zone is known by its instant and its
clock, and **one of them follows from the move**, and the other only within the largest offset of it:

| A value with a zone, plus | The line that is exact | The other |
| ------------------------- | ---------------------- | --------- |
| months | the clock | the instant, within 18 hours of it |
| seconds | the instant (a zone id moves by elapsed time as a fixed offset does), less the time the clock went back in the zone, if it did, which the run reads as the earlier instant (`Feel.Type.Moment.overlap/1`) | the clock, within 18 hours of it |
| seconds, for a time | both: the zone of a time has an offset that does not change with the day | |

The fraction of a second is a line of the set (D-118), and a number of seconds that is not whole carries it over a
second: `10:00:00.7` plus `PT0.5S` is `10:00:01.2`, and `10:00:00.2` plus it is `10:00:00.7`. The seconds are added in
their whole part and their fraction, and the fractions of the value and of the number are added: a sum below a second
moves the second by the whole part and leaves the sum, and a sum of a second or more moves it by one more and leaves
the rest. So the second and the fraction of the result are exact where the fractions of the value are, and where they
are not (any, `[0..1)`) the second is that of the whole part or one more.

The result is `null` for a date and time exactly when the clock of a move is outside the calendar (for a value with a
zone, when it can be, since the clock is known to 18 hours), and never for a time.

## The difference

Two times, two dates and times, or a date and a date and time, are subtracted (`difference/4`, D-109) as the run does
(`Feel.Runtime.Temporal`): the seconds between their **clocks** when neither has a zone, between their **instants** when
both have one, and `null` when one has a zone and the other has none. A date is taken as midnight UTC, so it has a zone.
The image is the second and the value has a fraction of it (a line of the set, D-118), so the seconds between two are
those between their images and the fraction of the first less that of the second: `10:00:00.9` less `09:00:00.1` is
3,600.8, and `10:00:00` less `09:00:00` is 3,600 exactly. Where the fractions are not known (any, `[0..1)`) it is a
second either way, and for a date, which has none, on the side of the fraction of the date and time. The run rounds
the seconds to the 34 digits of a number, so the ends are rounded outwards to them.

    iex> alias Feel.Type.{Moment, NumberSet}
    iex> {:ok, date_time} = Feel.Value.DateTime.parse("2021-01-31T10:00:00")
    iex> {:ok, [clock]} = Moment.images(date_time)
    iex> {set, false} = Feel.Analysis.MomentArithmetic.shift(:date_and_time, NumberSet.point(clock), :years_and_months_duration, NumberSet.point(Decimal.new(1)), :add)
    iex> Feel.Type.Union.format([{:date_and_time, set}])
    "date and time = 2021-02-28T10:00:00"

# `difference`

```elixir
@spec difference(atom(), Feel.Type.NumberSet.t(), atom(), Feel.Type.NumberSet.t()) ::
  {Feel.Type.NumberSet.t(), boolean()} | :error
```

The seconds between a time, a date and time or a date and another of the kinds that the run subtracts, as the set of the
days and time durations they can be, and whether the result can be `null` (one has a zone and the other has none). It is
`:error` for kinds that are not subtracted.

    iex> alias Feel.Type.{Moment, NumberSet}
    iex> {:ok, later} = Feel.Value.Time.parse("10:00:00")
    iex> {:ok, earlier} = Feel.Value.Time.parse("09:00:00")
    iex> {:ok, [a]} = Moment.images(later)
    iex> {:ok, [b]} = Moment.images(earlier)
    iex> {seconds, false} = Feel.Analysis.MomentArithmetic.difference(:time, NumberSet.point(a), :time, NumberSet.point(b))
    iex> NumberSet.format(seconds)
    "[3599..3601]"

# `shift`

```elixir
@spec shift(
  Feel.Type.Moment.kind(),
  Feel.Type.NumberSet.t(),
  :years_and_months_duration | :days_and_time_duration,
  Feel.Type.NumberSet.t(),
  :add | :subtract
) :: {Feel.Type.NumberSet.t(), boolean()} | :error
```

The set of a time or a date and time plus (`:add`) or minus (`:subtract`) a duration of a set, and whether the result
can be `null`. It is `:error` if the duration has no value that the move takes (a set with no whole month).

---

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