# `Tempus`
[🔗](https://github.com/am-kantox/tempus/blob/v1.0.0-rc.0/lib/tempus.ex#L1)

`Tempus` is a high-performance Elixir library for working with time slots,
business schedules, availability streams, and slot arithmetics.

### Features & Abstractions
- **Schedules (`Tempus.Slots`)**: Represents collections of time slots forming an Abelian group structure. Supports both eager list (`Tempus.Slots.List`) and lazy stream (`Tempus.Slots.Stream`) backends.
- **Interval Model**: Models slots as half-open intervals $[from, to)$ (`from_open: false`, `to_open: true`). Infinite bounds (`nil`) are open limits ($-infty, +infty$).
- **Schedule Arithmetics**:
  - `Tempus.add/4`: Adds or subtracts durations (seconds, hours, days) while skipping busy/unavailable slots.
  - `Tempus.next_free/2` & `Tempus.next_busy/2`: Navigating schedules forward or backward to locate available or occupied time slices.
  - `Tempus.days_ahead/3`: Computes N business days ahead considering holidays, weekends, or arbitrary busy slots.

# `count`

```elixir
@type count() :: :infinity | :stream | non_neg_integer()
```

Number of slots (`:stream` means lazy folding, unknown upfront)

# `direction`

```elixir
@type direction() :: :fwd | :bwd
```

Direction for slots navigation

# `option`

```elixir
@type option() ::
  {:origin, Tempus.Slot.origin()}
  | {:count, count() | neg_integer()}
  | {:direction, direction()}
```

Navigation option

# `options`

```elixir
@type options() :: [option()]
```

Argument containing navigation options

# `slice_type`

```elixir
@type slice_type() :: :greedy | :reluctant
```

The type defining how slicing is to be applied.
When `:greedy`, overlapping boundary slots would be included,
  `:reluctant` would take only those fully contained in the interval.

# `add`

```elixir
@spec add(
  slots :: Tempus.Slots.t(),
  origin :: DateTime.t(),
  amount_to_add :: integer(),
  unit :: System.time_unit()
) :: DateTime.t()
```

Adds an amount of units to the origin, considering slots given.

### Examples

    iex> slots = [
    ...>   ~D|2020-08-07|,
    ...>   ~D|2020-08-10|,
    ...>   ~D|2020-08-11|,
    ...>   ~D|2020-08-14|
    ...> ] |> Enum.into(Tempus.Slots.new(:stream, []))
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, 0, :second)
    ~U[2020-08-12 00:00:00Z]
    iex> Tempus.add(slots, ~U|2020-08-12 01:00:00Z|, 0, :second)
    ~U[2020-08-12 01:00:00Z]
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, -10*60+1, :second)
    ~U[2020-08-09 23:50:01Z]
    iex> Tempus.add(slots, ~U|2020-08-12 00:09:00Z|, -10*60, :second)
    ~U[2020-08-09 23:59:00Z]
    iex> Tempus.add(slots, ~U|2020-08-12 00:10:00Z|, -10*60, :second)
    ~U[2020-08-12 00:00:00Z]
    iex> Tempus.add(slots, ~U|2020-08-12 00:10:00Z|, -10*60-1, :second)
    ~U[2020-08-09 23:59:59Z]
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, 10*60, :second)
    ~U[2020-08-12 00:10:00Z]
    iex> Tempus.add(slots, ~U|2020-08-12 00:00:00Z|, 10*60, :second)
    ~U[2020-08-12 00:10:00Z]
    iex> Tempus.add(slots, ~U|2020-08-09 23:55:00Z|, 10*60, :second)
    ~U[2020-08-12 00:05:00Z]
    iex> Tempus.add(slots, ~U|2020-08-08 23:55:00Z|, 10*60, :second)
    ~U[2020-08-09 00:05:00Z]
    iex> Tempus.add(slots, ~U|2020-08-06 23:55:00Z|, 2*3600*24 + 10*60, :second)
    ~U[2020-08-12 00:05:00Z]

    iex> slots = Tempus.Slots.new(:stream, [])
    iex> Tempus.add(slots, ~U|2020-08-12 01:00:00Z|, 0, :second)
    ~U[2020-08-12 01:00:00Z]
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, 5*60+1, :second)
    ~U[2020-08-11 23:05:01.000000Z]
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, -10*60+1, :second)
    ~U[2020-08-11 22:50:01Z]

    iex> slots = Tempus.Slots.new(:list, [])
    iex> Tempus.add(slots, ~U|2020-08-12 01:00:00Z|, 0, :second)
    ~U[2020-08-12 01:00:00Z]
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, 5*60+1, :second)
    ~U[2020-08-11 23:05:01Z]
    iex> Tempus.add(slots, ~U|2020-08-11 23:00:00Z|, -10*60+1, :second)
    ~U[2020-08-11 22:50:01Z]
    iex> slots |> Tempus.add(1) |> DateTime.to_date()
    Date.utc_today()

# `days_add`
*since 0.2.0* 

```elixir
@spec days_add(slots :: Tempus.Slots.t(), opts :: options()) :: [Date.t()]
```

Returns the reversed list of free days after origin.

### Examples

    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> Tempus.days_add(slots, origin: ~D|2020-08-07|, count: 3) |> hd()
    ~D|2020-08-12|
    iex> Tempus.days_add(slots, origin: ~D|2020-08-07|, count: 3, direction: :fwd) |> hd()
    ~D|2020-08-12|
    iex> Tempus.days_add(slots, origin: ~D|2020-08-07|, count: -3, direction: :bwd) |> hd()
    ~D|2020-08-12|
    iex> Tempus.days_add(slots, origin: ~D|2020-08-12|, count: -4) |> hd()
    ~D|2020-08-06|
    iex> Tempus.days_add(slots, origin: ~D|2020-08-12|, count: 4, direction: :bwd) |> hd()
    ~D|2020-08-06|
    iex> Tempus.days_add(slots, origin: ~D|2020-08-12|, count: -4, direction: :fwd) |> hd()
    ~D|2020-08-06|

# `days_ago`

> This function is deprecated. Use days_add/2 with negative count or `:bwd` forth parameter instead.

```elixir
@spec days_ago(slots :: Tempus.Slots.t(), origin :: Date.t(), count :: integer()) :: [
  Date.t()
]
```

Returns the reversed list of free days after origin.

### Examples

    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> Tempus.days_ago(slots, ~D|2020-08-07|, 0)
    [~D|2020-08-06|]
    iex> Tempus.days_ago(slots, ~D|2020-08-12|, 4) |> hd()
    ~D|2020-08-06|

# `days_ahead`

> This function is deprecated. Use days_add/2 instead.

```elixir
@spec days_ahead(slots :: Tempus.Slots.t(), origin :: Date.t(), count :: integer()) ::
  [Date.t()]
```

Returns the reversed list of free days after origin.

### Examples

    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> Tempus.days_ahead(slots, ~D|2020-08-07|, 0)
    [~D|2020-08-08|]
    iex> Tempus.days_ahead(slots, ~D|2020-08-07|, 3) |> hd()
    ~D|2020-08-12|

# `drop_while`
*since 0.7.0* 

```elixir
@spec drop_while(slots :: Tempus.Slots.t(), fun :: (Tempus.Slot.t() -&gt; boolean())) ::
  Tempus.Slots.t()
```

Drops slots at the beginning of the `%Slots{}` struct while `fun` returns a truthy value.

### Examples

    iex> import Tempus.Guards
    ...> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> slots
    ...> |> Tempus.drop_while(&is_slot_coming_before(&1, Tempus.Slot.wrap(~D|2020-08-09|)))
    ...> |> Enum.count()
    1

# `free?`

```elixir
@spec free?(slots :: Tempus.Slots.t(), slot :: Tempus.Slot.origin()) :: boolean()
```

Checks whether the slot is disjoined against slots.

### Examples

    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> Tempus.free?(slots, ~D|2020-08-07|)
    false
    iex> Tempus.free?(slots, ~D|2020-08-08|)
    true
    iex> Tempus.free?(slots, ~U|2020-08-09T23:59:59.999999Z|)
    true
    iex> Tempus.free?(slots, ~U|2020-08-10T00:00:00.000000Z|)
    false

# `guess`

```elixir
@spec guess(input :: nil | binary()) :: {:ok, Tempus.Slot.t()} | {:error, any()}
```

Helper to instantiate slot from any known format, by wrapping the argument.

## Examples

    iex> Tempus.guess("2023-04-10")
    {:ok, Tempus.Slot.wrap(~D[2023-04-10])}
    iex> Tempus.guess(nil)
    {:ok, %Tempus.Slot{from: nil, to: nil, from_open: true, to_open: true}}
    iex> Tempus.guess("∞")
    {:ok, %Tempus.Slot{from: nil, to: nil, from_open: true, to_open: true}}
    iex> Tempus.guess("")
    {:ok, %Tempus.Slot{from: nil, to: nil, from_open: true, to_open: true}}
    iex> Tempus.guess("2023-04-10T10:00:00Z")
    {:ok, Tempus.Slot.wrap(~U[2023-04-10T10:00:00Z])}
    iex> Tempus.guess("10:00:00")
    {:ok, Date.utc_today() |> DateTime.new!(Time.from_erl!({10, 0, 0})) |> Tempus.Slot.wrap()}
    iex> Tempus.guess("20230410T235007.123+0230")
    if Version.compare(System.version(), "1.14.0") == :lt do
      {:error, :invalid_format}
    else
      {:ok, Tempus.Slot.wrap(~U[2023-04-10T21:20:07.123Z])}
    end
    iex> Tempus.guess("2023-04-10-15")
    {:error, :invalid_format}

# `guess`

```elixir
@spec guess(from :: nil | binary(), to :: nil | binary()) ::
  {:ok, Tempus.Slot.t()} | {:error, any()}
```

Helper to instantiate slot from any known format, by joining the arguments.

## Examples

    iex> import Tempus.Sigils
    iex> Tempus.guess("2023-04-10", "2023-04-12")
    {:ok, %Tempus.Slot{from: ~U[2023-04-10 00:00:00.000000Z], to: ~U[2023-04-13 00:00:00.000000Z], from_open: false, to_open: true}}
    iex> Tempus.guess("2023-04-10", nil)
    {:ok, %Tempus.Slot{from: ~U[2023-04-10 00:00:00.000000Z], to: ~U[2023-04-11 00:00:00.000000Z], from_open: false, to_open: true}}
    iex> Tempus.guess("2023-04-10T10:00:00Z", "2023-04-12")
    {:ok, %Tempus.Slot{from: ~U[2023-04-10 10:00:00Z], to: ~U[2023-04-13 00:00:00.000000Z], from_open: false, to_open: true}}
    iex> Tempus.guess("20230410T235007.123+0230", "2023-04-12")
    if Version.compare(System.version(), "1.14.0") == :lt do
      {:error, {:invalid_arguments, [from: :invalid_format]}}
    else
      {:ok, %Tempus.Slot{from: ~U[2023-04-10 21:20:07.123Z], to: ~U[2023-04-13 00:00:00.000000Z], from_open: false, to_open: true}}
    end
    iex> Tempus.guess("2023-04-10", :ok)
    {:error, {:invalid_arguments, [to: :invalid_argument]}}
    iex> Tempus.guess(:ok, "2023-04-10")
    {:error, {:invalid_arguments, [from: :invalid_argument]}}
    iex> Tempus.guess("2023-04-10-15", :ok)
    {:error, {:invalid_arguments, [from: :invalid_format, to: :invalid_argument]}}
    iex> Tempus.guess("2023-20-40", "10:70:80")
    {:error, {:invalid_arguments, [from: :invalid_date, to: :invalid_time]}}

# `new`

```elixir
@spec new(data :: Tempus.Slots.container()) :: Tempus.Slots.t(Tempus.Slots.Stream)
```

Creates the new instance of `Tempus.Slots.t(Tempus.Slots.Stream.t())` backed by stream.

This is a convinience wrapper for `Tempus.Slots.new(:stream, data)`.

# `next_busy`

> This function is deprecated. Use `slice/3` instead.

```elixir
@spec next_busy(Tempus.Slots.t(), options()) ::
  [Tempus.Slot.t()] | Tempus.Slot.t() | nil | no_return()
```

Returns the next **busy** slot from the slots passed as a first argument,
  that immediately follows `origin`. If slots are overlapped, the overlapped
  one gets returned.

### Examples

    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> Tempus.next_busy(slots, origin: %Tempus.Slot{from: ~U|2020-08-08 23:00:00Z|, to: ~U|2020-08-09 12:00:00Z|})
    %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: %Tempus.Slot{from: ~U|2020-08-07 11:00:00Z|, to: ~U|2020-08-07 12:00:00Z|}, count: 2) |> hd()
    %Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: %Tempus.Slot{from: ~U|2020-08-07 11:00:00Z|, to: ~U|2020-08-08 12:00:00Z|})
    %Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: %Tempus.Slot{from: ~U|2020-08-07 11:00:00Z|, to: ~U|2020-08-10 12:00:00Z|})
    %Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-07|)
    %Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-08|)
    %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-08|, direction: :bwd)
    %Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-10|, direction: :bwd)
    %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-10|, direction: :bwd, count: :infinity)
    slots
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-10|, count: 3.1415)
    ** (Tempus.ArgumentError) invalid argument: expected ‹Elixir.Integer›, got: ‹3.1415›
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-10|, direction: :bwd, count: 2)
    Enum.to_list(slots)
    iex> Tempus.next_busy(slots, origin: ~D|2020-08-10|, direction: :bwd, count: 1)
    Enum.drop(slots, 1)
    iex> Tempus.next_busy(slots, origin: %Tempus.Slot{from: ~U|2020-08-11 11:00:00Z|, to: ~U|2020-08-11 12:00:00Z|})
    nil
    iex> Tempus.next_busy(slots, origin: %Tempus.Slot{from: ~U|2020-08-06 11:00:00Z|, to: ~U|2020-08-06 12:00:00Z|}, direction: :bwd)
    nil
    iex> Tempus.next_busy(%Tempus.Slots{})
    nil

# `next_free`

> This function is deprecated. Use `slice/3` instead.

```elixir
@spec next_free(Tempus.Slots.t(), options()) ::
  [Tempus.Slot.t()] | Tempus.Slot.t() | no_return()
```

Returns the next **free** slot from the slots passed as a first argument,
  that immediately follows `origin`. If slots are overlapped, the overlapped
  one gets returned.

### Examples

    iex> import Tempus.Sigils
    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|),
    ...>   Tempus.Slot.wrap(~D|2020-08-12|),
    ...>   Tempus.Slot.wrap(~D|2020-08-14|),
    ...>   Tempus.Slot.wrap(~D|2030-08-14|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> Tempus.next_free(slots, origin: %Tempus.Slot{from: ~U|2020-08-08 23:00:00Z|, to: ~U|2020-08-09 12:00:00Z|})
    %Tempus.Slot{from: ~U[2020-08-08 00:00:00.000000Z], to: ~U[2020-08-10 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_free(slots, origin: %Tempus.Slot{from: ~U|2020-08-06 11:00:00Z|, to: ~U|2020-08-06 12:00:00Z|})
    %Tempus.Slot{from: nil, to: ~U[2020-08-07 00:00:00.000000Z], from_open: true, to_open: true}
    iex> Tempus.next_free(slots, origin: ~U|2020-08-13 01:00:00.000000Z|)
    %Tempus.Slot{from: ~U[2020-08-13 00:00:00.000000Z], to: ~U[2020-08-14 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_free(slots, origin: ~D|2020-08-13|)
    %Tempus.Slot{from: ~U[2020-08-13 00:00:00.000000Z], to: ~U[2020-08-14 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_free(slots, origin: ~D|2020-08-14|)
    %Tempus.Slot{from: ~U[2020-08-15 00:00:00.000000Z], to: ~U[2030-08-14 00:00:00.000000Z], from_open: false, to_open: true}
    iex> Tempus.next_free(slots)
    %Tempus.Slot{from: ~U[2020-08-15 00:00:00.000000Z], to: ~U[2030-08-14 00:00:00.000000Z], from_open: false, to_open: true}

# `parse_cron`

```elixir
@spec parse_cron(
  origin :: Tempus.Slot.origin() | nil,
  cron :: binary() | Tempus.Crontab.t()
) ::
  Tempus.Slots.t(Stream)
```

Creates a `Tempus.Slots.Stream.t()` slots stream by a cron-like definition.

### Examples

    iex> import Tempus.Sigils
    iex> ~U[2024-06-07 12:00:00Z] |> Tempus.parse_cron("10-30/15 */4 1 */1 6,7") |> Enum.take(5)
    [
      %Tempus.Slot{from: ~U[2024-06-08 00:10:00Z], to: ~U[2024-06-08 00:10:00Z], from_open: false, to_open: false},
      %Tempus.Slot{from: ~U[2024-06-08 00:25:00Z], to: ~U[2024-06-08 00:25:00Z], from_open: false, to_open: false},
      %Tempus.Slot{from: ~U[2024-06-08 04:10:00Z], to: ~U[2024-06-08 04:10:00Z], from_open: false, to_open: false},
      %Tempus.Slot{from: ~U[2024-06-08 04:25:00Z], to: ~U[2024-06-08 04:25:00Z], from_open: false, to_open: false},
      %Tempus.Slot{from: ~U[2024-06-08 08:10:00Z], to: ~U[2024-06-08 08:10:00Z], from_open: false, to_open: false}
    ]

# `slice`
*since 0.7.0* 

```elixir
@spec slice(
  slots :: Tempus.Slots.t(),
  from :: Tempus.Slots.locator(),
  to :: Tempus.Slots.locator(),
  type :: slice_type()
) :: Tempus.Slots.t()
```

Slices the `%Slots{}` based on origins `from` and `to` and an optional type
  (default: `:reluctant`.) Returns sliced `%Slots{}` back.

### Examples

    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> slots |> Tempus.slice(~D|2020-08-07|, ~D|2020-08-11|) |> Enum.count()
    1

# `slot`

# `slot!`

# `slots`

Syntactic sugar for `|> Enum.into(%Slots{})`.

## Examples

    iex> [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|),
    ...>   Tempus.Slot.wrap(~D|2020-08-12|)
    ...> ] |> Tempus.slots()
    %Tempus.Slots{slots: %Tempus.Slots.List{slots: [
        %Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true},
        %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true},
        %Tempus.Slot{from: ~U[2020-08-12 00:00:00.000000Z], to: ~U[2020-08-13 00:00:00.000000Z], from_open: false, to_open: true}]}}

# `stream`

```elixir
@spec stream(Tempus.Slot.origin(), (Tempus.Slot.t() -&gt; Tempus.Slot.t())) ::
  Tempus.Slots.t(Tempus.Slots.Stream)
```

Syntactic sugar for `Tempus.Slots.Stream.iterate/3` with default options `return_as: :slots, join: true`.

### Examples

    iex> import Tempus.Sigils
    ...> Tempus.stream(~D|2024-01-20|, &Tempus.Slot.shift(&1, by: rem(&1.from.day, 2) + 1, unit: :day)) |> Enum.take(3)
    [%Tempus.Slot{from: ~U[2024-01-20 00:00:00.000000Z], to: ~U[2024-01-22 00:00:00.000000Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2024-01-23 00:00:00.000000Z], to: ~U[2024-01-24 00:00:00.000000Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2024-01-25 00:00:00.000000Z], to: ~U[2024-01-26 00:00:00.000000Z], from_open: false, to_open: true}]

# `take`

```elixir
@spec take(Tempus.Slots.t(), non_neg_integer()) :: Tempus.Slots.t(Tempus.Slots.List)
```

Takes an amount of elements from the beginning of the `Tempus.Slots` structure, returning `Tempus.Slots.List` instance.

If amount is 0, it returns `Tempus.Slots.List.new/0`.

### Examples

    iex> import Tempus.Sigils
    ...> Tempus.stream(~D|2024-01-20|, &Tempus.Slot.shift(&1, by: rem(&1.from.day, 2) + 1, unit: :day)) |> Tempus.take(3)
    Tempus.Slots.new(:list, [%Tempus.Slot{from: ~U[2024-01-20 00:00:00.000000Z], to: ~U[2024-01-22 00:00:00.000000Z], from_open: false, to_open: true},
                             %Tempus.Slot{from: ~U[2024-01-23 00:00:00.000000Z], to: ~U[2024-01-24 00:00:00.000000Z], from_open: false, to_open: true},
                             %Tempus.Slot{from: ~U[2024-01-25 00:00:00.000000Z], to: ~U[2024-01-26 00:00:00.000000Z], from_open: false, to_open: true}])

# `take_while`
*since 0.7.0* 

```elixir
@spec take_while(slots :: Tempus.Slots.t(), fun :: (Tempus.Slot.t() -&gt; boolean())) ::
  Tempus.Slots.t()
```

Takes slots at the beginning of the `%Slots{}` struct while `fun` returns a truthy value.

### Examples

    iex> import Tempus.Guards
    ...> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(%Tempus.Slots{})
    iex> slots
    ...> |> Tempus.take_while(&is_slot_coming_before(&1, Tempus.Slot.wrap(~D|2020-08-09|)))
    ...> |> Enum.count()
    1

---

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