Packages

Cron schedule generator for Elixir.

Current section

Files

Jump to
ex_cron lib ex_cron.ex
Raw

lib/ex_cron.ex

defmodule ExCron do
alias ExCron.Cron
alias ExCron.Parser
@moduledoc """
Ex-Cron is a library for generating scheduled dates based on a given
cron pattern.
"""
@doc ~S"""
Gets the date and time of the next instance of a cron schedule after the
specified date and time.
## Examples
iex> ExCron.next "* * * * *", {{2016, 5, 10},{22, 13, 13}}
{{2016, 5, 10}, {22, 14, 0}}
iex> ExCron.next "5 20 * DEC MON-WED", {{2016, 5, 10},{22, 13, 13}}
{{2016, 12, 5}, {20, 5, 0}}
"""
def next(cron_string, start = {{_,_,_},{_,_,_}}) do
cron = Parser.parse cron_string
start_seconds = to_seconds start
do_next cron, start_seconds
end
defp to_seconds(start = {{_,_,_},{_,_,0}}) do
:calendar.datetime_to_gregorian_seconds(start)
end
defp to_seconds(start = {{_,_,_},{_,_,seconds}}) do
:calendar.datetime_to_gregorian_seconds(start) + 60 - seconds
end
defp do_next(cron = %Cron{}, seconds) do
date = :calendar.gregorian_seconds_to_datetime seconds
case is_match cron, date do
true -> date
_ -> do_next cron, seconds + 60
end
end
defp is_match(cron = %Cron{}, {{year, month, day},{hour, minute, _}}) do
day_of_week = :calendar.day_of_the_week(year, month, day)
matches = [
cron.minutes |> Enum.any?(&(&1 == minute)),
cron.hours |> Enum.any?(&(&1 == hour)),
cron.days_of_month |> Enum.any?(&(&1 == day)),
cron.months |> Enum.any?(&(&1 == month)),
cron.days_of_week |> Enum.any?(&(&1 == day_of_week))
]
matches |> Enum.all?
end
@doc ~S"""
Gets the date and time of all instances of a cron schedule between the
given dates and times.
## Examples
iex> ExCron.within "* * * * *", {{2016, 5, 10},{22, 13, 13}}, {{2016, 5, 10},{22, 15, 13}}
[{{2016, 5, 10}, {22, 14, 0}}, {{2016, 5, 10}, {22, 15, 0}},]
"""
def within(cron_string, start = {{_,_,_},{_,_,_}}, ending = {{_,_,_},{_,_,_}}) do
cron = Parser.parse cron_string
start_seconds = to_seconds start
end_seconds = to_seconds ending
do_within cron, start_seconds, end_seconds, []
end
defp do_within(cron = %Cron{}, seconds, seconds_end, dates) when seconds < seconds_end do
date = :calendar.gregorian_seconds_to_datetime seconds
dates = case is_match cron, date do
true -> [date | dates]
_ -> dates
end
do_within cron, seconds + 60, seconds_end, dates
end
defp do_within(_, _, _, dates), do: dates |> Enum.reverse
end