Instant — uma classe imutável do pacote java.time, representando um ponto na linha do tempo em UTC com precisão de nanossegundos. Ao contrário de LocalDateTime, Instant não contém data e hora em formato legível por humanos — é uma representação de máquina de um momento. De acordo com a especificação Oracle Java 17 (2024), Instant foi projetado para troca de máquina de timestamps e é um análogo de System.currentTimeMillis(), mas com precisão de nanossegundos.
Pontos Principais
Instant é uma classe que modela um único ponto na linha do tempo. Sua representação interna consiste em dois campos: long seconds (o número de segundos desde 1970-01-01T00:00:00Z) e int nanos (nanossegundos dentro do segundo atual, de 0 a 999999999).
O intervalo de valores de Instant é de -31557014167219200 a 31556889864403199 segundos desde a época, cobrindo aproximadamente 292 milhões de anos em ambas as direções. Isso é suficiente para qualquer tarefa prática, incluindo cálculos astronômicos.
De acordo com Baeldung (2024), Instant é uma ponte entre tipos legíveis por humanos (LocalDateTime, ZonedDateTime) e formatos de máquina (timestamp em milissegundos). Instant é usado para registro, cache, sincronização e todas as tarefas onde um momento absoluto no tempo é importante.
A classe implementa as interfaces Comparable (para comparar momentos) e Temporal (para uso na API comum java.time). Instant é imutável — todos os métodos retornam um novo objeto.
Antes do Java 8, java.util.Date e System.currentTimeMillis() eram usados para trabalhar com momentos no tempo. Ambas as abordagens têm desvantagens. Date é mutável, não thread-safe, armazena tempo em milissegundos desde a época, mas seus nomes de métodos são desatualizados (getYear() retorna 116 para 2016).
Long (um timestamp simples) é rápido e compacto, mas não tem suporte integrado para nanossegundos, não é exibido em formato legível e requer análise manual durante a depuração. A abordagem Long também não distingue tipos de dados — um desenvolvedor pode passar um valor incorreto.
Instant resolve todos esses problemas. É imutável, contém informações explícitas de precisão (segundos + nanossegundos), serializa para o formato ISO-8601 “2026-07-21T15:00:00Z” e possui uma API rica para conversões. De acordo com SonarSource (2024), Instant é a substituição recomendada para Date em todos os projetos novos.
O momento atual é obtido via Instant.now(). Ao contrário de LocalDateTime.now(), Instant.now() sempre retorna a hora em UTC, ignorando o fuso horário do dispositivo. Isso o torna ideal para timestamps de servidor.
A partir de valores existentes: Instant.ofEpochSecond(long epochSecond) — de segundos desde a época, Instant.ofEpochMilli(long epochMilli) — de milissegundos, Instant.parse(CharSequence) — de uma string ISO-8601 (“2026-07-21T15:00:00Z”).
Para leitura: getEpochSecond() — o número de segundos desde a época, toEpochMilli() — o número de milissegundos, getNano() — nanossegundos. O método toString() retorna uma string no formato ISO-8601.
val now = Instant.now()
val fromSeconds = Instant.ofEpochSecond(1784700000)
val fromMillis = Instant.ofEpochMilli(1784700000000)
val parsed = Instant.parse("2026-07-21T15:00:00Z")
val epochSecond = now.getEpochSecond()
val epochMilli = now.toEpochMilli()
val nanos = now.getNano()
Instant é convertido para ZonedDateTime via atZone(ZoneId). Por exemplo, Instant.now().atZone(ZoneId.of(“Europe/Moscow”)) retorna um ZonedDateTime para Moscou. Sem uma zona, a conversão é impossível — Instant não contém informações de calendário.
Para converter Instant para LocalDateTime: atZone(ZoneId).toLocalDateTime(). Esta abordagem é explícita e não perde informações. Conversão reversa: LocalDateTime.atZone(ZoneId).toInstant().
Para compatibilidade com java.util.Date: Date.from(instant) e date.toInstant(). Esta é uma conversão bidirecional que preserva a precisão até milissegundos (Date não suporta nanossegundos). Para java.sql.Timestamp, use Timestamp.from(instant) com suporte a nanossegundos.
val instant = Instant.now()
val zoned = instant.atZone(ZoneId.of("Europe/Moscow"))
val localDateTime = instant
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
val oldDate = Date.from(instant)
val backToInstant = oldDate.toInstant()
A principal característica do Instant é que ele é completamente independente de fusos horários. Instant.now() retorna o mesmo resultado em qualquer dispositivo em qualquer lugar do mundo. Isso é alcançado fixando o tempo em UTC.
Um fuso horário só é necessário para exibir Instant para um humano. Para isso, atZone(ZoneId) é usado. ZoneId.systemDefault() retorna o fuso horário do dispositivo definido no sistema operacional. ZoneOffset.UTC é a constante para UTC.
Em sistemas distribuídos, é recomendável armazenar e transmitir todos os timestamps em Instant (ou OffsetDateTime com ZoneOffset.UTC). A conversão para hora local é realizada apenas no cliente antes de exibir ao usuário. Isso evita confusão com fusos horários.
Em aplicações Android distribuídas, a sincronização de tempo é crítica para cache correto, notificações e edição colaborativa. Instant é a escolha natural para esta tarefa devido à sua ancoragem em UTC.
Ao comparar timestamps de diferentes dispositivos, é preciso considerar que os relógios do sistema podem divergir. Recomenda-se usar o tempo do servidor como referência. O servidor retorna Instant em UTC, e o cliente compara com o Instant local apenas para cálculos relativos.
Para calcular a diferença entre dois momentos, use Duration.between(Instant start, Instant end). Este método retorna uma Duration que pode ser convertida em horas, minutos, segundos. Os métodos isAfter() e isBefore() permitem comparar momentos.
fun isCacheExpired(
cachedAt: Instant,
ttlMinutes: Long
): Boolean {
val elapsed = Duration.between(cachedAt, Instant.now())
return elapsed.toMinutes() >= ttlMinutes
}
O primeiro exemplo é registrar eventos com um timestamp. Instant é salvo no banco de dados Room e enviado ao servidor. O timestamp é registrado em UTC para interpretação inequívoca.
data class EventLog(
val id: Long = 0,
val eventName: String,
val timestamp: Instant
)
class Converters {
@TypeConverter
fun fromInstant(value: Instant?): Long? {
return value?.toEpochMilli()
}
@TypeConverter
fun toInstant(value: Long?): Instant? {
return value?.let { Instant.ofEpochMilli(it) }
}
}
O segundo exemplo é determinar o tempo decorrido desde um evento. Usamos Duration.between para exibir “há 5 minutos”, “há 2 horas” — um formato comum em mensageiros e redes sociais.
fun timeAgo(instant: Instant): String {
val duration = Duration.between(instant, Instant.now())
return when {
duration.toMinutes() < 1 -> "just now"
duration.toHours() < 1 -> "${duration.toMinutes()} min ago"
duration.toDays() < 1 -> "${duration.toHours()} h ago"
else -> "${duration.toDays()} d ago"
}
}
O terceiro exemplo é a sincronização de dados entre servidor e cliente. Usamos Instant para rastrear o momento da última atualização.
class SyncManager {
private var lastSyncAt: Instant? = null
fun sync() {
val syncStart = Instant.now()
// server request with lastSyncAt
lastSyncAt = syncStart
}
fun shouldSync(intervalMinutes: Long): Boolean {
val last = lastSyncAt ?: return true
return Duration.between(last, Instant.now())
.toMinutes() >= intervalMinutes
}
}
O primeiro erro é usar Instant.now().toString() para exibição ao usuário. Instant gera no formato UTC “2026-07-21T15:00:00Z”, que é ilegível para humanos. Sempre converta Instant via atZone() para o fuso horário local antes de exibir.
O segundo erro é perder nanossegundos ao converter para java.util.Date. Date só suporta milissegundos. Se Instant tem nanossegundos, eles serão descartados em Date.from(instant). Use Instant.truncatedTo(ChronoUnit.MILLIS) para especificar explicitamente a precisão.
O terceiro erro é confusão entre toEpochMilli() e getEpochSecond(). toEpochMilli() retorna o número de milissegundos desde a época (long), enquanto getEpochSecond() retorna o número de segundos (long). Confundir esses métodos pode resultar em um erro de 1000x.
O quarto erro é assumir que Instant.now() está sincronizado em todos os dispositivos. Os relógios do sistema podem diferir por minutos ou até horas. Para operações críticas de tempo (autenticação, pagamentos), use Instant do servidor como fonte da verdade.
Perguntas Frequentes
System.currentTimeMillis() retorna um long — o número de milissegundos desde a época sem vinculação de fuso horário. Instant fornece a mesma funcionalidade, mas com precisão de nanossegundos e uma API rica para conversões, comparações e compatibilidade com java.time.
Room não suporta Instant diretamente. Use TypeConverter que converte Instant para Long (toEpochMilli) e de volta (Instant.ofEpochMilli). Para precisão de nanossegundos, salve dois campos: época-segundos e nanossegundos.
Sim, Instant é imutável e implementa corretamente equals() e hashCode(). Dois Instants com o mesmo valor serão iguais. Isso o torna uma chave confiável para HashMap e outras coleções, ao contrário do java.util.Date mutável.
Use Duration.between(start, end) para obter uma Duration ou ChronoUnit.SECONDS.between(start, end) para a diferença em segundos (long). Duration fornece os métodos toMinutes(), toHours(), toDays() e toNanos().
Instant é projetado como um ponto absoluto na linha do tempo. Sem especificar um fuso horário ou UTC, a análise é impossível porque Instant não contém informações de calendário. O sufixo “Z” denota deslocamento zero (UTC) e é obrigatório para o formato ISO-8601.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também