Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Leco RP

Calcular prazos com "horas úteis"

Recommended Posts

Fala galera,

 

Estou montando um sistema de abertura de chamados e me surgiu a seguinte dúvida: como calcular prazo, considerando dias e horas úteis?

 

Preciso calcular qual seria a data/hora após 6h, considerando dias e horas úteis (digamos assim) sendo que essas horas seriam o intervalo de 08:00 as 19:00 de segunda a sexta.

 

Para cálculo de DIAS úteis, eu achei uma função da internet que funciona perfeitamente:

 

function Feriados($ano,$posicao){
 $dia = 86400;
 $datas = array();
 $datas['pascoa'] = easter_date($ano);
 $datas['sexta_santa'] = $datas['pascoa'] - (2 * $dia);
 $datas['carnaval'] = $datas['pascoa'] - (47 * $dia);
 $datas['corpus_cristi'] = $datas['pascoa'] + (60 * $dia);
 $feriados = array (
	'01/01',
	date('d/m',$datas['carnaval']),
	date('d/m',$datas['sexta_santa']),
	date('d/m',$datas['pascoa']),
	date('d/m',$datas['corpus_cristi']),
	'25/12', 
 );
 
return $feriados[$posicao]."/".$ano;
}      

// Formata como timestamp
function dataToTimestamp($data){
 $ano = substr($data, 6,4);
 $mes = substr($data, 3,2);
 $dia = substr($data, 0,2);
return mktime(0, 0, 0, $mes, $dia, $ano);  
} 

// Soma 01 dia
function Soma1dia($data){
 $ano = substr($data, 6,4);
 $mes = substr($data, 3,2);
 $dia = substr($data, 0,2);
return   date("d/m/Y", mktime(0, 0, 0, $mes, $dia+1, $ano));
}

function SomaDiasUteis($xDataInicial,$xSomarDias){
 for($ii=1; $ii<=$xSomarDias; $ii++){
	
	$xDataInicial=Soma1dia($xDataInicial); // Soma dia normal
	
	// Verifica se é dia útil
	if(date("w", dataToTimestamp($xDataInicial))=="0"){
	   // Se for domingo ou feriado , soma 01
	   $xDataInicial=Soma1dia($xDataInicial);
	   
	}else if(date("w", dataToTimestamp($xDataInicial))=="6"){
	   // Se for sábado soma 02
	   $xDataInicial=Soma1dia($xDataInicial);
	   $xDataInicial=Soma1dia($xDataInicial);
	   
	}else{
	   // Verifica se é feriado
	   for($i=0; $i<=12; $i++){
		  if($xDataInicial==Feriados(date("Y"),$i)){
			 $xDataInicial=Soma1dia($xDataInicial);
		  }
	   }
	}
 }
 // Retorna a data
return $xDataInicial;
}

 

Agora a dúvida é como acrescentar as horas...

 

Exemplificando:

 

Cadastro um chamado em: 04-07-2013 18:50. Considerando as 6h "úteis", o prazo final para atender esse chamado seria 05-07-2013 13:50, ou seja, as 13h50m do dia seguinte.

 

Agradeço desde já.

Compartilhar este post


Link para o post
Compartilhar em outros sites
// objeto de data/hora
// para indicar uma data/hora de inicio, passe ela no construtor, exemplo:
// $date = new DateTime('2013-07-08 09:00:00');
$date = new DateTime();

// prazo, em horas. se precisar especificar em minutos, 
// coloque valores quebrados, como 15.5
$prazo = 30; 

// inicio do expediente 9 = horas * 60 para transformar em minutos
$inicioExpediente = 9 * 60;
// fim do expediente
$fimExpediente = 19 * 60;

// feriados
$feriados = array('09/07/2013');

// dias encontrados para trampo
$diasUteis = array();

// ----------- agora começa a brincadeira
// convertemos o prazo para minutos
$prazoMinutos = $prazo * 60;

// enquanto for maior que zero
while( $prazoMinutos > 0 ){
	// transformamos a hora atual em minutos
	$hora = ($date->format('H') * 60) + $date->format('i');

	// se for menor que a hora do inicio do expediente
	if($hora < $inicioExpediente){
		// colocamos igual a hora do expediente
		$date->setTime(0, $inicioExpediente, 0);
		continue;
	}
	
	// data calculada
	$data = $date->format('d/m/Y');

	// se 
	// - for um feriado OU
	// - passar da hora do expediente OU
	// - for um dia de fim de semana (sabado|domingo)
	// vamos para o dia seguinte, no inicio do expediente
	if(in_array($data,$feriados) || $hora >= $fimExpediente || $date->format('w') == 0 || $date->format('w') == 6){
		$date->modify('+1 day');
		$date->setTime(0, $inicioExpediente, 0);
		continue;
	}
	
	// se chegou aqui, é um dia util.
	// vamos ver se já está na nossa lista de dias
	// se não estiver, colocamos
	if(!in_array($data, $diasUteis)){
		$diasUteis[] = $data;
	}
	
	// minutos que temos que acrescentar para chegar no
	// fim do expediente de hoje
	$minutos = $fimExpediente - $hora;
	
	// tiramos do prazo
	$prazoMinutos -= $minutos;
	
	// se estourou
	if($prazoMinutos < 0){
		// tiramos o que estourou
		$minutos += $prazoMinutos;
	}
	
	// adicionamos os minutos do calculo na data
	$date->modify('+' . $minutos . ' minute');
}

echo $prazoFinal = $date->format('d/m/Y H:i:s'), PHP_EOL;
print_r($diasUteis);

[ Editado ]

Otimização no código.

Estava meio lento quando o prazo era grande.

 

@braços e fiquem com Deus!

Compartilhar este post


Link para o post
Compartilhar em outros sites

E olha que eu procurei pelo fórum, hein?

 

Muito obrigado pela ajuda Hugo!

Fiz uns testes e funcionou perfeitamente.

 

Fiquei com uma dúvida: é possível "fundir" com a função de cálculo de dias úteis que mencionei no primeiro post?

Pergunto pois lá já estão previstos todos os feriados (só falta acrescentar o restante) e independe do ano, ou seja, para qualquer ano basta acrescentar os dias sem que seja necessário mudar o código para cada ano.

 

Mais uma vez, muito obrigado pela ajuda, Hugo!

:clap:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas esta que passei já separa os dias úteis, no array $diasUteis.

 

Sobre os feriados, também coloquei ali um array com as datas que podem ser feriados (que no caso, o próximo dia 09/07/2013 é feriado em São Paulo).

 

Se quiser mais, é só acrescentar a data ao array.

 

$feriados = array('09/07/2013','25/12/2013','07/09/2013');

@braços

Compartilhar este post


Link para o post
Compartilhar em outros sites

Fala Hugo,

 

Essa parte eu entendi, inclusive completei o vetor com os feriados até o fim do ano.

 

Coloquei essa possibilidade de fundir com a outra função, pois nela não é preciso colocar o ano. Ela me retorna o dia útil referente a qualquer ano...

 

Seria mais um "complemento", digamos assim.

 

Abraços

Compartilhar este post


Link para o post
Compartilhar em outros sites

É porque aquela função só calcula os feriados móveis.

Os fixos não estão lá.

 

Logo, é só você complementar a lista de feriados que está na função que te passei com os feriados fixos e que estão na função que você pegou (adicionando o ano na data).

 

@braços

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá Hugo,

 

Se observar, os fixos estão também, veja:

function Feriados($ano,$posicao){
 $dia = 86400;
 $datas = array();
 $datas['pascoa'] = easter_date($ano);
 $datas['sexta_santa'] = $datas['pascoa'] - (2 * $dia);
 $datas['carnaval'] = $datas['pascoa'] - (47 * $dia);
 $datas['corpus_cristi'] = $datas['pascoa'] + (60 * $dia);
 $feriados = array (
	'01/01', // Primeiro dia do ano
	date('d/m',$datas['carnaval']),
	date('d/m',$datas['sexta_santa']),
	date('d/m',$datas['pascoa']),
	date('d/m',$datas['corpus_cristi']),
	'25/12', // Natal
 );

Seria só complementar os que estão faltando... Certo?

 

 

Abraços

Compartilhar este post


Link para o post
Compartilhar em outros sites

 

// objeto de data/hora
// para indicar uma data/hora de inicio, passe ela no construtor, exemplo:
// $date = new DateTime('2013-07-08 09:00:00');
$date = new DateTime();

// prazo, em horas. se precisar especificar em minutos, 
// coloque valores quebrados, como 15.5
$prazo = 30; 

// inicio do expediente 9 = horas * 60 para transformar em minutos
$inicioExpediente = 9 * 60;
// fim do expediente
$fimExpediente = 19 * 60;

// feriados
$feriados = array('09/07/2013');

// dias encontrados para trampo
$diasUteis = array();

// ----------- agora começa a brincadeira
// convertemos o prazo para minutos
$prazoMinutos = $prazo * 60;

// enquanto for maior que zero
while( $prazoMinutos > 0 ){
	// transformamos a hora atual em minutos
	$hora = ($date->format('H') * 60) + $date->format('i');

	// se for menor que a hora do inicio do expediente
	if($hora < $inicioExpediente){
		// colocamos igual a hora do expediente
		$date->setTime(0, $inicioExpediente, 0);
		continue;
	}
	
	// data calculada
	$data = $date->format('d/m/Y');

	// se 
	// - for um feriado OU
	// - passar da hora do expediente OU
	// - for um dia de fim de semana (sabado|domingo)
	// vamos para o dia seguinte, no inicio do expediente
	if(in_array($data,$feriados) || $hora >= $fimExpediente || $date->format('w') == 0 || $date->format('w') == 6){
		$date->modify('+1 day');
		$date->setTime(0, $inicioExpediente, 0);
		continue;
	}
	
	// se chegou aqui, é um dia util.
	// vamos ver se já está na nossa lista de dias
	// se não estiver, colocamos
	if(!in_array($data, $diasUteis)){
		$diasUteis[] = $data;
	}
	
	// minutos que temos que acrescentar para chegar no
	// fim do expediente de hoje
	$minutos = $fimExpediente - $hora;
	
	// tiramos do prazo
	$prazoMinutos -= $minutos;
	
	// se estourou
	if($prazoMinutos < 0){
		// tiramos o que estourou
		$minutos += $prazoMinutos;
	}
	
	// adicionamos os minutos do calculo na data
	$date->modify('+' . $minutos . ' minute');
}

echo $prazoFinal = $date->format('d/m/Y H:i:s'), PHP_EOL;
print_r($diasUteis);

[ Editado ]

Otimização no código.

Estava meio lento quando o prazo era grande.

 

@braços e fiquem com Deus!

Cara, muito bom o fonte, porém ao final preciso armazenar a Data Fim em uma variável, pois depois preciso gravar no banco. E não simplesmente exibir em tela. Como faço?

Compartilhar este post


Link para o post
Compartilhar em outros sites

PHP - Somar ou Subtrair dias de uma data

Adicionar

♦ 10 dias a partir de hoje

echo date('d/m/Y', strtotime("+10 days"));

 

♦ 10 dias a partir de uma data

echo date('d/m/Y', strtotime("+10 days",strtotime('20-07-2011')));

 

Subtrair

♦ 10 dias a partir de hoje

echo date('d/m/Y', strtotime("-10 days"));

 

♦ 10 dias a partir de uma data

echo date('d/m/Y', strtotime("-10 days",strtotime('20-07-2011')));

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.