-
-
Save nonsensecreativity/f84dc2e0cb592f915da324ea2985d231 to your computer and use it in GitHub Desktop.
DateTime Range and Ranges
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * @link http://stackoverflow.com/questions/13129336/split-a-time-range-into-pieces-by-other-time-ranges | |
| * @link https://gist.github.com/gists/3977645 | |
| * @author hakre | |
| */ | |
| class Range | |
| { | |
| /** | |
| * @var DateTime | |
| */ | |
| protected $start; | |
| /** | |
| * @var DateTime | |
| */ | |
| protected $end; | |
| public function __construct(DateTime $start, DateTime $end) { | |
| $this->setStart($start); | |
| $this->setEnd($end); | |
| } | |
| /** | |
| * @return DateTime | |
| */ | |
| public function getStart() { | |
| return $this->start; | |
| } | |
| public function setStart(DateTime $start) { | |
| $this->start = $start; | |
| } | |
| /** | |
| * @return \DateTime | |
| */ | |
| public function getEnd() { | |
| return $this->end; | |
| } | |
| /** | |
| * @param DateTime $end | |
| * @throws InvalidArgumentException | |
| */ | |
| public function setEnd(DateTime $end) { | |
| if ($end < $this->start) { | |
| throw new InvalidArgumentException('End before Start'); | |
| } | |
| $this->end = $end; | |
| } | |
| public function hasTime(DateTime $time) { | |
| return $this->start <= $time and $this->end >= $time; | |
| } | |
| public function hasContact(Range $range) { | |
| return $this->hasTime($range->start) | |
| or $this->hasTime($range->end); | |
| } | |
| public function isSame(Range $range) { | |
| return $this->start == $range->start | |
| and $this->end == $range->end; | |
| } | |
| public function isWithin(Range $range) { | |
| return $range->start > $this->start | |
| and $range->end < $this->end; | |
| } | |
| public function isSubset(Range $range) { | |
| return $range->hasTime($this->start) | |
| and $range->hasTime($this->end); | |
| } | |
| public function add(Range $range) { | |
| if (!$this->hasContact($range)) { | |
| throw new InvalidArgumentException('Range needs to overlap.'); | |
| } | |
| if ($range->start < $this->start) { | |
| $this->start = $range->start; | |
| } | |
| if ($range->end > $this->end) { | |
| $this->end = $range->end; | |
| } | |
| } | |
| public function substract(Range $range) { | |
| if ($this->isWithIn($range)) { | |
| throw new InvalidArgumentException('Range would divide.'); | |
| } | |
| if ($this->isSubset($range)) { | |
| throw new InvalidArgumentException('Range would delete.'); | |
| } | |
| if (!$this->hasContact($range)) { | |
| return; | |
| } | |
| if ($range->start == $this->start) { | |
| $this->start = $range->end; | |
| return; | |
| } | |
| if ($range->end == $this->end) { | |
| $this->end = $range->start; | |
| return; | |
| } | |
| if ($range->start < $this->end) { | |
| $this->end = $range->start; | |
| } elseif ($range->end > $this->start) { | |
| $this->start = $range->end; | |
| } | |
| } | |
| public function getDifferenceArray(Range $range) { | |
| if ($this->isSubset($range)) { | |
| return []; | |
| } | |
| if (!$this->hasContact($range)) { | |
| return [clone $this]; | |
| } | |
| if ($this->isWithin($range)) { | |
| $result[1] = clone $result[0] = clone $this; | |
| $result[0]->end = $range->start; | |
| $result[1]->start = $range->end; | |
| return $result; | |
| } | |
| $result = clone $this; | |
| $result->substract($range); | |
| return [$result]; | |
| } | |
| public function format($format) { | |
| return [ | |
| $this->start->format($format), | |
| $this->end->format($format) | |
| ]; | |
| } | |
| } | |
| class Ranges implements IteratorAggregate, Countable | |
| { | |
| protected $ranges = []; | |
| public function __construct($ranges = NULL, DateTime $end = NULL) { | |
| if ($ranges) { | |
| if ($ranges instanceof DateTime) { | |
| if (null === $end) { | |
| throw new InvalidArgumentException('Need start and end.'); | |
| } | |
| $ranges = new Range($ranges, $end); | |
| } | |
| if ($ranges instanceof Range) { | |
| $ranges = [$ranges]; | |
| } | |
| foreach ($ranges as $range) { | |
| $this->append($range); | |
| } | |
| } | |
| } | |
| public function getStart() { | |
| if (!$this->ranges) { | |
| throw new BadMethodCallException('Enpty Range'); | |
| } | |
| return $this->ranges[0]->getStart(); | |
| } | |
| public function getEnd() { | |
| if (!$this->ranges) { | |
| throw new BadMethodCallException('Enpty Range'); | |
| } | |
| return $this->ranges[count($this->ranges) - 1]->getEnd(); | |
| } | |
| public function append(Range $range) { | |
| if ($this->ranges) { | |
| if ($range->getStart() <= $this->getEnd()) { | |
| throw new InvalidArgumentException('Can not append Range that is inside ranged time already'); | |
| } | |
| } | |
| $this->ranges[] = $range; | |
| } | |
| /** | |
| * @param Range $range | |
| * @return Ranges | |
| */ | |
| public function substractRange(Range $range) { | |
| $result = new self(); | |
| foreach ($this as $member) { | |
| /* @var Range $member */ | |
| foreach ($member->getDifferenceArray($range) as $new) { | |
| $result->append($new); | |
| } | |
| } | |
| $this->ranges = $result->ranges; | |
| } | |
| public function substract(Ranges $ranges) { | |
| $result = clone $this; | |
| foreach ($ranges as $range) { | |
| $result->substractRange($range); | |
| } | |
| $this->ranges = $result->ranges; | |
| } | |
| public function getIterator() { | |
| return new ArrayIterator($this->ranges); | |
| } | |
| public function getRange() { | |
| return new Range($this->getStart(), $this->getEnd()); | |
| } | |
| public function count() { | |
| return count($this->ranges); | |
| } | |
| } | |
| $shift = new Ranges(new DateTime('14:30:00'), new DateTime('18:30:00')); | |
| $unavailables = new Ranges([ | |
| new Range(new DateTime('15:30:00'), new DateTime('16:30:00')), | |
| new Range(new DateTime('17:30:00'), new DateTime('18:30:00')), | |
| ]); | |
| $shift->substract($unavailables); | |
| foreach ($shift as $range) { | |
| vprintf("%s - %s\n", $range->format('H:i:s')); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment