Search code examples
phpdatedatetimedifferencedatestamp

Difference between 2 dates in minutes excluding weekends and holidays


I'm relative new to PHP programming and run into an interesting problem. I've tried multiple searches and found different solutions, but none of them fit my precise problem.

I have 2 datestamps in mysql format (2014-04-10 09:00:00 for example). I need to know the difference in minutes between those 2 timestamps, but must exclude the out-of-office hours, weekends and holidays.

For example, a timestamp of today (2014-04-11 14:00:00) and a timestamp of monday (2014-04-14 11:00:00) must show as a result of 390 minutes (workdays are 08.30 to 18.00).

All of the solutions on stackexchange show the results as hours or days, but I need more accuracy.

Thanks in advance and apologies of there is something unclear.


Solution

  • Use example :

    $from = '2013-09-06 15:45:32';
    $to   = '2013-09-14 21:00:00';
    echo some_func_name($from, $to);
    

    Output :

    1 day, 22 hours, 14 minutes, 28 seconds
    

    Function :

    function some_func_name($from, $to) {
        $workingDays = [1, 2, 3, 4, 5]; # date format = N
        $workingHours = ['from' => ['08', '00'], 'to' => ['17', '00']];
    
        $start = new DateTime($from);
        $end = new DateTime($to);
    
        $startP = clone $start;
        $startP->setTime(0, 0, 0);
        $endP = clone $end;
        $endP->setTime(23, 59, 59);
        $interval = new DateInterval('P1D');
        $periods = new DatePeriod($startP, $interval, $endP);
    
        $sum = [];
        foreach ($periods as $i => $period) {
            if (!in_array($period->format('N'), $workingDays)) continue;
    
            $startT = clone $period;
            $startT->setTime($workingHours['from'][0], $workingHours['from'][1]);
            if (!$i && $start->diff($startT)->invert) $startT = $start;
    
            $endT = clone $period;
            $endT->setTime($workingHours['to'][0], $workingHours['to'][1]);
            if (!$end->diff($endT)->invert) $endT = $end;
    
            #echo $startT->format('Y-m-d H:i') . ' - ' . $endT->format('Y-m-d H:i') . "\n"; # debug
    
            $diff = $startT->diff($endT);
            if ($diff->invert) continue;
            foreach ($diff as $k => $v) {
                if (!isset($sum[$k])) $sum[$k] = 0;
                $sum[$k] += $v;
            }
        }
    
        if (!$sum) return 'ccc, no time on job?';
    
        $spec = "P{$sum['y']}Y{$sum['m']}M{$sum['d']}DT{$sum['h']}H{$sum['i']}M{$sum['s']}S";
        $interval = new DateInterval($spec);
        $startS = new DateTime;
        $endS = clone $startS;
        $endS->sub($interval);
        $diff = $endS->diff($startS);
    
        $labels = [
            'y' => 'year',
            'm' => 'month',
            'd' => 'day',
            'h' => 'hour',
            'i' => 'minute',
            's' => 'second',
        ];
        $return = [];
        foreach ($labels as $k => $v) {
            if ($diff->$k) {
                $return[] = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
            }
        }
    
        return implode(', ', $return);
    }
    

    This function can be shorter/better; but that is your job now ;)

    If you wish to exclude holidays, see this example: https://stackoverflow.com/a/19221403/67332