Usamos cookies para medir audiência e melhorar sua experiência. Você pode aceitar ou recusar a qualquer momento. Veja sobre o iMasters.
Bom dia pessoal, após tanta pesquisa, tanta leitura de classes eu resolvi pedir ajuda a vocês!! Estou necessitando de ajuda para calcular o SLA de um sistema de chamados, pegar data1 e calcular com data2 para exibir quanto tempo demorou para ser atendido em horas uteis(Comerciais). Por exemplo: Expediente entrada = 07:30:00 Horario de Almoço saida = 11:00:00 Horário de Almoço volta = 13:00:00 Fim de Expediente = 17:30:00 Sábado = 7:30 as 11:30
$data1 = "2014-08-18 09:30:00". $data2 = "2014-08-19 09:30:00". ## APÓS O CALCULO "" echo "A difereça em horas uteis do de data1 para data2 é 07:30:00". Neste caso ai deu 7 horas e 30 minutos úteis ou seja no horário comercial. Procurei diversas classes para tentar realizar este cálculo no entanto elas não me atenderam, alguém poderia me ajudar?Muito grato Gabriel, a empresa em que eu trabalho me solicitou isto más sou apenas um iniciante de programação, sem sua ajuda provavelmente perderia meu emprego rsrs!!
Muito obrigado!!
Após a utilização tive um problema:
Quando envio para a classe os dados assim
Data inicial: 2014-08-25 07:30:00
Data final: 2014-08-25 08:00:00
em vez dela retornar os 30 minutos, está retornao erro Fatal error: Call to undefined function RuntimeException()
conferi no código e vi que tem uma função que if ($end > $start) ele retorna return RuntimeException('Cannot calculate worked period');
Poderia me auxiliar?
-
-
Na realidade, isso ocorre quando a data de término do período é igual ou menor que a data de início.
Como você está inserindo datas diferentes, verifique os períodos que você adicionou, tal como os dias da semana.
Runtime exception sempre será retornado quando não puder calcular o período.
Também encontrei um calculo que não estava correto e realizei a correção, além de uma nova validação.
Segue somente a função para ser alterada, corrigirei no original também
/**
* calcula o tempo trabalhado dentro do período
* @param DateTime $start
* @param DateTime $end
* @return DateInterval
**/
public function calculateWorkedDateInterval($start , $end) {
if($start == $end) {
return $start->diff($end , true);
} else if ($start > $end) {
return RuntimeException('Cannot calculate worked period');
}
/** variáveis auxiliar **/
$estimatedDateInterval = $start->diff($end , true);//diferença entre a data de início e a data de entrada
$isAfterWorkTime = false;//se é um horário após o expediente
$dateTimeStartWork = null;//Data de início do trabalho
$date = new DateTime();//Cria um dateInterval zerado;
$zeroDateInterval = $date->diff($date , true);
if($this->isWorkDay($start)) {
foreach($this->period AS $period) {
if($period->isBetween($start)) {
$dateInterval = $period->calculateWorkedDateInterval($start , $end);
if(!$dateInterval instanceof DateInterval) {
return $zeroDateInterval;
}
$date1 = clone $date2 = new \DateTime();
$date1->add($estimatedDateInterval);
$date2->add($dateInterval);
if($date1 <= $date2) {
return $this->sumDateInterval($dateInterval , $zeroDateInterval);
} else {
return $this->sumDateInterval(
$this->calculateWorkedDateInterval($start->add($dateInterval) , $end),
$dateInterval
);
}
} else {
$dateStartWork = new DateTime($start->format('Y-m-d').' '.$period->getStart()->format('H:i:s'));
if($dateStartWork >= $start) {
if(is_null($dateTimeStartWork)) {
$dateTimeStartWork = $dateStartWork;
} else {
$dateTimeStartWork = $dateStartWork < $dateTimeStartWork ? $dateStartWork : $dateTimeStartWork;
}
} else {
$isAfterWorkTime = true;
}
}
}
}
if(!is_null($dateTimeStartWork)) {
return $this->sumDateInterval($this->calculateWorkedDateInterval($dateTimeStartWork , $end) , $zeroDateInterval);
}
if(!$this->isWorkDay($start) || $isAfterWorkTime) {
$start = $this->goToNextDay($start);
if($end >= $start) {
return $this->sumDateInterval($this->calculateWorkedDateInterval($start , $end) , $zeroDateInterval);
} else {
return RuntimeException('Cannot calculate worked period');
}
}
}
No mais, passe o código que você está usando para o cálculo.
O Código é este:
include("../cfg/mysql.php");
include("class/calc.php");
$sql = "SELECT * FROM `local_chamado` WHERE id = 42";
$query = mysql_query($sql) or die (mysql_error());
$sac = mysql_fetch_assoc($query);
$data1 = $sac['data_inicial'];
$data2 = $sac['data_final'];
echo "Data Inicial: ".$data1."<br>";
echo "Data Final: ".$data2."<br>";
$manha = new Period();
$manha->addWorkday(CalendarDay::MONDAY)
->addWorkday(CalendarDay::TUESDAY)
->addWorkday(CalendarDay::WEDNESDAY)
->addWorkday(CalendarDay::THURSDAY)
->addWorkday(CalendarDay::FRIDAY);
$manha->setStart(DateTime::createFromFormat('H:i:s' , '07:30:00'));
$manha->setEnd(DateTime::createFromFormat('H:i:s' , '11:00:00'));
$tarde = new Period();
$tarde->addWorkday(CalendarDay::MONDAY)
->addWorkday(CalendarDay::TUESDAY)
->addWorkday(CalendarDay::WEDNESDAY)
->addWorkday(CalendarDay::THURSDAY)
->addWorkday(CalendarDay::FRIDAY);
$tarde->setStart(DateTime::createFromFormat('H:i:s' , '13:00:00'));
$tarde->setEnd(DateTime::createFromFormat('H:i:s' , '17:30:00'));
$workPeriod = new WorkPeriod();
$workPeriod->addPeriod($manha)
->addPeriod($tarde);
$periodo = $workPeriod->calculateWorkedDateInterval(new DateTime($data1) , new DateTime($data2));
printf('Periodo trabalhado de %s' , $periodo->format('%H:%I:%S'));
o resultado é o runtimeExcpetion..
Segue o link do código acima:
http://www.colhabonsfrutos.com.br/intranet/admin/teste.php
@Edit
Após verificar, percebi que o problema ocorre quando as datas são iguais... mesmo que as horas sejam diferentes o erro ocorre.
Na realidade, ocorre porque a data 2014-06-07 é um sábado, e como pode ver, só foi configurado de segunda (Monday) a sexta (Friday). Logo, a exception é lançada pois não é possível calcular.
Tenho que salientar que, nesse caso, zero (00:00:00) é totalmente diferente de RuntimeException.
>
Na realidade, ocorre porque a data 2014-06-07 é um sábado, e como pode ver, só foi configurado de segunda (Monday) a ---ta (Friday). Logo, a exception é lançada pois não é possível calcular.
Tenho que salientar que, nesse caso, zero (00:00:00) é totalmente diferente de RuntimeException.
Havia esquecido do sábado... más fiz o teste com uma quinta feira se não me engano recebi o seguinte erro:
>
Warning: Invalid argument supplied for foreach() in /home/colhabon/public_html/intranet/admin/class/calc.php on line 97
Warning: DateTime::add() expects parameter 1 to be DateInterval, null given in /home/colhabon/public_html/intranet/admin/class/calc.php on line 211
Warning: DateTime::add() expects parameter 1 to be DateInterval, null given in /home/colhabon/public_html/intranet/admin/class/calc.php on line 217
Fatal error: Call to a member function diff() on a non-object in /home/colhabon/public_html/intranet/admin/class/calc.php on line 196
o Código é o mesmo acima só mudou a data.
Link: http://www.colhabonsfrutos.com.br/intranet/admin/teste.php
Sim, esse erro foi o que eu corrigo, apenas atualize seu código.
Sim, esse erro foi o que eu corrigo, apenas atualize seu código.
Na verdade eu atualizei, refiz o processo más o problema persiste
Código geral atual
Realizei testes aqui, e não encontrei esses problemas.
blacktrindade vc disse : em vez dela retornar os 30 minutos, está retornao erro Fatal error: Call to undefined function RuntimeException()
desculpe minha iginorancia.. mas parece que o código está dizendo que a RuntimeException NAO EXISTE... pois ela é indefinida...
na documentacao do php diz que foi implementada a partir da versao 5.1.0 do php http://php.net/manual/pt_BR/class.runtimeexception.php
- da uma conferida na escrita/sintaxe se esta correta neste treho;
- qual a sua versao do php ?
Problema resolvido.
Ultima duvida sobre o sabado..
Realizei a implementação do período más acho que ficou de forma errada, acho não tenho certeza pois quando adicionei o período não retornou resultado.
Código:
$sabado = new Period();
$sabado->addWorkday(CalendarDay::SATURDAY);
$sabado->setStart(DateTime::createFromFormat('H:i:s', '07:30:00'));
$sabado->setEnd(DateTime::createFromFormat('H:i:s', '11:30:00'));
$workPeriod = new WorkPeriod();
$workPeriod->addPeriod($manha)
->addPeriod($tarde)
->addPeriod($sabado);
Erro: simplesmente não gera o resultado?
Se aos sábados há um horário diferenciado do horário da manhã, está correto.
Se o horário for o mesmo, basta adicionar o sábado ao período da manhã.
>
Se aos sábados há um horário diferenciado do horário da manhã, está correto.
Se o horário for o mesmo, basta adicionar o sábado ao período da manhã.
Depois que adicionei o periodo sábado o código não executa esta parte e nem exibe erros:
$periodo = $workPeriod->calculateWorkedDateInterval(new DateTime($data1) , new DateTime($data2));
printf('Periodo trabalhado de %s' , $periodo->format('%H:%I:%S'));
@Edit
Apaguei o periodo sábado e adicionei o sábado no periodo da manha:
$manha = new Period();
$manha->addWorkday(CalendarDay::MONDAY)
->addWorkday(CalendarDay::TUESDAY)
->addWorkday(CalendarDay::WEDNESDAY)
->addWorkday(CalendarDay::THURSDAY)
->addWorkday(CalendarDay::FRIDAY)
->addWorkday(CalendarDay::SATURDAY);
$manha->setStart(DateTime::createFromFormat('H:i:s' , '07:30:00'));
$manha->setEnd(DateTime::createFromFormat('H:i:s' , '11:00:00'));
E novamente o código não executa aquela função para exibir o tempo util.. sempre que é adicionado o sábado como um dia de trabalho.
Realizei uma correção no código, teste novamente. Não consegui encontrar mais cenários que promovam algum erro.
Realizei uma correção no código, teste novamente. Não consegui encontrar mais cenários que promovam algum erro.
Na verdade o problema do sábado continua ali no exemplo você não adicionou o sábado ai quando adiciona ele não gera o cálculo e nem "aciona" o catch
Se ali no seu exemplo você adicionar o sábado como abaixo:
$manha = new Period();
$manha->addWorkday(CalendarDay::MONDAY)
->addWorkday(CalendarDay::TUESDAY)
->addWorkday(CalendarDay::WEDNESDAY)
->addWorkday(CalendarDay::THURSDAY)
->addWorkday(CalendarDay::FRIDAY)
->addWorkday(CalendarDay::SATURDAY);
$manha->setStart(DateTime::createFromFormat('H:i:s' , '07:30:00'));
$manha->setEnd(DateTime::createFromFormat('H:i:s' , '11:00:00'));
$tarde = new Period(); ->addWorkday(CalendarDay::FRIDAY);
$tarde->setStart(DateTime::createFromFormat('H:i:s' , '13:00:00'));
$tarde->setEnd(DateTime::createFromFormat('H:i:s' , '17:30:00'));
$workPeriod = new WorkPeriod(); $periodo = $workPeriod->calculateWorkedDateInterval(new DateTime('2014-08-18 09:30:00') , new DateTime('2014-08-19 09:30:00'));
printf('Período trabalhado de %s' , $periodo->format('%H:%I:%S')); echo "não é possível calcular o período trabalhado";
}
Como acima, a uncia coisa que alterei do seu exemplo foi adicionar o sábado se você realizar o teste ele não vai nem cair no catch :/
@Edit
Desculpe o trabalho que estou te dando Gabriel, sou fortemente cobrado aqui na empresa e estou aqui fazendo você perder seu tempo. Desculpe!!
Havia uma exception que estava sendo chamada incorretamente e uma validação necessária. Atualize seu código.
Apesar de você ser fortemente cobrado no seu emprego, nós, como programadores e vonluntários do fórum, não recebemos nada em troca pelo nosso trabalho.
A única coisa que peço, é que mantenha os créditos como autor do código, pois no link que passou, a maioria das referências foram removidas.
>
Havia uma exception que estava sendo chamada incorretamente e uma validação necessária. Atualize seu código.
Apesar de você ser fortemente cobrado no seu emprego, nós, como programadores e vonluntários do fórum, não recebemos nada em troca pelo nosso trabalho.
A única coisa que peço, é que mantenha os créditos como autor do código, pois no link que passou, a maioria das referências foram removidas.
Quando eu disse que sou fortemente cobrado não foi para cobrar uma resposta e sim para dizer o motivo de eu estar incomodando vocês sabendo que são voluntários estava me explicando o porque enviei mensagens privadas a você, lamento se pensou que era uma forma de cobrança não foi esta minha intenção.
Sobre o créditos bom como eu criei um arquivo com todas as classes deixei os créditos no inicio do código com seu nome e email, apenas removi os que ficava a cima das classes abaixo não achei que faria diferença devido a estar no mesmo arquivo, más posso colocar conforme mandou assim pois sou muito grato por sua ajuda!!
Esta classe tem um problema.
Quando fizemos o seguinte cálculo:
$periodo = $workPeriod->calculateWorkedDateInterval(new DateTime('2015-04-13 09:30:00') , new DateTime('2015-04-16 09:29:00'));
Recebemos a mensagem: Período trabalhado de 23:59:00
Agora se for:
$periodo = $workPeriod->calculateWorkedDateInterval(new DateTime('2015-04-13 09:30:00') , new DateTime('2015-04-16 09:30:00'));Recebemos a mensagem: Período trabalhado de 00:00:00
Pois inteirou 24 horas...
Dai chamados com mais de 24 horas uteis reseta a hora..
Na realidade não é um erro da classe, e sim a formatação de saída dos dados.Altere a saída de
printf('Período trabalhado de %s' , $periodo->format('%H:%I:%S'));
Para
printf('Período trabalhado de %s' , $periodo->format('%a %H:%I:%S'));
Deve retornar algo similiar a:
Período trabalhado de 1 00:00:00
Ou para:
$horas = ((int)$periodo->format('%a')) * 24;
$horas += (int)$periodo->format('%H');
printf('Período trabalhado de %s:%s' , str_pad($horas , 2 , '0' , STR_PAD_LEFT) , $periodo->format('%I:%S'));
Saída:
Período trabalhado de 24:00:00
>
Na realidade não é um erro da classe, e sim a formatação de saída dos dados.Altere a saída de
printf('Período trabalhado de %s' , $periodo->format('%H:%I:%S'));
Alterar para:
$horas = ((int)$periodo->format('%a')) * 24;
$horas += (int)$periodo->format('%H');
printf('Período trabalhado de %s:%s' , str_pad($horas , 2 , '0' , STR_PAD_LEFT) , $periodo->format('%I:%S'));
Funcionou perfeitamente, edite na classe principal para o pessoal vou marcar como solucionado.
Obrigado novamente Gabriel Ninja :)
Acredito que eu tenha o que você precisa, só que é um pouco extenso. Eu desenvolvi um cálculo similar, e com algumas modificações, pude adaptar para o que você precisava.
Por não ser algo tão trivial, eu preferi lhe passar a solução. O que você precisar a mair, é bem importante você implementar.
Eu tive que remover a maioria dos comentários pois o código é extenso, mas é de fácil entendimento. Vamos lá então:
CalendarDay:
/**
* Classe "enum" representa os dias da semana
* @author Gabriel Heming <gabriel.heming@hotmail.com>
**/
class CalendarDay {
Period:
/**
WorkPeriod* Classe que representa um período de tempo
* @author Gabriel Heming <gabriel.heming@hotmail.com>
**/
class Period {
/**
E finalmente, o seu uso:* Classe que representa o período de trabalho
* @author Gabriel Heming <gabriel.heming@hotmail.com>
**/
class WorkPeriod {
$manha = new Period();
$manha->addWorkday(CalendarDay::MONDAY)
->addWorkday(CalendarDay::TUESDAY)
->addWorkday(CalendarDay::WEDNESDAY)
->addWorkday(CalendarDay::THURSDAY)
->addWorkday(CalendarDay::FRIDAY)
$tarde->addWorkday(CalendarDay::MONDAY)
->addWorkday(CalendarDay::TUESDAY)
->addWorkday(CalendarDay::WEDNESDAY)
->addWorkday(CalendarDay::THURSDAY)
$workPeriod->addPeriod($manha)
->addPeriod($tarde);
try {
} catch (RuntimeException $exception) {
Saída:
Como é um código mais antigo, pode haver algumas otimizações para fazer.
Nota: Na realidade, não são 7:30 como comentou, e sim 8 horas exatas.