2

Somar e subtrair horas em JavaScript sem Date: o truque de converter tudo para segundos

Quase todo sistema de ponto, timesheet ou controle de turnos acaba precisando de uma função boba: somar 07:45 + 01:30, ou descobrir quanto tempo passou entre 22:10 e 05:40. E quase sempre alguém tenta resolver isso com new Date(), fuso horário entra no meio, o horário de verão de algum país aparece, e o bug vai parar em produção.

Para durações (e não datas), existe um jeito bem mais simples: trate tempo como um número inteiro de segundos e só formate no final.

1. Converter "HH:MM:SS" em segundos

function paraSegundos(texto) {
  const partes = texto.trim().split(':').map(Number);
  while (partes.length < 3) partes.push(0); // aceita "HH" e "HH:MM"
  const [h, m, s] = partes;
  if ([h, m, s].some(Number.isNaN) || m > 59 || s > 59) {
    throw new Error(`Horário inválido: ${texto}`);
  }
  return h * 3600 + m * 60 + s;
}

Repare que as horas não têm limite: "40:30" é uma duração válida (uma semana de trabalho, por exemplo). Isso já elimina um monte de gambiarra com o "estouro" das 24h.

2. Formatar de volta

function formatar(totalSegundos) {
  const sinal = totalSegundos < 0 ? '-' : '';
  let t = Math.abs(totalSegundos);
  const h = Math.floor(t / 3600); t %= 3600;
  const m = Math.floor(t / 60);
  const s = t % 60;
  const dd = (n) => String(n).padStart(2, '0');
  return `${sinal}${dd(h)}:${dd(m)}:${dd(s)}`;
}

Manter o sinal é importante: em banco de horas, saldo negativo é informação, não erro.

3. Somar uma lista de durações

const somar = (...tempos) => formatar(tempos.reduce((acc, t) => acc + paraSegundos(t), 0));

somar('07:45', '01:30', '00:50:20'); // "10:05:20"

4. Diferença entre horários que viram a meia-noite

Aqui está o caso que mais quebra código por aí. Se a saída é menor que a entrada, o turno atravessou a meia-noite, então basta somar 24h:

function diferenca(inicio, fim) {
  let d = paraSegundos(fim) - paraSegundos(inicio);
  if (d < 0) d += 24 * 3600;
  return formatar(d);
}

diferenca('22:10', '05:40'); // "07:30:00"

Esse ajuste só vale para turnos menores que 24h. Se você precisa de plantões de 36h, aí sim guarde data + hora e trabalhe com timestamps em UTC.

5. Horas decimais (para folha de pagamento)

Muita planilha de RH quer "7,75 horas" em vez de "07:45". Com segundos isso é só uma divisão:

const decimal = (t) => +(paraSegundos(t) / 3600).toFixed(2);
decimal('07:45'); // 7.75

O erro clássico é fazer 7 + 45/100 e pagar 7,45 horas. São 30 minutos de diferença por dia que viram processo trabalhista no fim do ano.

Por que não usar Date?

  • Date representa um instante, não uma duração;
  • conversões de fuso e horário de verão aparecem onde você não espera;
  • durações acima de 24h não cabem naturalmente em um objeto de data.

Com inteiros você ganha testes triviais (expect(paraSegundos('01:00')).toBe(3600)), serialização fácil no banco e nenhum problema de fuso.

Conferindo os resultados

Quando estou escrevendo testes desse tipo de função, gosto de conferir alguns casos manualmente numa calculadora de tempo em horas, minutos e segundos antes de fixar o valor esperado no teste — principalmente os casos de borda, como somas que passam de 24h e diferenças negativas.

Se alguém tiver uma abordagem diferente para turnos acima de 24h sem recorrer a timestamps, deixa nos comentários que eu quero ver.

— Verse Media

Carregando publicação patrocinada...
1

A condição m > 59 não pega minuto negativo. 07:-5:00 passa no Number.isNaN e vira duração negativa, então o sinal de menos deixa de ser só saldo de banco de horas.