Question

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.

Was it helpful?

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

OTHER TIPS

I got the answer from here

For PHP >= 5.3.0, use the DatePeriod class. It's unfortunately barely documented.

$start = new DateTime('6/30/2010');
$end = new DateTime('7/6/2010');
$oneday = new DateInterval("P1D");

$days = array();
$data = "7.5";

/* Iterate from $start up to $end+1 day, one day in each iteration.
   We add one day to the $end date, because the DatePeriod only iterates up to,
   not including, the end date. */
foreach(new DatePeriod($start, $oneday, $end->add($oneday)) as $day) {
    $day_num = $day->format("N"); /* 'N' number days 1 (mon) to 7 (sun) */
    if($day_num < 6) { /* weekday */
        $days[$day->format("Y-m-d")] = $data;
    } 
}    
print_r($days);

I found this and converted it in to minutes,

$start = new DateTime('2014-03-03 09:21:30');
$end = new DateTime('2014-03-11 17:23:15');
// otherwise the  end date is excluded (bug?)
$end->modify('+1 day');

$interval = $end->diff($start);

// total days
$days = $interval->days;
$days_inMin = ($interval->d*24*60) + ($interval->h*60) + $interval->i;

// create an iterateable period of date (P1D equates to 1 day)
$period = new DatePeriod($start, new DateInterval('P1D'), $end);

// best stored as array, so you can add more than one
$holidays = array('2014-03-07');

foreach($period as $dt) {
    $curr = $dt->format('D');

    // for the updated question
    if (in_array($dt->format('Y-m-d'), $holidays)) {
       $days--;
       $days_inMin -= (24*60);
    }

    // substract if Saturday or Sunday
    if ($curr == 'Sat' || $curr == 'Sun') {
        $days--;
        $days_inMin -= (24*60);
    }
}

echo 'Days: ' . $days; 
echo '<br>Days in Minutes: ' . $days_inMin . ' min = ' . $days_inMin/(24*60) . ' days';

Edit:

$office_hrs_min = $days_inMin - ($days * (14.5*60));
// as out of 24 only 8.5 hrs are working
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top