# `Zizq.Cron`
[🔗](https://github.com/zizq-labs/zizq-elixir/blob/v0.8.0/lib/zizq/cron.ex#L4)

A named group of scheduled jobs.

The same struct is used for both input and what the server returns,
so installing a schedule and amending one use the same shape:

    Zizq.Cron.new("my_app")
    |> Zizq.Cron.put_entry(
      name: "nightly_cleanup",
      expression: "0 3 * * *",
      job: MyApp.Cleanup.new(%{})
    )
    |> Zizq.replace_cron(MyApp.Zizq)

Declared all at once instead, if that reads better:

    Zizq.Cron.new("my_app",
      entries: [
        [name: "nightly_cleanup", expression: "0 3 * * *", job: MyApp.Cleanup.new(%{})]
      ]
    )
    |> Zizq.replace_cron(MyApp.Zizq)

Groups exist so a schedule can be replaced, paused or removed as a
unit — typically one group per application, holding every entry that
application owns.

Normally the schedule lives in code and is installed on boot.
Installing is atomic and idempotent, so every instance of an
application can do it on startup without coordinating.

## Amending a schedule already on the server

A schedule can also be read, changed and put back:

    Zizq.get_cron!("my_app", MyApp.Zizq)
    |> Zizq.Cron.delete_entry("nightly_cleanup")
    |> Zizq.replace_cron(MyApp.Zizq)

This suits one-off changes made by one operator. It reads and writes
as two steps, so it is not the way to make a change from running
application code where several instances might do it at once — for
that, `Zizq.pause_cron_entry/2` and `Zizq.delete_cron_entry/2`
change a single entry in one request.

## Fields

  * `:name` — the group's name.
  * `:entries` — the `Zizq.CronEntry` structs it holds.
  * `:timezone` — an IANA name, e.g. `"Australia/Melbourne"`,
    applied to every entry that does not specify one of its own. The
    server's own timezone when unset. Because installing replaces
    the group whole, leaving it out clears the timezone.
  * `:paused` — whether the whole group is suspended. A paused group
    fires nothing, whatever its entries say. Left `nil`, an existing
    group keeps its current state and a new one starts running.
  * `:paused_at`, `:resumed_at` — when it was last suspended and
    resumed. Read-only.

## Timezones

Most schedules want one timezone throughout, which is what the
group's `:timezone` is for:

    Zizq.Cron.new("my_app",
      timezone: "Australia/Melbourne",
      entries: [
        [name: "nightly_cleanup", expression: "0 3 * * *", job: MyApp.Cleanup.new(%{})]
      ]
    )

An entry specifying its own `:timezone` uses that instead, so one
schedule can hold entries in several zones. With neither set, the
server evaluates the expression in its own local timezone.

Requires Zizq 0.7.0 or newer on the server. Against an older server
the group's timezone is ignored, and entries fall back to the
server's local timezone.

# `t`

```elixir
@type t() :: %Zizq.Cron{
  entries: [Zizq.CronEntry.t()],
  name: String.t() | nil,
  paused: boolean() | nil,
  paused_at: DateTime.t() | nil,
  resumed_at: DateTime.t() | nil,
  timezone: String.t() | nil
}
```

# `delete_entry`

```elixir
@spec delete_entry(t(), String.t()) :: t()
```

Remove an entry by name. Removing one that is not there changes
nothing.

# `entry`

```elixir
@spec entry(t(), String.t()) :: Zizq.CronEntry.t() | nil
```

Look up one entry by name, or `nil`.

# `new`

```elixir
@spec new(String.t(), keyword()) :: t()
```

Build a schedule.

## Options

  * `:entries` — `Zizq.CronEntry` structs, keyword lists or maps.
  * `:timezone` — an IANA name applied to entries that do not specify
    one of their own.
  * `:paused` — whether the group starts suspended.

# `put_entry`

```elixir
@spec put_entry(t(), Zizq.CronEntry.t() | keyword() | map()) :: t()
```

Add an entry, or replace the one with that name.

Upsert rather than append, so putting the same schedule twice leaves
one entry — the behaviour the whole group already has when
installed twice.

---

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