diff --git a/README.md b/README.md index 4cd7407..6b2906a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,38 @@ The 'L' and 'W' characters can also be combined for the day-of-month expression ## Usage +### Helper Functions + +```elixir +iex> Crontab.get_next_run_date("* * * * *") +{:ok, ~N[2016-12-23 16:00:00.348751]} + +iex> Crontab.get_next_run_date("* * * * *", ~N[2016-12-17 00:00:00]) +{:ok, ~N[2016-12-17 00:00:00]} + +iex> Crontab.get_next_run_dates(3, "* * * * *") +[{:ok, ~N[2016-12-23 16:00:00]}, + {:ok, ~N[2016-12-23 16:01:00]}, + {:ok, ~N[2016-12-23 16:02:00]}] + +iex> Crontab.get_next_run_dates(3, "* * * * *", ~N[2016-12-17 00:00:00]) +[{:ok, ~N[2016-12-17 00:00:00]}, + {:ok, ~N[2016-12-17 00:01:00]}, + {:ok, ~N[2016-12-17 00:02:00]}] + +iex> Crontab.matches_date("*/2 * * * *") +{:ok, true} + +iex> Crontab.matches_date("*/7 * * * *") +{:ok, false} + +iex> Crontab.matches_date("*/2 * * * *", ~N[2016-12-17 00:02:00]) +{:ok, true} + +iex> Crontab.matches_date("*/7 * * * *", ~N[2016-12-17 00:06:00]) +{:ok, false} +``` + ### Parse Cron Format Strings ```elixir iex> Crontab.CronFormatParser.parse "* * * * *" diff --git a/lib/crontab.ex b/lib/crontab.ex index 915b41f..1862801 100644 --- a/lib/crontab.ex +++ b/lib/crontab.ex @@ -1,2 +1,116 @@ defmodule Crontab do + @moduledoc """ + This Library is built to parse & write cron expressions, test them against a + given date and finde the next execution date. + + In the main module defined are helper functions which work directlyfrom a + string cron expression. + """ + + @doc """ + Find the next execution date relative to now for a string cron expression. + + ### Examples + + iex> Crontab.get_next_run_date("* * * * *") + {:ok, ~N[2016-12-23 16:00:00.348751]} + + """ + def get_next_run_date(cron_expression) when is_binary(cron_expression) do + date = DateTime.to_naive(DateTime.utc_now) + get_next_run_date(cron_expression, date) + end + + @doc """ + Find the next execution date relative to a given date for a string cron + expression. + + ### Examples + + iex> Crontab.get_next_run_date("* * * * *", ~N[2016-12-17 00:00:00]) + {:ok, ~N[2016-12-17 00:00:00]} + + """ + def get_next_run_date(cron_expression, date) when is_binary(cron_expression) do + case Crontab.CronFormatParser.parse(cron_expression) do + {:ok, cron_format} -> Crontab.CronScheduler.get_next_run_date(cron_format, date) + error = {:error, _} -> error + end + end + + @doc """ + Find the next n execution dates relative to now for a string cron expression. + + ### Examples + + iex> Crontab.get_next_run_dates(3, "* * * * *") + [{:ok, ~N[2016-12-23 16:00:00]}, + {:ok, ~N[2016-12-23 16:01:00]}, + {:ok, ~N[2016-12-23 16:02:00]}] + + """ + def get_next_run_dates(n, cron_expression) when is_binary(cron_expression) do + date = DateTime.to_naive(DateTime.utc_now) + get_next_run_dates(n, cron_expression, date) + end + + @doc """ + Find the next n execution dates relative to a given date for a string cron + expression. + + ### Examples + + iex> Crontab.get_next_run_dates(3, "* * * * *", ~N[2016-12-17 00:00:00]) + [{:ok, ~N[2016-12-17 00:00:00]}, + {:ok, ~N[2016-12-17 00:01:00]}, + {:ok, ~N[2016-12-17 00:02:00]}] + + """ + def get_next_run_dates(0, _, _), do: [] + def get_next_run_dates(n, cron_expression, date) when is_binary(cron_expression) do + case Crontab.CronFormatParser.parse(cron_expression) do + {:ok, cron_format} -> + result = {:ok, run_date} = Crontab.CronScheduler.get_next_run_date(cron_format, date) + [result | get_next_run_dates(n - 1, cron_expression, Timex.shift(run_date, minutes: 1))] + error = {:error, _} -> error + end + end + + @doc """ + Check if now matches a given string cron expression. + + ### Examples + + iex> Crontab.matches_date("*/2 * * * *") + {:ok, true} + + iex> Crontab.matches_date("*/7 * * * *") + {:ok, false} + + """ + def matches_date(cron_expression) when is_binary(cron_expression) do + date = DateTime.to_naive(DateTime.utc_now) + matches_date(cron_expression, date) + end + + + + @doc """ + Check if given date matches a given string cron expression. + + ### Examples + + iex> Crontab.matches_date("*/2 * * * *", ~N[2016-12-17 00:02:00]) + {:ok, true} + + iex> Crontab.matches_date("*/7 * * * *", ~N[2016-12-17 00:06:00]) + {:ok, false} + + """ + def matches_date(cron_expression, date) when is_binary(cron_expression) do + case Crontab.CronFormatParser.parse(cron_expression) do + {:ok, cron_format} -> {:ok, Crontab.CronDateChecker.matches_date(cron_format, date)} + error = {:error, _} -> error + end + end end diff --git a/lib/crontab/cron_date_checker.ex b/lib/crontab/cron_date_checker.ex index 57092bd..e3268aa 100644 --- a/lib/crontab/cron_date_checker.ex +++ b/lib/crontab/cron_date_checker.ex @@ -9,17 +9,19 @@ defmodule Crontab.CronDateChecker do Check a Crontab.CronInterval against a given date. ### Examples - iex> Crontab.CronDateChecker.matches_date :hour, [{:"/", :*, 4}, 7], ~N[2004-04-16 04:07:08] - true - iex> Crontab.CronDateChecker.matches_date :hour, [8], ~N[2004-04-16 04:07:08] - false + iex> Crontab.CronDateChecker.matches_date :hour, [{:"/", :*, 4}, 7], ~N[2004-04-16 04:07:08] + true + + iex> Crontab.CronDateChecker.matches_date :hour, [8], ~N[2004-04-16 04:07:08] + false + + iex> Crontab.CronDateChecker.matches_date %Crontab.CronInterval{minute: [{:"/", :*, 8}]}, ~N[2004-04-16 04:08:08] + true - iex> Crontab.CronDateChecker.matches_date %Crontab.CronInterval{minute: [{:"/", :*, 8}]}, ~N[2004-04-16 04:08:08] - true + iex> Crontab.CronDateChecker.matches_date %Crontab.CronInterval{minute: [{:"/", :*, 9}]}, ~N[2004-04-16 04:07:08] + false - iex> Crontab.CronDateChecker.matches_date %Crontab.CronInterval{minute: [{:"/", :*, 9}]}, ~N[2004-04-16 04:07:08] - false """ def matches_date(_, [:* | _], _), do: true def matches_date(_, [], _), do: false diff --git a/lib/crontab/cron_format_parser.ex b/lib/crontab/cron_format_parser.ex index 8b2907e..aec64e2 100644 --- a/lib/crontab/cron_format_parser.ex +++ b/lib/crontab/cron_format_parser.ex @@ -1,6 +1,6 @@ defmodule Crontab.CronFormatParser do @moduledoc """ - Parse string like "* * * * * *" to a %Crontab.CronInterval{}. + Parse string like `* * * * * *` to a `%Crontab.CronInterval{}`. """ @specials %{ @@ -48,15 +48,18 @@ defmodule Crontab.CronFormatParser do } @doc """ - Parse string like "* * * * * *" to a %Crontab.CronInterval{}. + Parse string like `* * * * * *` to a `%Crontab.CronInterval{}`. ### Examples - iex> Crontab.CronFormatParser.parse "* * * * *" - {:ok, - %Crontab.CronInterval{day: [:*], hour: [:*], minute: [:*], - month: [:*], weekday: [:*], year: [:*]}} - iex> Crontab.CronFormatParser.parse "fooo" - {:error, "Can't parse fooo as interval minute."} + + iex> Crontab.CronFormatParser.parse "* * * * *" + {:ok, + %Crontab.CronInterval{day: [:*], hour: [:*], minute: [:*], + month: [:*], weekday: [:*], year: [:*]}} + + iex> Crontab.CronFormatParser.parse "fooo" + {:error, "Can't parse fooo as interval minute."} + """ def parse("@" <> identifier) do special(String.to_atom(identifier)) diff --git a/lib/crontab/cron_format_writer.ex b/lib/crontab/cron_format_writer.ex index 3c0aba4..40a7803 100644 --- a/lib/crontab/cron_format_writer.ex +++ b/lib/crontab/cron_format_writer.ex @@ -2,17 +2,17 @@ defmodule Crontab.CronFormatWriter do import Crontab.CronInterval @moduledoc """ - Genrate from %Crontab.CronInterval{} to "* * * * * *" + Genrate from `%Crontab.CronInterval{}` to `* * * * * *` """ @doc """ - Genrate from %Crontab.CronInterval{} to "* * * * * *" + Genrate from `%Crontab.CronInterval{}` to `* * * * * *` ### Examples - iex> Crontab.CronFormatWriter.write %Crontab.CronInterval{} - "* * * * * *" - iex> Crontab.CronFormatWriter.write %Crontab.CronInterval{minute: [9, {:-, 4, 6}, {:/, :*, 9}]} - "9,4-6,*/9 * * * * *" + iex> Crontab.CronFormatWriter.write %Crontab.CronInterval{} + "* * * * * *" + iex> Crontab.CronFormatWriter.write %Crontab.CronInterval{minute: [9, {:-, 4, 6}, {:/, :*, 9}]} + "9,4-6,*/9 * * * * *" """ def write(cron_interval = %Crontab.CronInterval{}) do cron_interval diff --git a/lib/crontab/cron_interval.ex b/lib/crontab/cron_interval.ex index 09cf5f5..303e712 100644 --- a/lib/crontab/cron_interval.ex +++ b/lib/crontab/cron_interval.ex @@ -6,14 +6,14 @@ defmodule Crontab.CronInterval do @doc """ Defines the Cron Interval - \\* \\* \\* \\* \\* \\*\n - | | | | | |\n - | | | | | +-- :year Year (range: 1900-3000)\n - | | | | +---- :weekday Day of the Week (range: 1-7, 1 standing for Monday)\n - | | | +------ :month Month of the Year (range: 1-12)\n - | | +-------- :day Day of the Month (range: 1-31)\n - | +---------- :hour Hour (range: 0-23)\n - +------------ :minute Minute (range: 0-59)\n + * * * * * * + | | | | | | + | | | | | +-- :year Year (range: 1900-3000) + | | | | +---- :weekday Day of the Week (range: 1-7, 1 standing for Monday) + | | | +------ :month Month of the Year (range: 1-12) + | | +-------- :day Day of the Month (range: 1-31) + | +---------- :hour Hour (range: 0-23) + +------------ :minute Minute (range: 0-59) """ defstruct minute: [:*], hour: [:*], day: [:*], month: [:*], weekday: [:*], year: [:*] @@ -21,13 +21,13 @@ defmodule Crontab.CronInterval do Convert Crontab.CronInterval struct to Tuple List ### Examples - iex> Crontab.CronInterval.to_condition_list %Crontab.CronInterval{minute: [1], hour: [2], day: [3], month: [4], weekday: [5], year: [6]} - [ {:minute, [1]}, - {:hour, [2]}, - {:day, [3]}, - {:month, [4]}, - {:weekday, [5]}, - {:year, [6]}] + iex> Crontab.CronInterval.to_condition_list %Crontab.CronInterval{minute: [1], hour: [2], day: [3], month: [4], weekday: [5], year: [6]} + [ {:minute, [1]}, + {:hour, [2]}, + {:day, [3]}, + {:month, [4]}, + {:weekday, [5]}, + {:year, [6]}] """ def to_condition_list(%Crontab.CronInterval{minute: minute, hour: hour, day: day, month: month, weekday: weekday, year: year}) do [ {:minute, minute}, diff --git a/lib/crontab/cron_scheduler.ex b/lib/crontab/cron_scheduler.ex index 62c7240..375391f 100644 --- a/lib/crontab/cron_scheduler.ex +++ b/lib/crontab/cron_scheduler.ex @@ -4,27 +4,32 @@ defmodule Crontab.CronScheduler do @moduledoc """ This module provides the functionality to retrieve the next run date or the - previous run date from a %Crontab.CronInterval{}. + previous run date from a `%Crontab.CronInterval{}`. """ @max_runs Application.get_env(:crontab, :max_runs, 10000) @doc """ This function provides the functionality to retrieve the next run date from a - %Crontab.CronInterval{}. + `%Crontab.CronInterval{}`. ### Examples - iex> Crontab.CronScheduler.get_next_run_date(%Crontab.CronInterval{}, ~N[2002-01-13 23:00:07]) - {:ok, ~N[2002-01-13 23:00:00]} + iex> Crontab.CronScheduler.get_next_run_date(%Crontab.CronInterval{}, ~N[2002-01-13 23:00:07]) + {:ok, ~N[2002-01-13 23:00:00]} - iex> Crontab.CronScheduler.get_next_run_date(%Crontab.CronInterval{year: [{:/, :*, 9}]}, ~N[2002-01-13 23:00:07]) - {:ok, ~N[2007-01-01 00:00:00]} + iex> Crontab.CronScheduler.get_next_run_date(%Crontab.CronInterval{year: [{:/, :*, 9}]}, ~N[2002-01-13 23:00:07]) + {:ok, ~N[2007-01-01 00:00:00]} """ def get_next_run_date(cron_interval = %Crontab.CronInterval{}, date) do cron_interval |> to_condition_list |> get_next_run_date(reset(date, :seconds), @max_runs) end + def get_next_run_date(cron_interval = %Crontab.CronInterval{}, date, max_runs) do + cron_interval + |> to_condition_list + |> get_next_run_date(reset(date, :seconds), max_runs) + end def get_next_run_date(conditions, date, max_runs) when max_runs > 0 do {status, corrected_date} = search_and_correct_date(conditions, date); case status do diff --git a/mix.exs b/mix.exs index 33476fe..f306828 100644 --- a/mix.exs +++ b/mix.exs @@ -48,7 +48,6 @@ defmodule Crontab.Mixfile do files: ["lib", "mix.exs", "README*", "LICENSE*"], maintainers: ["Jonatan Männchen"], licenses: ["MIT"], - links: %{"GitHub" => "https://github.com/sk-t/crontab", - "Docs" => "https://github.com/sk-t/crontab"}] + links: %{"GitHub" => "https://github.com/sk-t/crontab"}] end end diff --git a/test/crontab_test.exs b/test/crontab_test.exs index a15e278..4652ef3 100644 --- a/test/crontab_test.exs +++ b/test/crontab_test.exs @@ -1,5 +1,5 @@ defmodule CrontabTest do use ExUnit.Case - doctest Crontab + doctest Crontab, except: [get_next_run_date: 1, matches_date: 1, get_next_run_dates: 2] import Crontab end