Create a regular expression for Cron statement

To validate cron expressions in general, it'll depend greatly on the specific implementation you're using

Rules

General Format

In general, most adhere to the the following format:

Field name Mandatory? Allowed values Special characters
Seconds No* 0-59 * / , -
Minutes Yes 0-59 * / , -
Hours Yes 0-23 * / , -
Day of month Yes 1-31 * / , - L W
Month Yes 1-12 or JAN-DEC * / , -
Day of week Yes 0-6 or SUN-SAT * / , - L #
Year No* 1970–2099 * / , -

*where seconds and years are non-standard and sometimes not included

Predefined Scheduling Macros:

Some flavors allow predefined time periods like this:

Entry Equivalent to
@annually 0 0 0 1 1 * *
@yearly 0 0 0 1 1 * *
@monthly 0 0 0 1 * * *
@weekly 0 0 0 * * 0 *
@daily 0 0 0 * * * *
@hourly 0 0 * * * * *
@reboot

Intervals

Some flavors allow using the @every <duration> syntax with the following timespan units:

Unit Definition
ns nanosecond
us, µs microsecond
ms millisecond
s second
m minute
h hour

Validating

Predefined Macros Regex

To validate predefined macros, you can use the following regex:

/@(annually|yearly|monthly|weekly|daily|hourly|reboot)/

Which will pass the following test cases:

@daily
@hourly

@every Regex

To validate @every durations, you can use the following regex:

/@every (\d+(ns|us|µs|ms|s|m|h))+/

Which will pass the following test cases:

@every 5s
@every 20h30m

Individual Cron Terms

Validating cron terms in the regular expression is a little trickier since there are so many variations.

Just focusing in on a single term, the following are all valid:

  • Two or more numbers separated by a ,
  • Two numbers separated by a / or -
  • A 1-2 digit number
  • A single *

To validate a single term, we can use the following regex:

/(\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*/

where \d+ just guarantees you have 1 or more numeric digits

Which will pass the following test cases:

1,2,3
1,2
1/2
1-2
1
*

Combining Cron Terms

To check the full expression, we can just make sure we have {5,7} terms with the following regex:

/(((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7}/

If we wanted to distinguish between each term, we'd need to validate numbers within a certain range:

Allowed values Regex
0-59 [1-5]?[0-9]
0-23 2[0-3]|1[0-9]|[0-9]
1-31 3[01]|[12][0-9]|[1-9]
1-12 1[0-2]|[1-9]
0-6 [0-6]
1970–2099 19[7-9][0-9]|20[0-9][0-9]

If however, we just want to make sure something looks like a regex expression without worrying about which term is days vs. hours, the expression stays a lot cleaner, so that's what I'll go with for simplicity

Putting it all together

By combining the above statements, we can have a relatively simple sanity check that the term looks regex-y with the following expression:

/(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){5,7})/

Additional Resources

  • crontab.guru
  • regexr.com

I just finished writing one, so here is mine, hope it helps.

This should allow the * or */num and also limit the number values to their logical range (1-12 for months, 0-24 for hours, and so on)

/^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])) (\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]|1[0-9]|2[0-3])) (\*|([1-9]|1[0-9]|2[0-9]|3[0-1])|\*\/([1-9]|1[0-9]|2[0-9]|3[0-1])) (\*|([1-9]|1[0-2])|\*\/([1-9]|1[0-2])) (\*|([0-6])|\*\/([0-6]))$/

Tags:

Regex