08 de agosto de 2026 · 6 min de leitura
Por que a data de vencimento nunca deveria depender do fuso horário (e como isso quebra silenciosamente)

Se você já cobra clientes de forma recorrente — mensalidade de gestão de tráfego, plano fixo, retainer — provavelmente já viu isso acontecer em algum sistema: um boleto ou cobrança que aparece com vencimento em "29" quando devia ser "30", ou um resumo diário por e-mail que mostra a data errada por um dia. Não é falha de digitação. Na maioria das vezes é um bug de fuso horário — e ele é traiçoeiro porque só aparece perto da meia-noite.
O bug que a gente encontrou (e corrigiu) no nosso próprio sistema
No igency, o resumo diário por e-mail (o "digest") estava mostrando o vencimento de pagamentos e tarefas um dia antes do real. A causa: a data de vencimento é salva internamente como meia-noite UTC representando uma data de calendário (por exemplo, "10 de agosto" vira 2026-08-10T00:00:00Z, sem hora local nenhuma — é só uma data, não um instante). O código do e-mail, porém, convertia essa data para o fuso America/Sao_Paulo antes de formatar. Como o Brasil está a 3 horas atrás de UTC (e não tem mais horário de verão), meia-noite UTC vira 21h do dia anterior no horário de Brasília — e a formatação exibia esse dia anterior.
O resultado prático: todo cliente que recebia o resumo diário por e-mail via nosso digest recebia a data de vencimento errada, sempre um dia adiantado. Silencioso, consistente, e fácil de não notar — porque "um dia de diferença" parece só um pequeno atraso, não um bug.
A correção teve que separar dois conceitos que o código tratava como um só:
- Datas-calendário (vencimento, data de competência) — nunca devem passar por conversão de fuso. Formatar direto em UTC (formatDateUTC()).
- Instantes reais (quando um webhook do Stripe chegou, quando um pagamento foi confirmado) — esses sim fazem sentido em horário local, porque representam um momento específico do relógio.
Essa não foi a primeira vez que esse padrão apareceu no nosso código — é a mesma classe de bug que já tínhamos corrigido em outros três pontos do sistema antes. Fuso horário é, sem dúvida, uma das categorias de bug mais recorrentes em qualquer sistema de cobrança — porque o erro só se manifesta perto da virada do dia, e é fácil um teste manual "passar" só por não ter sido rodado às 21h-23h59.
Um segundo caso: recorrência que "encolhe" com o tempo
Outro bug relacionado, também de calendário: nossa rotina de despesas recorrentes lia o dia de recorrência a partir da última ocorrência já gerada. Isso parece razoável até você lembrar que fevereiro tem 28 dias. Uma despesa recorrente ancorada no dia 31 gera, em fevereiro, uma ocorrência no dia 28 (o clamp correto). Só que, se o sistema usa essa ocorrência de fevereiro como referência pro próximo mês, o dia 28 vira permanente — março, que tem 31 dias, também fica preso no 28, mesmo sem precisar.
A correção: usar sempre o maior dia-do-mês já visto em qualquer ocorrência da série, nunca a última gerada. Pequena mudança de lógica, grande diferença pra quem depende da data certa pra cobrar ou pagar uma despesa recorrente todo mês.
Como o igency resolve isso na prática
No igency, essa separação entre data-calendário e instante virou regra de implementação: toda data de vencimento ou competência é salva como meia-noite UTC (um dia de calendário, sem hora local) e formatada direto nessa base, sem conversão de fuso antes de virar texto. Isso aparece em dois pontos que você usa no dia a dia:
- O campo Vencimento — na tela de Pagamentos, em Novo Pagamento / Cobrança, o campo Vencimento pede apenas o dia de calendário (ex.: 10/08). É esse mesmo dia que aparece na listagem de pagamentos e no resumo diário por e-mail: no código do digest, a data é formatada com
formatDateUTC()(emlib/email.ts), que lê o dia direto em UTC em vez de convertê-lo pro horário local. Se você definiu o vencimento como dia 10, o cliente vê "10" no e-mail — esteja você ou ele em qualquer fuso horário. - Despesas recorrentes — no formulário de despesas, o tipo Mensal pede apenas o dia do mês (a âncora da recorrência), e a rotina que gera as próximas ocorrências (
app/api/cron/recurring-expenses/route.ts) usa sempre o maior dia-do-mês já visto na série como referência. Se a despesa foi ancorada no dia 31, fevereiro gera a ocorrência no dia 28 (o ajuste correto pra um mês mais curto), mas março volta a gerar no dia 31 — o "dia 28" não vira permanente.
O efeito prático pra quem cobra: a data que aparece na cobrança e no lembrete é sempre a data que você definiu — nunca "um dia antes" porque o sistema formatou a meia-noite UTC como 21h do dia anterior.
O que isso significa pra quem gerencia clientes e cobranças
Se você usa (ou está avaliando) qualquer ferramenta pra gestão de clientes e cobrança recorrente, vale perguntar: as datas de vencimento são tratadas como datas de calendário ou como timestamps com fuso? A diferença parece técnica, mas o efeito é muito prático — é a diferença entre um cliente receber a cobrança certa no dia certo, ou uma notificação de atraso um dia antes da hora.
No igency, depois de mapear esse padrão nos dois casos acima (e nos três anteriores), tratamos qualquer data-de-calendário nova no sistema — vencimento, competência, recorrência — com o mesmo cuidado: nunca passa por conversão de fuso antes de virar texto. É um detalhe pequeno de implementação que evita um problema grande de confiança: ninguém quer explicar pro cliente por que o vencimento "mudou" sozinho.
Quer cobrar na data certa sem depender de um sistema que "adianta" o vencimento?
Vencimento que aparece um dia antes no e-mail, recorrência que "encolhe" no mês mais curto — esse tipo de imprecisão não parece um bug até o dia em que o cliente recebe o aviso errado e a relação começa a desgastar. No igency, toda data de vencimento ou competência é tratada como dia de calendário, formatada direto em UTC e nunca passa por conversão de fuso: o dia que você definiu é o dia que o cliente vê, em qualquer fuso horário.
Se você tem até 3 clientes, o plano Free já inclui pagamentos e despesas recorrentes com essa confiabilidade, sem limite de tempo e sem cartão de crédito. Se sua agência já passou disso, o Pro libera clientes ilimitados por R$19,90/mês (ou R$199/ano) — com 30 dias de teste grátis antes da primeira cobrança: o cartão é pedido no cadastro, mas nada sai até o trial terminar, e você cancela quando quiser enquanto isso. Cobrança certa no dia certo, todo mês.
Gerencie clientes, tarefas e cobrança sem planilha
Grátis para até 3 clientes. No Pro, 30 dias de teste antes da primeira cobrança.
Testar o igency Pro grátis →