123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- <?php
- namespace App\Helpers;
- use DateTime;
- class TimeLine {
- public $start = null;
- public $end = null;
- public $available = [];
- public $unavailable = [];
- public $removeStart = null;
- public $removeEnd = null;
- public function __construct(DateTime $_start, DateTime $_end)
- {
- $this->start = $_start->getTimestamp();
- $this->end = $_end->getTimestamp();
- }
- public function addAvailability(DateTime $_start, DateTime $_end) {
- $this->available[] = new TimeSlot($_start, $_end);
- // sort by start
- usort($this->available, function ($item1, $item2) {
- return $item1->start <=> $item2->start;
- });
- // compact (join adjacent overlapping slots)
- // $this->vdump($this->available);
- $this->normalize();
- $this->vdump($this->available);
- }
- public function removeAvailability(DateTime $_start, DateTime $_end) {
- $removeStart = $_start->getTimestamp();
- $removeEnd = $_end->getTimestamp();
- $cleaned = $this->removeEmbeddedInAvailSlot($removeStart, $removeEnd);
- if(!$cleaned) {
- for ($i=0; $i<count($this->available); $i++) $this->available[$i]->processed = false;
- $this->removeStart($removeStart, $removeEnd);
- for ($i=0; $i<count($this->available); $i++) $this->available[$i]->processed = false;
- $this->removeEnd($removeStart, $removeEnd);
- $this->removeEmbeddedInRemoveSlot($removeStart, $removeEnd);
- }
- // sort by start
- usort($this->available, function ($item1, $item2) {
- return $item1->start <=> $item2->start;
- });
- $this->normalize();
- $this->vdump($this->available);
- // add to unavailable
- $this->unavailable[] = new TimeSlot($_start, $_end);
- // $this->normalizeUnavailable();
- }
- private function removeEmbeddedInAvailSlot($removeStart, $removeEnd) {
- for ($i=0; $i<count($this->available); $i++) {
- // removeStart at or after slot start
- // removeEnd at or before slot end
- // split slot into 2
- if ($removeStart >= $this->available[$i]->start &&
- $removeEnd <= $this->available[$i]->end) {
- $newSlot = new TimeSlot(new DateTime(), new DateTime());
- $newSlot->start = $removeEnd;
- $newSlot->end = $this->available[$i]->end;
- $this->available[$i]->end = $removeStart;
- array_splice($this->available, $i + 1, 0, [$newSlot]);
- return true; // nothing more to do
- }
- }
- return false;
- }
- private function removeEmbeddedInRemoveSlot($removeStart, $removeEnd) {
- $allDone = true;
- for ($i=0; $i<count($this->available); $i++) {
- // removeStart at or before slot start
- // removeEnd at or after slot end
- // remove slot
- if ($removeStart <= $this->available[$i]->start &&
- $removeEnd >= $this->available[$i]->end) {
- array_splice($this->available, $i, 1);
- $allDone = false;
- }
- }
- if(!$allDone) {
- $this->removeEmbeddedInRemoveSlot($removeStart, $removeEnd); // recurse till clean
- }
- return false;
- }
- private function removeStart($removeStart, $removeEnd) {
- $allDone = true;
- for ($i=0; $i<count($this->available); $i++) {
- if($this->available[$i]->processed) continue;
- // removeStart at or after slot start
- // removeStart at or before slot end
- // update slot to end at removeStart
- if($removeStart >= $this->available[$i]->start &&
- $removeStart <= $this->available[$i]->end) {
- $this->available[$i]->end = $removeStart;
- $this->available[$i]->processed = true;
- $allDone = false;
- break;
- }
- }
- if(!$allDone) {
- $this->removeStart($removeStart, $removeEnd); // recurse till clean
- }
- }
- private function removeEnd($removeStart, $removeEnd) {
- $allDone = true;
- for ($i=0; $i<count($this->available); $i++) {
- if($this->available[$i]->processed) continue;
- // removeEnd at or after slot start
- // removeEnd at or before slot end
- // update slot to start at removeEnd
- if($removeEnd >= $this->available[$i]->start &&
- $removeEnd <= $this->available[$i]->end) {
- $this->available[$i]->start = $removeEnd;
- $this->available[$i]->processed = true;
- $allDone = false;
- break;
- }
- }
- if(!$allDone) {
- $this->removeEnd($removeStart, $removeEnd); // recurse till clean
- }
- }
- private function normalize() {
- $allDone = true;
- for ($i=0; $i<count($this->available)-1; $i++) {
- if($this->available[$i]->end >= $this->available[$i+1]->start && // ends in the middle of next slot
- $this->available[$i]->end <= $this->available[$i+1]->end)
- {
- // extend self & delete next
- $this->available[$i]->end = $this->available[$i+1]->end;
- array_splice($this->available, $i+1, 1);
- $allDone = false;
- break;
- }
- if($this->available[$i]->end >= $this->available[$i+1]->end) // ends after next slot end
- {
- // delete next
- array_splice($this->available, $i+1, 1);
- $allDone = false;
- break;
- }
- if($this->available[$i]->start >= $this->available[$i]->end) // starts and ends at the same time!
- {
- // delete self
- array_splice($this->available, $i, 1);
- $allDone = false;
- break;
- }
- }
- if(!$allDone) {
- $this->normalize(); // recurse till clean
- }
- }
- private function normalizeUnavailable() {
- $allDone = true;
- for ($i=0; $i<count($this->unavailable)-1; $i++) {
- if($this->unavailable[$i]->end >= $this->unavailable[$i+1]->start && // ends in the middle of next slot
- $this->unavailable[$i]->end <= $this->unavailable[$i+1]->end)
- {
- // extend self & delete next
- $this->unavailable[$i]->end = $this->unavailable[$i+1]->end;
- array_splice($this->unavailable, $i+1, 1);
- $allDone = false;
- break;
- }
- if($this->unavailable[$i]->end >= $this->unavailable[$i+1]->end) // ends after next slot end
- {
- // delete next
- array_splice($this->unavailable, $i+1, 1);
- $allDone = false;
- break;
- }
- if($this->unavailable[$i]->start >= $this->unavailable[$i]->end) // starts and ends at the same time!
- {
- // delete self
- array_splice($this->unavailable, $i, 1);
- $allDone = false;
- break;
- }
- }
- if(!$allDone) {
- $this->normalizeUnavailable(); // recurse till clean
- }
- }
- public function getAvailable() {
- return $this->available;
- }
- public function getUnavailable() {
- return $this->unavailable;
- }
- public function vdump($_x) {
- // echo "<pre>";
- // print_r($_x);
- // echo "</pre><hr>";
- }
- }
|