Search code examples
phpphp-carbon

PHP Carbon date difference with only working days


I'm trying to get difference between two dates in human format but only with working days. Here is my actual code:

$start = '2018-09-13 09:30:00';
$end = '2018-10-16 16:30:00';

$from = Carbon::parse($start);
$to = Carbon::parse($end);

$weekDay = $from->diffInWeekdays($to);
$human = $to->diffForHumans($from, true, false, 6);

var_dump($weekDay); //24
var_dump($human); // 1 month 3 days 7 hours

diffForHumans is perfect for my needs but I can't find any way of filtering like diffInDaysFiltered

What I would like to achieve is to get this result: 24 days 7 hours because we only have 24 working days

I tried a preg_replace first to replace 3 days by $weekDay result but if we have a month before it's incorrect and I have: 1 month 24 days 7 hours

Is there any solution for my problem ?


Solution

  • Here is the answer thanks to imbrish

    I need to use the cascade factor to define:

    • How many hours is a day
    • How many days is a week
    • How many days is a month

    Then with the diffFiltered I can only select weekday and working hours. You can also add a filter on public holiday thanks to macro

    CarbonInterval::setCascadeFactors(array(
        'days' => array(10, 'hours'),
        'weeks' => array(5, 'days'),
        'months' => array(20, 'days'),
    ));
    
    $resolution = CarbonInterval::hour();
    
    $start = Carbon::parse('2017-07-13 11:00');
    $end = Carbon::parse('2017-07-17 18:00');
    
    $hours = $start->diffFiltered($resolution, function ($date) {
        return $date->isWeekday() && $date->hour >= 8 && $date->hour < 18;
    }, $end);
    
    $interval = CarbonInterval::hours($hours)->cascade();
    
    echo $interval->forHumans(); // 2 days 7 hours
    

    Here is the macro part:

    Carbon::macro('isHoliday', function ($self = null) {
        // compatibility chunk
        if (!isset($self) && isset($this)) {
            $self = $this;
        }
        // Put your array of holidays here
        return in_array($self->format('d/m'), [
            '25/12', // Christmas
            '01/01', // New Year
        ]);
    });
    

    Then simply add && !$date->isHoliday() inside the diffFiltered