Fix cronjob month calculation
authorMorik <Sebastian.Zimmer@gmx.net>
Sat, 7 Jan 2017 16:22:03 +0000 (17:22 +0100)
committerGitHub <noreply@github.com>
Sat, 7 Jan 2017 16:22:03 +0000 (17:22 +0100)
The current calculation ignores given months, this is fixxed with this pull request.
To detect any issues with the new implementation i'll run several tests without any differences with the currently used cronjobs used by the wsc.
https://gist.github.com/Morik/27804aaa57916150dd08268cd8e224d8
Running those tests took quite a while but there have been no differences for all calculations without a month selection so this fix should have no problems with them.
It also allows some exotic cronjobs like the 29. February etc.

wcfsetup/install/files/lib/util/CronjobUtil.class.php

index 6cfca4e5e8ffad4b5ff5cc181984e7ab201670b5..489f5067b1224c29bab1d95be14afb7f40689123 100644 (file)
@@ -138,14 +138,30 @@ final class CronjobUtil {
         * @param       array           $values
         */
        protected static function calculateTime(array &$values) {
-               // calculation starts with month, thus start with
-               // month of $time (if within values)
-               $currentMonth = gmdate('n', self::$timeBase);
-               self::findKey($currentMonth, $values['month']);
-               
                self::calculateDay($values);
        }
        
+       /**
+        * Calculates the next month and year to match given criteria.
+        * 
+        * @param       integer         $month
+        * @param       integer         $year
+        * @param       array           $values
+        */
+       protected static function calculateMonth($month, $year, array &$values) {
+               $index = self::findKey($month, $values['month']);
+               
+               // swap to the next year if the next execution month is before the current month
+               if ($values['month'][$index] < $month) {
+                       $year++;
+               }
+               
+               return [
+                       'month' => $values['month'][$index],
+                       'year' => $year
+               ];
+       }
+       
        /**
         * Calculates the day while adjusting month and year to match given criteria.
         * 
@@ -169,6 +185,21 @@ final class CronjobUtil {
                $month = gmdate('n', $timeBase);
                $year = gmdate('Y', $timeBase);
                
+               // calculate month of next execution and if its not the current one reset previous calculations
+               $dateMonth = self::calculateMonth($month, $year, $values);
+               if ($month != $dateMonth['month'] || $year != $dateMonth['year']) {
+                       $day = 1;
+                       $month = $dateMonth['month'];
+                       $year = $dateMonth['year'];
+                       
+                       $timeBase = gmmktime(0, 0, 1, $month, $day, $year);
+                       
+                       if (!$addAnDay) {
+                               self::calculateHour($values, $timeBase);
+                               $addAnDay = true;
+                       }
+               }
+               
                // calculate date of next execution based upon day of week
                $dateDow = self::calculateDow($month, $year, $values, $day);
                $dateDowTimestamp = gmmktime(0, 0, 1, $dateDow['month'], $dateDow['day'], $dateDow['year']);
@@ -245,7 +276,8 @@ final class CronjobUtil {
                }
                
                // try next month
-               return self::calculateDow(++$month, $year, $values);
+               $nextMonth = self::calculateMonth(++$month, $year, $values);
+               return self::calculateDow($nextMonth['month'], $nextMonth['year'], $values);
        }
        
        /**
@@ -271,7 +303,8 @@ final class CronjobUtil {
                }
                
                // try next month
-               return self::calculateDom(++$month, $year, $values);
+               $nextMonth = self::calculateMonth(++$month, $year, $values);
+               return self::calculateDom($nextMonth['month'], $nextMonth['year'], $values);
        }
        
        /**