Datas e horas são fundamentais em quase todo sistema web, desde a simples exibição da data atual até o cálculo de prazos, agendamentos e fusos horários. O PHP oferece um conjunto robusto de classes para lidar com esses conceitos, sendo as principais DateTime e DateTimeImmutable. Nesta aula, você vai dominar a criação, manipulação, formatação e comparação de datas, além de entender como trabalhar com intervalos e fusos horários de forma eficiente.

Vamos começar com uma visão geral das classes, depois mergulhar em formatação, intervalos e fusos. Ao final, você terá uma base sólida para implementar funcionalidades relacionadas a tempo em seus projetos.

DateTime e DateTimeImmutable

As classes DateTime e DateTimeImmutable representam um ponto específico no tempo. A diferença crucial é que DateTime é mutável, ou seja, métodos como modify() alteram o objeto original. Já DateTimeImmutable retorna um novo objeto com as alterações, preservando o original. Isso evita efeitos colaterais e é recomendado para evitar bugs.

Para criar uma instância, basta chamar o construtor com uma string de data/hora. Se nenhum argumento for passado, a data/hora atual é usada. Veja exemplos:

$date = new DateTime(); // data e hora atual
$date2 = new DateTime('2025-03-15 10:30:00');
$immutable = new DateTimeImmutable('now'); // imutável

echo $date->format('Y-m-d H:i:s'); // ex.: 2025-03-15 10:30:00
echo $immutable->format('Y-m-d'); // ex.: 2025-03-15

Métodos comuns incluem format(), modify(), setDate(), setTime(), getTimestamp() e diff(). Com DateTimeImmutable, lembre-se de sempre capturar o retorno dos métodos que modificam o objeto:

$immutable = new DateTimeImmutable('2025-03-15');
$newImmutable = $immutable->modify('+1 day');
echo $immutable->format('Y-m-d'); // 2025-03-15
echo $newImmutable->format('Y-m-d'); // 2025-03-16

Use DateTimeImmutable sempre que possível, especialmente em funções que recebem datas como parâmetro, para garantir que o valor original não seja alterado.

Formatação

A formatação é feita com o método format(), que aceita uma string de formato com caracteres especiais. Os mais comuns são: d dia com dois dígitos, m mês com dois dígitos, Y ano com quatro dígitos, H hora 24h, i minutos, s segundos. Você pode combinar esses caracteres com separadores arbitrários.

$date = new DateTime('2025-03-15 10:30:00');
echo $date->format('d/m/Y'); // 15/03/2025
echo $date->format('H:i'); // 10:30
echo $date->format('l, j \d\e F \d\e Y'); // sábado, 15 de março de 2025

Note que para caracteres literais no formato, é preciso escapá-los com uma barra invertida. Para uma lista completa, consulte a documentação oficial.

Outra forma de formatar é usando strftime(), mas ela está obsoleta desde o PHP 8.1. Prefira format().

Para exibir datas em português, você pode usar IntlDateFormatter ou manipular o formato com arrays de nomes. Exemplo com IntlDateFormatter:

$date = new DateTime('2025-03-15');
$formatter = new IntlDateFormatter('pt_BR', IntlDateFormatter::LONG, IntlDateFormatter::NONE);
echo $formatter->format($date); // 15 de março de 2025

Isso requer a extensão intl, que geralmente vem habilitada.

Intervalos

Intervalos representam uma duração de tempo, como '1 dia', '2 horas', '3 meses'. A classe DateInterval é usada com add() e sub() para modificar datas, e diff() para calcular a diferença entre duas datas.

Para criar um intervalo, use o construtor com uma string no formato ISO 8601, como 'P1D' para 1 dia, 'PT1H' para 1 hora, 'P2M' para 2 meses. Exemplos:

$date = new DateTime('2025-03-15');
$interval = new DateInterval('P1D'); // 1 dia
$date->add($interval);
echo $date->format('Y-m-d'); // 2025-03-16

Para calcular a diferença entre duas datas, use diff():

$date1 = new DateTime('2025-03-15');
$date2 = new DateTime('2025-03-20');
$diff = $date1->diff($date2);
echo $diff->days; // 5
echo $diff->format('%d dias'); // 5 dias

O objeto DateInterval tem propriedades como y, m, d, h, i, s, e days (total de dias). Cuidado: days só é preenchido quando o diff é feito entre duas datas, não em intervalos criados manualmente.

Para criar intervalos mais complexos, você pode usar DateInterval::createFromDateString():

$interval = DateInterval::createFromDateString('2 weeks + 3 days');
echo $interval->format('%d dias'); // 17 dias (2 semanas = 14 dias + 3)

Fusos horários

Fusos horários são essenciais para aplicações que lidam com usuários em diferentes regiões. O PHP usa a base de dados da IANA (Olson). Você pode definir o fuso horário no php.ini ou dinamicamente com date_default_timezone_set().

Ao criar um objeto DateTime, você pode especificar o fuso horário no construtor ou com setTimezone():

$date = new DateTime('2025-03-15 10:00:00', new DateTimeZone('America/Sao_Paulo'));
echo $date->format('Y-m-d H:i:s P'); // 2025-03-15 10:00:00 -03:00

// Convertendo para outro fuso
$date->setTimezone(new DateTimeZone('UTC'));
echo $date->format('Y-m-d H:i:s P'); // 2025-03-15 13:00:00 +00:00

Para listar todos os fusos suportados, use DateTimeZone::listIdentifiers().

Uma boa prática é armazenar datas em UTC no banco de dados e converter para o fuso do usuário apenas na exibição. Isso evita problemas com horário de verão e mudanças de fuso.

Exemplo de conversão:

$utc = new DateTime('2025-03-15 10:00:00', new DateTimeZone('UTC'));
$utc->setTimezone(new DateTimeZone('America/Sao_Paulo'));
echo $utc->format('Y-m-d H:i:s'); // 2025-03-15 07:00:00

Lembre-se de que alguns fusos têm horário de verão, e o PHP já lida com isso automaticamente.

Boas práticas e observações finais

Sempre prefira DateTimeImmutable para evitar mutações inesperadas. Use o fuso horário UTC para armazenamento e converta para o fuso do usuário na exibição. Ao formatar datas para usuários, considere usar IntlDateFormatter para localização. Evite usar timestamps inteiros quando precisar de operações complexas, pois as classes DateTime são mais expressivas.

Referências

Exercícios

  1. Crie um objeto DateTimeImmutable para a data '2025-12-31 23:59:59' e exiba o dia da semana em português.
  2. ✓ Resposta:
    $date = new DateTimeImmutable('2025-12-31 23:59:59');
    $formatter = new IntlDateFormatter('pt_BR', IntlDateFormatter::LONG, IntlDateFormatter::NONE);
    echo $formatter->format($date); // quarta-feira, 31 de dezembro de 2025
  3. Calcule a diferença entre '2025-01-01' e '2025-12-31' e exiba o número de dias.
  4. ✓ Resposta:
    $start = new DateTime('2025-01-01');
    $end = new DateTime('2025-12-31');
    $diff = $start->diff($end);
    echo $diff->days; // 364
  5. Adicione 3 meses e 10 dias à data '2025-03-15' e exiba o resultado.
  6. ✓ Resposta:
    $date = new DateTime('2025-03-15');
    $date->add(new DateInterval('P3M10D'));
    echo $date->format('Y-m-d'); // 2025-06-25
  7. Converta a data/hora '2025-03-15 10:00:00' do fuso 'America/Sao_Paulo' para 'UTC' e exiba.
  8. ✓ Resposta:
    $date = new DateTime('2025-03-15 10:00:00', new DateTimeZone('America/Sao_Paulo'));
    $date->setTimezone(new DateTimeZone('UTC'));
    echo $date->format('Y-m-d H:i:s'); // 2025-03-15 13:00:00
  9. Crie um objeto DateTime a partir de um timestamp (ex.: 1742000000) e exiba a data formatada.
  10. ✓ Resposta:
    $timestamp = 1742000000;
    $date = new DateTime('@' . $timestamp);
    echo $date->format('Y-m-d H:i:s'); // 2025-03-15 00:53:20 (depende do fuso)