# `Moar.DateTime`
[🔗](https://github.com/synchronal/moar/blob/main/lib/datetime.ex#L1)

DateTime-related functions. See also `Moar.NaiveDateTime`.

# `add`

```elixir
@spec add(DateTime.t(), Moar.Duration.t()) :: DateTime.t()
```

Adds `duration` to `date_time`.

See also `subtract/1` and `Moar.NaiveDateTime.add/2`.

> #### Note {: .info}
>
> This function is naive and intentionally doesn't account for real-world calendars and all of their complexity,
> such as leap years, leap days, daylight saving time, past and future calendar oddities, etc.
>
> As ["Falsehoods programmers believe about time"](https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca)
> says, "If you think you understand everything about time, you're probably doing it wrong."
>
> See [`Cldr.Calendar.plus/2`](https://hexdocs.pm/ex_cldr_calendars/Cldr.Calendar.html#plus/2) for one example
> of a function that is far more likely to be correct.

```elixir
iex> start = ~U[2022-01-01T00:00:00.000Z]
iex> Moar.DateTime.add(start, {3, :minute})
~U[2022-01-01T00:03:00.000Z]
```

# `at`

Makes a `DateTime` at a particular `Time` today, optionally shifting by a `Duration`.

``` elixir
iex> Moar.DateTime.at(~T[13:00:00])
DateTime.new!(Date.utc_today(), ~T[13:00:00])

iex> Moar.DateTime.at(~T[13:00:00], shift: [day: 2, hour: -3])
DateTime.new!(Date.utc_today(), ~T[10:00:00]) |> DateTime.add(2, :day)
```

# `between?`

```elixir
@spec between?(
  DateTime.t(),
  {DateTime.t(), DateTime.t()}
) :: boolean()
```

Returns true if `date_time` is inclusively inside `range` which is a tuple containing a start datetime and an end
datetime.

```elixir
iex> Moar.DateTime.between?(~U[2022-01-01T02:00:00Z], {~U[2022-01-01T01:00:00Z], ~U[2022-01-01T03:00:00Z]})
true

iex> Moar.DateTime.between?(~U[2022-01-01T04:00:00Z], {~U[2022-01-01T01:00:00Z], ~U[2022-01-01T03:00:00Z]})
false
```

# `from_iso8601!`

```elixir
@spec from_iso8601!(date_time_string :: String.t()) :: DateTime.t()
```

Like `DateTime.from_iso8601/1` but raises if the string cannot be parsed.

```elixir
iex> Moar.DateTime.from_iso8601!("2022-01-01T00:00:00Z")
~U[2022-01-01T00:00:00Z]

iex> Moar.DateTime.from_iso8601!("2022-01-01T00:00:00+0800")
** (ArgumentError) Expected "2022-01-01T00:00:00+0800" to have a UTC offset of 0, but was: 28800

iex> Moar.DateTime.from_iso8601!("Next Thursday after lunch")
** (ArgumentError) Invalid ISO8601 format: "Next Thursday after lunch"
```

# `recent?`

```elixir
@spec recent?(DateTime.t(), Moar.Duration.t() | nil) :: boolean()
```

Returns true if `date_time` is now or in the past and no older than `duration` ago (which defaults to 1 minute).
Returns false if `date_time` is in the future.

```elixir
iex> Moar.DateTime.recent?(Moar.DateTime.utc_now(minus: {30, :second}))
true

iex> Moar.DateTime.recent?(Moar.DateTime.utc_now(minus: {5, :minute}))
false

iex> Moar.DateTime.recent?(Moar.DateTime.utc_now(minus: {5, :minute}), {1, :hour})
true

iex> Moar.DateTime.recent?(Moar.DateTime.utc_now(plus: {5, :second}))
false
```

# `subtract`

```elixir
@spec subtract(DateTime.t(), Moar.Duration.t()) :: DateTime.t()
```

Subtracts `duration` from `date_time`.

See also `add/1` and `Moar.NaiveDateTime.subtract/2`.

> #### Note {: .info}
>
> This function is naive and intentionally doesn't account for real-world calendars and all of their complexity,
> such as leap years, leap days, daylight saving time, past and future calendar oddities, etc.
>
> As ["Falsehoods programmers believe about time"](https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca)
> says, "If you think you understand everything about time, you're probably doing it wrong."
>
> See [`Cldr.Calendar.minus/4`](https://hexdocs.pm/ex_cldr_calendars/Cldr.Calendar.html#minus/4) for one example
> of a function that is far more likely to be correct.

```elixir
iex> start = ~U[2022-01-01T00:03:00.000Z]
iex> Moar.DateTime.subtract(start, {3, :minute})
~U[2022-01-01T00:00:00.000Z]
```

# `to_iso8601_rounded`

```elixir
@spec to_iso8601_rounded(date_time :: DateTime.t()) :: String.t()
```

Like `DateTime.to_iso8601/1` but rounds to the nearest second first.

```elixir
iex> Moar.DateTime.to_iso8601_rounded(~U[2022-01-01T01:02:03.456789Z])
"2022-01-01T01:02:03Z"
```

# `utc_now`

```elixir
@spec utc_now([{:plus, Moar.Duration.t()}] | [{:minus, Moar.Duration.t()}]) ::
  DateTime.t()
```

Returns the current UTC time plus or minus the given duration.

```elixir
iex> Moar.DateTime.utc_now(plus: {10500, :millisecond})
...> |> Moar.Duration.format([:approx, :from_now])
"10 seconds from now"

iex> Moar.DateTime.utc_now(minus: {10, :second})
...> |> Moar.Duration.format([:approx, :ago])
"10 seconds ago"
```

# `within?`

```elixir
@spec within?(DateTime.t(), Moar.Duration.t() | nil) :: boolean()
```

Returns true if `date_time` is no older than `duration` ago, and no later than `duration` from now.

```elixir
iex> Moar.DateTime.within?(Moar.DateTime.utc_now(minus: {30, :second}), {1, :minute})
true

iex> Moar.DateTime.within?(Moar.DateTime.utc_now(plus: {30, :second}), {1, :minute})
true

iex> Moar.DateTime.within?(Moar.DateTime.utc_now(minus: {5, :minute}), {1, :minute})
false

iex> Moar.DateTime.within?(Moar.DateTime.utc_now(plus: {5, :minute}), {1, :minute})
false
```

---

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