简体   繁体   中英

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?

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

The above-mentioned answers are good correct. But there are some other ways also with carbon.

$startDate = Carbon::create(2017, 4, 4);
$endDate = Carbon::create(2017, 4, 18);
$no_of_days = $startDate->diffInDaysFiltered(function(Carbon $date) {
            return !$date->isSunday();
        }, $endDate);

Some useful functions of Carbon

  $dt->isMonday();
  $dt->isTuesday();
  $dt->isWednesday();
 $dt->isThursday();
 $dt->isFriday();
 $dt->isSaturday();
 $dt->isSunday();
 $dt->isDayOfWeek(Carbon::SATURDAY); // is a saturday

For more information go to https://carbon.nesbot.com/docs/

Ok, The solution works, but i had some problem with more complex time calculation, so i figured out another solution with while cycle


    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'), [
                '23/12', 
                '24/12', 
                '25/12', 
                '26/12', 
                '27/12', 
                '28/12', 
                '29/12', 
                '30/12', 
                '31/12', 
                '01/01', 
                '02/01', 
                '03/01', 
                '04/01', 
                '05/01', 
                '06/01', 
                '07/01', 
            ]);
        });


        $end = Carbon::now();

$hours = 160;
while($hours > 0) {
    if ($end->hour < 9 || $end->hour >= 18 || $end->hour == 13 || $end->isWeekend() || $end->isHoliday()) {
        $end->addHour();
    } else {
        $end->addHour();
        $hours--;
    }
}

        Debugbar::addMessage($end);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM