Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 13 |
CRAP | |
0.00% |
0 / 88 |
| WorkDayCalculator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 13 |
1892.00 | |
0.00% |
0 / 88 |
| __construct | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 4 |
|||
| roundDate | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 1 |
|||
| setTime | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 1 |
|||
| hourToWorkableHour | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| add | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| sub | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| addWorkDay | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 11 |
|||
| nextWorkDay | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 4 |
|||
| subWorkDay | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 11 |
|||
| prevWorkDay | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 4 |
|||
| numberOfWorkDays | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 9 |
|||
| workDaysArray | |
0.00% |
0 / 1 |
110.00 | |
0.00% |
0 / 22 |
|||
| is_nonWorkDay | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 1 |
|||
| _is_nonWorkDay | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 1 |
|||
| set_workHoursByWeekDay | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 2 |
|||
| workHoursByDayCacheCommon | |
0.00% |
0 / 1 |
20.00 | |
0.00% |
0 / 17 |
|||
| toJson | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| <?php | |
| namespace ia\Work\WorkDay; | |
| // https://period.thephpleague.com/ | |
| class WorkDayCalculator { | |
| protected $byHour = false; | |
| protected $dayCalculator; | |
| /** @var array [dayOfWeek=>[[8,14],[15, 18]], ...] 0=Sun,...,6=Sat. */ | |
| public static $workHoursByWeekDayDefault = [ | |
| 1=>[[9,17],], | |
| 2=>[[9,17],], | |
| 3=>[[9,17],], | |
| 4=>[[9,17],], | |
| 5=>[[9,17],], | |
| ]; | |
| /** @var array [dayOfWeek=>[[8,14],[15, 18]], ...] 0=Sun,...,6=Sat. */ | |
| protected $workHoursByWeekDay; | |
| /** @var array [dayOfWeek=>[[15,18],[8, 14]], ...] 0=Sun,...,6=Sat. */ | |
| protected $workHoursByWeekDayReversed; | |
| /** @var array [dayOfWeek=>int, ...] */ | |
| protected $totalWorkHourByWeekDay; | |
| /** @var array [dayOfWeek=>int, ...] | |
| */ | |
| protected $firstWorkHourByWeekDay; | |
| /** @var array [dayOfWeek=>int, ...] | |
| * simulate array_key_first by caching first key, for previous to php 7.3 | |
| */ | |
| protected $lastWorkHourByWeekDay; | |
| /** @var DateInterval 1 day interval | |
| * simulate array_key_last by caching last key, for previous to php 7.3 | |
| */ | |
| protected $dateIntervalOneDay; | |
| public function __construct($dayCalculator) { | |
| $this->dateIntervalOneDay = new \DateInterval("P1D"); | |
| $this->dayCalculator = $dayCalculator; | |
| $this->set_workHoursByWeekDay($dayCalculator->get_weekDaySchedule()); | |
| } | |
| // OVERRIDE _______________________________________ | |
| protected function roundDate($anyDate) { | |
| return self::toDateImmutable($anyDate); // DAY | |
| // return self::toDateHourImmutable($anyDate); // Hour | |
| } | |
| protected function setTime(\DateTimeImmutable $dateTime) { | |
| return $dateTime->setTime(0, 0, 0); // day | |
| //return $dateTime->setTime($this->firstWorkHourByWeekDay[$dateTime->format('w')], 0, 0); // por hour | |
| } | |
| public function hourToWorkableHour(\DateTimeImmutable $dateTime) { return $dateTime->setTime(0, 0, 0); } | |
| /** | |
| * Add units (days in days, overrided for hours in WorkHourCalculator) | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @param integer|string $numDays | |
| * @return DateTimeImmutable with $numDays workable units days added, ie after | |
| */ | |
| public function add($anyDate, $numDays) { return $this->addWorkDay($anyDate, $numDays); } | |
| /** | |
| * Substract units (days in days, overrided for hours in WorkHourCalculator) | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @param integer|string $numDays | |
| * @return DateTimeImmutable with $numDays workable units days/hours subsctracted, ie before | |
| */ | |
| public function sub($anyDate, $numDays) { return $this->subWorkDay($anyDate, $numDays); } | |
| // BY DAY ________________ | |
| /** | |
| * Add workable days to a date | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @param integer|string $numDays | |
| * @return DateTimeImmutable with $numDays workable days added, ie after | |
| */ | |
| public function addWorkDay($anyDate, $numDays = 1) { | |
| $numDays = (int)$numDays; | |
| if($numDays < 0) { | |
| return $this->subWorkDay($anyDate, -$numDays); | |
| } | |
| $dateTime = $this->hourToWorkableHour(self::toDateHourImmutable($anyDate)); | |
| if($this->_is_nonWorkDay($dateTime)) { | |
| $dateTime = $this->nextWorkDay($dateTime); | |
| } | |
| if($numDays == 0) { | |
| return $dateTime; | |
| } | |
| for($i=1; $i<=$numDays; $i++) { | |
| $dateTime = $this->nextWorkDay($dateTime); | |
| } | |
| return $this->hourToWorkableHour($dateTime); | |
| } | |
| /** | |
| * Get next or following workable day | |
| * | |
| * @param DateTimeImmutable $dateTime | |
| * @return DateTimeImmutable next or following workable day | |
| */ | |
| protected function nextWorkDay(\DateTimeImmutable $dateTime) { | |
| $preventEternalLoop = 0; | |
| do { | |
| $dateTime = $dateTime->add($this->dateIntervalOneDay); | |
| } while($this->_is_nonWorkDay($dateTime) && ++$preventEternalLoop < 367); | |
| return $this->setTime($dateTime); | |
| //return $dateTime->setTime($this->firstWorkHourByWeekDay[$dateTime->format('w')], 0, 0); | |
| } | |
| /** | |
| * Substract workable days to a date | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @param integer|string $numDays | |
| * @return DateTimeImmutable with $numDays workable days subsctracted, ie before | |
| */ | |
| public function subWorkDay($anyDate, $numDays = 1) { | |
| $numDays = (int)$numDays; | |
| if($numDays < 0) { | |
| return $this->addWorkDay($anyDate, -$numDays); | |
| } | |
| $dateTime = $this->hourToWorkableHour($this->roundDate($anyDate)); | |
| if( $this->_is_nonWorkDay($dateTime) ) { | |
| $dateTime = $this->prevWorkDay($dateTime); | |
| } | |
| if($numDays === 0) { | |
| return $dateTime; | |
| } | |
| for($i = 1; $i <= $numDays; $i++) { | |
| $dateTime = $this->prevWorkDay($dateTime); | |
| } | |
| return $dateTime; | |
| } | |
| /** | |
| * Get previous, or the day before, workable day | |
| * | |
| * @param DateTimeImmutable $dateTime | |
| * @return DateTimeImmutable previous, or the day before, workable day | |
| */ | |
| protected function prevWorkDay(\DateTimeImmutable $dateTime) { | |
| $preventEternalLoop = 0; | |
| do { | |
| $dateTime = $dateTime->sub($this->dateIntervalOneDay); | |
| } while($this->_is_nonWorkDay($dateTime) && ++$preventEternalLoop < 367); | |
| return $this->setTime($dateTime); | |
| //return $dateTime->setTime($this->firstWorkHourByWeekDay[$dateTime->format('w')], 0, 0); | |
| } | |
| /** | |
| * Number of workable days between dates $anyDateFrom and $anyDateTo | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDateFrom | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDateTo | |
| * @return int Number of days workable between dates $anyDateFrom and $anyDateTo | |
| */ | |
| public function numberOfWorkDays($anyDateFrom, $anyDateTo) { | |
| $start = $this->roundDate($anyDateFrom); | |
| $end = $this->roundDate($anyDateTo); | |
| $swaped = self::orderd($start, $end); | |
| $days = 0; | |
| while($end >= $start) { | |
| if(!$this->_is_nonWorkDay($start)) { | |
| $days++; | |
| } | |
| $start = $start->add($this->dateIntervalOneDay); | |
| } | |
| return $swaped * $days; | |
| } | |
| /** | |
| * Array of workable days between dateTimes $anyDateFrom to $anyDateTo | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDateFrom | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDateTo | |
| * @return array ['Y-m-d'=>Number_WorkableHours, ...] orderd from $anyDateFrom to $anyDateTo | |
| */ | |
| public function workDaysArray($anyDateFrom, $anyDateTo = null) { | |
| $firstDate = $this->roundDate($anyDateFrom); | |
| if($anyDateTo == null) { | |
| $anyDateTo = $anyDateFrom; | |
| } | |
| $lastDate = $this->roundDate($anyDateTo); | |
| $swaped = self::orderd($firstDate, $lastDate); | |
| $lastDayIsWorkDay = !$this->_is_nonWorkDay($lastDate); | |
| if($firstDate->format('Y-m-d') === $lastDate->format('Y-m-d')) { | |
| if(!$lastDayIsWorkDay) { | |
| return []; | |
| } | |
| if($this->byHour && $firstDate == $lastDate) { | |
| return [$firstDate->format('Y-m-d') => 0]; | |
| } | |
| return [ $firstDate->format('Y-m-d') => $this->totalWorkHourByWeekDay[$firstDate->format('w')] ]; | |
| } | |
| $workDays = []; | |
| while($lastDate > $firstDate) { | |
| if(!$this->_is_nonWorkDay($firstDate)) { | |
| $workDays[$firstDate->format('Y-m-d')] = $this->totalWorkHourByWeekDay[$firstDate->format('w')]; | |
| } | |
| $firstDate = $firstDate->add($this->dateIntervalOneDay); | |
| } | |
| if($lastDayIsWorkDay) { | |
| $workDays[$lastDate->format('Y-m-d')] = $this->totalWorkHourByWeekDay[$lastDate->format('w')]; | |
| } | |
| if($swaped === -1) { | |
| return \array_reverse($workDays, true); | |
| } | |
| return $workDays; | |
| } | |
| // ___________________________________________________________________________________________ | |
| /** | |
| * Is day not workable | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @return boolean true for non workable day, true is a work day | |
| */ | |
| public function is_nonWorkDay($anyDate) { | |
| return $this->_is_nonWorkDay(self::toDateImmutable($anyDate)); | |
| } | |
| /** | |
| * Is day not workable | |
| * | |
| * @param DateTimeImmutable $date | |
| * @return boolean true for non workable day, true is a work day | |
| */ | |
| protected function _is_nonWorkDay(\DateTimeImmutable $date) { | |
| return !$this->dayCalculator->is_workable($date); | |
| } | |
| // ___________________________________________________________________________________________ | |
| /** | |
| * Set work hours per day of week | |
| * | |
| * @param array $workHoursByWeekDay [dayOfWeek=>[[8,14],[15, 18]], ...] 0=Sun,...,6=Sat. | |
| * @return void | |
| */ | |
| protected function set_workHoursByWeekDay(array $workHoursByWeekDay) { | |
| //$workHoursByWeekDay = self::validateWorkHoursByWeekDay($workHoursByWeekDay); | |
| $this->workHoursByDayCacheCommon($workHoursByWeekDay); | |
| } | |
| /** | |
| * Calculate and save common used values | |
| * | |
| * @param array $workHoursByWeekDay [dayOfWeek=>[[8,14],[15, 18]], ...] 0=Sun,...,6=Sat. | |
| * @return void | |
| */ | |
| protected function workHoursByDayCacheCommon(array $workHoursByWeekDay) { | |
| $firstWorkHourByWeekDay = []; | |
| $lastWorkHourByWeekDay = []; | |
| $totalWorkHourByWeekDay = []; | |
| foreach($workHoursByWeekDay as $dayOfWeek => &$horarios) { | |
| $totalHours = 0; | |
| foreach($horarios as $rango) { | |
| $totalHours += (int)$rango[1] - (int)$rango[0]; | |
| } | |
| if($totalHours > 0) { | |
| $totalWorkHourByWeekDay[$dayOfWeek] = $totalHours; | |
| } | |
| $lastWorkHourByWeekDay[$dayOfWeek] = \end($horarios)[1]; | |
| $firstWorkHourByWeekDay[$dayOfWeek] = \reset($horarios)[0]; | |
| $this->workHoursByWeekDayReversed[$dayOfWeek] = \array_reverse($horarios); | |
| } | |
| $this->workHoursByWeekDay = $workHoursByWeekDay; | |
| $this->firstWorkHourByWeekDay = $firstWorkHourByWeekDay; | |
| $this->lastWorkHourByWeekDay = $lastWorkHourByWeekDay; | |
| $this->totalWorkHourByWeekDay = $totalWorkHourByWeekDay; | |
| } | |
| /** | |
| * Get json encoded workday Data | |
| * | |
| * @return string|bool json encoded workday Data false on error | |
| * | |
| * @codeCoverageIgnore | |
| */ | |
| public function toJson() { | |
| return \json_encode([ | |
| 'workHoursByWeekDay' => $this->workHoursByWeekDay, | |
| 'workHoursByWeekDayReversed' => $this->workHoursByWeekDayReversed, | |
| 'firstWorkHourByWeekDay' => $this->firstWorkHourByWeekDay, | |
| 'lastWorkHourByWeekDay' => $this->lastWorkHourByWeekDay, | |
| 'totalWorkHourByWeekDay' => $this->totalWorkHourByWeekDay, | |
| 'holidays' => $this->dayCalculator->get_AllMarkedDays() | |
| ]); | |
| } | |
| } |