Calculate when a cron job will be executed then next time

后端 未结 8 909
庸人自扰
庸人自扰 2020-12-02 08:19

I have a cron \"time definition\"

1 * * * * (every hour at xx:01)
2 5 * * * (every day at 05:02)
0 4 3 * * (every third day of the month at 04:00)
* 2 * * 5          


        
相关标签:
8条回答
  • 2020-12-02 09:03

    For anyone interested, here's my final PHP implementation, which pretty much equals dlamblin pseudo code:

    class myMiniDate {
        var $myTimestamp;
        static private $dateComponent = array(
                                        'second' => 's',
                                        'minute' => 'i',
                                        'hour' => 'G',
                                        'day' => 'j',
                                        'month' => 'n',
                                        'year' => 'Y',
                                        'dow' => 'w',
                                        'timestamp' => 'U'
                                      );
        static private $weekday = array(
                                    1 => 'monday',
                                    2 => 'tuesday',
                                    3 => 'wednesday',
                                    4 => 'thursday',
                                    5 => 'friday',
                                    6 => 'saturday',
                                    0 => 'sunday'
                                  );
    
        function __construct($ts = NULL) { $this->myTimestamp = is_null($ts)?time():$ts; }
    
        function __set($var, $value) {
            list($c['second'], $c['minute'], $c['hour'], $c['day'], $c['month'], $c['year'], $c['dow']) = explode(' ', date('s i G j n Y w', $this->myTimestamp));
            switch ($var) {
                case 'dow':
                    $this->myTimestamp = strtotime(self::$weekday[$value], $this->myTimestamp);
                    break;
    
                case 'timestamp':
                    $this->myTimestamp = $value;
                    break;
    
                default:
                    $c[$var] = $value;
                    $this->myTimestamp = mktime($c['hour'], $c['minute'], $c['second'], $c['month'], $c['day'], $c['year']);
            }
        }
    
    
        function __get($var) {
            return date(self::$dateComponent[$var], $this->myTimestamp);
        }
    
        function modify($how) { return $this->myTimestamp = strtotime($how, $this->myTimestamp); }
    }
    
    
    $cron = new myMiniDate(time() + 60);
    $cron->second = 0;
    $done = 0;
    
    echo date('Y-m-d H:i:s') . '<hr>' . date('Y-m-d H:i:s', $cron->timestamp) . '<hr>';
    
    $Job = array(
                'Minute' => 5,
                'Hour' => 3,
                'Day' => 13,
                'Month' => null,
                'DOW' => 5,
           );
    
    while ($done < 100) {
        if (!is_null($Job['Minute']) && ($cron->minute != $Job['Minute'])) {
            if ($cron->minute > $Job['Minute']) {
                $cron->modify('+1 hour');
            }
            $cron->minute = $Job['Minute'];
        }
        if (!is_null($Job['Hour']) && ($cron->hour != $Job['Hour'])) {
            if ($cron->hour > $Job['Hour']) {
                $cron->modify('+1 day');
            }
            $cron->hour = $Job['Hour'];
            $cron->minute = 0;
        }
        if (!is_null($Job['DOW']) && ($cron->dow != $Job['DOW'])) {
            $cron->dow = $Job['DOW'];
            $cron->hour = 0;
            $cron->minute = 0;
        }
        if (!is_null($Job['Day']) && ($cron->day != $Job['Day'])) {
            if ($cron->day > $Job['Day']) {
                $cron->modify('+1 month');
            }
            $cron->day = $Job['Day'];
            $cron->hour = 0;
            $cron->minute = 0;
        }
        if (!is_null($Job['Month']) && ($cron->month != $Job['Month'])) {
            if ($cron->month > $Job['Month']) {
                $cron->modify('+1 year');
            }
            $cron->month = $Job['Month'];
            $cron->day = 1;
            $cron->hour = 0;
            $cron->minute = 0;
        }
    
        $done = (is_null($Job['Minute']) || $Job['Minute'] == $cron->minute) &&
                (is_null($Job['Hour']) || $Job['Hour'] == $cron->hour) &&
                (is_null($Job['Day']) || $Job['Day'] == $cron->day) &&
                (is_null($Job['Month']) || $Job['Month'] == $cron->month) &&
                (is_null($Job['DOW']) || $Job['DOW'] == $cron->dow)?100:($done+1);
    }
    
    echo date('Y-m-d H:i:s', $cron->timestamp) . '<hr>';
    
    0 讨论(0)
  • 2020-12-02 09:07

    This is basically doing the reverse of checking if the current time fits the conditions. so something like:

    //Totaly made up language
    next = getTimeNow();
    next.addMinutes(1) //so that next is never now
    done = false;
    while (!done) {
      if (cron.minute != '*' && next.minute != cron.minute) {
        if (next.minute > cron.minute) {
          next.addHours(1);
        }
        next.minute = cron.minute;
      }
      if (cron.hour != '*' && next.hour != cron.hour) {
        if (next.hour > cron.hour) {
          next.hour = cron.hour;
          next.addDays(1);
          next.minute = 0;
          continue;
        }
        next.hour = cron.hour;
        next.minute = 0;
        continue;
      }
      if (cron.weekday != '*' && next.weekday != cron.weekday) {
        deltaDays = cron.weekday - next.weekday //assume weekday is 0=sun, 1 ... 6=sat
        if (deltaDays < 0) { deltaDays+=7; }
        next.addDays(deltaDays);
        next.hour = 0;
        next.minute = 0;
        continue;
      }
      if (cron.day != '*' && next.day != cron.day) {
        if (next.day > cron.day || !next.month.hasDay(cron.day)) {
          next.addMonths(1);
          next.day = 1; //assume days 1..31
          next.hour = 0;
          next.minute = 0;
          continue;
        }
        next.day = cron.day
        next.hour = 0;
        next.minute = 0;
        continue;
      }
      if (cron.month != '*' && next.month != cron.month) {
        if (next.month > cron.month) {
          next.addMonths(12-next.month+cron.month)
          next.day = 1; //assume days 1..31
          next.hour = 0;
          next.minute = 0;
          continue;
        }
        next.month = cron.month;
        next.day = 1;
        next.hour = 0;
        next.minute = 0;
        continue;
      }
      done = true;
    }
    

    I might have written that a bit backwards. Also it can be a lot shorter if in every main if instead of doing the greater than check you merely increment the current time grade by one and set the lesser time grades to 0 then continue; however then you'll be looping a lot more. Like so:

    //Shorter more loopy version
    next = getTimeNow().addMinutes(1);
    while (true) {
      if (cron.month != '*' && next.month != cron.month) {
        next.addMonths(1);
        next.day = 1;
        next.hour = 0;
        next.minute = 0;
        continue;
      }
      if (cron.day != '*' && next.day != cron.day) {
        next.addDays(1);
        next.hour = 0;
        next.minute = 0;
        continue;
      }
      if (cron.weekday != '*' && next.weekday != cron.weekday) {
        next.addDays(1);
        next.hour = 0;
        next.minute = 0;
        continue;
      }
      if (cron.hour != '*' && next.hour != cron.hour) {
        next.addHours(1);
        next.minute = 0;
        continue;
      }
      if (cron.minute != '*' && next.minute != cron.minute) {
        next.addMinutes(1);
        continue;
      }
      break;
    }
    
    0 讨论(0)
提交回复
热议问题