Skip to content

Commit

Permalink
Merge pull request #97 from Dragooon/master
Browse files Browse the repository at this point in the history
Optimise minute and hour increments, fix hour increments for partial hour timezones
  • Loading branch information
dragonmantank committed Jan 26, 2016
2 parents 25ea4b5 + 65f68a9 commit c843c46
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 27 deletions.
39 changes: 39 additions & 0 deletions src/Cron/AbstractField.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,43 @@ public function isInIncrementsOfRanges($dateValue, $value)

return false;
}

/**
* Returns a range of values for the given cron expression
*
* @param string $expression The expression to evaluate
* @param int $max Maximum offset for range
*
* @return array
*/
public function getRangeForExpression($expression, $max)
{
$values = array();

if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) {
if (!$this->isIncrementsOfRanges($expression)) {
list ($offset, $to) = explode('-', $expression);
$stepSize = 1;
}
else {
$range = array_map('trim', explode('/', $expression, 2));
$stepSize = isset($range[1]) ? $range[1] : 0;
$range = $range[0];
$range = explode('-', $range, 2);
$offset = $range[0];
$to = isset($range[1]) ? $range[1] : $max;
}
$offset = $offset == '*' ? 0 : $offset;
for ($i = $offset; $i <= $to; $i += $stepSize) {
$values[] = $i;
}
sort($values);
}
else {
$values = array($expression);
}

return $values;
}

}
4 changes: 2 additions & 2 deletions src/Cron/CronExpression.php
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,14 @@ protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $a

// If the field is not satisfied, then start over
if (!$satisfied) {
$field->increment($nextRun, $invert);
$field->increment($nextRun, $invert, $part);
continue 2;
}
}

// Skip this match if needed
if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
$this->fieldFactory->getField(0)->increment($nextRun, $invert);
$this->fieldFactory->getField(0)->increment($nextRun, $invert, isset($parts[0]) ? $parts[0] : null);
continue;
}

Expand Down
54 changes: 40 additions & 14 deletions src/Cron/HoursField.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,51 @@ public function isSatisfiedBy(\DateTime $date, $value)
return $this->isSatisfied($date->format('H'), $value);
}

public function increment(\DateTime $date, $invert = false)
public function increment(\DateTime $date, $invert = false, $parts = null)
{
// Change timezone to UTC temporarily. This will
// allow us to go back or forwards and hour even
// if DST will be changed between the hours.
$timezone = $date->getTimezone();
$localMinutes = $date->format('i');
$date->setTimezone(new \DateTimeZone('UTC'));
// handle timezones with non-hour-offsets
$utcMinutes = $date->format('i');
$minDiff = $localMinutes - $utcMinutes;
if ($invert) {
$date->modify('-1 hour');
$date->setTime($date->format('H'), 59 - $minDiff);
} else {
$date->modify('+1 hour');
$date->setTime($date->format('H'), 0 - $minDiff);
if (is_null($parts) || $parts == '*') {
$timezone = $date->getTimezone();
$date->setTimezone(new \DateTimeZone('UTC'));
if ($invert) {
$date->modify('-1 hour');
} else {
$date->modify('+1 hour');
}
$date->setTimezone($timezone);

$date->setTime($date->format('H'), $invert ? 59 : 0);
return $this;
}

$parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts);
$hours = array();
foreach ($parts as $part) {
$hours = array_merge($hours, $this->getRangeForExpression($part, 23));
}

$current_hour = $date->format('H');
$position = $invert ? count($hours) - 1 : 0;
if (count($hours) > 1) {
for ($i = 0; $i < count($hours) - 1; $i++) {
if ((!$invert && $current_hour >= $hours[$i] && $current_hour < $hours[$i + 1]) ||
($invert && $current_hour > $hours[$i] && $current_hour <= $hours[$i + 1])) {
$position = $invert ? $i : $i + 1;
break;
}
}
}

$hour = $hours[$position];
if ((!$invert && $date->format('H') >= $hour) || ($invert && $date->format('H') <= $hour)) {
$date->modify(($invert ? '-' : '+') . '1 day');
$date->setTime($invert ? 23 : 0, $invert ? 59 : 0);
}
else {
$date->setTime($hour, $invert ? 59 : 0);
}
$date->setTimezone($timezone);

return $this;
}
Expand Down
39 changes: 34 additions & 5 deletions src/Cron/MinutesField.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,41 @@ public function isSatisfiedBy(\DateTime $date, $value)
return $this->isSatisfied($date->format('i'), $value);
}

public function increment(\DateTime $date, $invert = false)
public function increment(\DateTime $date, $invert = false, $parts = null)
{
if ($invert) {
$date->modify('-1 minute');
} else {
$date->modify('+1 minute');
if (is_null($parts)) {
if ($invert) {
$date->modify('-1 minute');
} else {
$date->modify('+1 minute');
}
return $this;
}

$parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts);
$minutes = array();
foreach ($parts as $part) {
$minutes = array_merge($minutes, $this->getRangeForExpression($part, 59));
}

$current_minute = $date->format('i');
$position = $invert ? count($minutes) - 1 : 0;
if (count($minutes) > 1) {
for ($i = 0; $i < count($minutes) - 1; $i++) {
if ((!$invert && $current_minute >= $minutes[$i] && $current_minute < $minutes[$i + 1]) ||
($invert && $current_minute > $minutes[$i] && $current_minute <= $minutes[$i + 1])) {
$position = $invert ? $i : $i + 1;
break;
}
}
}

if ((!$invert && $current_minute >= $minutes[$position]) || ($invert && $current_minute <= $minutes[$position])) {
$date->modify(($invert ? '-' : '+') . '1 hour');
$date->setTime($date->format('H'), $invert ? 59 : 0);
}
else {
$date->setTime($date->format('H'), $minutes[$position]);
}

return $this;
Expand Down
29 changes: 23 additions & 6 deletions tests/Cron/HoursFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,40 @@ public function testIncrementsDate()
$f->increment($d, true);
$this->assertEquals('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s'));
}

/**
* @covers Cron\HoursField::increment
*/
public function testIncrementsDateWithThirtyMinuteOffsetTimezone()
{
$tz = date_default_timezone_get();
date_default_timezone_set('America/St_Johns');
$d = new DateTime('2011-03-15 11:15:00');
$tz = date_default_timezone_get();
date_default_timezone_set('America/St_Johns');
$d = new DateTime('2011-03-15 11:15:00');
$f = new HoursField();
$f->increment($d);
$this->assertEquals('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s'));

$d->setTime(11, 15, 0);
$f->increment($d, true);
$this->assertEquals('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s'));
date_default_timezone_set($tz);
}

/**
* @covers Cron\HoursField::increment
*/
public function testIncrementDateWithFifteenMinuteOffsetTimezone()
{
$tz = date_default_timezone_get();
date_default_timezone_set('Asia/Kathmandu');
$d = new DateTime('2011-03-15 11:15:00');
$f = new HoursField();
$f->increment($d);
$this->assertEquals('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s'));

$d->setTime(11, 15, 0);
$f->increment($d, true);
$this->assertEquals('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s'));
date_default_timezone_set($tz);
date_default_timezone_set($tz);
}

}

0 comments on commit c843c46

Please sign in to comment.