Search code examples
phpmysqllaravelphp-carbon

Using PHP Carbon, remove time in seconds where two time periods overlap


An outage may range from a couple of minutes extending to multiple days. I store this in an outages table that has start date/time and end date/time. On saving an outage record, the duration in seconds is calculated using Carbon.

$outage->duration = $outage->start_time->diffInSeconds($outage->end_time, true);

I now need to remove any time that is between 5pm and 7:59:59 am (i.e. outside of business hours) but I have no idea where to start and appreciate any help :)


Solution

  • What you can do is make a DatePeriod of 1 second intervals (I assume this is business critical, so it has to be to the second) and check if each second is within the business hours of the day. If it is, subtract it from the total.

    This is horribly inefficient, though. For example, just for a full day you'd have to make 86400 checks. It gets slow quickly. Maybe you can instead use 1 minute or even 1 hour interval if your business requirements allow it, and make some estimations. Anyway, this is how you can do this:

    <?php
    use Carbon\Carbon;
    
    $startTime = Carbon::create(2017, 9, 10, 8, 20, 0);
    $endTime = Carbon::create(2017, 9, 10, 18, 35, 0);
    $duration = $startTime->diffInSeconds($endTime, true);
    
    $interval = new DateInterval("PT1S");
    $period = new DatePeriod($startTime, $interval, $endTime);
    
    $secondsToSubtract = 0;
    
    foreach ($period as $second) {
        $businessStart = clone $second;
        $businessStart->setTime(8, 0); // start business day
        $businessEnd = clone $second;
        $businessEnd->setTime(17, 0); // end business day
    
        if (!($second > $businessStart && $second < $businessEnd)) { // if the second is not a "business second", subtract it
            $secondsToSubtract++;
        }
    }
    var_dump($secondsToSubtract);
    $realDuration = $duration - $secondsToSubtract;
    var_dump($realDuration);
    

    You don't mention if weekends are or aren't business days for you. If they are, just check if the current day in the iteration is a Saturday or Sunday and subtract all of those seconds.

    You can make a lot of optimizations here (caching the day, for example), but it should get you in the right direction.

    (I can't show you a demo because I can't use Carbon in the typical providers)