AutorÃa | Ultima modificación | Ver Log |
<?phpnamespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;use DateTime;use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;use PhpOffice\PhpSpreadsheet\Calculation\Exception;use PhpOffice\PhpSpreadsheet\Calculation\Functions;class WorkDay{use ArrayEnabled;/*** WORKDAY.** Returns the date that is the indicated number of working days before or after a date (the* starting date). Working days exclude weekends and any dates identified as holidays.* Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected* delivery times, or the number of days of work performed.** Excel Function:* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])** @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),* PHP DateTime object, or a standard date string* Or can be an array of date values* @param array|int $endDays The number of nonweekend and nonholiday days before or after* startDate. A positive value for days yields a future date; a* negative value yields a past date.* Or can be an array of int values* @param null|mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation** @return array|DateTime|float|int|string Excel date/time serial value, PHP date/time serial value or PHP date/time object,* depending on the value of the ReturnDateType flag* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result* will also be an array with matching dimensions*/public static function date(mixed $startDate, array|int|string $endDays, mixed ...$dateArgs): array|float|int|DateTime|string{if (is_array($startDate) || is_array($endDays)) {return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__],2,$startDate,$endDays,...$dateArgs);}// Retrieve the mandatory start date and days that are referenced in the function definitiontry {$startDate = Helpers::getDateValue($startDate);$endDays = Helpers::validateNumericNull($endDays);$holidayArray = array_map([Helpers::class, 'getDateValue'], Functions::flattenArray($dateArgs));} catch (Exception $e) {return $e->getMessage();}$startDate = (float) floor($startDate);$endDays = (int) floor($endDays);// If endDays is 0, we always return startDateif ($endDays == 0) {return $startDate;}if ($endDays < 0) {return self::decrementing($startDate, $endDays, $holidayArray);}return self::incrementing($startDate, $endDays, $holidayArray);}/*** Use incrementing logic to determine Workday.*/private static function incrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime{// Adjust the start date if it falls over a weekend$startDoW = self::getWeekDay($startDate, 3);if ($startDoW >= 5) {$startDate += 7 - $startDoW;--$endDays;}// Add endDays$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);$endDays = $endDays % 5;while ($endDays > 0) {++$endDate;// Adjust the calculated end date if it falls over a weekend$endDow = self::getWeekDay($endDate, 3);if ($endDow >= 5) {$endDate += 7 - $endDow;}--$endDays;}// Test any extra holiday parametersif (!empty($holidayArray)) {$endDate = self::incrementingArray($startDate, $endDate, $holidayArray);}return Helpers::returnIn3FormatsFloat($endDate);}private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float{$holidayCountedArray = $holidayDates = [];foreach ($holidayArray as $holidayDate) {if (self::getWeekDay($holidayDate, 3) < 5) {$holidayDates[] = $holidayDate;}}sort($holidayDates, SORT_NUMERIC);foreach ($holidayDates as $holidayDate) {if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {if (!in_array($holidayDate, $holidayCountedArray)) {++$endDate;$holidayCountedArray[] = $holidayDate;}}// Adjust the calculated end date if it falls over a weekend$endDoW = self::getWeekDay($endDate, 3);if ($endDoW >= 5) {$endDate += 7 - $endDoW;}}return $endDate;}/*** Use decrementing logic to determine Workday.*/private static function decrementing(float $startDate, int $endDays, array $holidayArray): float|int|DateTime{// Adjust the start date if it falls over a weekend$startDoW = self::getWeekDay($startDate, 3);if ($startDoW >= 5) {$startDate += -$startDoW + 4;++$endDays;}// Add endDays$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);$endDays = $endDays % 5;while ($endDays < 0) {--$endDate;// Adjust the calculated end date if it falls over a weekend$endDow = self::getWeekDay($endDate, 3);if ($endDow >= 5) {$endDate += 4 - $endDow;}++$endDays;}// Test any extra holiday parametersif (!empty($holidayArray)) {$endDate = self::decrementingArray($startDate, $endDate, $holidayArray);}return Helpers::returnIn3FormatsFloat($endDate);}private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float{$holidayCountedArray = $holidayDates = [];foreach ($holidayArray as $holidayDate) {if (self::getWeekDay($holidayDate, 3) < 5) {$holidayDates[] = $holidayDate;}}rsort($holidayDates, SORT_NUMERIC);foreach ($holidayDates as $holidayDate) {if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {if (!in_array($holidayDate, $holidayCountedArray)) {--$endDate;$holidayCountedArray[] = $holidayDate;}}// Adjust the calculated end date if it falls over a weekend$endDoW = self::getWeekDay($endDate, 3);/** int $endDoW */if ($endDoW >= 5) {$endDate += -$endDoW + 4;}}return $endDate;}private static function getWeekDay(float $date, int $wd): int{$result = Functions::scalar(Week::day($date, $wd));return is_int($result) ? $result : -1;}}