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.

Por Leonardo Gaertner · Publicado em

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.

JavaScript
// 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.

Python
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
<?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:

JavaScript
// 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
Python
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.

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.