Formatando datas em português brasileiro na prática
Você já tentou transformar uma data como 01/09/2024 em "1 de setembro de 2024" num projeto e descobriu que simplesmente chamar uma função de formatação não funciona tão bem quanto parece. O problema é que muitas bibliotecas e frameworks tratam o locale pt_BR de forma inconsistente. O resultado típico é uma data misturada ou um erro silencioso que só aparece em produção.
Como exibir dia primeiro de setembro corretamente em código
A abordagem mais direta depende da stack que você está usando. Vou cobrir os cenários mais comuns porque cada um tem suas armadilhas específicas. No Python, o módulo datetime nativo não formata para português por padrão. Ele vai te devolver "Sep 1, 2024" ou algo similar, o que não serve se o público-alvo for brasileiro. A solução é usar a biblioteca babel, que é robusta e lida bem com variações regionais:
instale com pip install babel, depois importe DateFormat e normalize a data: from babel.dates import DateFormat from datetime import date d = date(2024, 9, 1) print(DateFormat('MMMM', locale='pt_BR').format(d))
Isso imprime "setembro". Para a forma completa com o dia, use o token d MMMM de yyyy. O resultado seria algo como "1 de setembro de 2024". Simples, mas há um detalhe que costuma pegar todo mundo: o babel funciona com datas já construídas como objetos, não com strings. Se você recebe a data como string "01/09/2024" de um formulário ou API, precisa converter primeiro com datetime.strptime(data_str, "%d/%m/%Y") antes de passar para o formatador. Pular esse passo gera o erro mais comum que vejo em threads de desenvolvimento. No JavaScript, a história é diferente. O método nativo toLocaleDateString com as opções corretas resolve:
const d = new Date(2024, 8, 1); console.log(d.toLocaleDateString('pt-BR', { day: 'numeric', month: 'long', year: 'numeric' })); O resultado é "1 de setembro de 2024". Note o detalhe importante: o mês 8 no construtor Date corresponde a setembro porque os meses começam do zero. Esse off-by-one é a causa número um de datas erradas em projetos JavaScript. Se você passar 9 em vez de 8, vai exibir "outubro" e levará meia hora pra descobrir onde errou.
Uma versão mais limpa e menos propensa a erro é criar a data a partir de uma string ISO: const d = new Date('2024-09-01'); console.log(d.toLocaleDateString('pt-BR', { day: 'numeric', month: 'long', year: 'numeric' }));
Assim você evita completamente a confusão dos índices de mês e ainda garante que o fuso horário não interfira. A string ISO 8601 é interpretada como UTC pelo padrão, o que elimina surpresas de horário. No PHP, a função strftime foi depreciada nas versões recentes. O recomendado é usar o IntlDateFormatter, que é a classe do extension intl:
$d = new DateTime('2024-09-01'); $fmt = new IntlDateFormatter( 'pt_BR', IntlDateFormatter::LONG, IntlDateFormatter::NONE, 'America/Sao_Paulo', IntlDateFormatter::GREGORIAN ); echo $fmt->format($d); O resultado depende do padrão LONG, que em pt_BR gera algo como "1 de setembro de 2024". Se quiser controle mais fino, passe IntlDateFormatter::MEDIUM no lugar e o formato muda ligeiramente. O terceiro parâmetro, o fuso horário, é crucial. Deixar como null pode fazer a data renderizar com uma hora a mais ou a menos dependendo do servidor, o que quebra agendamentos e relatórios gerenciais.
Edge case que quase destruiu um lançamento
Num projeto real, eu me deparei com um bug onde datas em setembro eram formatadas corretamente em desenvolvimento mas exibiam "septiembre" em produção. O ambiente de produção rodava na América/Mexico_City em vez de America/Sao_Paulo. O locale pt_BR existe no PHP como um alias, mas o comportmento do IntlDateFormatter varia com o fuso. A correção foi explicitar o fuso horario no construtor e adicionar uma verificacao de validacao nos testes automatizados que checa se a string formatada contem "setembro" e nao "septiembre". Sem esse teste, o erro passaria despercebido ate o cliente final notar.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Alternativas quando a formatação manual nao funciona
Se voce esta num ecossistema que nao tem bibliotecas de internacionalizacao confiaveis, ha alternativas praticas. Uma delas é construir um array de nomes manualmente: const meses = [ 'janeiro', 'fevereiro', 'marco', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro' ]; function formatarData(data) { return `${data.getDate()} de ${meses[data.getMonth()]} de ${data.getFullYear()}`; }
Isso funciona em qualquer linguagem e nao depende de extensoes externas. A desvantagem é que voce perde a flexibilidade de alterar o formato sem tocar no codigo. Se depois o negocio mudar e quiser "01 de setembro de 2024" no lugar, precisa atualizar a string. Tambem nao lida com pluralizacao automatica nem com variacoes regionais como "1º de setembro" versus "1 de setembro". Outra alternativa valida e usar uma biblioteca como date-fns no JavaScript, que tem o modulo format com tokens customizaveis e locale pre-configurado para pt_BR:
import { format } from 'date-fns'; import { ptBR } from 'date-fns/locale'; const d = new Date('2024-09-01'); console.log(format(d, "d 'de' MMMM 'de' yyyy", { locale: ptBR })); O resultado é exatamente "1 de setembro de 2024". A biblioteca date-fns é leve, tree-shakeable e evita dependências pesadas como a jQuery UI datepicker oumoment.js, que já está em manutenção somente de correções de bugs críticos. Se seu projeto ainda usa moment.js, convém migrar — a curva de migração é baixa e você ganha consistência de comportamento entre ambientes.
O que geralmente dá errado e como evitar
Aqui vão os problemas que vejo com mais frequência na prática, listados em ordem de irritação: primeiro, a conversao de string para data. Formularios web frequentemente enviam datas no formato americano MM/DD/YYYY. Se voce passar essa string direto para um construtor de data sem validar, setembro vira janeiro e janeiro vira setembro. Sempre defina o formato explicitamente ao fazer o parse.
segundo, fusos horarios. Datas sao especialmente sensiveis a isso porque uma transicao de fuso pode fazer uma data de 1 de setembro em Sao Paulo virar 31 de agosto em Londres. Se seu sistema processa dados de usuarios globais, armazene sempre em UTC e converta para o fuso local apenas na exibicao. Nunca armazene a data ja formatada em portuguÊs no banco de dados — guarde o valor bruto e formate sob demanda. terceiro, meses abreviados versus nomes completos. Em algumas interfaces você precisa de "Set" em vez de "setembro". As bibliotecas modernas permitem isso passando o estilo 'short' ou 'narrow' nas opções. Confundir esses dois levels gera inconsistências visuais que parecem amadoras em qualquer sistema profissional.
quarto, legado. Se voce está mantendo um sistema antigo que usava strftime com %B para o nome do mês, saiba que em servidores com glibc mais recente esse formato pode variar. O mesmo código que funcionava num Debian antigo pode falhar numa versão nova. O jeito seguro é testar em todos os ambientes de deploy antes de confiar cegamente na saida do strftime. quinto, caching de resultados. Numa aplicação web, é comum caches de template ou views. Se você cacheia uma view que contem uma data formatada em português, o cache pode ser servido com a formatação errada para um usuario de outro fuso. Use chaves de cache que incluam o locale do usuario, ou melhor ainda, formate as datas no momento da renderizacao, nunca antes.
Referência rapida de tokens
Para consulta pratica, aqui estao os tokens mais uteis que voce vai precisar: d — dia do mes sem zero à esquerda (1, 2, ..., 30) dd — dia com zero à esquerda (01, 02, ..., 30) M — mês numerico sem zero à esquerda (1 a 12) MM — mês numerico com zero à esquerda (01 a 12) MMMM — nome completo do mês em português MMM — abreviacao do mês (set) yyyy — ano completo yy — ano com dois digitos
Combinando esses tokens voce cobre 95% dos casos. O restante 5% envolve formatos especiais como "1º de setembro" com ordinal, que requer logica personalizada em praticamente qualquer linguagem, pois nenhuma biblioteca padroniza ordinais em português de forma uniforme.
Um caso especifico: ordinais em datas
Em português brasileiro, o formato "1º de setembro" é comum em documentos oficiais e materiais impressos. Nenhuma biblioteca mainstream gera isso automaticamente. A solucao mais pratica é criar uma funcao pequena que trata o dia 1 de forma especial: function ordinalDia(dia) { if (dia === 1) return dia + 'º'; return dia.toString(); } function formatarComOrdinal(data) { return `${ordinalDia(data.getDate())} de ${meses[data.getMonth()]} de ${data.getFullYear()}`; }
Essa funcao funciona para qualquer data e pode ser incorporada num helper global do projeto. A unica limitação é que ela não lida com dias compostos como "11º" ou "21º" no plural — se precisar disso, basta estender a lógica com uma condicao para os dias 11, 21, 31 usarem "º" tambem, mas com pluralizacao no sufixo quando aplicavel. Na prática, a maioria dos sistemas só precisa do formato básico "1 de setembro de 2024". Ir além disso deve ser motivado por requisitos reais do negocio, nao por perfeccionismo. Formatacao de data parece simples até o dia em que um relatório fiscal sai com o mes errado porque alguem passou 8 em vez de 9 no construtor de data.