When will an interval cron execute the first time? (Ex: */3 days)

The crontab(5) man page use a wording that is pretty clear:

Step values can be used in conjunction with ranges. Following a range with "/number" specifies skips of the number's value through the range. For example, "0-23/2" can be used in the hours field to specify command execution every other hour (the alternative in the V7 standard is "0,2,4,6,8,10,12,14,16,18,20,22"). Steps are also permitted after an asterisk, so if you want to say "every two hours", just use "*/2".

The exact wording (and the example) is "skips of the number's value through the range" - and it is implied that it starts at the first number in the range.

This mean if the range is 1-31 for days, the values returned in the case of 1-31/2 or */2 is 1,3,5,7.. etc. This also means that the range is reset to the start value when it has run through.

So you are also correct that in this case, the cronjob would run both on the 31th and 1st the month after.

Please note that cron has 2 fields that are mutually exclusive - the "day of month" and "day of week". So you have to choose one or the other, when running jobs with an interval of days.

If you want to define a cronjob that runs perfectly every other day, you have to use multiple lines and custom define each month according to the current calendar.


Today (2020-07-31) is the perfect day to ask this question, because 30 has an awful lot of factors.

My understanding (from memory) is that (a) the * expands to the range 1-31, then (b) the /3 is a skip increment for that list. So if you wrote 3-31/3 it would run on the 3rd, 6th, 9th, .., 27th (in Feb) or 30th (in other months). man -s 5 crontab implies this with ranges, but does not include an example that starts other than at the base value.

I set up a crontab (Linux Mint 18.1) with every skip value:

30 13 */1 * * date > /home/paul/cron.1.log
30 13 */2 * * date > /home/paul/cron.2.log
30 13 */3 * * date > /home/paul/cron.3.log
...
30 13 */30 * * date > /home/paul/cron.30.log
30 13 */31 * * date > /home/paul/cron.31.log

It runs only where the skip is 1, 2, 3, 5, 6, 10, 15 and 30. That looks like all the factors of (31 - 1).

Then I altered each range to be 7-31/, and it fires when the skip is 1, 2, 3, 4, 6, 8, 12 and 24. That is all the factors of (31 - 7).

With range 8-31, only skips 1 and 23 fire, because (31 - 8) is prime.


Just for those that would like to look at some code here, this confirms the conclusion in the other answers.

cron.h defines the first and last possible elements of the different types (like HOUR, MONTH, DAY).

#define FIRST_HOUR  0
#define LAST_HOUR   23
#define HOUR_COUNT  (LAST_HOUR - FIRST_HOUR + 1)

#define FIRST_DOM   1
#define LAST_DOM    31
#define DOM_COUNT   (LAST_DOM - FIRST_DOM + 1)

#define FIRST_MONTH 1
#define LAST_MONTH  12
#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1)

In entry.c, '*' is parsed into a range using those limits

if (ch == '*') {
    /* '*' means "first-last" but can still be modified by /step
     */
    num1 = low;
    num2 = high;

Step size (num3) defaults to 1, but can be overriden if present in the crontab

    ch = get_number(&num3, 0, PPC_NULL, ch, file);
    if (ch == EOF)
        return EOF;
} else {
    /* no step.  default==1.
     */
    num3 = 1;

And then all the valid elements are created by iterating from first to last using the given step size. So the first element is always the beginning of the range.

/* range. set all elements from num1 to num2, stepping
 * by num3.  (the step is a downward-compatible extension
 * proposed conceptually by bob@acornrc, syntactically
 * designed then implmented by paul vixie).
 */
for (i = num1;  i <= num2;  i += num3)
    if (EOF == set_element(bits, low, high, i))
        return EOF;