Pergunta

Sou relativamente novo em programação PHP e me deparo com um problema interessante.Tentei várias pesquisas e encontrei soluções diferentes, mas nenhuma delas se adequava ao meu problema específico.

Eu tenho 2 carimbos de data no formato mysql (2014-04-10 09:00:00 por exemplo).Preciso saber a diferença em minutos entre esses 2 carimbos de data e hora, mas devo excluir horários fora do escritório, fins de semana e feriados.

Por exemplo, um carimbo de data/hora de hoje (11/04/2014 14:00:00) e um carimbo de data/hora de segunda-feira (14/04/2014 11:00:00) devem aparecer como resultado de 390 minutos (dias úteis são das 8h30 às 18h00). ).

Todas as soluções no stackexchange mostram os resultados em horas ou dias, mas preciso de mais precisão.

Agradecemos antecipadamente e pedimos desculpas por algo não estar claro.

Foi útil?

Solução

Exemplo de uso:

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

Saída :

1 day, 22 hours, 14 minutes, 28 seconds

Função:

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);
}

Esta função pode ser mais curta/melhor;mas esse é o seu trabalho agora;)

Se desejar excluir feriados, veja este exemplo: https://stackoverflow.com/a/19221403/67332

Outras dicas

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top