Somar meses e calcular idade sem erro: o problema do dia 31
Somar 1 mês a 31 de janeiro não tem resposta óbvia: fevereiro não tem dia 31. O Excel, o relativedelta do Python e a maioria das bibliotecas usam o último dia do mês (28 ou 29 de fevereiro), enquanto o Date do JavaScript e o DateTime do PHP "transbordam" para 3 de março. Este guia mostra a regra, o código que soma meses do mesmo jeito em JavaScript, Python e PHP, e como calcular a idade sem os erros de dividir dias por 365.
O problema: meses têm tamanhos diferentes
Somar dias é simples: cada dia tem o mesmo tamanho. Somar meses não, porque eles têm de 28 a 31 dias. Para a maioria das datas, somar 1 mês mantém o dia: 15 de março mais 1 mês é 15 de abril. O problema aparece nos dias 29, 30 e 31, quando o mês de destino é mais curto.
- Excel (
DATAM), Python (relativedelta), date-fns e Java (plusMonths): 31/01 + 1 mês = 28/02 (ou 29/02 em ano bissexto). O dia é limitado ao último do mês. - JavaScript (
setMonth) e PHP (modify("+1 month")): 31/01 + 1 mês = 03/03. O dia 31 de fevereiro "transborda" para março. - Código Civil, art. 132: um prazo em meses vence no dia de igual número; se ele não existir, no dia seguinte, 1º de março.
Nenhuma é errada em si, mas misturar regras entre o sistema e a planilha gera cobranças em datas diferentes. Escolha uma e use a mesma em todo lugar. A calculadora de somar dias a uma data usa a do último dia do mês.
Somando meses com o último dia do mês
A conta mais segura trabalha com a data como texto AAAA-MM-DD, sem hora e sem fuso: transforma ano e mês em um total de meses, soma, volta para ano e mês e limita o dia ao último do mês de destino.
// Soma meses a uma data AAAA-MM-DD. Se o dia não existe no mês final, usa o último dia dele:
// 31/01 + 1 mês = 28/02 (ou 29/02 em ano bissexto). É a regra do Excel (DATAM) e do relativedelta.
function somarMeses(data, meses) {
const [a, m, d] = data.split('-').map(Number);
const total = a * 12 + (m - 1) + meses;
const ano = Math.floor(total / 12);
const mes = (total % 12) + 1;
const ultimoDia = new Date(Date.UTC(ano, mes, 0)).getUTCDate(); // dia 0 do mês seguinte
const dia = Math.min(d, ultimoDia);
return `${String(ano).padStart(4, '0')}-${String(mes).padStart(2, '0')}-${String(dia).padStart(2, '0')}`;
}
console.log(somarMeses('2026-01-31', 1)); // 2026-02-28
console.log(somarMeses('2024-01-31', 1)); // 2024-02-29
console.log(somarMeses('2026-03-31', -1)); // 2026-02-28 Sem setMonth, que transborda o dia.
import calendar
from datetime import date
def somar_meses(d: date, meses: int) -> date:
"""Soma meses mantendo o dia; se ele não existe no mês final, usa o último dia do mês."""
total = d.year * 12 + (d.month - 1) + meses
ano, mes = divmod(total, 12)
mes += 1
ultimo = calendar.monthrange(ano, mes)[1]
return date(ano, mes, min(d.day, ultimo))
print(somar_meses(date(2026, 1, 31), 1)) # 2026-02-28
print(somar_meses(date(2024, 1, 31), 1)) # 2024-02-29
# Com a biblioteca dateutil, o mesmo resultado: date(2026, 1, 31) + relativedelta(months=1) <?php
// Soma meses mantendo o dia; se ele não existe no mês final, usa o último dia do mês.
// Cuidado: (new DateTime('2026-01-31'))->modify('+1 month') dá 2026-03-03, porque o PHP "transborda" o dia.
function somarMeses(string $data, int $meses): string
{
[$a, $m, $d] = array_map('intval', explode('-', $data));
$total = $a * 12 + ($m - 1) + $meses;
$ano = intdiv($total, 12);
$mes = $total % 12 + 1;
$ultimo = (int) date('t', mktime(0, 0, 0, $mes, 1, $ano)); // dias do mês
return sprintf('%04d-%02d-%02d', $ano, $mes, min($d, $ultimo));
}
echo somarMeses('2026-01-31', 1), PHP_EOL; // 2026-02-28
echo (new DateTime('2026-01-31'))->modify('+1 month')->format('Y-m-d'), PHP_EOL; // 2026-03-03 A segunda linha mostra o transbordo do modify do PHP.
Calculando a idade
O erro mais comum é dividir os dias vividos por 365, ou por 365,25. As duas contas erram perto do aniversário, porque a quantidade de anos bissextos no período varia. A forma certa é subtrair os anos e tirar 1 se o aniversário daquele ano ainda não chegou:
// Idade em anos completos na data de referência (padrão: hoje), com datas no formato AAAA-MM-DD.
function calcularIdade(nascimento, referencia = new Date().toISOString().slice(0, 10)) {
const [an, mn, dn] = nascimento.split('-').map(Number);
const [ar, mr, dr] = referencia.split('-').map(Number);
let idade = ar - an;
// Ainda não fez aniversário neste ano? Tira 1.
// Nesta conta, quem nasceu em 29/02 faz aniversário em 1º/03 nos anos não bissextos.
if (mr < mn || (mr === mn && dr < dn)) idade--;
return idade;
}
console.log(calcularIdade('1990-10-02', '2026-10-01')); // 35
console.log(calcularIdade('1990-10-02', '2026-10-02')); // 36 from datetime import date
def calcular_idade(nascimento: date, referencia: date | None = None) -> int:
"""Idade em anos completos na data de referência (padrão: hoje)."""
referencia = referencia or date.today()
# Ainda não fez aniversário neste ano? A comparação de tuplas resolve: (mês, dia).
# Nesta conta, quem nasceu em 29/02 faz aniversário em 1º/03 nos anos não bissextos.
ainda_nao = (referencia.month, referencia.day) < (nascimento.month, nascimento.day)
return referencia.year - nascimento.year - ainda_nao
print(calcular_idade(date(1990, 10, 2), date(2026, 10, 1))) # 35
print(calcular_idade(date(1990, 10, 2), date(2026, 10, 2))) # 36 Para a idade exata em anos, meses e dias, use a calculadora de idade, e para a distância entre duas datas quaisquer, a de diferença entre datas. As duas contam meses de calendário, com a mesma regra do último dia do mês.
Quem nasceu em 29 de fevereiro
Nos anos não bissextos, o aniversário de quem nasceu em 29 de fevereiro cai em 28 de fevereiro ou em 1º de março, conforme a regra. O código de idade acima, que compara mês e dia, faz a idade mudar em 1º de março, o que coincide com a leitura do Código Civil. Bibliotecas que somam anos com o último dia do mês, como o relativedelta, mudam em 28 de fevereiro. Se a data importa para um direito, como a maioridade, documente qual regra o sistema usa.
Achou um erro neste guia? Avise por e-mail.
Ferramentas para usar junto
- Somar Dias em DatasSome dias corridos, dias úteis, semanas, meses ou anos a uma data e veja o dia da semana do resultado
- Calcular IdadeCalcule a idade exata em anos, meses e dias a partir da data de nascimento, os dias vividos e quanto falta para o próximo aniversário
- Diferença entre DatasCalcule a diferença entre duas datas em anos, meses e dias, e o total em dias, semanas, meses e horas
Perguntas frequentes
Quanto é 31 de janeiro mais 1 mês?
Depende da regra. No Excel e no Python com relativedelta, 28 de fevereiro (ou 29 em ano bissexto). No JavaScript com setMonth e no PHP com modify, 3 de março. Pelo Código Civil, para prazos, 1º de março.
Somar 30 dias é o mesmo que somar 1 mês?
Não. Trinta dias a partir de 1º de fevereiro de 2026 dão 3 de março, e 1 mês dá 1º de março. Contratos costumam dizer qual das duas formas vale.
Por que não usar a diferença em milissegundos para calcular a idade?
Porque dividir o tempo por um ano médio erra no dia do aniversário e perto dele, e o horário de verão pode tirar ou pôr uma hora na conta. Compare ano, mês e dia, como no código do guia.
Outros guias
- Datas e fuso horário em JavaScript: por que sua data muda de diaPor que new Date("2026-01-15") vira dia 14 no Brasil, como mostrar datas no horário de Brasília e como evitar erros de fuso em JavaScript, Python e PHP.
- Como calcular dias úteis com os feriados nacionaisComo calcular dias úteis e prazos com os feriados nacionais do Brasil, incluindo Sexta-feira Santa pela Páscoa e Consciência Negra, em JavaScript, Python e PHP.