Fusos horários em Kotlin: como não enlouquecer com o tempo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Por que o tempo é uma dor (e como Kotlin a trata)

\n\n

Se você já escreveu um aplicativo que atende usuários de diferentes fusos horários, conhece essa dor. A requisição chega com um timestamp, no banco está UTC, o usuário em Nova York reclama que a notificação chegou às 3 da manhã, mas no banco são 8 da manhã. Familiar?

\n\n

Trabalhar com fusos horários é aquela área da programação onde a aparente simplicidade se transforma em dezenas de bugs. Mas Kotlin, herdando as melhores práticas do Java, nos dá um arsenal poderoso para combater o caos. Neste artigo, vamos abordar como trabalhar corretamente com fusos horários em Kotlin, evitar erros típicos e escrever código que não quebre na mudança para o horário de verão.

\n\n

Breve introdução: o que é um fuso horário?

\n\n

Antes de mergulhar no código, vamos alinhar os termos. Um fuso horário (time zone) não é apenas um deslocamento em relação ao UTC. É também um conjunto de regras de transição para horário de verão/inverno (DST). Por exemplo, Europe/Moscow não é apenas +3 horas, mas também mudanças históricas (em 2014, a Rússia parou de ajustar os relógios).

\n\n

No mundo Java/Kotlin, dois conceitos-chave são responsáveis por isso:

\n\n
    \n
  • ZoneId — identificador do fuso horário (ex.: "America/New_York"). Contém todo o histórico de DST.
  • \n
  • ZoneOffset — apenas o deslocamento (ex.: +05:00). Sem regras de transição.
  • \n
\n\n

A diferença é crítica: se você usar ZoneOffset em vez de ZoneId, seu código quebrará no dia da mudança de horário. Lembre-se disso.

\n\n

Classes básicas do java.time em Kotlin

\n\n

Kotlin suporta totalmente o pacote java.time (Java 8+). Se você está escrevendo para Android, não esqueça de adicionar desugaring ou use ThreeTenABP. Para Kotlin no servidor, não há problemas.

\n\n

Instant — momento no tempo

\n\n

Instant é um momento absoluto na linha do tempo. Não está vinculado a um fuso horário. É exatamente isso que você deve armazenar no banco de dados.

\n\n
import java.time.Instant\n\nfun main() {\n    val now = Instant.now()\n    println(now) // 2025-03-15T10:30:00.123Z\n}
\n\n

Observe a letra Z no final — é o horário Zulu, ou seja, UTC. Armazene tudo em UTC — essa é a regra de ouro.

\n\n

ZonedDateTime — hora com fuso horário

\n\n

Quando você precisa mostrar a hora para o usuário, use ZonedDateTime. Ele armazena data, hora, deslocamento e o identificador do fuso.

\n\n
import java.time.ZonedDateTime\nimport java.time.ZoneId\n\nfun main() {\n    val moscowTime = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))\n    println(moscowTime) // 2025-03-15T13:30:00.123+03:00[Europe/Moscow]\n}
\n\n

Viu os colchetes? Isso é o ZoneId, que permite ao sistema processar corretamente o DST.

\n\n

OffsetDateTime — hora com deslocamento

\n\n

Se você não precisa da informação do fuso, mas apenas do deslocamento, use OffsetDateTime. Ele é mais leve e rápido.

\n\n
import java.time.OffsetDateTime\nimport java.time.ZoneOffset\n\nfun main() {\n    val offsetTime = OffsetDateTime.now(ZoneOffset.ofHours(5))\n    println(offsetTime) // 2025-03-15T15:30:00.123+05:00\n}
\n\n

Mas lembre-se: se você salvar um OffsetDateTime para Nova York e depois os EUA entrarem no horário de verão, seu deslocamento ficará desatualizado.

\n\n

Conversão entre fusos horários em Kotlin

\n\n

A tarefa mais comum é pegar a hora do banco (UTC) e mostrá-la no fuso horário do usuário. Vamos ver como fazer isso corretamente.

\n\n

De UTC para o fuso local

\n\n
import java.time.Instant\nimport java.time.ZoneId\nimport java.time.ZonedDateTime\n\nfun convertUtcToLocal(utcInstant: Instant, targetZone: String): ZonedDateTime {\n    return utcInstant.atZone(ZoneId.of(targetZone))\n}\n\nfun main() {\n    val utcNow =                

Blogs

Book Recommendations