Calculando a diferença de dias úteis entre duas datas utilizando PHP
Neste artigo vamos ver como calcular a diferença de dias úteis entre duas datas utilizando PHP.
Primeiro vamos ver quais as datas iniciais e finais que vamos trabalhar.
$data1 = \'2021-01-01\';
$data2 = \'2021-12-31\';
Primeiro vamos ver se a data inicial não é maior que a data final.
$data_inicial = strtotime($data1);
$data_final = strtotime($data2);
if ($data_inicial > $data_final) {
echo "A data inicial é maior que a data final";
}
else {
//Codigo a ser inserido
}
Nosso próximo passo é criar um array auxiliar que contenha todos os feriados contidos dentro do intervalo de datas informado. Alguns feriados tem data fixa, e outros tem data variável. Começando pelos feriados de data fixa.
$feriados = array();
$ano_inicial = date(\'Y\', $data_inicial);
$ano_final = date(\'Y\', $data_final);
$ano = $ano_inicial;
for ($x = $ano_inicial; $x <= $ano_final; $x++) {
//Feriados com data fixa
$feriados[] = mktime(0, 0, 0, 1, 1, $ano);
$feriados[] = mktime(0, 0, 0, 4, 21, $ano);
$feriados[] = mktime(0, 0, 0, 5, 1, $ano);
$feriados[] = mktime(0, 0, 0, 9, 7, $ano);
$feriados[] = mktime(0, 0, 0, 10, 12, $ano);
$feriados[] = mktime(0, 0, 0, 11, 2, $ano);
$feriados[] = mktime(0, 0, 0, 11, 15, $ano);
$feriados[] = mktime(0, 0, 0, 12, 25, $ano);
}
Neste exemplo foi criado um array auxiliar chamado $feriados. Para adicionar cada data fixa no array, criamos a data utilizando a funçao mktime. Ele cria um timestamp UNIX com a data informada. Seus parâmetros na ordem são hora, minuto, segundo, mês, dia e ano. Para fins didáticos, adicionamos as datas manualmente. Você pode refatorar este código depois e criar uma função para simplicar este processo.
Agora vamos ver os feriados variáveis. Os feriados com data variável dependem da data da páscoa.
for ($x = $ano_inicial; $x <= $ano_final; $x++) {
$pascoa = easter_date($ano);
$dia_pascoa = date(\'j\', $pascoa);
$mes_pascoa = date(\'n\', $pascoa);
$ano_pascoa = date(\'Y\', $pascoa);
//Feriados com data variável
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa - 48, $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa - 47, $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa - 2 , $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa, $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa + 60, $ano_pascoa);
$ano++;
}
Os feriados variáveis são a páscoa, a sexta-feira santa, os dias de carnaval e o Corpus Christi
Agora que temos os feriados, podemos verificar a quantidade de dias uteis entre duas datas.
$fim_semana = 0;
$numero_dias = 0;
$numero_feriados = 0;
while ($data_inicial <= $data_final) {
$numero_dias++; //Numero de dias do intervalo informado
if (in_array(date("y-m-d", $data_inicial), $feriados)) {
$numero_feriados++;
}
$dia_semana = date("N", $data_inicial);
if ($dia_semana > 5) { // 6 e 7 são sábado e domingo
$fim_semana++;
};
$data_inicial += 86400; // +1 dia
};
$dias_trabalho = $numero_dias - $fim_semana - $numero_feriados;
Para o calculo de dias fizemos um loop usando WHILE entre a data inicial e a data final, e fizemos o incremento da data em 1 dia, somando 86400 segundos a data informada.
Para validar se a data atual é um feriado, utilizamos a função in_array para verificar se aquela data existe no array de feriados.
if (in_array(date("y-m-d", $data_inicial), $feriados)) {
$numero_feriados++;
}
Em nosso exemplo não consideremos o sábado e o domingo como dia úteis. Para verificar o dia da semana da data informada, utilizamos a função DATE desta forma:
$dia_semana = date("N", $data_inicial);
O retorno é um número inteiro. Ele vai retornar 6 para sábado e 7 para domingo.
O código completo fica da seguinte forma:
$data1 = \'2021-01-01\';
$data2 = \'2021-12-31\';
$data_inicial = strtotime($data1);
$data_final = strtotime($data2);
if ($data_inicial > $data_final) {
echo "A data inicial é maior que a data final";
return 0;
} else {
$feriados = array();
$ano_inicial = date(\'Y\', $data_inicial);
$ano_final = date(\'Y\', $data_final);
$ano = $ano_inicial;
for ($x = $ano_inicial; $x <= $ano_final; $x++) {
$pascoa = easter_date($ano);
$dia_pascoa = date(\'j\', $pascoa);
$mes_pascoa = date(\'n\', $pascoa);
$ano_pascoa = date(\'Y\', $pascoa);
//Feriados com data fixa
$feriados[] = mktime(0, 0, 0, 1, 1, $ano);
$feriados[] = mktime(0, 0, 0, 4, 21, $ano);
$feriados[] = mktime(0, 0, 0, 5, 1, $ano);
$feriados[] = mktime(0, 0, 0, 9, 7, $ano);
$feriados[] = mktime(0, 0, 0, 10, 12, $ano);
$feriados[] = mktime(0, 0, 0, 11, 2, $ano);
$feriados[] = mktime(0, 0, 0, 11, 15, $ano);
$feriados[] = mktime(0, 0, 0, 12, 25, $ano);
//Feriados com data variável
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa - 48, $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa - 47, $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa - 2 , $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa, $ano_pascoa);
$feriados[] = mktime(0, 0, 0, $mes_pascoa, $dia_pascoa + 60, $ano_pascoa);
$ano++;
}
$fim_semana = 0;
$numero_dias = 0;
$numero_feriados = 0;
while ($data_inicial <= $data_final) {
$numero_dias++; //Numero de dias do intervalo informado
if (in_array(date("y-m-d", $data_inicial), $feriados)) {
$numero_feriados++;
}
$dia_semana = date("N", $data_inicial);
if ($dia_semana > 5) { // 6 e 7 são sábado e domingo
$fim_semana++;
};
$data_inicial += 86400; // +1 dia
};
$dias_trabalho = $numero_dias - $fim_semana - $numero_feriados;
echo $dias_trabalho;
}
Outra forma de resolver o mesmo problema utilizando DateTime
Agora que vimos como calcular a diferença de dias úteis utilizando timestamps e funções como mktime e strtotime, vamos conhecer uma forma alternativa de resolver o mesmo problema utilizando a classe DateTime do PHP.
A classe DateTime é nativa do PHP e facilita bastante o trabalho com datas, permitindo criar, comparar e percorrer intervalos de forma mais simples e legível.
Além disso, ela elimina a necessidade de trabalhar com timestamps diretamente.
Veja o exemplo abaixo:
function diasUteis($dataInicio, $dataFim, $feriados = [])
{
$inicio = new DateTime($dataInicio);
$fim = new DateTime($dataFim);
$fim->modify(\'+1 day\'); // Inclui a data final no cálculo
$intervalo = new DateInterval(\'P1D\');
$periodo = new DatePeriod($inicio, $intervalo, $fim);
$diasUteis = 0;
foreach ($periodo as $data) {
$diaSemana = $data->format(\'N\'); // 1 = segunda, 7 = domingo
$dataStr = $data->format(\'Y-m-d\');
// Ignora sábados e domingos
if ($diaSemana >= 6) continue;
// Ignora feriados
if (in_array($dataStr, $feriados)) continue;
$diasUteis++;
}
return $diasUteis;
}
// Exemplo de uso:
$feriados = [
\'2021-01-01\', \'2021-04-21\', \'2021-05-01\',
\'2021-09-07\', \'2021-10-12\', \'2021-11-02\',
\'2021-11-15\', \'2021-12-25\',
];
echo diasUteis(\'2021-01-01\', \'2021-12-31\', $feriados);
Neste exemplo, utilizamos as classes DateTime, DateInterval e DatePeriod. A função DatePeriod permite criar um intervalo entre duas datas, e o DateInterval define o passo , neste caso, de um dia (P1D).
Dessa forma, podemos percorrer o intervalo completo sem precisar somar segundos manualmente.
Para verificar se o dia atual é um final de semana, usamos:
$diaSemana = $data->format(\'N\');
A função format(\'N\') retorna um número entre 1 (segunda-feira) e 7 (domingo). Assim, basta ignorar os dias 6 e 7 para não contar sábados e domingos.
Também usamos o formato Y-m-d ao comparar as datas com o array de feriados, garantindo que elas estejam no mesmo padrão e evitando erros de comparação.
O resultado final é o mesmo, mas o código fica mais limpo, legível e fácil de manter. Além disso, utilizando DateTime, é possível estender essa lógica para lidar com fusos horários, períodos personalizados e outros cálculos de datas com mais facilidade.



