PHP

IntlDateFormatter: data por extenso em pt-BR sem array de meses

Toda vez que preciso imprimir “31 de agosto de 2026” aparece de novo aquele array com os doze meses no meio do template. Dá para apagar essa herança: a extensão intl já sabe escrever data em português, e o IntlDateFormatter aceita um pattern seu no lugar dos formatos prontos.

$tz  = new DateTimeZone('America/Sao_Paulo');
$fmt = new IntlDateFormatter(
    'pt_BR',
    IntlDateFormatter::NONE,
    IntlDateFormatter::NONE,
    $tz,
    IntlDateFormatter::GREGORIAN,
    "d 'de' MMMM 'de' y"
);

echo $fmt->format(new DateTimeImmutable('2026-08-31', $tz));
// 31 de agosto de 2026

As aspas simples dentro do pattern marcam texto literal do ICU, por isso o “de” sai como palavra e não como código de campo. MMMM é o mês por extenso, MMM abrevia para “ago.” (com ponto, em pt-BR) e EEEE traz o dia da semana. Os dois NONE no meio existem porque, com pattern explícito, os estilos prontos de data e hora ficam fora do jogo. O mês vem minúsculo: se precisar de “Agosto” no começo da frase, resolva com mb_convert_case() na saída, não trocando o pattern.

A pegadinha do fuso

date_default_timezone_get();  // UTC na maioria dos servidores

$fmt->format(new DateTimeImmutable('2026-08-31'));
// 30 de agosto de 2026  <- um dia a menos

$fmt->format(new DateTimeImmutable('2026-08-31', $tz));
// 31 de agosto de 2026

Data sem hora vira meia-noite no fuso padrão do PHP. Se o servidor roda em UTC e o formatter converte para São Paulo, aquelas três horas a menos jogam o resultado para o dia anterior. Passe o mesmo DateTimeZone na criação e na formatação e o problema some. Vale o mesmo cuidado ao formatar um post_date vindo do MySQL, que é hora local sem fuso nenhum embutido.

Antes de subir isso para produção rode php -m | grep intl. Sem a extensão instalada a classe nem existe e o deploy quebra com Error: Class “IntlDateFormatter” not found.

Post Recentes

register_post_meta com show_in_rest: campo personalizado no /wp-json

O meta está no banco mas some do /wp-json/wp/v2/posts. Veja o register_post_meta certo, com single, type, schema de array e…

4 horas atrás

wp_postmeta lento: índice em meta_key e meta_value para meta_query

Listagem de CPT com meta_query que demora segundos: veja o EXPLAIN da query do WP_Query e o índice composto em…

3 dias atrás

WP-Cron travado: eventos agendados que não rodam no WordPress 6.9

Post com Perdeu o agendamento e backup que não roda há dias: inspecione a fila do WP-Cron, apague a trava…

3 dias atrás

Desligar o AMP no WordPress 6.9: redirect do /amp/ sem perder tráfego

Como aposentar o plugin AMP sem transformar centenas de URLs /amp/ indexadas em 404: o rewrite 301 no nginx, a…

4 dias atrás

Redis OOM Magento 2: ajustar a maxmemory-policy e separar os bancos

A loja cai com OOM command not allowed when used memory > maxmemory: ajuste a maxmemory-policy do Redis e separe…

4 dias atrás

Checkbox de privacidade no checkout do WooCommerce: texto e registro do aceite

Adicione a caixa de aceite no checkout do WooCommerce e grave data e versão da política como meta do pedido,…

5 dias atrás