SuperPi.Schedule.Cron (super_pi v0.1.0)

Copy Markdown View Source

Cron expressions with time zones.

Parsing and field matching reuse Oban.Cron.Expression, which only answers "does this minute match". next_after/3 adds the missing "when is the next one": a walk over local calendar days that skips a whole day when its month, day of month or weekday cannot match, then lists the matching local times of the days that remain. Granularity is one minute.

Zone rules come from Tzdata (IANA data bundled with the package, no network at run time).

Daylight saving time

The result is always an instant (UTC, whole minute), strictly after the argument.

  • An expression that matches every hour (* in the hour field) follows the real clock. A local time that does not exist (spring forward) cannot fire, and a local time that occurs twice (fall back) fires twice. 0 * * * * therefore fires once per real hour across both changes: no duplicate, no gap.
  • An expression with fixed hours (30 2 * * *) fires once per local day. When the local time falls in a spring-forward gap, the run happens at the first minute after the gap (as Vixie cron does) instead of being skipped. When it occurs twice, only the first occurrence fires.

Summary

Functions

Does the local wall-clock minute of datetime match? datetime is read as it is, so pass it already shifted to the schedule's zone.

The first matching minute strictly after after_dt, as a UTC DateTime, for the expression read in zone. {:error, :no_match} when none exists within eight years (for example 0 0 30 2 *) and {:error, :invalid_timezone} for an unknown zone.

Parses a five-field expression or an @daily-style alias. @reboot is rejected.

True when zone is a time zone of the bundled IANA database.

Types

expr()

@type expr() :: Oban.Cron.Expression.t()

Functions

matches?(expr, naive)

@spec matches?(expr(), DateTime.t() | NaiveDateTime.t()) :: boolean()

Does the local wall-clock minute of datetime match? datetime is read as it is, so pass it already shifted to the schedule's zone.

next_after(expr, after_dt, zone \\ "Etc/UTC")

@spec next_after(expr() | String.t(), DateTime.t(), String.t()) ::
  {:ok, DateTime.t()} | {:error, :no_match | :invalid_timezone | String.t()}

The first matching minute strictly after after_dt, as a UTC DateTime, for the expression read in zone. {:error, :no_match} when none exists within eight years (for example 0 0 30 2 *) and {:error, :invalid_timezone} for an unknown zone.

parse(input)

@spec parse(String.t()) :: {:ok, expr()} | {:error, String.t()}

Parses a five-field expression or an @daily-style alias. @reboot is rejected.

valid_timezone?(zone)

@spec valid_timezone?(term()) :: boolean()

True when zone is a time zone of the bundled IANA database.