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:

CaractereDescriçãoExemplo
dDia do mês com dois dígitos.01 até 31
jDia do mês sem zero.1 até 31
DRepresentaçã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
NRepresentação numérica ISO-8601 para os dias da semana. Segunda = 1 … Domingo = 71 até 7
wRepresentação numérica para os dias da semana. Domingo = 0 … Sábado = 60 até 6
zDia do ano começando em 00 até 365
WNúmero do ano da semana ISO-860142
FRepresentação textual completa do mêsJanuary, February, etc.
mRepresentação numérica do mês com dois digitos01 até 12
MRepresentação textual curta do mês com três caracteresJan, Feb, … Dec
nRepresentação numérica do mês sem zero1 até 12
tNúmero de dias em um determinado mês28 até 31
YRepresentação do ano com 4 dígitos 2020
yRepresentação do ano com 2 dígitos20
LRepresentação numérica se um ano é bissexto. 1 = bissexto , 0 = não bissexto. 1 ou 0
aRepresentação Ante meridiem e Post meridiem minúscula.am, pm
ARepresentação  Ante meridiem e Post meridiem maiúscula.AM, PM
gHora no formato de 12 horas sem o zero1 até 12
GHora no formato 24 horas sem zero0 até 23
hHora no formato 12 com zero01 até 12
HHora no formato 24 com zero00 até 23
iMinutos com zero00 até 59
sSegundo com zero00 até 59
uMicrossegundos.(a função date sempre retorna 000000, mas pode ser utilizado com a classe DateTime que veremos adiante). Ex.: 654321
vMillisegundo(mesma observação de microssegundos acima)Ex.: 654
eIdentificador de timezoneEx.: UTC, GMT, America/Sao_Paulo,
ODiferença para o “Greenwich time (GMT)” sem dois pontos(“:”) entre as horas e minutos.Ex.: +0200
PDiferença para o “Greenwich time (GMT)” com dois(“:”) pontos entre as horas e minutos.Ex.: +02:00
cData ISO 8601Ex.: 2020-11-07T10:27:43-03:00
rData no formato RFC 2822 Ex.: Sat, 07 Nov 2020 10:28:17 -0300
Tabela caracteres reconhecidos na string de parâmetro de formato de data

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++.