Cron (Poolder v0.1.10)
View SourceCron
parses cron expressions and calculates execution timings.
Expressions with 6 and 5 fields are expected. The variant with 6 fields adds a
field for seconds at the first position. The variant with 5 fields corresponds
to the standard Unix behavior. The field second
will be set to 0
when
Cron.new/1
gets 5 fields.
Possible values for the fields:
+----------- second (0 - 59)
| +--------- minute (0 - 59)
| | +------- hour (0 - 23)
| | | +----- day of the month (1 - 31)
| | | | +--- month (1 - 12)
| | | | | +- day of the week (0 - 6, SUN - SAT)
| | | | | |
* * * * * *
A field may contain an asterisk *
, which means any of the possible values.
A range of values can be specified with an -
.
Example: 10-15
.
A /
can be used to setup steps. The value before the /
specifies the start
value and the value behind the /
specifies the step width.
Example: */2
, 4/2
.
Multiple values can be declared with a ,
separated list without any
whitespace.
Example: 1,5,10-15,*/2
.
The fields month
and day_of_week
are also accept names. Month accepts
Jan
, Feb
, Mar
, Apr
, May
, Jun
, Jul
, Aug
, Sep
. Oct
, Nov
and Dez
. The case does not matter.
day_of_week
accepts Sun
, Mon
, Tue
, Wed
, Thu
, Fri
and Sat
. The
case does not matter. Keep in mind that Sun
is equal to 0
and Sat
is
equal to 6
.
All Cron
functions are working with NaiveDateTime
or DateTime
that are
using the Calendar.ISO
. Any DateTime
must have the time zone Etc/UTC
.
Examples
A cron expression that triggers daily at 12:00:00
iex> "0 12 * * *"
...> |> Cron.new!()
...> |> Cron.next(~U[2021-12-06 22:11:44Z])
~U[2021-12-07 12:00:00Z]
Cron.stream/2
calculates multiple values. The following cron expression
triggers ever 30 minutes from 12 to 14 at the first day in a month.
iex> "0 */30 12-14 1 * *"
...> |> Cron.new!()
...> |> Cron.stream(from: ~U[2021-12-06 11:22:33Z])
...> |> Enum.take(8)
[
~N[2022-01-01 12:00:00],
~N[2022-01-01 12:30:00],
~N[2022-01-01 13:00:00],
~N[2022-01-01 13:30:00],
~N[2022-01-01 14:00:00],
~N[2022-01-01 14:30:00],
~N[2022-02-01 12:00:00],
~N[2022-02-01 12:30:00]
]
Summary
Functions
Returns true if the given datetime
matches the cron
.
Returns an :ok
tuple with a cron struct for the given expression string. If
the expression is invalid an :error
will be returned.
Same as new/1
, but raises an ArgumentError
exception in case of an invalid
expression.
Returns the next execution datetime.
Returns an :ok
tuple with the next execution datetime for which fun returns
a truthy value.
Same as next_while/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
Returns the previous execution datetime.
Returns an :ok
tuple with the previous execution datetime for which fun
returns a truthy value.
Same as previous_while/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
Same as previous/3
, but returns the milliseconds since last execution
datetime.
Same as previous_while/3
, but returns the milliseconds since last execution
datetime.
Same as since_while!/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
Returns a Stream
for the given cron
.
Same as next/3
, but returns the milliseconds until next execution datetime.
Same as next_while/3
, but returns the milliseconds until next execution
datetime.
Same as until_while!/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
Types
@type expression() :: String.t()
@type millisecond() :: pos_integer()
@type t() :: %Cron{ day: 1..31 | [1..31, ...] | Range.t(1..31, 1..31), day_of_week: 0..6 | [0..6, ...] | Range.t(0..6, 0..6), expression: String.t(), hour: 0..23 | [0..23, ...] | Range.t(0..23, 0..23), minute: 0..59 | [0..59, ...] | Range.t(0..59, 0..59), month: 1..12 | [1..13, ...] | Range.t(1..12, 1..12), second: 0..59 | [0..59, ...] | Range.t(0..59, 0..59) }
Functions
@spec match?(t(), DateTime.t() | NaiveDateTime.t()) :: boolean()
Returns true if the given datetime
matches the cron
.
The function truncates the precision of the given datetime
to seconds.
Examples
iex> cron = Cron.new!("0 * * * *")
iex> Cron.match?(cron, ~U[2021-11-13 06:41:39Z])
false
iex> Cron.match?(cron, ~U[2021-11-13 13:00:00Z])
true
iex> Cron.match?(cron, ~U[2021-11-13 13:00:00.999Z])
true
iex> "* * * * * *" |> Cron.new!() |> Cron.match?()
true
@spec new(expression()) :: {:ok, t()} | :error | {:error, reason()}
Returns an :ok
tuple with a cron struct for the given expression string. If
the expression is invalid an :error
will be returned.
Will accept expression with 6 (including second
) and 5 (second: 0
) fields.
Examples
iex> {:ok, cron} = Cron.new("1 2 3 * *")
iex> cron
#Cron<1 2 3 * *>
iex> {:ok, cron} = Cron.new("0 1 2 3 * *")
iex> cron
#Cron<0 1 2 3 * *>
iex> Cron.new("66 1 2 3 * *")
{:error, second: "66"}
@spec new!(expression()) :: t()
Same as new/1
, but raises an ArgumentError
exception in case of an invalid
expression.
@spec next(t(), DateTime.t() | NaiveDateTime.t()) :: DateTime.t() | NaiveDateTime.t()
Returns the next execution datetime.
If the given datetime
matches cron
, then also the following datetime is
returning. That means the resulting datetime is always greater than the given.
The function truncates the precision of the given datetime
to seconds.
Examples
iex> {:ok, cron} = Cron.new("0 0 0 * * *")
iex> Cron.next(cron, ~U[2022-01-01 12:00:00Z])
~U[2022-01-02 00:00:00Z]
iex> Cron.next(cron, ~U[2022-01-02 00:00:00Z])
~U[2022-01-03 00:00:00Z]
iex> Cron.next(cron, ~U[2022-01-02 00:00:00.999Z])
~U[2022-01-03 00:00:00Z]
@spec next_while( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime.t() -> as_boolean(term())) ) :: {:ok, DateTime.t() | NaiveDateTime.t()} | :error
Returns an :ok
tuple with the next execution datetime for which fun returns
a truthy value.
If no datetime can be found, an :error
will be returned.
The function truncates the precision of the given datetime
to seconds.
Examples
iex> {:ok, cron} = Cron.new("0 0 29 2 *")
iex> Cron.next_while(cron, fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, ~N[2044-02-29 00:00:00]}
iex> Cron.next_while(
...> cron, ~U[2044-02-29 00:00:00Z], fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, ~U[2072-02-29 00:00:00Z]}
iex> {:ok, cron} = Cron.new("0 0 1 1 *")
iex> Cron.next_while(cron, fn _ -> false end)
:error
@spec next_while!( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime.t() -> as_boolean(term())) ) :: DateTime.t() | NaiveDateTime.t()
Same as next_while/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
@spec previous(t(), DateTime.t() | NaiveDateTime.t()) :: DateTime.t() | NaiveDateTime.t()
Returns the previous execution datetime.
If the given datetime
matches cron
, then also the previous datetime is
returning. That means the resulting datetime is always lower than the given.
The function truncates the precision of the given datetime
to seconds.
Examples
iex> {:ok, cron} = Cron.new("0 0 0 * * *")
iex> Cron.previous(cron, ~U[2022-01-01 12:00:00Z])
~U[2022-01-01 00:00:00Z]
iex> Cron.previous(cron, ~U[2022-01-01 00:00:00Z])
~U[2021-12-31 00:00:00Z]
iex> Cron.previous(cron, ~U[2022-01-01 00:00:00.999Z])
~U[2021-12-31 00:00:00Z]
@spec previous_while( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime.t() -> as_boolean(term())) ) :: {:ok, DateTime.t() | NaiveDateTime.t()} | :error
Returns an :ok
tuple with the previous execution datetime for which fun
returns a truthy value.
If no datetime can be found, an :error
will be returned.
The function truncates the precision of the given datetime
to seconds.
Examples
iex> {:ok, cron} = Cron.new("0 0 29 2 *")
iex> Cron.previous_while(cron, fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, ~N[2016-02-29 00:00:00]}
iex> Cron.previous_while(
...> cron, ~U[2016-02-29 00:00:00Z], fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, ~U[1988-02-29 00:00:00Z]}
iex> {:ok, cron} = Cron.new("0 0 1 1 *")
iex> Cron.previous_while(cron, fn _ -> false end)
:error
@spec previous_while!( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime.t() -> as_boolean(term())) ) :: DateTime.t() | NaiveDateTime.t()
Same as previous_while/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
@spec since(t(), DateTime.t() | NaiveDateTime.t()) :: millisecond()
Same as previous/3
, but returns the milliseconds since last execution
datetime.
Examples
iex> {:ok, cron} = Cron.new("0 0 0 * * *")
iex> Cron.since(cron, ~U[2022-01-02 00:00:00Z])
86_400_000
iex> Cron.since(cron, ~U[2022-01-02 00:01:00Z])
60_000
iex> Cron.since(cron, ~U[2022-01-02 00:01:00.999Z])
60_999
iex> Cron.since(cron, ~U[2022-01-02 00:00:00.999Z])
86_400_999
@spec since_while( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime -> as_boolean(term())) ) :: {:ok, millisecond()} | :error
Same as previous_while/3
, but returns the milliseconds since last execution
datetime.
Examples
iex> {:ok, cron} = Cron.new("0 0 29 2 *")
iex> Cron.since_while(
...> cron,
...> ~U[2022-01-01 00:00:00Z],
...> fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, 184_291_200_000}
iex> Cron.since_while(
...> cron,
...> ~U[2016-02-29 00:00:01.999Z],
...> fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, 1_999}
iex> {:ok, cron} = Cron.new("0 0 1 1 *")
iex> Cron.since_while(cron, fn _ -> false end)
:error
@spec since_while!( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime -> as_boolean(term())) ) :: millisecond()
Same as since_while!/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.
@spec stream( t(), keyword() ) :: Enumerable.t()
Returns a Stream
for the given cron
.
The stream ends after the last execution datetime in the year 9000 or -9000.
Options:
:from
- the start datetime. Defaults toNaiveDateTime.utc_now("Etc/UTC")
.:oder
-:asc
or:desc
to get execution datetimes before or after start datetime. Defaults to:asc
.
Examples
iex> cron = Cron.new!("0 0 12 1 * *")
iex> stream = Cron.stream(cron, from: ~U[2022-06-05 00:00:00Z])
iex> Enum.take(stream, 3)
[
~N[2022-07-01 12:00:00],
~N[2022-08-01 12:00:00],
~N[2022-09-01 12:00:00],
]
iex> stream = Cron.stream(cron, from: ~U[2022-06-05 00:00:00Z], order: :desc)
iex> Enum.take(stream, 3)
[
~N[2022-06-01 12:00:00],
~N[2022-05-01 12:00:00],
~N[2022-04-01 12:00:00],
]
iex> stream = Cron.stream(cron, from: ~U[9000-10-05 00:00:00Z])
iex> Enum.take(stream, 3)
[
~N[9000-11-01 12:00:00],
~N[9000-12-01 12:00:00]
]
@spec until(t(), DateTime.t() | NaiveDateTime.t()) :: millisecond()
Same as next/3
, but returns the milliseconds until next execution datetime.
Examples
iex> {:ok, cron} = Cron.new("0 0 0 * * *")
iex> Cron.until(cron, ~U[2022-01-01 12:00:00Z])
43200000
iex> Cron.until(cron, ~U[2022-01-02 00:00:00Z])
86400000
iex> Cron.until(cron, ~U[2022-01-02 00:00:00.999Z])
86399001
@spec until_while( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime -> as_boolean(term())) ) :: {:ok, millisecond()} | :error
Same as next_while/3
, but returns the milliseconds until next execution
datetime.
Examples
iex> {:ok, cron} = Cron.new("0 0 29 2 *")
iex> Cron.until_while(
...> cron,
...> ~U[2022-01-01 00:00:00Z],
...> fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, 699_321_600_000}
iex> Cron.until_while(
...> cron,
...> ~U[2044-02-28 23:59:59.100Z],
...> fn datetime -> Date.day_of_week(datetime) == 1 end)
{:ok, 900}
iex> {:ok, cron} = Cron.new("0 0 1 1 *")
iex> Cron.until_while(cron, fn _ -> false end)
:error
@spec until_while!( t(), DateTime.t() | NaiveDateTime.t(), (NaiveDateTime -> as_boolean(term())) ) :: millisecond()
Same as until_while!/3
, but raises a RuntimeError
exception in case no
execution datetime can be found.