Viewing file: HoursField.php (6.94 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
declare(strict_types=1);
namespace Cron;
use DateTimeInterface; use DateTimeZone;
/** * Hours field. Allows: * , / -. */ class HoursField extends AbstractField { /** * {@inheritdoc} */ protected $rangeStart = 0;
/** * {@inheritdoc} */ protected $rangeEnd = 23;
/** * @var array|null Transitions returned by DateTimeZone::getTransitions() */ protected $transitions = [];
/** * @var int|null Timestamp of the start of the transitions range */ protected $transitionsStart = null;
/** * @var int|null Timestamp of the end of the transitions range */ protected $transitionsEnd = null;
/** * {@inheritdoc} */ public function isSatisfiedBy(DateTimeInterface $date, $value, bool $invert): bool { $checkValue = (int) $date->format('H'); $retval = $this->isSatisfied($checkValue, $value); if ($retval) { return $retval; }
// Are we on the edge of a transition $lastTransition = $this->getPastTransition($date); if (($lastTransition !== null) && ($lastTransition["ts"] > ((int) $date->format('U') - 3600))) { $dtLastOffset = clone $date; $this->timezoneSafeModify($dtLastOffset, "-1 hour"); $lastOffset = $dtLastOffset->getOffset();
$dtNextOffset = clone $date; $this->timezoneSafeModify($dtNextOffset, "+1 hour"); $nextOffset = $dtNextOffset->getOffset();
$offsetChange = $nextOffset - $lastOffset; if ($offsetChange >= 3600) { $checkValue -= 1; return $this->isSatisfied($checkValue, $value); } if ((! $invert) && ($offsetChange <= -3600)) { $checkValue += 1; return $this->isSatisfied($checkValue, $value); } }
return $retval; }
public function getPastTransition(DateTimeInterface $date): ?array { $currentTimestamp = (int) $date->format('U'); if ( ($this->transitions === null) || ($this->transitionsStart < ($currentTimestamp + 86400)) || ($this->transitionsEnd > ($currentTimestamp - 86400)) ) { // We start a day before current time so we can differentiate between the first transition entry // and a change that happens now $dtLimitStart = clone $date; $dtLimitStart = $dtLimitStart->modify("-12 months"); $dtLimitEnd = clone $date; $dtLimitEnd = $dtLimitEnd->modify('+12 months');
$this->transitions = $date->getTimezone()->getTransitions( $dtLimitStart->getTimestamp(), $dtLimitEnd->getTimestamp() ); if (empty($this->transitions)) { return null; } $this->transitionsStart = $dtLimitStart->getTimestamp(); $this->transitionsEnd = $dtLimitEnd->getTimestamp(); }
$nextTransition = null; foreach ($this->transitions as $transition) { if ($transition["ts"] > $currentTimestamp) { continue; }
if (($nextTransition !== null) && ($transition["ts"] < $nextTransition["ts"])) { continue; }
$nextTransition = $transition; }
return ($nextTransition ?? null); }
/** * {@inheritdoc} * * @param string|null $parts */ public function increment(DateTimeInterface &$date, $invert = false, $parts = null): FieldInterface { $originalTimestamp = (int) $date->format('U');
// 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. if (null === $parts || '*' === $parts) { if ($invert) { $date = $date->sub(new \DateInterval('PT1H')); } else { $date = $date->add(new \DateInterval('PT1H')); }
$date = $this->setTimeHour($date, $invert, $originalTimestamp); return $this; }
$parts = false !== strpos($parts, ',') ? explode(',', $parts) : [$parts]; $hours = []; foreach ($parts as $part) { $hours = array_merge($hours, $this->getRangeForExpression($part, 23)); }
$current_hour = (int) $date->format('H'); $position = $invert ? \count($hours) - 1 : 0; $countHours = \count($hours); if ($countHours > 1) { for ($i = 0; $i < $countHours - 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; } } }
$target = (int) $hours[$position]; $originalHour = (int)$date->format('H');
$originalDay = (int)$date->format('d'); $previousOffset = $date->getOffset();
if (! $invert) { if ($originalHour >= $target) { $distance = 24 - $originalHour; $date = $this->timezoneSafeModify($date, "+{$distance} hours");
$actualDay = (int)$date->format('d'); $actualHour = (int)$date->format('H'); if (($actualDay !== ($originalDay + 1)) && ($actualHour !== 0)) { $offsetChange = ($previousOffset - $date->getOffset()); $date = $this->timezoneSafeModify($date, "+{$offsetChange} seconds"); }
$originalHour = (int)$date->format('H'); }
$distance = $target - $originalHour; $date = $this->timezoneSafeModify($date, "+{$distance} hours"); } else { if ($originalHour <= $target) { $distance = ($originalHour + 1); $date = $this->timezoneSafeModify($date, "-" . $distance . " hours");
$actualDay = (int)$date->format('d'); $actualHour = (int)$date->format('H'); if (($actualDay !== ($originalDay - 1)) && ($actualHour !== 23)) { $offsetChange = ($previousOffset - $date->getOffset()); $date = $this->timezoneSafeModify($date, "+{$offsetChange} seconds"); }
$originalHour = (int)$date->format('H'); }
$distance = $originalHour - $target; $date = $this->timezoneSafeModify($date, "-{$distance} hours"); }
$date = $this->setTimeHour($date, $invert, $originalTimestamp);
$actualHour = (int)$date->format('H'); if ($invert && ($actualHour === ($target - 1) || (($actualHour === 23) && ($target === 0)))) { $date = $this->timezoneSafeModify($date, "+1 hour"); }
return $this; } }
|