Manipulando datas em PHP
Uma questão relevante em todas as linguagens é como ela manipula datas, pois trabalhar com datas é algo comum a todos os desenvolvedores e em muitos situações uma dor de cabeça.
O PHP tem duas formas de trabalhar com datas, através das funções clássicas: date
, time
, strtotime
e do objeto DateTime
. Vamos ver os dois modos.
Função Date
Esta é a velha conhecida, basicamente retorna uma string dado um formato de data como primeiro parâmetro. No segundo parâmetro recebe um inteiro timestamp UNIX, que é a quantidade de segundos desde a era UNIX, que é de 1º de Janeiro de 1970 00:00:00. Se o segundo parâmetro não for fornecido ele pegará o timestamp atual fornecido pela função time()
.
$atual = date('d/m/Y H:i:s');
// data atual do sistema ex.: 07/11/2020 09:19:08
$dia = date('d/m/Y H:i:s', 1604665181 );
// uma data especificada pelo timestamp: 06/11/2020 09:19:41
O formato da data é representado por uma série de caracteres cada um representando uma informação da data como por exemplo “d” representa o dia o “m“(minúsculo) o mês, etc. A tabela abaixo mostra o que cada caractere significa:
Caractere | Descrição | Exemplo |
---|---|---|
d | Dia do mês com dois dígitos. | 01 até 31 |
j | Dia do mês sem zero. | 1 até 31 |
D | Representação textual do dia da semana com três letras. | Mon até Sat |
l (“L” minúsculo) | Representa textual do dia da semana de forma completa. | Monday até Saturday |
N | Representação numérica ISO-8601 para os dias da semana. Segunda = 1 … Domingo = 7 | 1 até 7 |
w | Representação numérica para os dias da semana. Domingo = 0 … Sábado = 6 | 0 até 6 |
z | Dia do ano começando em 0 | 0 até 365 |
W | Número do ano da semana ISO-8601 | 42 |
F | Representação textual completa do mês | January, February, etc. |
m | Representação numérica do mês com dois digitos | 01 até 12 |
M | Representação textual curta do mês com três caracteres | Jan, Feb, … Dec |
n | Representação numérica do mês sem zero | 1 até 12 |
t | Número de dias em um determinado mês | 28 até 31 |
Y | Representação do ano com 4 dígitos | 2020 |
y | Representação do ano com 2 dígitos | 20 |
L | Representação numérica se um ano é bissexto. 1 = bissexto , 0 = não bissexto. | 1 ou 0 |
a | Representação Ante meridiem e Post meridiem minúscula. | am, pm |
A | Representação Ante meridiem e Post meridiem maiúscula. | AM, PM |
g | Hora no formato de 12 horas sem o zero | 1 até 12 |
G | Hora no formato 24 horas sem zero | 0 até 23 |
h | Hora no formato 12 com zero | 01 até 12 |
H | Hora no formato 24 com zero | 00 até 23 |
i | Minutos com zero | 00 até 59 |
s | Segundo com zero | 00 até 59 |
u | Microssegundos.(a função date sempre retorna 000000, mas pode ser utilizado com a classe DateTime que veremos adiante). | Ex.: 654321 |
v | Millisegundo(mesma observação de microssegundos acima) | Ex.: 654 |
e | Identificador de timezone | Ex.: UTC, GMT, America/Sao_Paulo, |
O | Diferença para o “Greenwich time (GMT)” sem dois pontos(“:”) entre as horas e minutos. | Ex.: +0200 |
P | Diferença para o “Greenwich time (GMT)” com dois(“:”) pontos entre as horas e minutos. | Ex.: +02:00 |
c | Data ISO 8601 | Ex.: 2020-11-07T10:27:43-03:00 |
r | Data no formato RFC 2822 | Ex.: Sat, 07 Nov 2020 10:28:17 -0300 |
Função time()
A função time()
retorna o número de segundos desde a era UNIX, que é de 1º de Janeiro de 1970 00:00:00 até o momento atual.
$agora = time();
echo 'Time: ' . $agora . "\n";
echo 'Agora: '. date('d/m/Y H:i:s', $agora )."\n";
//Time: 1604756792
//Agora: 07/11/2020 10:46:32
$amanha = time() + 60 * 60 * 24;
echo 'Time: '.$amanha."\n";
echo 'Amanhã: '. date('d/m/Y H:i:s', $amanha)."\n";
//Time: 1604843192
//Amanhã: 08/11/2020 10:46:32
Função strtotime()
A função strtotime()
recebe uma string com uma descrição de uma data em inglês para um timestamp Unix. Por exemplo utilizando a string now é retornado o timestamp atual, com a string ‘+1 hour’ será retornado o timestamp atual acrescido de uma hora. Veja mais exemplos.
$time = strtotime("now");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1604757421
//07/11/2020 10:57:01
$time = strtotime("10 September 2020");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1599706800
//10/09/2020 00:00:00
$time = strtotime("+1 day");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1604843821
//08/11/2020 10:57:01
$time = strtotime("+1 week");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1605362221
//14/11/2020 10:57:01
$time = strtotime("+1 week 2 days 4 hours 2 seconds");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1605549423
//16/11/2020 14:57:03
$time = strtotime("next Thursday");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1605150000
//12/11/2020 00:00:00
$time = strtotime("last Monday");
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1604286000
//02/11/2020 00:00:00
Função mktime()
A função mktime()
permite criar um timestamp fornecendo os valores de hora, minuto, segundo, mês, dia e ano como parâmetros.
$time = mktime(12, 42, 06, 11, 7, 2020);
echo 'Time: ' . $time . "\n";
echo date('d/m/Y H:i:s', $time )."\n";
//Time: 1604763726
//07/11/2020 12:42:06
A Classe DateTime
As funções apresentadas anteriormente nos permitem manipular datas e realizar diversas operações, mas elas não são muito práticas, sendo uma abordagem orientada a objetos melhor na minha opinião, já que permite criar, ajustar e calcular diferenças entre datas de uma forma muito mais simples e legível.
Para isso podemos criar um objeto DateTime
com a data atual facilmente, e de diversas formas. Ao criá-lo se nenhum parâmetro for passado para o construtor o objeto de data será criado com a data atual, podendo também ser possível passar uma string que será avaliada da mesma forma que o strtotime()
para gerar a data.
$data = new DateTime(); // data atual
echo $data->format('d/m/Y H:i:s')."\n";
// 07/11/2020 11:30:21
$data = new DateTime('2020-10-25');
echo $data->format('d/m/Y H:i:s')."\n";
// 25/10/2020 00:00:00
$data = new DateTime('+1 hour');
echo $data->format('d/m/Y H:i:s')."\n";
// 07/11/2020 12:30:21
Para exibir a data criada é utilizado o método format()
que aceita uma string utilizando o mesmo formato apresentado na tabela mostrada anteriormente.
Caso a data em string que se deseja criar o objeto DateTime
estiver em um formato não reconhecido pelo construtor como uma data em formato br é possível utilizar o método estático DateTime::createFromFormat
para criá-lo, para isso deve-se passar uma string de formato de data desejado e a data em string que se seja criar o objeto.
$input = '25/12/2020 23:45';
$data = DateTime::createFromFormat('d/m/Y H:i', $input);
echo $data->format('d/m/Y H:i:s')."\n";
// 25/12/2020 23:45:00
Modificando uma data
Podemos alterar uma data através do método modify()
, podendo adicionar ou subtrair um determinado tempo. O método aceita uma string sendo avaliada do mesmo modo que a função strtotime()
.
$data = new DateTime();
echo $data->format('d/m/Y H:i:s')."\n";
// 07/11/2020 11:47:21
$data->modify('+1 hour +30 minutes');
echo $data->format('d/m/Y H:i:s')."\n";
// 07/11/2020 13:17:21
Outra maneira de altera uma data é através do método setDate()
que recebe como parâmetros o ano, o mês e o dia.
$data = new DateTime('2020-05-10 12:10:42');
echo $data->format('d/m/Y H:i:s')."\n";
// 10/05/2020 12:10:42
$data->setDate(2019,10,28);
echo $data->format('d/m/Y H:i:s')."\n";
// 28/10/2019 12:10:42
Para alterar o hora, minuto e segundo é utilizado outro método o setTime()
$data = new DateTime('27-11-2020');
$data->setTime(10, 30, 42);
echo $data->format('d/m/Y H:i:s');
// 27/11/2020 10:30:42
Tanto o método setDate()
quanto o método setTime()
retornam o próprio objeto DateTime
permitindo assim realizar as duas chamadas encadeadas, facilitando a escrita.
$data = new DateTime('27-11-2020');
$data->setDate(2019,5,28)->setTime(10, 30, 42);
echo $data->format('d/m/Y H:i:s');
// 28/05/2019 10:30:42
Também podemos modificar uma data adicionando ou subtraindo um intervalo(veremos sobre intervalo logo a frente). Isso é feito através dos métodos add()
e sub()
que recebem um objeto do tipo DateInterval
.
$data = new DateTime('2020-05-10');
$intervalo = new DateInterval('P1D');
$data->add($intervalo);
echo $data->format('d/m/Y H:i:s')."\n";
// 11/05/2020 00:00:00
$data = new DateTime('2020-05-10');
$intervalo = new DateInterval('P1D');
$data->sub($intervalo);
echo $data->format('d/m/Y H:i:s')."\n";
// 09/05/2020 00:00:00
Definindo TimeZone
Para definir uma TimeZone ou fuso horário diferente do padrão do sistema primeiro precisamos criar um novo objeto DateTimeZone
passando para seu construtor o timezone desejado veja aqui uma lista dos timezones disponíveis. Com esse objeto criado basta passá-lo para o método setTimezone
.
$data = new DateTime();
echo $data->format('r')."\n";
// Sat, 07 Nov 2020 16:38:42 -0300
$timeZone = new DateTimeZone('America/New_York');
$data->setTimezone($timeZone);
echo $data->format('r')."\n";
// Sat, 07 Nov 2020 14:38:42 -0500
Trabalhando com Intervalos
Em diversas situação é necessário trabalharmos com intervalos seja para adicioná-los a uma data ou para verificarmos a diferença entre duas datas. O PHP possui uma classe para representar esses intervalos, a classe DateInterval
que recebe em seu construtor uma string com o formato de intervalo que é composta da seguinte maneira:
Deve começar com o caractere P e cada parte do período deve ser formado por um número seguido de um caractere que represente o tipo de período(anos, meses, horas, etc. Veja a lista abaixo). Se a expressão de intervalo tiver algum periodo de horas, minutos ou segundos deve ser adicionado o caractere “T” antes de ser informado qualquer um destes(mas apenas uma vez).
Resumindo: regex P(\dY)?(\dM)?(\dD)?(\dW)?(T(\dH)?(\dM)?(\dS)?)?
- Y: Anos
- M: Meses
- D: Dias
- W: Semanas( equivalente a 7D)
- H: Horas
- M: Minutos
- S: Segundos
A expressão de intervalo pode ser um pouco confusa então vamos a alguns exemplos:
- P2D : 2 Dias
- P3M : 3 mêses
- P2Y3M : 2 anos e 3 meses
- PT5S : 5 segundos
- P3YT8M : 3 anos e 8 minutos(note que o 8M esta depois do T então é referente a minutos)
- P3Y3WT8M3S: 3 anos, 3 semanas, 8 minutos e 3 segundos
$intervalo = new DateInterval('P3Y3WT8M3S');
print_r($intervalo);
// DateInterval Object
// (
// [y] => 3
// [m] => 0
// [d] => 21
// [h] => 0
// [i] => 8
// [s] => 3
// [f] => 0
// [weekday] => 0
// [weekday_behavior] => 0
// [first_last_day_of] => 0
// [invert] => 0
// [days] =>
// [special_type] => 0
// [special_amount] => 0
// [have_weekday_relative] => 0
// [have_special_relative] => 0
// )
Qualquer dos valores do intervalo podem ser acessados através dos atributos, por exemplo $intervalor->d
para acessar o número de dias do intervalo. Também é possível formar o intervalo para exibição através do método format() que recebe uma string de formato seguindo a
- %y: Ano numérico
- %Y: Ano, numérico com mínimo 2 dígitos preenchido com 0
- %m: Mês, numérico
- %M: Mês, numérico com mínimo 2 dígitos preenchido com 0
- %d: Dia, numérico
- %D: Dia, numérico com mínimo 2 dígitos preenchido com 0
- %a: Total de dias
- %h: Hora, numérico
- %H: Hora, numérico com mínimo 2 dígitos preenchido com 0
- %i: Minuto, numérico
- %I: Minuto, numérico com mínimo 2 dígitos preenchido com 0
- %s: Segundo, numérico
- %S: Segundo, numérico com mínimo 2 dígitos preenchido com 0
- %R: Sinal de mais quando for um período positivo, menos quando negativo
- %r: Sinal de menos quando for um período negativo, vazio quando positivo
$intervalo = new DateInterval('P3Y3WT8M3S');
echo $intervalo->format("%Y anos, %M meses, %D dias")."\n";
echo $intervalo->format("%H horas, %I minutos, %S segundos")."\n";
// 03 anos, 00 meses, 21 dias
// 00 horas, 08 minutos, 03 segundos
Diferença entre datas
Em muitos casos necessitamos saber quanto tempo decorreu entre duas datas. Para obtermos esta informação temos o método diff()
que retorna um objeto DateInterval
com a diferença entre a data do objeto do qual foi chamado o método e do objeto passado por parâmetro.
$data1 = new DateTime('2020-05-10 12:10:42');
$data2 = new DateTime('2020-05-12 19:15:42');
$intervalo = $data1->diff($data2);
echo $intervalo->format("%R%Y anos, %R%M meses, %R%D dias")."\n";
echo $intervalo->format("%R%H horas, %R%I minutos, %R%S segundos")."\n";
// +00 anos, +00 meses, +02 dias
// +07 horas, +05 minutos, +00 segundos
$intervalo = $data2->diff($data1);
echo $intervalo->format("%R%Y anos, %R%M meses, %R%D dias")."\n";
echo $intervalo->format("%R%H horas, %R%I minutos, %R%S segundos")."\n";
// -00 anos, -00 meses, -02 dias
// -07 horas, -05 minutos, -00 segundos
Comparando datas
Uma das comodidades de trabalhar com objetos DateTime
é que torna fácil a comparação entre duas datas, bastando utilizar os operadores (>, <, ==, != ).
$data1 = new DateTime('2020-05-10 12:10:42');
$data2 = new DateTime('2020-05-12 19:15:42');
echo var_dump($data1 < $data2)."\n"; //true
echo var_dump($data1 > $data2)."\n"; //false
echo var_dump($data1 == $data2)."\n"; //false
echo var_dump($data1 != $data2)."\n"; //true
Trabalhando com períodos(sequencias)
Em muitos casos precisamos gerar datas em sequencia espaçadas por um determinado período de tempo. Para isso podemos utilizar a classe DatePeriod
que aceita um dado intervalo de tempo, ou seja, uma data de inicio e uma data de termino, e um intervalo(DateInterval
) e permite iterar entre todas as datas deste período de intervalo em intervalo de tempo. Vejamos um exemplo:
$inicio = new DateTime( '2012-08-01 12:00' );
$termino = new DateTime( '2012-08-01 18:00' );
$intervalo = new DateInterval('PT30M');// 30 min
$periodo = new DatePeriod($inicio, $intervalo ,$termino);
foreach($periodo as $data){
echo $data->format("d/m/Y H:i:s") . "\n";
}
// 01/08/2012 12:00:00
// 01/08/2012 12:30:00
// 01/08/2012 13:00:00
// 01/08/2012 13:30:00
// 01/08/2012 14:00:00
// 01/08/2012 14:30:00
// 01/08/2012 15:00:00
// 01/08/2012 15:30:00
// 01/08/2012 16:00:00
// 01/08/2012 16:30:00
// 01/08/2012 17:00:00
// 01/08/2012 17:30:00
Validando datas
Em muitos casos precisamos verificar se uma data(principalmente informada pelo usuário) é valida antes de fazer qualquer coisa com ela. O PHP possui a função checkdate($mes, $dia, $ano)
para realizar esta verificação, e mesmo assim não é prática pois necessita quebrar um string para passar os valores separados, o que pode ser um problema maior dependendo do formato da data. A classe DateTime
não possui um método de validação de data, mas não é muito difícil criar uma método de validação utilizando createFromFormat
. Vejamos um exemplo:
function validateDateTime($dateStr, $format)
{
$date = DateTime::createFromFormat($format, $dateStr);
return $date && ($date->format($format) === $dateStr);
}
var_dump( validateDateTime('22/11/2020','d/m/Y') );//bool(true)
var_dump( validateDateTime('22/13/2020','d/m/Y') );//bool(false)
var_dump( validateDateTime('30/02/2020','d/m/Y') );//bool(false)
var_dump( validateDateTime('22/11/2020 12:70:00','d/m/Y H:i:s') );//bool(false)
var_dump( validateDateTime('22/11/2020 12:30:00','d/m/Y H:i:s') );//bool(true)
Dado um formato de data e uma data em string, tentamos criar um objeto DateTime
através do método createFromFormat
. Se foi criado um objeto ($date
não é false
) e formatando a data com a mesma string de formato usado para criar o objeto DateTime
for igual ao valor da data inicial quer dizer que o objeto foi criado corretamente, então a data é válida.
Bom pessoal era isso, espero ter ajudado, T++.