'Get number of working days between two dates in PHP [duplicate]

I am trying to find the total working days by specifying the first and end date and also the number of working days in a week. But unfortunately, the final result is coming wrong.

Below is my script

 echo getWorkingDays("2022-04-01","2022-04-30",6)

function getWorkingDays($startDate,$endDate,$working_days_in_a_week){


            // do strtotime calculations just once
            $endDate = strtotime($endDate);
            $startDate = strtotime($startDate);


            //The total number of days between the two dates. We compute the no. of seconds and divide it to 60*60*24
            //We add one to inlude both dates in the interval.
            $days = ($endDate - $startDate) / 86400 + 1;

            $no_full_weeks = floor($days / 7);
            $no_remaining_days = fmod($days, 7);

            //It will return 1 if it's Monday,.. ,7 for Sunday
            $the_first_day_of_week = date("N", $startDate);
            $the_last_day_of_week = date("N", $endDate);

            //---->The two can be equal in leap years when february has 29 days, the equal sign is added here
            //In the first case the whole interval is within a week, in the second case the interval falls in two weeks.
            if ($the_first_day_of_week <= $the_last_day_of_week) {
                if ($the_first_day_of_week <= 6 && 6 <= $the_last_day_of_week) $no_remaining_days--;
                if ($the_first_day_of_week <= 7 && 7 <= $the_last_day_of_week) $no_remaining_days--;
            }
            else {
                // (edit by Tokes to fix an edge case where the start day was a Sunday
                // and the end day was NOT a Saturday)

                // the day of the week for start is later than the day of the week for end
                if($working_days_in_a_week !=7) {
                    if ($the_first_day_of_week == 7) {
                        // if the start date is a Sunday, then we definitely subtract 1 day
                        $no_remaining_days--;

                        if ($the_last_day_of_week == 6) {
                            // if the end date is a Saturday, then we subtract another day
                            $no_remaining_days--;
                        }
                    } else {
                        // the start date was a Saturday (or earlier), and the end date was (Mon..Fri)
                        // so we skip an entire weekend and subtract 2 days
                        $no_remaining_days -= 2;
                    }
                }
            }

            //The no. of business days is: (number of weeks between the two dates) * (5 working days) + the remainder
//---->february in none leap years gave a remainder of 0 but still calculated weekends between first and last day, this is one way to fix it
            $workingDays = $no_full_weeks * $working_days_in_a_week;
            if ($no_remaining_days > 0 )
            {
                $workingDays += $no_remaining_days;
            }

            //We subtract the holidays
            /*foreach($holidays as $holiday){
                $time_stamp=strtotime($holiday);
                //If the holiday doesn't fall in weekend
                if ($startDate <= $time_stamp && $time_stamp <= $endDate && date("N",$time_stamp) != 6 && date("N",$time_stamp) != 7)
                    $workingDays--;
            }*/

            return $workingDays;
        } 

The final result is coming 25 when in fact the result should be 26 as there are only 4 Sundays in April and the number of business days in the week is 6 which means only Sunday is the weekend. What I am doing here? please help?

php


Solution 1:[1]

The below should do what you're looking for.

The difference with this function and yours is that the third parameter can be an array of days or an integer. For example, if you have working days that are:

  • Monday
  • Tuesday
  • Friday

You would set the third parameter as [1, 2, 5].

/**
 * @param string $start_date Pass the start date in `Y-m-d` format.
 * @param string $end_date Pass the end date in `Y-m-d` format.
 * @param array|int $working_days Pass an array of working days where 1 is Monday and 7 is Sunday. Or pass the number of working days.
 * @return float|int
 */
function get_working_days( string $start_date, string $end_date, array|int $working_days = [ 1, 2, 3, 4, 5 ] ) {

    $start = DateTime::createFromFormat( 'Y-m-d', $start_date );
    $end = DateTime::createFromFormat( 'Y-m-d', $end_date );

    // Get numeric representation of the day of the week.
    // 1 (for Monday) through 7 (for Sunday)
    $start_day = $start->format( 'w' ) + 1;
    $end_day = $end->format( 'w' ) + 1;

    // Convert int into array of days
    if ( is_int( $working_days ) ) {
        $working_int = $working_days;
        $working_days = [];
        for ( $i = 0; $i < $working_int; $i++ ) {
            if ( $i >= 7 ) break;
            $working_days[] = $i + 1;
        }
    }

    $counts = [];

    foreach ( $working_days as $day_num ) {
        $day_count = 0;

        if ( $start_day < $end_day ) {
            $is_partial = $day_num >= $start_day && $day_num <= $end_day;
        } else if ( $start_day === $end_day ) {
            $is_partial = $start_day === $day_num;
        } else {
            $is_partial = $day_num >= $start_day || $day_num <= $end_day;
        }

        $complete_weeks = floor( ( $end->getTimestamp() - $start->getTimestamp() ) / 60 / 60 / 24 / 7 );
        $patial_weeks = $is_partial ? 1 : 0;

        $counts[ $day_num ] = $complete_weeks + $patial_weeks;
    }

    return array_sum( $counts );
}

echo get_working_days( '2022-04-01', '2022-04-30', 6 );

Solution 2:[2]

check this solution:


echo getWorkingDays("2022-04-01","2022-04-30",6);

function getWorkingDays($startDate,$endDate,$working_days_in_a_week){


            // do strtotime calculations just once
            $endDate = strtotime($endDate);
            $startDate = strtotime($startDate);
            $start_day = date("N", $startDate);

            //The total number of days between the two dates. We compute the no. of seconds and divide it to 60*60*24
            //We add one to inlude both dates in the interval.
            $days = ($endDate - $startDate) / 86400 + 1;
            $fullweeks = floor($days/7);
            $restofdays = $days%7;
            
            $workingDays=$fullweeks*$working_days_in_a_week;
            
            for($x=0;$x<$restofdays;$x++){
                if((($start_day+$x)%7)<=$working_days_in_a_week)$workingDays++;
            }
            return $workingDays;
        }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 designer132