View Source ecron (ecron v1.1.0)

ecron enables dynamic scheduling of tasks using crontab-style expressions. It provides a flexible API for managing recurring jobs, one-time tasks, and time-based message delivery.

Summary

Job Management

Activates a previously deactivated job from the default registry. The job can be deactivated using deactivate/1.

Activates a previously deactivated job from the specified registry. The job will resume from the current time. The job can be deactivated using deactivate/2.

Adds a new crontab job with specified parameters. Jobs exceeding their limits are automatically removed.

Deactivates an existing job temporarily from the default registry. The job can be reactivated using activate/1.

Deactivates an existing job temporarily from the specified registry. The job can be reactivated using activate/2.

Deletes an existing job from the default registry. If the job does not exist, it will be ignored. Use delete/2 to delete a job from a specified registry.

Deletes an existing job from the specified registry. If the job does not exist, it will be ignored. Use delete/1 to delete a job from the default registry.

Reloads all tasks manually. Useful when system time has changed significantly.

Reloads all tasks manually. Useful when system time has changed significantly.

Create a new registry. Same as start_link(Register, []).

Starts an new registry.

Timer Functions

Create a one-time timer that sends a message when the crontab spec is triggered.

Sends a message to a process repeatedly based on a crontab schedule from the default registry. Same as send_interval(Spec, Pid, Message, #{register => ecron_local}).

Sends a message to a process repeatedly based on a crontab schedule from the default registry. Same as send_interval(ecron_local, make_ref(), Spec, Pid, Message, unlimited, unlimited, []).

Evaluates Pid ! Message repeatedly after crontab schedule milliseconds.

Debugging & Testing

Parses a crontab specification and returns the next N trigger times. Useful for debugging and validating crontab expressions.

Retrieves statistics for both default and global registered jobs.

Retrieves statistics for default registry. Use statistic/2 to get statistics for a specific registry.

Retrieves statistics for a specific job from the specified registry. Use statistic/1 to get statistics for the default registry.

Deprecated Functions

Same as add(ecron_local, JobName, Spec, MFA). ecron_local is the default register name, always exists.

Adds a new crontab job with specified parameters. Jobs exceeding their limits are automatically removed.

Same as add_with_count(ecron_local, make_ref(), Spec, MFA, RunCount). ecron_local is the default register name, always exists.

Add a job into default register with start and end time. If you want to use the specified register, use add_with_time/6.

Add a job into specified register with start and end time. If you want to use the default register, use add_with_time/5.

Sends a message to a process repeatedly based on a crontab schedule from the specified registry. Same as send_interval(Register, Name, Spec, Pid, Message, unlimited, unlimited, []).

Sends a message to a process repeatedly based on a crontab schedule.

Job Management

activate(JobName)

-spec activate(name()) -> ok | {error, not_found}.

Activates a previously deactivated job from the default registry. The job can be deactivated using deactivate/1.

activate(Register, JobName)

-spec activate(register(), name()) -> ok | {error, not_found}.

Activates a previously deactivated job from the specified registry. The job will resume from the current time. The job can be deactivated using deactivate/2.

Parameters:

  • Register - Process name where job is registered
  • JobName - Name of job to activate

Returns: ok | {error, not_found}

create(JobName, Spec, MFA)

-spec create(name(), crontab_spec(), mfargs()) -> ecron_result().

Same as create(JobName, Spec, MFA, #{}).

Examples

UniqueJobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, UniqueJobName} = ecron:create(UniqueJobName, "0 4 * * *", MFA),
ecron:statistic(UniqueJobName).

create/4

-spec create(name(), crontab_spec(), mfargs(), options()) -> ecron_result().

Adds a new crontab job with specified parameters. Jobs exceeding their limits are automatically removed.

Parameters:

  • JobName - Unique identifier for the job. Returns {error, already_exist} if duplicate
  • Spec - Crontab expression defining execution schedule
  • MFA - {Module, Function, Args} to execute when triggered
  • Opts - Options map:
    • register: atom() - The process name where the job will be registered (default: ecron_local)
    • start_time: {Hour,Min,Sec}|unlimited - Start time for job execution (default: unlimited)
    • end_time: {Hour,Min,Sec}|unlimited - End time for job execution (default: unlimited)
    • singleton: boolean() - If true, prevents concurrent execution (default: false)
    • max_count: pos_integer()|unlimited - Maximum number of executions allowed (default: unlimited). It will be automatically removed When a job reaches its max_count limit.
    • max_runtime_ms: pos_integer()|unlimited - Maximum runtime in milliseconds per execution (default: unlimited)

Returns: {ok, JobName} | {error, already_exist} | {error, parse_error(), term()}

Examples

Register = my_job_register,
{ok, _Pid} = ecron:start_link(Register, []),
JobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, JobName} = ecron:create(JobName, "0 4 * * *", MFA, #{
    start_time => {1, 0, 0}, 
    end_time => unlimited, 
    singleton => false,
    max_count => unlimited,
    max_runtime_ms => 1000,
    register => Register
}),
ecron:statistic(Register, JobName).

Singleton

When a job's singleton option is set to true, the system checks if there is already an instance of the job running before starting a new execution. If an instance is already running (old pid is alive), the new execution will be skipped.

TimeRange

The start time must be less than the maximum value in the spec, and the end time must be greater than the minimum value in the spec.

For example: With spec 0 1-12/1 * * * the max value is 12 and min value is 1, so start time must be less than {12,0,0} and end time must be greater than {1,0,0}, with start < end.

If we can not find the next schedule time in the next 5 years, return 'cant find next schedule time in the next 5 years'.

 ecron:create(invalid_job, "* 0,13 * * *", {io, format, [test]}, #{
       start_time => {1,0,0},
       end_time => {12,0,0}
   }).
{error,invalid_time,
       #{reason => "cant find next schedule time in the next 5 years",
         spec => "* 0,13 * * *",
         start_time => {1,0,0},
         end_time => {12,0,0}}}

deactivate(JobName)

-spec deactivate(name()) -> ok | {error, not_found}.

Deactivates an existing job temporarily from the default registry. The job can be reactivated using activate/1.

deactivate(Register, JobName)

-spec deactivate(register(), name()) -> ok | {error, not_found}.

Deactivates an existing job temporarily from the specified registry. The job can be reactivated using activate/2.

Parameters:

  • Register - Process name where job is registered
  • JobName - Name of job to deactivate

Returns: ok | {error, not_found}

delete(JobName)

-spec delete(name()) -> ok.

Deletes an existing job from the default registry. If the job does not exist, it will be ignored. Use delete/2 to delete a job from a specified registry.

Returns: ok

delete(Register, JobName)

-spec delete(register(), name()) -> ok.

Deletes an existing job from the specified registry. If the job does not exist, it will be ignored. Use delete/1 to delete a job from the default registry.

Parameters:

  • Register - Process name where job is registered
  • JobName - Name of job to delete

Returns: ok

reload()

-spec reload() -> ok.

Reloads all tasks manually. Useful when system time has changed significantly.

This will:

  • Recalculate next execution times for all jobs
  • Reset internal timers
  • Apply to both default and global registries

Returns: ok

reload(Register)

-spec reload(register()) -> ok.

Reloads all tasks manually. Useful when system time has changed significantly.

Parameters:

  • Register - Process name where job is registered

Returns: ok

start_link(Register)

-spec start_link(register() | {local | global, register()}) -> {ok, pid()} | {error, term()} | ignore.

Create a new registry. Same as start_link(Register, []).

Parameters:

  • Register - Process name where job is registered

Returns: {ok, Pid} | {error, Reason}

start_link/2

-spec start_link(atom() | {local | global, atom()}, [{name(), crontab_spec(), mfargs()}]) ->
                    {ok, pid()} | {error, term()} | ignore.

Starts an new registry.

Parameters:

  • Name - Process name where job is registered
  • JobSpec - Crontab expression to parse list

Returns: {ok, Pid} | {error, Reason}

Timer Functions

send_after(Spec, Pid, Message)

-spec send_after(crontab_spec(), pid() | atom(), term()) ->
                    {ok, reference()} | {error, parse_error(), term()}.

Create a one-time timer that sends a message when the crontab spec is triggered.

Parameters:

  • Spec - Crontab expression defining when to trigger
  • Dest - Destination pid() or registered name to receive message
  • Message - Term to send when timer triggers

Notes:

  • Similar to erlang:send_after/3 but uses crontab format
  • Dest pid must be local
  • Maximum time value is 4294967295 milliseconds
  • Timer auto-cancels if destination process dies

Returns: {ok, reference()} | {error, parse_error(), term()}

Examples

ecron:send_after("*/3 * * * * *", self(), hello_world).

Statistic

This is one-time timer, so it not seen in statistic/0 result.

Use erlang:cancel_timer/1 to cancel, not ecron:delete/1

send_interval(Spec, Message)

-spec send_interval(crontab_spec(), term()) -> ecron_result().

Sends a message to a process repeatedly based on a crontab schedule from the default registry. Same as send_interval(Spec, Pid, Message, #{register => ecron_local}).

send_interval(Spec, Pid, Message)

-spec send_interval(crontab_spec(), pid(), term()) -> ecron_result().

Sends a message to a process repeatedly based on a crontab schedule from the default registry. Same as send_interval(ecron_local, make_ref(), Spec, Pid, Message, unlimited, unlimited, []).

send_interval(Spec, Pid, Message, Opts)

-spec send_interval(crontab_spec(), pid(), term(), options()) -> ecron_result().

Evaluates Pid ! Message repeatedly after crontab schedule milliseconds.

Parameters:

  • Spec - Crontab expression defining when to trigger
  • Pid - Destination process ID or registered name
  • Message - Term to send on each trigger
  • Opts - Same options as create/4

Returns: {ok, reference()} | {error, parse_error(), term()}

Examples

ecron:send_interval("*/3 * * * * *", self(), hello_world).

Statistic

This is repeatable timer, so it seen in statistic/0 result.

Use ecron:delete/1 to cancel, not erlang:cancel_timer/1

Debugging & Testing

parse_spec(Spec, Num)

-spec parse_spec(crontab_spec(), pos_integer()) ->
                    {ok, #{type => cron | every, crontab => crontab(), next => [rfc3339_string()]}} |
                    {error, atom(), term()}.

Parses a crontab specification and returns the next N trigger times. Useful for debugging and validating crontab expressions.

Parameters:

  • Spec - Crontab expression to parse
  • Num - Number of future trigger times to calculate

Returns: {ok, #{type => cron|every, crontab => parsed_spec, next => [rfc3339_string()]}} | {error, parse_error(), term()}

statistic()

-spec statistic() -> [statistic()].

Retrieves statistics for both default and global registered jobs.

Returns: List of statistics for all jobs in both local and global registries. Each statistic entry contains the same information as statistic/2.

UniqueJobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, UniqueJobName} = ecron:create(UniqueJobName, "0 4 * * *", MFA),
ecron:statistic(UniqueJobName).

[#{name => every_4am_job,node => nonode@nohost,ok => 0,
   status => activate,type => cron,
   next =>
       ["2025-02-19T04:00:00+08:00","2025-02-20T04:00:00+08:00",
        "2025-02-21T04:00:00+08:00","2025-02-22T04:00:00+08:00",
        ...
        ],
   opts => [{singleton,false}, {max_count,unlimited}, {max_runtime_ms,unlimited}],
   mfa => {io,format,["Run at 04:00 everyday.~n",[]]},
   aborted => 0,crashed => 0,skipped => 0, run_microsecond => [],
   start_time => {0,0,0},
   end_time => {23,59,59},
   crontab =>
       #{second => [0],
         month => '*',
         minute => [0],
         hour => [4],
         day_of_month => '*',day_of_week => '*'},
   results => []}]

statistic(Register)

-spec statistic(register() | name()) -> [statistic()].

Retrieves statistics for default registry. Use statistic/2 to get statistics for a specific registry.

Returns: List of statistics for all jobs in local registries. Each statistic entry contains the same information as statistic/2.

statistic(Register, JobName)

-spec statistic(register(), name()) -> {ok, statistic()} | {error, not_found}.

Retrieves statistics for a specific job from the specified registry. Use statistic/1 to get statistics for the default registry.

Parameters:

  • Register - Process name where job is registered
  • JobName - Name of job to get statistics for

Returns: {ok, statistic()} | {error, not_found}

Where statistic() contains:

  • Job configuration
  • Execution counts (ok/crashed/aborted/skipped)
  • Latest results
  • Run times
  • Next scheduled runs
    1> ecron:statistic(basic).
    [#{name => basic,node => nonode@nohost,ok => 0,
     status => activate,type => cron,
     next => ["2025-02-20T22:15:00+08:00"...],        
     opts => [{singleton,false},{max_count,unlimited},{max_runtime_ms,unlimited}],
     mfa => {io,format,['Runs on 0, 15, 30, 45 minutes~n']},
     aborted => 0,crashed => 0,skipped => 0,
     start_time => {0,0,0},
     end_time => {23,59,59},
     run_microsecond => [],
     crontab =>
         #{second => [0],
           month => '*',
           minute => [0,15,30,45],
           hour => '*',day_of_month => '*',day_of_week => '*'},
     results => []}]
  • ok: successful job.
  • crashed: crashed job.
  • skipped: singleton(true) job has been skipped due to the previous task still running.
  • aborted: job has been aborted due to exceeding max_runtime_ms.

statistic()

-type statistic() ::
          #{name => name(),
            node => node(),
            type => cron | every,
            crontab => crontab(),
            status => deactivate | activate,
            crashed => non_neg_integer(),
            ok => non_neg_integer(),
            aborted => non_neg_integer(),
            opts => option_list(),
            results => [term()],
            run_microsecond => [pos_integer()],
            start_time => rfc3339_string() | unlimited,
            end_time => rfc3339_string() | unlimited,
            next => [calendar:datetime()]}.

Deprecated Functions

add(JobName, Spec, MFA)

This function is deprecated. ecron:add/3 is deprecated; use create/3 instead.
-spec add(name(), crontab_spec(), mfargs()) -> ecron_result().

Same as add(ecron_local, JobName, Spec, MFA). ecron_local is the default register name, always exists.

Examples

JobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, JobName} = ecron:add(JobName, "0 4 * * *", MFA),
ecron:statistic(JobName).

add(Register, JobName, Spec, MFA)

This function is deprecated. ecron:add/4 is deprecated; use create/4 instead.
-spec add(register(), name(), crontab_spec(), mfargs()) -> ecron_result().

Same as add(Register, JobName, Spec, MFA, unlimited, unlimited, []).

Examples

Register = my_cronjob_register,
{ok, _Pid} = ecron:start_link(Register, []),
JobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, JobName} = ecron:add(Register, JobName, "0 4 * * *", MFA),
ecron:statistic(Register, JobName).

add(JobName, Spec, MFA, Start, End, Opts)

This function is deprecated. ecron:add/6 is deprecated; use create/4 instead.
-spec add(name(), crontab_spec(), mfargs(), start_at(), end_at(), option_list()) -> ecron_result().

Same as add(ecron_local, JobName, Spec, MFA, Start, End, Opts).

add(Register, JobName, Spec, MFA, StartAt, EndAt, Opts)

This function is deprecated. ecron:add/7 is deprecated; use create/4 instead.
-spec add(register(), name(), crontab_spec(), mfargs(), start_at(), end_at(), option_list()) ->
             ecron_result().

Adds a new crontab job with specified parameters. Jobs exceeding their limits are automatically removed.

Parameters:

  • Register - The process name where the job will be registered
  • JobName - Unique identifier for the job. Returns {error, already_exist} if duplicate
  • Spec - Crontab expression defining execution schedule
  • MFA - {Module, Function, Args} to execute when triggered
  • Start - Start time {Hour,Min,Sec} or unlimited for immediate start
  • End - End time {Hour,Min,Sec} or unlimited for no end
  • Opts - Options list:
    • {singleton, boolean()} - If true (default), prevents concurrent execution
    • {max_count, pos_integer() | unlimited} - Maximum executions allowed

    • {max_runtime_ms, pos_integer() | unlimited} - Maximum runtime milliseconds allowed for each execution

Returns: {ok, JobName} | {error, already_exist} | {error, parse_error(), term()}

Examples

Register = my_cronjob_register,
{ok, _Pid} = ecron:start_link(Register, []),
JobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, JobName} = ecron:add(Register, JobName, "0 4 * * *", MFA, {1, 0, 0}, unlimited, [{singleton, true}]),
ecron:statistic(Register, JobName).

Singleton

When a job's singleton option is set to true, the system checks if there is already an instance of the job running before starting a new execution. If an instance is already running(old pid is alive), the new execution will be skipped.

add_with_count(Spec, MFA, RunCount)

This function is deprecated. ecron:add_with_count/3 is deprecated; use create/4 instead.
-spec add_with_count(crontab_spec(), mfargs(), pos_integer()) -> ecron_result().

Same as add_with_count(ecron_local, make_ref(), Spec, MFA, RunCount). ecron_local is the default register name, always exists.

Examples

The job will be auto deleted after running 10 times.

JobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, JobName} = ecron:add_with_count(JobName, "0 4 * * *", MFA, 10),
ecron:statistic(JobName).

add_with_count(Register, JobName, Spec, MFA, RunCount)

This function is deprecated. ecron:add_with_count/5 is deprecated; use create/4 instead.
-spec add_with_count(register(), name(), crontab_spec(), mfargs(), pos_integer()) -> ecron_result().

Same as add(Register, JobName, Spec, MFA, unlimited, unlimited, [{max_count, RunCount}]).

Examples

The job will be auto deleted after running 10 times.

Register = my_cronjob_register,
{ok, _Pid} = ecron:start_link(Register, []),
JobName = every_4am_job,
MFA = {io, format, ["Run at 04:00 everyday.~n", []]},
{ok, JobName} = ecron:add_with_count(Register, JobName, "0 4 * * *", MFA, 10),
ecron:statistic(Register, JobName).

add_with_time(JobName, Spec, MFA, Start, End)

This function is deprecated. ecron:add_with_time/5 is deprecated; use create/4 instead.
-spec add_with_time(name(), crontab_spec(), mfargs(), start_at(), end_at()) -> ecron_result().

Add a job into default register with start and end time. If you want to use the specified register, use add_with_time/6.

Examples

The job will be auto skipped if the current time is not between 04:00 and 12:00 everyday.

JobName = on_hour_job,
MFA = {io, format, ["Run at 04:00-12:00 on the hour.~n", []]},
{ok, JobName} = ecron:add_with_time(JobName, "0 1-12/1 * * *", MFA, {4, 0, 0}, {12, 0, 0}),
ecron:statistic(JobName).

TimeRange

The start time must be less than the maximum value in the spec, and the end time must be greater than the minimum value in the spec.

For example: With spec 0 1-12/1 * * * the max value is 12 and min value is 1, so start time must be less than {12,0,0} and end time must be greater than {1,0,0}, with start < end.

If we can not find the next schedule time in the next 5 years, return cant find next schedule time in the next 5 years.

ecron:add_with_time(invalid_job, "* 0,13 * * *", {io, format, ["test"]},{1,0,0},{12,0,0}).
{error,invalid_time,
        #{reason => "cant find next schedule time in the next 5 years",
            start => {1,0,0},
            stop => {12,0,0},
            spec => "* 0,13 * * *"}}

add_with_time(Register, JobName, Spec, MFA, Start, End)

This function is deprecated. ecron:add_with_time/6 is deprecated; use create/4 instead.
-spec add_with_time(register(), name(), crontab_spec(), mfargs(), start_at(), end_at()) ->
                       ecron_result().

Add a job into specified register with start and end time. If you want to use the default register, use add_with_time/5.

Examples

The job will be auto skipped if the current time is not between 04:00 and 12:00 everyday.

Register = my_cronjob_register,
{ok, _Pid} = ecron:start_link(Register, []),
JobName = on_hour_job,
MFA = {io, format, ["Run at 04:00-12:00 on the hour.~n", []]},
{ok, JobName} = ecron:add_with_time(Register, JobName, "0 1-12/1 * * *", MFA, {4, 0, 0}, {12, 0, 0}),
ecron:statistic(Register, JobName).

TimeRange

The start time must be less than the maximum value in the spec, and the end time must be greater than the minimum value in the spec.

For example: With spec 0 1-12/1 * * * the max value is 12 and min value is 1, so start time must be less than {12,0,0} and end time must be greater than {1,0,0}, with start < end.

If we can not find the next schedule time in the next 5 years, return cant find next schedule time in the next 5 years.

ecron:add_with_time(invalid_job, "* 0,13 * * *", {io, format, ["test"]},{1,0,0},{12,0,0}).
{error,invalid_time,
       #{reason => "cant find next schedule time in the next 5 years",
         start => {1,0,0},
         stop => {12,0,0},
         spec => "* 0,13 * * *"}}

send_interval(Register, Name, Spec, Pid, Message)

This function is deprecated. ecron:send_interval/5 is deprecated; use send_interval/4 instead.
-spec send_interval(register(), name(), crontab_spec(), pid(), term()) -> ecron_result().

Sends a message to a process repeatedly based on a crontab schedule from the specified registry. Same as send_interval(Register, Name, Spec, Pid, Message, unlimited, unlimited, []).

send_interval(Register, Name, Spec, Message, Start, End, Option)

This function is deprecated. ecron:send_interval/7 is deprecated; use send_interval/4 instead.
-spec send_interval(register(), name(), crontab_spec(), term(), start_at(), end_at(), option_list()) ->
                       ecron_result().

Same as send_interval(register(), name(), Spec, self(), Message, Start, End, Option).

send_interval(Register, JobName, Spec, Pid, Message, Start, End, Option)

This function is deprecated. ecron:send_interval/8 is deprecated; use send_interval/4 instead.
-spec send_interval(register(),
                    name(),
                    crontab_spec(),
                    pid(),
                    term(),
                    start_at(),
                    end_at(),
                    option_list()) ->
                       ecron_result().

Sends a message to a process repeatedly based on a crontab schedule.

Parameters

  • Register - Process name where job will be registered
  • JobName - Unique identifier for the job
  • Spec - Crontab expression defining execution schedule
  • Pid - Destination process ID or registered name
  • Message - Term to send on each trigger
  • Start - Start time {Hour,Min,Sec} or unlimited
  • End - End time {Hour,Min,Sec} or unlimited
  • Option - Same options as add/7

Returns: {ok, reference()} | {error, parse_error(), term()}

Types

crontab()

-type crontab() ::
          #{second => '*' | [0..59 | {0..58, 1..59}, ...],
            minute => '*' | [0..59 | {0..58, 1..59}, ...],
            hour => '*' | [0..23, ...],
            month => '*' | [1..12 | {1..11, 2..12}, ...],
            day_of_month => '*' | [1..31 | {1..30, 2..31}, ...],
            day_of_week => '*' | [0..6 | {0..5, 1..6}, ...]}.

crontab_spec()

-type crontab_spec() :: crontab() | string() | binary() | 1..4294967.

ecron_result()

-type ecron_result() :: {ok, name()} | {error, parse_error(), term()} | {error, already_exist}.

end_at()

-type end_at() :: unlimited | calendar:time().

mfargs()

-type mfargs() :: {M :: module(), F :: atom(), A :: [term()]}.

name()

-type name() :: term().

option_list()

-type option_list() ::
          [{singleton, boolean()} |
           {max_count, pos_integer() | unlimited} |
           {max_runtime_ms, pos_integer() | unlimited}].

options()

-type options() ::
          #{singleton => boolean(),
            max_count => pos_integer() | unlimited,
            max_runtime_ms => pos_integer() | unlimited,
            start_time => start_at(),
            end_time => end_at(),
            register => register()}.

parse_error()

-type parse_error() ::
          invalid_time | invalid_opts | invalid_spec | month | day_of_month | day_of_week | hour |
          minute | second.

register()

-type register() :: atom().

rfc3339_string()

-type rfc3339_string() :: string() | binary().

start_at()

-type start_at() :: unlimited | calendar:time().