Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 10 |
CRAP | |
0.00% |
0 / 117 |
| WorkHourCalculator | |
0.00% |
0 / 1 |
|
0.00% |
0 / 10 |
3540.00 | |
0.00% |
0 / 117 |
| roundDate | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 1 |
|||
| setTime | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 1 |
|||
| add | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| sub | n/a |
0 / 0 |
1 | n/a |
0 / 0 |
|||||
| workDaysArray | |
0.00% |
0 / 1 |
156.00 | |
0.00% |
0 / 24 |
|||
| addWorkHours | |
0.00% |
0 / 1 |
90.00 | |
0.00% |
0 / 20 |
|||
| addHoursInSameDay | |
0.00% |
0 / 1 |
20.00 | |
0.00% |
0 / 6 |
|||
| subWorkHours | |
0.00% |
0 / 1 |
72.00 | |
0.00% |
0 / 20 |
|||
| numberOfWorkHours | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 17 |
|||
| hourToWorkableHour | |
0.00% |
0 / 1 |
72.00 | |
0.00% |
0 / 13 |
|||
| hoursLeftInDay | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 4 |
|||
| _hoursLeftInDay | |
0.00% |
0 / 1 |
56.00 | |
0.00% |
0 / 11 |
|||
| <?php | |
| namespace ia\Work\WorkDay; | |
| class WorkHourCalculator extends WorkDayCalculator { | |
| protected $byHour = true; | |
| protected function roundDate($anyDate) { | |
| return self::toDateHourImmutable($anyDate); // Hour | |
| } | |
| protected function setTime(\DateTimeImmutable $dateTime) { | |
| return $dateTime->setTime($this->firstWorkHourByWeekDay[$dateTime->format('w')], 0, 0); // por hour | |
| } | |
| public function add($dateTime, $numHours) { return $this->addWorkHours($dateTime, $numHours); } | |
| public function sub($dateTime, $numHours) { return $this->subWorkHours($dateTime, $numHours); } | |
| // BY DAY _____________________________________________ | |
| /** | |
| * 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); | |
| if(\is_string($anyDateTo) && \strlen($anyDateTo) === 10 && !$this->_is_nonWorkDay($lastDate)) { | |
| $lastDate = $lastDate->setTime($this->lastWorkHourByWeekDay[(int)$lastDate->format('w')], 0, 0); | |
| } | |
| $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->hourToWorkableHour($firstDate) == $this->hourToWorkableHour($lastDate)) { | |
| return [$firstDate->format('Y-m-d') => 0]; | |
| } | |
| return [$firstDate->format('Y-m-d') => $this->hoursLeftInDay($firstDate) - $this->hoursLeftInDay($lastDate)]; | |
| } | |
| $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')] - $this->hoursLeftInDay($lastDate); | |
| } | |
| if($swaped === -1) { | |
| return \array_reverse($workDays, true); | |
| } | |
| return $workDays; | |
| } | |
| // BY HOUR ____________________________________________ | |
| /** | |
| * Add workable hours to a date | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @param integer|string $numHours | |
| * @return DateTimeImmutable with $numDays workable hours added, ie after | |
| */ | |
| public function addWorkHours($anyDate, $numHours) { | |
| $numHours = (int)$numHours; | |
| if($numHours < 0) { | |
| return $this->subWorkHours($anyDate, -$numHours); | |
| } | |
| $dateTime = $this->hourToWorkableHour(self::toDateHourImmutable($anyDate)); | |
| if($this->_is_nonWorkDay($dateTime)) { | |
| $dateTime = $this->nextWorkDay($dateTime); | |
| } | |
| if($numHours === 0) { | |
| return $dateTime; | |
| } | |
| $dayOfWeek = (int)$dateTime->format('w'); | |
| $currentHour = (int)$dateTime->format('G'); | |
| // casos comunes optimizados. | |
| if( $currentHour === $this->firstWorkHourByWeekDay[$dayOfWeek]) { | |
| $totalHoursInDay = $this->totalWorkHourByWeekDay[$dayOfWeek]; | |
| if($numHours === $totalHoursInDay) { | |
| return $this->nextWorkDay($dateTime); | |
| } | |
| if($numHours > $totalHoursInDay) { | |
| return $this->addWorkHours( $this->nextWorkDay($dateTime), $numHours - $totalHoursInDay); | |
| } | |
| } | |
| $hoursLeftInDay = $this->_hoursLeftInDay($currentHour, $dayOfWeek); | |
| if($hoursLeftInDay < 0) return $this->nextWorkDay($dateTime); // @codeCoverageIgnore | |
| if($numHours > $hoursLeftInDay) { | |
| return $this->addWorkHours( $this->nextWorkDay($dateTime), $numHours - $hoursLeftInDay); | |
| } | |
| return $this->addHoursInSameDay($dateTime, $numHours, $currentHour); | |
| } | |
| /** | |
| * Adds hour in the same day to a date | |
| * | |
| * @param DateTimeImmutable $dateTime | |
| * @param int $needHours | |
| * @param int $currentHour | |
| * @return DateTimeImmutable $dateTime with hours added | |
| */ | |
| private function addHoursInSameDay(\DateTimeImmutable $dateTime, int $needHours, int $currentHour) { | |
| foreach($this->workHoursByWeekDay[(int)$dateTime->format('w')] as $rango) { | |
| if($currentHour <= $rango[1]) { | |
| $hoursLeftInRange = $rango[1] - \max($rango[0], $currentHour); | |
| if($hoursLeftInRange >= $needHours) { | |
| return $dateTime->setTime(\max($rango[0], $currentHour) + $needHours, 0, 0); | |
| } | |
| $needHours = \max($needHours - $hoursLeftInRange, 0); | |
| } | |
| } | |
| return $this->addWorkHours($this->nextWorkDay($dateTime), $needHours); // @codeCoverageIgnore | |
| } | |
| /** | |
| * Add workable hours to a date | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @param integer|string $numHours | |
| * @return DateTimeImmutable with $numDays workable hours added, ie after | |
| */ | |
| public function subWorkHours($anyDate, $numHours) { | |
| if($numHours < 0) { | |
| return $this->addWorkHours($anyDate, -$numHours); | |
| } | |
| $dateTime = $this->hourToWorkableHour(self::toDateHourImmutable($anyDate) ); | |
| if($numHours === 0) { | |
| return $dateTime; | |
| } | |
| $dayOfWeek = (int)$dateTime->format('w'); | |
| $currentHour = $dateTime->format('G'); | |
| $moveToStart = false; | |
| foreach($this->workHoursByWeekDayReversed[$dayOfWeek] as $rango) { | |
| if($moveToStart) { | |
| $currentHour = $rango[1]; | |
| } | |
| if($currentHour >= $rango[0] && $currentHour <= $rango[1] ) { | |
| $hoursLeftInRange = \min($rango[1], $currentHour) - $rango[0]; | |
| if($hoursLeftInRange >= $numHours) { | |
| return $dateTime->setTime($currentHour - $numHours, 0, 0); | |
| } | |
| $numHours = \max($numHours - $hoursLeftInRange, 0); | |
| $moveToStart=true; | |
| } | |
| } | |
| $prevDay = $this->prevWorkDay($dateTime); | |
| $prevDay = $prevDay->setTime($this->lastWorkHourByWeekDay[(int)$prevDay->format('w')], 0, 0); | |
| return $this->subWorkHours($prevDay, $numHours); | |
| } | |
| /** | |
| * Number of workable between hours dateTimes $anyDateFrom and $anyDateTo | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDateFrom | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDateTo | |
| * @return int Number of workable hours between dateTimes $anyDateFrom and $anyDateTo | |
| */ | |
| public function numberOfWorkHours($anyDateFrom, $anyDateTo) { | |
| $start = $this->hourToWorkableHour( self::toDateHourImmutable($anyDateFrom) ); | |
| if($this->_is_nonWorkDay($start)) { | |
| $start = $this->nextWorkDay($start); | |
| } | |
| $end = $this->hourToWorkableHour( self::toDateHourImmutable($anyDateTo) ); | |
| if($this->_is_nonWorkDay($end)) { | |
| $end = $this->nextWorkDay($end); | |
| } | |
| $swaped = self::orderd($start, $end); | |
| $endYmd = $end->format('Y-m-d'); | |
| $hours = 0; | |
| while($end >= $start) { | |
| if($start->format('Y-m-d') !== $endYmd) { | |
| $hours += $this->_hoursLeftInDay((int)$start->format('G'), (int)$start->format('w')); | |
| } else { | |
| $dayOfWeek = (int)$start->format('w'); | |
| $hours += $this->_hoursLeftInDay((int)$start->format('G'), $dayOfWeek) - $this->_hoursLeftInDay((int)$end->format('G'), $dayOfWeek); | |
| break; | |
| } | |
| $start = $this->nextWorkDay($start); | |
| } | |
| return $swaped * $hours; | |
| } | |
| // ________________________________________________________________________________________________________________________________________________________________________ | |
| public function hourToWorkableHour(\DateTimeImmutable $dateTime) { | |
| $dayOfWeek = (int)$dateTime->format('w'); | |
| if(empty($this->firstWorkHourByWeekDay[$dayOfWeek])) { | |
| return $dateTime; | |
| } | |
| $currentHour = $dateTime->format('G'); | |
| if($currentHour < $this->firstWorkHourByWeekDay[$dayOfWeek]) { | |
| return $dateTime->setTime($this->firstWorkHourByWeekDay[$dayOfWeek], 0, 0); | |
| } | |
| if($currentHour > $this->lastWorkHourByWeekDay[$dayOfWeek]) { | |
| return $dateTime->setTime($this->lastWorkHourByWeekDay[$dayOfWeek], 0, 0); | |
| } | |
| foreach($this->workHoursByWeekDay[$dayOfWeek] as $rango) { | |
| if($currentHour >= $rango[0] && $currentHour <= $rango[1]) { | |
| return $dateTime; | |
| } | |
| if($currentHour < $rango[0]) { | |
| return $dateTime->setTime($rango[0], 0, 0); | |
| } | |
| } | |
| return $dateTime->setTime($this->lastWorkHourByWeekDay[$dayOfWeek], 0, 0); // @codeCoverageIgnore | |
| } | |
| /** | |
| * How many workable hours are left in $anyDate | |
| * | |
| * @param string|integer|DateTime|DateTimeImmutable $anyDate | |
| * @return int number of workable hours are left in the day | |
| */ | |
| public function hoursLeftInDay($anyDate) { | |
| $dateTime = self::toDateHourImmutable($anyDate); | |
| if($this->_is_nonWorkDay($dateTime)) { | |
| return 0; | |
| } | |
| return $this->_hoursLeftInDay((int)$dateTime->format('G'), (int)$dateTime->format('w')); | |
| } | |
| /** | |
| * From current hour, how many workable hours are left in the day | |
| * | |
| * @param int $currentHour | |
| * @param int $dayOfWeek | |
| * @return int number of workable hours are left in the day | |
| */ | |
| private function _hoursLeftInDay(int $currentHour, $dayOfWeek) { | |
| if($currentHour <= $this->firstWorkHourByWeekDay[$dayOfWeek]) { | |
| return $this->totalWorkHourByWeekDay[$dayOfWeek]; | |
| } | |
| if($currentHour >= $this->lastWorkHourByWeekDay[$dayOfWeek]) { | |
| return 0; | |
| } | |
| $hours = 0; | |
| foreach($this->workHoursByWeekDay[$dayOfWeek] as $rango) { | |
| if($currentHour >= $rango[0] && $currentHour <= $rango[1]) { | |
| $hours += $rango[1] - $currentHour; | |
| } elseif($currentHour < $rango[0]) { | |
| $hours += $rango[1] - $rango[0]; | |
| } | |
| } | |
| return $hours; | |
| } | |
| } |