Search code examples
phparraysdatetimecalculation

Calculate daily minutes from an appointment lasting several days


I need a PHP array at the end based on the following information.

$startDateTime = "2024-01-06 14:00:00";
$endDateTime = "2024-01-13 14:00:00";

However, the working hours from
Monday to Friday 9:00 a.m. to 6:00 p.m.
and
Saturday 9:00 a.m. to 2:00 p.m.
should be taken into account.

My output as a PHP array should look like this (Date => Minutes - but only in Working hours):

Array
(
    [2024-01-06] => 0
    [2024-01-07] => 0
    [2024-01-08] => 540
    [2024-01-09] => 540
    [2024-01-10] => 540
    [2024-01-11] => 540
    [2024-01-12] => 540
    [2024-01-13] => 300
)

For explanation:

06 January 2024 is a Saturday and the appointment starts after work ends.

From 2024-01-07 to 2024-01-12 he works the full 9 hours every day. January 2024, 13 is again a Saturday and 5 hours are part of the working time.

I would be very happy about a solution.

I've tried a lot, but can't find a correct solution in PHP.


Solution

  • You should start by creating a DateTimeImmutable from your starting date. Then, calculate the end of the working hours of that date, get the difference and calculate the total minutes.

    Then, add one day, set the time to the start of the working hours and proceed as above. End your program when the calculated DateTime exceeds the end date:

    function getMinutesArray(string $startDateTime, string $endDateTime): array
    {
        $current = DateTimeImmutable::CreateFromFormat('Y-m-d H:i:s', $startDateTime);
        $end     = DateTimeImmutable::CreateFromFormat('Y-m-d H:i:s', $endDateTime);
    
        $dates = [];
    
        while ($current < $end) {
            // find the end of the working period
            $weekDay = (int) $current->format('N'); // 1=Monday, 7=Sunday
    
            $starting = max($current, $current->setTime(9, 0, 0));
    
            switch ($weekDay) {
                case 6: // Saturday
                    $end_hour = 14;
                    break;
                case 7:
                    $end_hour = 0; // no work at all
                    break;
                default:
                    $end_hour = 18;
            }
    
            $ending = min($end, $starting->setTime($end_hour, 0, 0));
    
            $minutes = 0;
            if ($starting < $ending) {
                $diff = $starting->diff($ending); // creates a DateInterval
                $minutes = $diff->h * 60 + $diff->i;
            }
    
            $dates[$current->format('Y-m-d')] = $minutes;
    
            $current = $current->setTime(9, 0, 0)->modify('+1 day');
        }
    
        return $dates;
    }