dragonmantank / cron-expression

CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due
MIT License
4.56k stars 123 forks source link

Calculate all run dates between two dates #1

Open geryguilbon opened 7 years ago

geryguilbon commented 7 years ago

I think this could be great to have this feature implemented =)

ComlOnline commented 6 years ago

Not pretty or very good but this works:

<?php
$start = "2010-01-12 00:00:00";
$end = "2011-01-13 00:00:00";
$ahead = 0;
$cron = Cron\CronExpression::factory('@monthly');
$init = $cron->getNextRunDate($start, $ahead)->format('Y-m-d H:i:s');
while (strtotime($init) <= strtotime($end)) {
                echo "<pre>$init\n<pre>";
                $ahead++;
                $init =  $cron->getNextRunDate($start, $ahead)->format('Y-m-d H:i:s');
}
?>
va5ja commented 1 year ago

Same for "get next X runs" or is that possible already?

cottton commented 1 year ago

Needed a from-to last days ... here is a concept i would suggest:

EDIT: i just realizeed that the param $invert makes no sense in this method. So ignore it - should be provided as false always at getMultipleRunDates =)

function getMultipleRunDatesByTimeframe(
    $cronExpression,
    $from = 'now',
    $to = 'now + 1 hour',
    // bool $invert = false,
    bool $allowCurrentDate = false,
    $timeZone = null
) {
    $model = new CronExpression($cronExpression);
    $from = new DateTime($from);
    $to = new DateTime($to);

    $matches = [];
    while ($from <= $to) {
        $match = $model->getMultipleRunDates(
            1, // $total,
            $from, // $currentTime,
            false, // $invert,
            $allowCurrentDate,
            $timeZone
        )[0];
        $matches[] = $match;
        // After every run re-set the $from var to not run into "Impossible CRON expression".
        // Example:
        // $cronExpression  : "*/15 * * * *"
        // $from            : "2023-07-29 16:12:49"
        // $match           : "2023-07-29 16:15:00"
        // $from re-set     : "2023-07-29 16:15:00"
        $from = clone $match;
        // After the first run we need to set the var $allowCurrentDate to value false
        // to not keep looping on the same datetime.
        // Example:
        // we just got "2023-07-29 19:15:00"
        // and would get "2023-07-29 19:15:00" again, and again, ... .
        $allowCurrentDate = false;
    }
    return $matches;
}

$cronExpression = '*/15 * * * *';
$from = 'now';
$to = 'now + 1 hour';
// $invert = false;
$allowCurrentDate = true;
$timeZone = null;
$matches = getMultipleRunDatesByTimeframe(
    $cronExpression,
    $from,
    $to,
    // $invert,
    $allowCurrentDate,
    $timeZone
);

I used getMultipleRunDates because its public unlike getRunDate. I choose to get always 1 single run date to not run into $maxIterationCount error. And this makes it actually pretty fast. Expression */15 * * * * from 2023-01-01 to 2023-12-31 took 0.354930 sec.


Suggestions:

One single method to get datetime input to datetime object

There are atm 3 places of $currentTime checks at https://github.com/dragonmantank/cron-expression/blob/master/src/Cron/CronExpression.php#L317 https://github.com/dragonmantank/cron-expression/blob/master/src/Cron/CronExpression.php#L403 https://github.com/dragonmantank/cron-expression/blob/master/src/Cron/CronExpression.php#L445 I would suggest to add a new method getDatetimeObject($datetime) to be able to

New method to get multiple run dates by timeframe

Implement a metod getMultipleRunDatesByTimeframe(...) like

public function getMultipleRunDatesByTimeframe(
    $from = 'now',
    $to = 'now + 1 hour',
    // bool $invert = false,
    bool $allowCurrentDate = false,
    $timeZone = null
): array {}

and use the new|suggested getDatetimeObject(...) method to

The code for this method would be ofc the one in the concept above, without the need of the cron expression model creation.