Hi,

Just want to make sure I'm doing this correct in my crontab.

5 10 */4 * *

Does this, indeed, run once at 10:05 am every four days?

Recommended Answers

That is how I would read it :)

I usually test this in a small VM that I have where I fast-forward the time a little bit... I know, that's cheating and not being hard-core, but hey!

Jump to Post

Hi, I have no idea how to get BACK to the chat room haha! See, noob of the year in the making!

*     *     *     *     *  command to be executed
-     -     -     -     -
|     |     |     |     |
|     |     |     |     +----- day of week (0 …
Jump to Post

why did it fire today, which is the 9th of the month?

Doesn't 5 10 */4 * * this mean run every four days, not every 4th day (4, 8, 12, etc..) of the month? So if you ran the job on Jan 1, it would run again …

Jump to Post

Yes. 1-31 is the range, you can use * which, in this case, is the same because cron internally sets the first_dom constant to be always 1 and the last_dom constant to be always 31.

So when you write:

*/4

And:

1-31/4

You get the same result. If …

Jump to Post

All 15 Replies

That is how I would read it :)

I usually test this in a small VM that I have where I fast-forward the time a little bit... I know, that's cheating and not being hard-core, but hey!

I lost you in the chat room, btw, Ewald :)

That aside, here's my next follow-up question. Is it possible to predict when the cron job will be fired? In other words, if I am saying every four days, then is it specifically when day % 4 == 0 ?? And, if so, how is day determined (is it a UNIX timestamp, is it the day of the year, etc?)

Ultimately, can it be predicted accurately in the future so that my PHP script can have a way of knowing the next time a cron job will be fired given the crontab "schedule"?

Hi, I have no idea how to get BACK to the chat room haha! See, noob of the year in the making!

*     *     *     *     *  command to be executed
-     -     -     -     -
|     |     |     |     |
|     |     |     |     +----- day of week (0 - 6) (Sunday=0)
|     |     |     +------- month (1 - 12)
|     |     +--------- day of month (1 - 31)
|     +----------- hour (0 - 23)
+------------- min (0 - 59)

According to the docs, it's the day of the month... So yes, it will be when day % 4 == 0 but it will be day of the month. With this information in hand, your PHP script should be able to figure out when the next job will be fired :)

If that's the case, why did it fire today, which is the 9th of the month?

why did it fire today, which is the 9th of the month?

Doesn't 5 10 */4 * * this mean run every four days, not every 4th day (4, 8, 12, etc..) of the month? So if you ran the job on Jan 1, it would run again Jan 5, then Jan 9... assuming the job started on Jan 1st.

assuming the job started on Jan 1st.

What does that mean that assuming the job started on January 1st? Can't I just put the same schedule into different crontabs on different servers and have them all fire at the same time?

Ok, so Im not a cron job expert, but I was under the impression from my limited exposure to cron jobs that is you do a */4 its going to run every 4 days from the time the job was created. If you want the job to run on a specific set of days, you would list them explicitly and seperate the days by a comma. 5 10 1,5,9,13 * * *

From the time the job was created?? OK that's basically what I was asking :)

Never heard of the whole comma-delimited thing. Are you sure?!

I'm hesistant to say that yes I'm absolutely sure. I would have to go back to look at some examples and read up on this some more, but I do recall creating a cron job and specifically listing the days that I wanted the job to run. There's got to be another cron job expert around...

The day of the month (DOM) refers to the 1-31 range, it does not consider when the job was created. There's a comment in cron.c source, extended in the FEATURES file that explains a bit how it works:

the dom/dow situation is odd. '* * 1,15 * Sun' will run on the first and fifteenth AND every Sunday; '* * * * Sun' will run only on Sundays; '* * 1,15 * ' will run *only the 1st and 15th. this is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up this behaviour; it's how cron has always worked but the documentation hasn't been very clear. I have been told that some AT&T crons do not act this way and do the more reasonable thing, which is (IMHO) to "or" the various field-matches together. In that sense this cron may not be completely similar to some AT&T crons.

DOW stands for Day Of Week, i.e. the last asterisk.

You could also write 1-31/4 to get the same result of the previous suggestions. The comma delimited list is known as V7 standard, from the Unix Programmer's Manual of Bell Labs, page 400:

a list of numbers separated by commas meaning any of the numbers;

Bye!

Sorry, I'm a bit confused. So writing 1-31/4 WOULD do the modular division thing??

Yes. 1-31 is the range, you can use * which, in this case, is the same because cron internally sets the first_dom constant to be always 1 and the last_dom constant to be always 31.

So when you write:

*/4

And:

1-31/4

You get the same result. If you want to perform a task every 4 days of the first 20 days of the month you would write:

1-20/4

In the cron source this is done by this loop in the entry.c file:

/* 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;

In PHP it's like writing this:

<?php

define('FIRST_DOM', 1);
define('LAST_DOM', 31);

# 1-31/4
$dom = '1-31';
$step = '4';
$range = array();

switch(true)
{
    case is_null($step) === FALSE && ctype_digit($step):
        $num3 = $step;
        break;
    default:
        $num3 = LAST_DOM - FIRST_DOM + 1;
        break;
}

switch($dom)
{
    case preg_match('/^(\d{1,2}\-\d{1,2})/', $dom, $match) == 1:
        $nums = explode('-', $match[1]);
        $num1 = $nums[0];
        $num2 = $nums[1];
        break;
    case ctype_digit($dom):
        $num1 = $dom;
        $num2 = LAST_DOM;
        break;
    default:
    case '*':
        $num1 = FIRST_DOM;
        $num2 = LAST_DOM;
        break;
}

for($i = $num1;  $i <= $num2;  $i += $num3)
{
    $range[] = $i;
}

echo implode(',', $range);

But you can also use:

# */4
$dom = '*';
$step = '4';

The output will be always:

1,5,9,13,17,21,25,29

Yes 1-31/4 would run on the 1st and every 4 days after that (so it would run the 1st, 5th, 9th, 13th, etc.)

OK got it. So to be guaranteed that it runs only on days divisible by 4 (ie date % 4 == 0), I would do 4-28/4. Right?

Yep. You can use 4-28/4 or the equivalent in V7 standard: 4,8,12,16,20,24,28.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of 1.20 million developers, IT pros, digital marketers, and technology enthusiasts learning and sharing knowledge.