15 de set. de 2025·7 min de leitura

Streaks em apps de hábitos: defina regras e trate fusos horários

Defina regras claras para streaks em apps de hábitos, trate fusos horários e mudanças de horário de verão e evite reinicializações falsas para que os usuários confiem no seu rastreador.

Streaks em apps de hábitos: defina regras e trate fusos horários

Por que os streaks ficam confusos rápido

Streaks parecem simples: faça o hábito todo dia, mantenha a corrente. Na realidade, são emocionais. Quando um streak quebra, as pessoas assumem que falharam — mesmo quando a lógica do app causou a reinicialização. Uma vez perdida essa confiança, é difícil recuperá‑la.

A maior parte da confusão vem de uma pergunta silenciosa que seu app precisa responder: o que conta como um dia?

Se você não definir isso claramente, usuários terão reinicializações-surpresa (“Eu fiz hoje!”) ou contagens duplicadas acidentais (um check‑in tarde da noite, depois outro depois da meia‑noite). Viagens pioram isso. Um usuário faz check‑in às 23:30 em Nova York, voa para Los Angeles e abre o app às 21:15 no horário local. Ele já fez “hoje”? Ou o limite do dia se moveu?

O objetivo não é o sistema de streaks mais puro tecnicamente. É ter regras que pareçam justas, se comportem igual sempre e possam ser explicadas dentro do app em uma ou duas frases.

Se você está construindo com ferramentas de IA, a lógica de streaks é um lugar comum onde casos de borda escapam. Protótipos tendem a cobrir o caminho feliz a menos que você force cenários como viagem, check‑ins tarde, horário de verão e alterações do relógio do dispositivo.

Decida o que significa “dia” no seu app

Streaks só parecem justos quando seu app é explícito sobre o que conta como dia. Se deixar vago, duas pessoas podem fazer a mesma ação e obter resultados diferentes.

Primeiro, escolha o relógio que você segue:

  • Dia local do usuário: uma conclusão vale na data do calendário local atual do usuário. Isso parece natural, mas mudanças de fuso precisam de tratamento cuidadoso.
  • Dia em UTC: uma conclusão conta pela data UTC. É mais fácil de implementar, mas pode parecer errado (check‑ins à noite podem cair em “amanhã”).

Em seguida, escolha um limite de dia. A maioria dos apps usa meia‑noite a meia‑noite no fuso escolhido, mas há alternativas razoáveis:

  • Meia‑noite: familiar e fácil de explicar.
  • Janela de 24 horas rolante: consistente matematicamente, mas confusa porque o “dia” fica sempre se movendo.
  • Corte personalizado (por exemplo 03:00): mais gentil para notívagos e trabalhadores por turnos, desde que seja consistente e bem explicado.

Um corte à meia‑noite pode punir alguém que completa um hábito às 00:30 após um turno tarde. Um corte às 03:00 pode parecer mais humano, mas apenas se for explicado de forma clara.

Escreva a regra como uma única frase que o usuário consiga repetir:

“Seu streak conta uma vez por dia do calendário, e o dia vai das 03:00 às 02:59 no seu fuso horário atual.”

Seja rígido sobre isso ao construir com ferramentas de IA. Muitos protótipos gerados acidentalmente misturam tempo local e UTC, o que leva a reinicializações súbitas que os usuários nunca perdoam.

Defina as regras do streak em linguagem simples

A maioria dos bugs de streak começa como problemas de redação. Se duas pessoas podem ler suas regras e discordar sobre o resultado, seu app vai desapontar alguém.

Decida o que “conclusão” significa:

  • É um check‑in por dia, ou o usuário pode checar várias vezes mas só recebe crédito uma vez?
  • Se o hábito rastreia quantidade (por exemplo, “beba 8 copos”), progresso parcial estende o streak, ou somente um dia totalmente completado conta?

Depois, defina quando o streak começa. Alguns apps começam no momento em que o usuário registra a primeira conclusão. Outros só começam depois que o primeiro dia completo é cumprido. Ambos são aceitáveis. O que importa é consistência entre onboarding, estatísticas e notificações.

Um conjunto padrão claro de regras fica assim:

  • Um dia conta se o hábito for marcado como completo pelo menos uma vez durante esse dia.
  • Progresso parcial é salvo, mas não estende o streak a menos que o hábito alcance 100%.
  • O streak começa no primeiro dia que o hábito é completado.
  • O streak se quebra se passar um dia inteiro sem conclusão.

Por fim, decida quando a UI atualiza. Usuários percebem quando o distintivo de streak diz 7 mas o calendário ainda parece incompleto. Escolha um momento para alterar o valor do streak (frequentemente imediatamente após a conclusão) e use a mesma lógica em todos os lugares.

Escolha um modelo: estrito, flexível ou janela de tolerância

Streaks deixam de ser simples na primeira vez que alguém faz check‑in às 00:05, viaja ou edita o dia anterior. Escolha um modelo cedo, escreva‑o, e trate‑o como decisão de produto (não detalhe de implementação de última hora).

1) Streaks estritos (limpos, mas implacáveis)

Estrito significa: você deve completar o hábito dentro do dia definido, e perder um dia zera o streak. Check‑ins tardios após a meia‑noite contam para o novo dia, não para o anterior.

Esse modelo é fácil de explicar e computar, mas frustra pessoas que fazem hábitos à noite.

2) Streaks flexíveis (melhor para a vida real)

Flexível significa que o app ajuda o usuário a manter o ritmo. Abordagens comuns incluem:

  • Um buffer noturno (por exemplo, check‑ins até 02:00 ainda contam para “ontem”).
  • Um dia de tolerância limitado (por exemplo, um dia perdido por período rolante).

Essas regras reduzem momentos “Eu fiz, mas o app me puniu”.

Trave as decisões que geram disputas

Os conflitos quase sempre vêm do mesmo punhado de escolhas:

  • Check‑ins tardios: contam para o dia anterior ou para o dia atual?
  • Dias perdidos: reset imediato, ou uma regra limitada de “salvar”?
  • Edições/retroativos: não permitidos, permitidos dentro de uma janela curta, ou permitidos com uma flag explícita?
  • Repetições: exatamente uma vez por dia, ou “pelo menos uma vez”?

O que quer que você escolha, mostre‑o na UI para que ninguém seja surpreendido. Uma linha curta de regra sob o número do streak ajuda (“Check‑ins tardios contam até 02:00”). Se uma edição mudaria um streak, mostre uma confirmação em vez de recalcular silenciosamente.

Dados que você precisa armazenar para tornar streaks confiáveis

Um streak só é tão confiável quanto os dados por trás dele. Armazene fatos que não mudam, depois derive chaves de dia e contagens de streak a partir desses fatos.

Comece registrando cada check‑in como um evento. Todo evento deve incluir um timestamp em UTC. UTC é sua verdade base porque continua correto mesmo se o usuário viajar, alterar o relógio do telefone ou cruzar o horário de verão.

Em seguida, armazene o fuso horário do usuário e de onde ele veio. Fuso horário não é só um rótulo — é parte de como você interpreta o mesmo evento UTC como uma data local.

Um conjunto simples de campos que previne a maioria das disputas:

  • checkin_at_utc (timestamp)
  • user_timezone (como America/New_York)
  • timezone_source (device, manual, inferred)
  • local_date_key (opcional, computado na escrita)
  • created_at_utc e created_by (para auditoria)

Você também pode armazenar uma linha de resumo diário indexada por (user_id, habit_id, local_date) se precisar de verificações rápidas de “ele fez hoje?”. Trate‑a como resumo derivado, não como fonte da verdade.

Se permitir edições em dias passados, mantenha um rastro de auditoria. A maioria dos tickets “meu streak quebrou!” vem de preenchimentos silenciosos no passado.

Construa com ferramentas de IA sem perder casos de borda

Deixe a base de código gerenciável
Substitua arquitetura espaguete por código que você pode manter e estender com segurança.

Comece com uma especificação pequena em inglês claro. Mantenha curta, mas específica:

  • O que conta como “feito”
  • Quando um dia começa e termina
  • O que acontece se o usuário viaja
  • Se um check‑in tardio ainda pode contar

Essa especificação vira sua única fonte de verdade.

Ao mandar prompts para sua ferramenta de IA para gerar código, peça um modelo de dados mais um pequeno conjunto de endpoints. Diga para manter a lógica de fuso no servidor e retornar resultados de streak calculados no servidor (não algo “computado na UI”). A UI deve exibir respostas, não inventá‑las.

Gere testes antes de construir a tela do calendário. Cubra:

  • Cruzar a meia‑noite
  • Mudança de fuso horário
  • Mudanças de horário de verão

Faça os testes falharem primeiro, depois implemente até que passem. Isso evita bugs de streak do tipo “funciona na minha máquina”.

Também adicione logging leve ao redor do recálculo de streaks: user ID, habit ID, fuso horário armazenado, chave de dia computada e valor final do streak. Quando alguém reportar “meu streak resetou”, você pode reproduzir o que aconteceu.

Lidando com viagens e mudanças de fuso horário

Viagens são onde os streaks frequentemente parecem injustos. Um usuário faz o hábito, mas o app usa silenciosamente um fuso horário diferente do que ele espera, e o streak vira.

Decida quem controla a regra de fuso. A abordagem mais limpa é torná‑la uma configuração do usuário com um padrão sensato.

Escolha uma regra de viagem (e mantenha‑a)

A maioria dos apps adota uma destas:

  • Seguir o horário local atual: o “dia” é o fuso horário do telefone no momento.
  • Fixar no fuso horário de casa: o “dia” é sempre baseado num fuso escolhido.
  • Override manual: o usuário escolhe um fuso nas configurações, que permanece até mudar.

O horário local faz sentido quando alguém está morando em um lugar novo por semanas. Um fuso de casa evita estranhezas em viagens curtas, onde voos podem criar um “dia” inesperadamente longo ou curto.

Evite desacordos cliente vs servidor (especialmente offline)

Mudanças de fuso podem criar duas respostas: o que o telefone mostra vs o que o servidor aceita depois. Trate cada check‑in como um evento com uma única fonte de verdade.

Uma regra prática: quando o usuário toca “Concluído”, salve tanto (1) o timestamp exato quanto (2) o fuso horário usado para calcular a chave do dia. Se o usuário estiver offline, use a última regra conhecida e não recalcule check‑ins antigos quando o dispositivo reconectar.

Nas configurações, comunique a regra em palavras simples:

“Seu streak se reinicia com base em: Horário local atual”

ou

“Fuso horário de casa: America/New_York”

Adicione um aviso de uma linha: “Mudar isso pode alterar para qual dia seus check‑ins passados contam.”

Horário de verão e dias de calendário estranhos

Envie sem falhas óbvias de segurança
Capturamos segredos expostos e riscos comuns de injeção enquanto limpamos a lógica do app.

O horário de verão cria dias “estranhos”: alguns têm 23 horas (adiantamento) e outros têm 25 horas (atraso). Se sua lógica de streak assumir que todo dia tem exatamente 24 horas, você eventualmente surpreenderá usuários com uma reinicialização ou um dia extra de streak.

A maior armadilha é calcular streaks subtraindo horas de “agora” (por exemplo, “o último check‑in foi dentro de 24 horas”). Em um dia de 23 horas, 24 horas pode te empurrar para “ontem” mesmo que o usuário tenha checado no dia do calendário correto. Em um dia de 25 horas, dois check‑ins que parecem de dias diferentes podem ainda estar “dentro de 24 horas”.

Como evitar bugs de horário de verão

Trate streaks como um problema de calendário, não de matemática. Decida o “dia” do usuário com base num fuso horário e um limite de dia, então compare datas de calendário nesse fuso.

Uma regra simples que sobrevive ao DST:

“Um dia conta se o usuário tiver pelo menos um check‑in entre 00:00 e 23:59:59 no fuso horário escolhido.”

Teste DST de propósito:

  • Dia de adiantamento em pelo menos dois fusos (um que observa DST e outro que não observa)
  • Dia de atraso, incluindo check‑ins em torno da hora repetida
  • Check‑ins pouco antes e pouco depois da meia‑noite

Lembretes em dias de mudança de DST

Lembretes podem parecer “errados” em finais de semana de DST. Escolha um comportamento e mantenha‑o. A maioria dos apps de hábito mantém lembretes no mesmo horário local (8:00 continua 8:00) porque corresponde à expectativa do usuário.

Erros comuns que quebram a confiança no streak

A confiança se quebra quando o calendário não bate com o que seu app conta. A forma mais rápida de perder confiança é quando a UI mostra “Feito hoje” mas o streak ainda cai.

Causas comuns:

  • Contar dias em UTC no servidor enquanto mostra um calendário local
  • Deixar o cliente computar streaks enquanto o servidor armazena a verdade
  • Recalcular streaks em vários lugares (perfil, tela inicial, job em background) com regras ligeiramente diferentes
  • Retroativos ilimitados que tornam streaks sem sentido
  • Testes que cobrem só “semanas normais” e ignoram datas de borda

Retroativos precisam de limites. Uma janela pequena (como “você pode marcar ontem até o meio‑dia de hoje”) pode ser justa. Edições ilimitadas convidam disputas.

Não pule datas de borda nos testes: limites de mês, anos bissextos e horas faltantes/repetidas ao redor do DST.

Checklist rápido antes do lançamento

Faça o comportamento do streak previsível e fácil de explicar. Se um usuário não consegue adivinhar se um check‑in tarde conta, ele vai deixar de confiar no streak.

Aqui está o checklist de pré‑lançamento que pega a maioria dos problemas:

  • Uma regra de uma frase que define o que “conta como hoje”, mostrada na UI
  • Check‑ins armazenados em UTC, mais o fuso horário do usuário no momento do check‑in
  • Uma única fonte de verdade para o cálculo de streak (uma função ou serviço)
  • Testes para viagem, DST, check‑ins offline, retroativos e atividade tarde perto do corte
  • Consistência entre dispositivos: a UI bate com o resultado do servidor mesmo se o relógio do dispositivo estiver errado

Teste um cenário simples: um usuário faz check‑in às 23:58, depois novamente às 00:05. Seu app deve se comportar igual no mobile e na web, e o streak deve atualizar do mesmo jeito após um refresh.

Exemplo: viajar sem zerar o streak

Pare reinicializações surpresa de streak
Se seu protótipo mistura UTC e horário local, podemos diagnosticar e reparar.

Um usuário está em Los Angeles. Na segunda à noite ele completa o hábito às 23:50 no horário local e vê: “Streak: 12 dias. Hoje está feito.”

Ele voa para Nova York e chega tarde. Depois da meia‑noite, abre o app e o telefone já está no horário do leste.

Se sua regra for “o dia é baseado na data local atual do usuário” (a expectativa mais comum), segunda ainda deve aparecer como completa naquela noite. Na manhã seguinte em Nova York, terça é o novo dia e permanece vazia até que ele faça check‑in.

Se ele fizer check‑in de novo às 00:10 em Nova York, você precisa decidir o que acontece:

  • Um modelo estrito diz que conta para terça, então o streak vira 13.
  • Uma janela de tolerância pode oferecer uma escolha (“Contar para segunda” vs “Contar para terça”), mas só se você puder explicar isso claramente.

Seja o que for, torne‑o depurável. Quando o suporte receber um “meu streak resetou”, eles devem poder ver:

  • O timestamp em UTC e o horário local do usuário
  • O fuso horário usado para a decisão (incluindo offset)
  • A chave de dia computada (como 2026-01-21)
  • Se uma janela de tolerância foi aplicada
  • O dia final ao qual o check‑in foi creditado

Próximos passos para deixar um rastreador de hábitos gerado por IA pronto para produção

Trate a lógica de streak como lógica de cobrança: detalhes pequenos importam, e edições futuras podem quebrá‑la.

Escreva uma especificação de uma página que você possa colar em prompts e compartilhar com um colega. Mantenha em linguagem simples. Inclua o limite do dia, o que conta como “feito” e o que acontece quando alguém perde um dia.

Depois proteja isso com um pequeno conjunto de testes (você não precisa de 200 testes). Escolha 6 a 10 que cubram check‑ins perto da meia‑noite, resets por dia perdido, troca de fuso, DST e edição/retroativo (se permitir).

Se você herdou um protótipo gerado por IA onde o tratamento de tempo é inconsistente, a autenticação está quebrada ou o código é difícil de entender, FixMyMess (fixmymess.ai) pode rodar uma auditoria de código gratuita e ajudar a diagnosticar e reparar a lógica subjacente para que você possa lançar com confiança.