Manipulando datas e horários com PHP
Introdução
Trabalhar com datas e horários é algo comum para um desenvolvedor. Mas como podemos fazer isso com efetividade usando a linguagem PHP?
Fui contratado por uma empresa para desenvolver um sistema de controle de pontos, e a solicitação foi bem clara ao pedir um sistema que funcione na Web.
Pensei comigo: Já que mais ou menos 80% de toda a Web usa PHP, parece uma boa linguagem para desenvolver essa solução.
Este sistema vai precisar exibir a data e hora de cada registro (entrada e saída) do funcionário e exibir quantas horas têm de diferença entre eles (as horas trabalhadas no dia).
A função date
Velha conhecida dos programadores PHP, a função date
pode ser a primeira opção que vem à mente para pegar a data atual. Com ela é bem fácil definir o formato em que queremos esta data, inclusive.
Para exibirmos a data atual no formato brasileiro, por exemplo, ficaria assim:
<?php
$hoje = date('d/m/Y');
echo $hoje;
E este código exibiria: 20/03/2019
.
Como você já deve ter suposto, o d
representa o dia, m
representa o mês enquanto Y
representa o ano com 4 dígitos. Se quiséssemos o ano apenas com 2 dígitos (19 no nosso exemplo), bastaria utilizar y
(em minúsculo).
Com esta mesma função podemos também pegar o horário atual, além da data. Continua bem fácil:
<?php
$agora = date('d/m/Y H:i');
echo $agora;
E a saída seria: 20/03/2019 16:58
Repare que como m
representa o mês, para representar os minutos a letra i
é utilizada. Para saber mais sobre quais letras utilizar em cada caso, você pode dar uma olhada na documentação da função.
Intervalo entre duas... strings?
Já conseguimos pegar as datas (como string, diga-se de passagem), então agora vamos pesquisar sobre como calcular o intervalo entre elas para exibir as horas trabalhadas.
Dando uma pesquisada, caí na documentação da função date_diff
, mas vi que como parâmetros ela não recebe strings. Esta função recebe objetos de classes que implementem a interface DateTimeInterface
. Bom, vamos com calma.
Até agora temos trabalhado somente com a função date
que retorna uma string, mas nós podemos de forma tão fácil quanto, utilizar Orientação a Objetos nesta tarefa. Existe uma classe que podemos instanciar e obter a data e hora atuais e que implementa a DateTimeInterface
que precisamos. "O nome dela é DateTime
, e eu encontrei ela na documentação".
O código que tínhamos antes não muda muito. Veja como fica utilizando a classe que acabamos de conhecer:
<?php
$agora = new DateTime(); // Pega o momento atual
echo $agora->format('d/m/Y H:i'); // Exibe no formato desejado
Caso a gente não passe nenhum parâmetro para o construtor de DateTime
, o momento atual será recuperado. Podemos ainda passar uma string informando o momento que queremos, por exemplo:
<?php
$agora = new DateTime('now');
$ontem = new DateTime('yesterday');
$dia15 = new DateTime('2019-03-15');
Se já tivermos uma string em outro formato, e quisermos criar um objeto do tipo DateTime
, podemos utilizar o método createFromFormat
, da seguinte forma:
<?php
$formato = 'd/m/Y';
$stringDataDia15 = '15/03/2019';
$dia15 = DateTime::createFromFormat($formato, $stringDataDia15);
Agora sim, intervalo entre duas datas
Agora que já conhecemos uma forma de trabalhar com datas utilizando Orientação a Objetos, vamos ver como pegar o intervalo entre 2 objetos do tipo DateTime
.
A interface DateTimeInterface
expõe um método chamado diff
, que recebe um outro objeto deste mesmo tipo como parâmetro, ou seja, podemos chamar a partir de nosso objeto de datas, o método diff
passando uma outra data como parâmetro. Vamos ver como ficaria isso:
<?php
$entrada = new DateTime('09:00');
$saida = new DateTime('18:00');
$intervalo = $saida->diff($entrada);
print_r($intervalo);
E com isso temos a seguinte saída:
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 0
[h] => 9
[i] => 0
[s] => 0
[f] => 0
[weekday] => 0
[weekday_behavior] => 0
[first_last_day_of] => 0
[invert] => 1
[days] => 0
[special_type] => 0
[special_amount] => 0
[have_weekday_relative] => 0
[have_special_relative] => 0
)
Vamos por partes analisar o que acabou de acontecer. Primeiro, quando criamos as datas e horas, passamos apenas os seus respectivos horários, não informando os dias. Pois é. Se eu passar apenas o horário, ele pega a data atual no horário informado.
Depois, quando exibimos o retorno do método diff
, vimos que temos acesso a um objeto do tipo DateInterval
. Nele, temos acesso a quantos anos, meses, dias, horas, minutos e segundos existem no intervalo entre uma data e outra. No nosso caso, há 9 horas de intervalo, e podemos exibir isso da seguinte forma:
// ...
echo $interval->h; // h = horas
PS.: Caso você tenha analisado a saída do print_r
, viu que há as propriedades d
e days
. Num intervalo de exatamente um ano, a propriedade y
teria o valor de 1, enquanto a propriedade d
teria o valor zerado, o que significa que há 1 ano e 0 dias de diferença. A propriedade days
teria 365 como seu valor, indicando o número total de dias.
O problema do fuso horário
Com o sistema já implementado, fui implantar em um dos servidores da empresa, e notei que o relógio dele estava com um fuso horário diferente do daqui no Brasil. Isso estava causando inconsistências nas marcações de ponto.
Não é legal deixar que o fuso horário de cada máquina atrapalhe nossos sistema, então seria interessante se pudéssemos informar no código com qual fuso estamos trabalhando.
A boa notícia é que com PHP isso é até fácil de fazer. Na hora de criar nossa data, basta informar mais um parâmetro que é o fuso horário desejado. Veja como fica:
<?php
$timezone = new DateTimeZone('America/Sao_Paulo');
$agora = new DateTime('now', $timezone);
Repare que nós colocamos o timezone como America/Sao_Paulo
. Mas e se quisermos saber outras possibilidades? É possível ver a lista de fusos horários suportados pelo DateTimeZone
com o método DateTimeZone::listIdentifiers()
:
<?php
print_r(DateTimeZone::listIdentifiers());
Trabalhar com datas e fusos horários com PHP é moleza!
Para saber mais: DateTimeImmutable
Conseguimos chegar na implementação desejada. Conseguimos pegar as datas que queremos (utilizando Orientação a Objetos, inclusive), calcular a diferença entre elas e até trabalhar com diferentes fusos horários.
Mas no artigo foi citada a interface DateTimeInterface
. Que outra classe será que implementa essa interface?
A classe DateTimeImmutable
! Ela pode ser utilizada da mesma forma que a classe DateTime
, mas as operações que ela expõe retornam uma nova data, e não alteram a original. Pareceu confuso? Vamos ver na prática essa diferença e de quebra aprender a realizar mais operações com as datas:
<?php
$umDia = new DateInterval('P1D'); // Intervalo de 1 dia
// Com DateTime:
$hoje = new DateTime();
echo $hoje->format('d/m'); // Saída 20/03
$hoje->add($umDia); // Altera o valor de $hoje
echo $hoje->format('d/m'); // Saída 21/03
// Com DateTimeImmutable
$hoje = new DateTimeImmutable();
echo $hoje->format('d/m'); // Saída 20/03
$amanha = $hoje->add($umDia); // Não altera o valor de $hoje
echo $hoje->format('d/m'); // Saída 20/03
echo $amanha->format('d/m'); // Saída 21/03
Repare que com um intervalo de um dia, quando o adicionamos (com o método add
) ao objeto do tipo DateTime
, o próprio objeto é alterado. Já quando utilizamos DateTimeImmutable
seu valor permaneceu intacto, retornando o valor com a alteração desejada.
Se ficou curioso sobre como criar um intervalo pode dar uma olhada na documentação. Mas basicamente, P
significa um período, e adicionamos 1D
, que quer dizer um dia. Se quiséssemos adicionar 2 dias e 3 horas, ficaria: P2DT3H
, onde o T
indica que as informações a seguir são sobre o horário (hora, minuto ou segundo).
Conclusão
Começamos o post com uma necessidade real de manipular datas utilizando PHP e vimos que não é necessária nenhuma biblioteca externa para isso. A própria API de datas do PHP é muito rica e nos fornece meios de criar uma data, realizar operações nela, buscar intervalos, trabalhar com fusos horários e muito mais.
Você pode ainda dar uma olhada no que mais o PHP te oferece quando o assunto é data na documentação oficial, e pode conhecer mais da linguagem que domina a web na formação PHP da Alura!