Vendor/phpspreadsheet/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTime.php
public static function WORKDAY($startDate, $endDays, ...$dateArgs)
    {
        //    Retrieve the mandatory start date and days that are referenced in the function definition
        $startDate = Functions::flattenSingleValue($startDate);
        $endDays = Functions::flattenSingleValue($endDays);
        //    Get the optional days
        $dateArgs = Functions::flattenArray($dateArgs);

        if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
            return Functions::VALUE();
        }
        $startDate = (float) floor($startDate);
        $endDays = (int) floor($endDays);
        //    If endDays is 0, we always return startDate
        if ($endDays == 0) {
            return $startDate;
        }

        $decrementing = ($endDays < 0) ? true : false;

        //    Adjust the start date if it falls over a weekend

        $startDoW = self::WEEKDAY($startDate, 3);
        if (self::WEEKDAY($startDate, 3) >= 5) {
            $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
            ($decrementing) ? $endDays++ : $endDays--;
        }

        //    Add endDays
        $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);

        //    Adjust the calculated end date if it falls over a weekend
        $endDoW = self::WEEKDAY($endDate, 3);
        if ($endDoW >= 5) {
            $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
        }

        //    Test any extra holiday parameters
        if (!empty($dateArgs)) {
            $holidayCountedArray = $holidayDates = [];
            foreach ($dateArgs as $holidayDate) {
                if (($holidayDate !== null) && (trim($holidayDate) > '')) {
                    if (is_string($holidayDate = self::getDateValue($holidayDate))) {
                        return Functions::VALUE();
                    }
                    if (self::WEEKDAY($holidayDate, 3) < 5) {
                        $holidayDates[] = $holidayDate;
                    }
                }
            }
            if ($decrementing) {
                rsort($holidayDates, SORT_NUMERIC);
            } else {
                sort($holidayDates, SORT_NUMERIC);
            }
            foreach ($holidayDates as $holidayDate) {
                if ($decrementing) {
                    if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
                        if (!in_array($holidayDate, $holidayCountedArray)) {
                            --$endDate;
                            $holidayCountedArray[] = $holidayDate;
                        }
                    }
                } else {
                    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::WEEKDAY($endDate, 3);
                if ($endDoW >= 5) {
                    $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
                }
            }
        }

        switch (Functions::getReturnDateType()) {
            case Functions::RETURNDATE_EXCEL:
                return (float) $endDate;
            case Functions::RETURNDATE_PHP_NUMERIC:
                return (int) Date::excelToTimestamp($endDate);
            case Functions::RETURNDATE_PHP_OBJECT:
                return Date::excelToDateTimeObject($endDate);
        }
    }
public static function WORKDAY($startDate, $endDays, ...$dateArgs)
    {
        //    Retrieve the mandatory start date and days that are referenced in the function definition
        $startDate = Functions::flattenSingleValue($startDate);
        $endDays = Functions::flattenSingleValue($endDays);
        //    Get the optional days
        $dateArgs = Functions::flattenArray($dateArgs);

        if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
            return Functions::VALUE();
        }
        $startDate = (float) floor($startDate);
        $endDays = (int) floor($endDays);
        //    If endDays is 0, we always return startDate
        if ($endDays == 0) {
            return $startDate;
        }

        $decrementing = ($endDays < 0) ? true : false;

        //    Adjust the start date if it falls over a weekend

        $startDoW = self::WEEKDAY($startDate, 3);
        if (self::WEEKDAY($startDate, 3) >= 5) {
            $startDate += ($decrementing) ? -$startDoW + 4 : 7 - $startDoW;
            ($decrementing) ? $endDays++ : $endDays--;
        }

        //    Add endDays
        $endDate = (float) $startDate + ((int) ($endDays / 5) * 7) + ($endDays % 5);

        //    Adjust the calculated end date if it falls over a weekend
        $endDoW = self::WEEKDAY($endDate, 3);
        if ($endDoW >= 5) {
            $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
        }

        //    Test any extra holiday parameters
        if (!empty($dateArgs)) {
            $holidayCountedArray = $holidayDates = [];
            foreach ($dateArgs as $holidayDate) {
                if (($holidayDate !== null) && (trim($holidayDate) > '')) {
                    if (is_string($holidayDate = self::getDateValue($holidayDate))) {
                        return Functions::VALUE();
                    }
                    if (self::WEEKDAY($holidayDate, 3) < 5) {
                        $holidayDates[] = $holidayDate;
                    }
                }
            }
            if ($decrementing) {
                rsort($holidayDates, SORT_NUMERIC);
            } else {
                sort($holidayDates, SORT_NUMERIC);
            }
            foreach ($holidayDates as $holidayDate) {
                if ($decrementing) {
                    if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
                        if (!in_array($holidayDate, $holidayCountedArray)) {
                            --$endDate;
                            $holidayCountedArray[] = $holidayDate;
                        }
                    }
                } else {
                    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::WEEKDAY($endDate, 3);
                if ($endDoW >= 5) {
                    $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
                }
            }
        }

        switch (Functions::getReturnDateType()) {
            case Functions::RETURNDATE_EXCEL:
                return (float) $endDate;
            case Functions::RETURNDATE_PHP_NUMERIC:
                return (int) Date::excelToTimestamp($endDate);
            case Functions::RETURNDATE_PHP_OBJECT:
                return Date::excelToDateTimeObject($endDate);
        }
    }
public static function WORKDAY ( $ startDate , $ endDays , ... $ dateArgs ) { //    Retrieve the mandatory start date and days that are referenced in the function definition $ startDate = Functions :: flattenSingleValue ( $ startDate ) ; $ endDays = Functions :: flattenSingleValue ( $ endDays ) ; //    Get the optional days $ dateArgs = Functions :: flattenArray ( $ dateArgs ) ; if ( ( is_string ( $ startDate = self :: getDateValue ( $ startDate ) ) ) || ( ! is_numeric ( $ endDays ) ) ) { return Functions :: VALUE ( ) ; } $ startDate = ( float ) floor ( $ startDate ) ; $ endDays = ( int ) floor ( $ endDays ) ; //    If endDays is 0, we always return startDate if ( $ endDays == 0 ) { return $ startDate ; } $ decrementing = ( $ endDays < 0 ) ? true : false ; //    Adjust the start date if it falls over a weekend $ startDoW = self :: WEEKDAY ( $ startDate , 3 ) ; if ( self :: WEEKDAY ( $ startDate , 3 ) >= 5 ) { $ startDate += ( $ decrementing ) ? - $ startDoW + 4 : 7 - $ startDoW ; ( $ decrementing ) ? $ endDays ++ : $ endDays -- ; } //    Add endDays $ endDate = ( float ) $ startDate + ( ( int ) ( $ endDays / 5 ) * 7 ) + ( $ endDays % 5 ) ; //    Adjust the calculated end date if it falls over a weekend $ endDoW = self :: WEEKDAY ( $ endDate , 3 ) ; if ( $ endDoW >= 5 ) { $ endDate += ( $ decrementing ) ? - $ endDoW + 4 : 7 - $ endDoW ; } //    Test any extra holiday parameters if ( ! empty ( $ dateArgs ) ) { $ holidayCountedArray = $ holidayDates = [ ] ; foreach ( $ dateArgs as $ holidayDate ) { if ( ( $ holidayDate !== null ) && ( trim ( $ holidayDate ) > '' ) ) { if ( is_string ( $ holidayDate = self :: getDateValue ( $ holidayDate ) ) ) { return Functions :: VALUE ( ) ; } if ( self :: WEEKDAY ( $ holidayDate , 3 ) < 5 ) { $ holidayDates [ ] = $ holidayDate ; } } } if ( $ decrementing ) { rsort ( $ holidayDates , SORT_NUMERIC ) ; } else { sort ( $ holidayDates , SORT_NUMERIC ) ; } foreach ( $ holidayDates as $ holidayDate ) { if ( $ decrementing ) { if ( ( $ holidayDate <= $ startDate ) && ( $ holidayDate >= $ endDate ) ) { if ( ! in_array ( $ holidayDate , $ holidayCountedArray ) ) { -- $ endDate ; $ holidayCountedArray [ ] = $ holidayDate ; } } } else { 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 :: WEEKDAY ( $ endDate , 3 ) ; if ( $ endDoW >= 5 ) { $ endDate += ( $ decrementing ) ? - $ endDoW + 4 : 7 - $ endDoW ; } } } switch ( Functions :: getReturnDateType ( ) ) { case Functions :: RETURNDATE_EXCEL : return ( float ) $ endDate ; case Functions :: RETURNDATE_PHP_NUMERIC : return ( int ) Date :: excelToTimestamp ( $ endDate ) ; case Functions :: RETURNDATE_PHP_OBJECT : return Date :: excelToDateTimeObject ( $ endDate ) ; } }
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[,...]]])

@category Date/Time Functions

@param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
PHP DateTime object, or a standard date string
@param 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.

@return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
depending on the value of the ReturnDateType flag
WORKDAY .