Manipulação de datas com DateTime e DateTimeImmutable
1. Introdução às classes DateTime e DateTimeImmutable
Desde o PHP 5.2, o tratamento de datas ganhou duas classes poderosas: DateTime e DateTimeImmutable. A diferença fundamental entre elas está na mutabilidade: enquanto DateTime altera o próprio objeto ao modificar sua data, DateTimeImmutable retorna um novo objeto a cada modificação, preservando o original intacto.
Criar objetos é simples:
$mutavel = new DateTime();
$imutavel = new DateTimeImmutable();
// Com string personalizada
$natal = new DateTime('2024-12-25');
$anoNovo = new DateTimeImmutable('2025-01-01');
O construtor aceita diversos formatos de string, mas para evitar ambiguidades (como 02/03/2024 ser interpretado como 2 de março ou 3 de fevereiro), recomenda-se usar o formato ISO 8601 (YYYY-MM-DD) ou o formato americano (m/d/Y). Para maior controle, utilize DateTime::createFromFormat():
$data = DateTime::createFromFormat('d/m/Y', '31/12/2024');
if (!$data) {
echo 'Formato inválido';
}
2. Métodos essenciais de formatação e exibição
O método format() é a principal ferramenta para exibir datas:
$agora = new DateTime();
echo $agora->format('d/m/Y H:i:s'); // 15/03/2025 14:30:00
echo $agora->format('l, F j, Y'); // Saturday, March 15, 2025
Caracteres de formatação comuns: d (dia com zero), m (mês numérico), Y (ano com 4 dígitos), H (hora 24h), i (minutos), s (segundos), l (nome do dia da semana), F (nome do mês).
Para localização, combine setlocale() com IntlDateFormatter (mais confiável que o antigo strftime):
$fmt = new IntlDateFormatter('pt_BR', IntlDateFormatter::LONG, IntlDateFormatter::NONE);
echo $fmt->format(new DateTime()); // 15 de março de 2025
Trabalhando com fusos horários:
$data = new DateTime('now', new DateTimeZone('America/Sao_Paulo'));
echo $data->format('H:i:s'); // Horário de Brasília
$data->setTimezone(new DateTimeZone('Asia/Tokyo'));
echo $data->format('H:i:s'); // Horário de Tóquio
Timezones comuns: America/Sao_Paulo, Europe/Lisbon, UTC, America/New_York. A lista completa está na documentação oficial de timezones do PHP.
3. Modificação de datas
Adicionando e subtraindo intervalos
$data = new DateTime('2024-01-15');
$data->add(new DateInterval('P10D')); // +10 dias
echo $data->format('Y-m-d'); // 2024-01-25
$data->sub(new DateInterval('P1M')); // -1 mês
echo $data->format('Y-m-d'); // 2023-12-25
Modificando componentes específicos
$data = new DateTime('2024-06-15 10:30:00');
$data->setDate(2025, 12, 31);
$data->setTime(23, 59, 59);
$data->setTimestamp(1704067199); // Timestamp Unix
O poderoso método modify()
$data = new DateTime('2024-01-01');
$data->modify('+1 month');
$data->modify('next monday');
$data->modify('last day of this month');
Expressões aceitas: +2 weeks, first day of January 2025, next thursday, last day of next month. Cuidado com ambiguidades: modify('+1 month') em 31 de janeiro resulta em 2 de março (devido ao fevereiro ter apenas 28 dias).
4. Comparação e diferença entre datas
Objetos DateTime podem ser comparados diretamente com operadores relacionais:
$inicio = new DateTime('2024-01-01');
$fim = new DateTime('2024-12-31');
var_dump($fim > $inicio); // true
var_dump($inicio == $fim); // false
Para calcular diferenças, use diff():
$inicio = new DateTime('2024-01-15');
$fim = new DateTime('2025-03-20');
$intervalo = $inicio->diff($fim);
echo $intervalo->y . ' anos, '; // 1 ano
echo $intervalo->m . ' meses, '; // 2 meses
echo $intervalo->d . ' dias'; // 5 dias
echo $intervalo->days; // 430 dias (total)
O objeto DateInterval retornado possui propriedades: y, m, d, h, i, s, days (total de dias), invert (se a diferença é negativa) e format():
echo $intervalo->format('%y anos, %m meses e %d dias');
// 1 anos, 2 meses e 5 dias
5. DateInterval e DatePeriod para intervalos e períodos
Criando intervalos flexíveis
// Formato ISO 8601: P1Y2M10DT2H30M
$intervalo = new DateInterval('P1Y2M10DT2H30M');
// Ou a partir de string natural
$intervalo = DateInterval::createFromDateString('2 weeks + 3 days');
Iterando com DatePeriod
$inicio = new DateTime('2024-01-01');
$fim = new DateTime('2024-01-31');
$intervalo = new DateInterval('P1D');
$periodo = new DatePeriod($inicio, $intervalo, $fim);
foreach ($periodo as $dia) {
echo $dia->format('d/m/Y') . ' - ' . $dia->format('l') . PHP_EOL;
}
Gerando apenas dias úteis:
$inicio = new DateTime('2024-01-01');
$periodo = new DatePeriod($inicio, new DateInterval('P1D'), new DateTime('2024-01-10'));
$diasUteis = [];
foreach ($periodo as $dia) {
if ($dia->format('N') <= 5) { // 1=segunda, 5=sexta
$diasUteis[] = $dia->format('d/m/Y');
}
}
print_r($diasUteis);
6. Imutabilidade e segurança com DateTimeImmutable
Em aplicações modernas, especialmente com programação funcional ou concorrência, a imutabilidade é uma grande vantagem:
function calculaPrazo(DateTimeImmutable $dataBase): DateTimeImmutable {
return $dataBase->modify('+30 days'); // Retorna NOVO objeto
}
$dataOriginal = new DateTimeImmutable('2024-01-01');
$novoPrazo = calculaPrazo($dataOriginal);
echo $dataOriginal->format('Y-m-d'); // 2024-01-01 (inalterada!)
echo $novoPrazo->format('Y-m-d'); // 2024-01-31
Com DateTime mutável, o mesmo código alteraria o objeto original:
function calculaPrazoMutavel(DateTime $dataBase): DateTime {
return $dataBase->modify('+30 days'); // Altera o original!
}
Encadeamento de métodos com imutabilidade:
$data = (new DateTimeImmutable('2024-01-15'))
->modify('+10 days')
->modify('next monday')
->setTime(9, 0, 0);
echo $data->format('Y-m-d H:i:s'); // 2024-01-29 09:00:00
Conversão entre os tipos:
$mutavel = new DateTime('2024-01-01');
$imutavel = DateTimeImmutable::createFromMutable($mutavel);
$voltaMutavel = DateTime::createFromImmutable($imutavel);
7. Tratamento de erros e validação de datas
Capturando exceções
try {
$data = new DateTime('data-invalida');
} catch (Exception $e) {
echo 'Erro ao criar data: ' . $e->getMessage();
}
// Com createFromFormat, verifique o retorno
$data = DateTime::createFromFormat('Y-m-d', '31/02/2024');
if (!$data || $data->getLastErrors()['warning_count'] > 0) {
echo 'Data inválida ou warnings gerados';
print_r(DateTime::getLastErrors());
}
Validando strings de data
function validarData(string $data, string $formato = 'Y-m-d'): bool {
$d = DateTime::createFromFormat($formato, $data);
return $d && $d->format($formato) === $data;
}
var_dump(validarData('2024-02-29')); // true (ano bissexto)
var_dump(validarData('2023-02-29')); // false
var_dump(validarData('31/12/2024', 'd/m/Y')); // true
A função checkdate() ainda é útil para validação simples:
var_dump(checkdate(2, 29, 2024)); // true
var_dump(checkdate(2, 29, 2023)); // false
Erros comuns a evitar
- Overflow de datas:
modify('+1 month')em 31/01 resulta em 02/03. Usemodify('last day of next month')para evitar. - Timezone inválido: Sempre verifique com
in_array($tz, DateTimeZone::listIdentifiers()). - Formato ambíguo: Prefira
createFromFormat()a strings soltas. - Objetos mutáveis compartilhados: Em funções que recebem DateTime, considere clonar ou usar DateTimeImmutable.
// Solução para overflow de meses
function adicionarMeses(DateTime $data, int $meses): DateTime {
$clone = clone $data;
$clone->modify("+{$meses} months");
return $clone;
}
Dominar DateTime e DateTimeImmutable é essencial para qualquer desenvolvedor PHP que trabalhe com calendários, prazos, agendamentos ou qualquer sistema que dependa de manipulação precisa de datas. A escolha entre mutável e imutável depende do contexto: para objetos que serão compartilhados ou reutilizados, prefira DateTimeImmutable; para operações simples e controladas, DateTime ainda é perfeitamente adequado.
Referências
- Documentação oficial da classe DateTime (PHP.net) — Referência completa com todos os métodos, exemplos e notas sobre a classe DateTime.
- Documentação oficial da classe DateTimeImmutable (PHP.net) — Descrição detalhada da versão imutável, incluindo diferenças e casos de uso.
- Documentação de DateInterval (PHP.net) — Como criar e manipular intervalos de data, formato ISO 8601 e propriedades disponíveis.
- Documentação de DatePeriod (PHP.net) — Iteração sobre períodos de datas, com exemplos práticos de uso.
- Lista de fusos horários suportados pelo PHP (PHP.net) — Catálogo completo de timezones válidos para uso com DateTimeZone.
- PHP: The Right Way - Datas e Horários — Guia de boas práticas para manipulação de datas em PHP, incluindo recomendações sobre imutabilidade.
- Tutorial de DateTime no SitePoint — Artigo técnico com exemplos avançados de manipulação de datas, incluindo cálculos de diferenças e períodos.