interval = $interval; $this->start = $start; $this->end = $end; $this->negative = $this->start > $this->end; } /** * Return when this range of time starts * * @return DateTime */ public function getStart() { return $this->start; } /** * Return when this range of time ends * * @return DateTime */ public function getEnd() { return $this->end; } /** * Return the interval by which this time range is split * * @return DateInterval */ public function getInterval() { return $this->interval; } /** * Return the appropriate timeframe for the given date and time or null if none could be found * * @param DateTime $dateTime The date and time for which to search the timeframe * @param bool $asTimestamp Whether the start of the timeframe should be returned as timestamp * @return stdClass|int|null An object with a ´start´ and ´end´ property or a timestamp */ public function findTimeframe(DateTime $dateTime, $asTimestamp = false) { foreach ($this as $timeframeIdentifier => $timeframe) { if ($this->negative) { if ($dateTime <= $timeframe->start && $dateTime >= $timeframe->end) { return $asTimestamp ? $timeframeIdentifier : $timeframe; } } elseif ($dateTime >= $timeframe->start && $dateTime <= $timeframe->end) { return $asTimestamp ? $timeframeIdentifier : $timeframe; } } } /** * Return whether the given time is within this range of time * * @param string|int|DateTime $time The timestamp or date and time to check */ public function validateTime($time) { if ($time instanceof DateTime) { $dateTime = $time; } elseif (is_string($time)) { $dateTime = DateTime::createFromFormat('d/m/Y g:i A', $time); } else { $dateTime = new DateTime(); $dateTime->setTimestamp($time); } return ($this->negative && ($dateTime <= $this->start && $dateTime >= $this->end)) || (!$this->negative && ($dateTime >= $this->start && $dateTime <= $this->end)); } /** * Return the appropriate timeframe for the given timeframe start * * @param int|DateTime $time The timestamp or date and time for which to return the timeframe * @return stdClass An object with a ´start´ and ´end´ property */ public function getTimeframe($time) { if ($time instanceof DateTime) { $startTime = clone $time; } else { $startTime = new DateTime(); $startTime->setTimestamp($time); } return $this->buildTimeframe($startTime, $this->applyInterval(clone $startTime, 1)); } /** * Apply the current interval to the given date and time * * @param DateTime $dateTime The date and time to apply the interval to * @param int $adjustBy By how much seconds the resulting date and time should be adjusted * * @return DateTime */ protected function applyInterval(DateTime $dateTime, $adjustBy) { if (!$this->interval->y && !$this->interval->m) { if ($this->negative) { return $dateTime->sub($this->interval)->add(new DateInterval('PT' . $adjustBy . 'S')); } else { return $dateTime->add($this->interval)->sub(new DateInterval('PT' . $adjustBy . 'S')); } } elseif ($this->interval->m) { for ($i = 0; $i < $this->interval->m; $i++) { if ($this->negative) { $dateTime->sub(new DateInterval('PT' . Format::secondsByMonth($dateTime) . 'S')); } else { $dateTime->add(new DateInterval('PT' . Format::secondsByMonth($dateTime) . 'S')); } } } elseif ($this->interval->y) { for ($i = 0; $i < $this->interval->y; $i++) { if ($this->negative) { $dateTime->sub(new DateInterval('PT' . Format::secondsByYear($dateTime) . 'S')); } else { $dateTime->add(new DateInterval('PT' . Format::secondsByYear($dateTime) . 'S')); } } } $adjustment = new DateInterval('PT' . $adjustBy . 'S'); return $this->negative ? $dateTime->add($adjustment) : $dateTime->sub($adjustment); } /** * Return an object representation of the given timeframe * * @param DateTime $start The start of the timeframe * @param DateTime $end The end of the timeframe * @return stdClass */ protected function buildTimeframe(DateTime $start, DateTime $end) { $timeframe = new stdClass(); $timeframe->start = $start; $timeframe->end = $end; return $timeframe; } /** * Reset the iterator to its initial state */ public function rewind(): void { $this->current = clone $this->start; } /** * Return whether the current iteration step is valid * * @return bool */ public function valid(): bool { if ($this->negative) { return $this->current > $this->end; } else { return $this->current < $this->end; } } /** * Return the current value in the iteration * * @return stdClass */ public function current(): object { return $this->getTimeframe($this->current); } /** * Return a unique identifier for the current value in the iteration * * @return int */ public function key(): int { return $this->current->getTimestamp(); } /** * Advance the iterator position by one */ public function next(): void { $this->applyInterval($this->current, 0); } }