W3docs

Java Instant

Represente um momento na linha do tempo em UTC com Instant em Java — ideal para timestamps e tempo de máquina.

Instant é um único momento na linha do tempo global, armazenado como nanossegundos desde o epoch Unix (1970-01-01T00:00:00Z). Não possui fuso horário, sem calendário, sem noção de "que dia é hoje" — apenas uma contagem. Essa contagem está em UTC por definição, então dois Instants de máquinas diferentes em fusos horários diferentes se comparam diretamente: aquele com o número menor é o anterior.

Este é o tipo para timestamps de máquina. Linhas de log. Timestamps de mensagens. "Quando o servidor recebeu a requisição." Trilhas de auditoria. Qualquer coisa que precise ser ordenável globalmente e que nunca deva ser ambígua sobre qual dia representa — porque não existe "dia" algum, apenas segundos.

Criando

Instant now    = Instant.now();                              // current moment, from System clock
Instant epoch  = Instant.EPOCH;                              // 1970-01-01T00:00:00Z
Instant max    = Instant.MAX;                                 // year +1000000000
Instant min    = Instant.MIN;                                 // year -1000000000

Instant fromS   = Instant.ofEpochSecond(1_700_000_000L);
Instant fromMs  = Instant.ofEpochMilli(1_700_000_000_000L);
Instant fromIso = Instant.parse("2025-11-04T19:30:00Z");     // ISO-8601, trailing Z is mandatory

A forma em string termina com um Z literal (de "Zulu time," termo militar para UTC). Instant.parse("2025-11-04T19:30:00") (sem Z) resulta em erro de parse — o tipo recusa-se a adivinhar qual fuso horário você quis dizer.

Dois factories que você usará com frequência:

Instant.ofEpochSecond(epochSec);                              // long seconds, no nanos
Instant.ofEpochSecond(epochSec, nanos);                       // with sub-second resolution

A maioria dos formatos de timestamp externos (Unix time(2), syslog, inteiros JSON created_at) são segundos ou milissegundos desde o epoch. Os factories ofEpochSecond / ofEpochMilli são a ponte padrão.

Resolução

Instant tem precisão de nanossegundos (1 segundo = 1.000.000.000 ns). Na maioria dos sistemas, o relógio subjacente tem resolução menor — milissegundos é o típico, microssegundos em Linux moderno. Instant.now() retorna um valor com a resolução disponível; os nanossegundos não utilizados são zero.

Accessors:

long seconds = inst.getEpochSecond();                         // long; can go past 2038
int nanos    = inst.getNano();                                // 0-999_999_999
long milli   = inst.toEpochMilli();                           // throws if out of long range

toEpochMilli é a conversão com perda: nanossegundos são truncados para milissegundos. Para linhas de log e timestamps JSON isso geralmente é aceitável; para registros de eventos de alta frequência, use getEpochSecond + getNano separadamente.

Sem calendário

Instant.getDayOfMonth() não existe. Nem getYear, getHour, nem nenhum dos accessors de calendário. O tipo genuinamente não sabe — informações de calendário requerem um fuso horário, e Instant não tem um. Se você quiser perguntar "que hora era em Nova York quando isso aconteceu," você precisa primeiro anexar um fuso horário:

ZonedDateTime zdt = inst.atZone(ZoneId.of("America/New_York"));
int hour = zdt.getHour();
LocalDate date = zdt.toLocalDate();

atZone(zone) é a ponte na direção oposta de ZonedDateTime.toInstant(). Os dois juntos oferecem o ciclo completo: momento ↔ rótulo com fuso horário. Veja Java ZonedDateTime para o lado do calendário dessa combinação.

Aritmética

Mesma forma fluente:

inst.plusSeconds(60);
inst.plusMillis(500);
inst.plusNanos(1_000_000);
inst.plus(Duration.ofMinutes(15));                            // any Duration

inst.minus(Duration.ofDays(1));                                // exactly 24h * 3600s

Não há plusDays em Instant (no sentido calendário). Existe plus(amount, ChronoUnit), e ChronoUnit.DAYS funciona porque o JDK define um Day como exatamente 24 horas de segundos para Instant. Isso não é o que um dia calendário é quando o horário de verão está em vigor, que é exatamente o motivo pelo qual Instant não finge ser um.

inst.plus(1, ChronoUnit.DAYS);                                // exactly 86_400 seconds
inst.plus(7, ChronoUnit.DAYS);                                // exactly 604_800 seconds

Para operações com forma de calendário ("um mês depois no fuso horário do usuário"), use ZonedDateTime:

Instant later = inst.atZone(zone).plusMonths(1).toInstant();

Comparando

inst1.isBefore(inst2);
inst1.isAfter(inst2);
inst1.equals(inst2);
inst1.compareTo(inst2);

Instant implementa Comparable<Instant> com ordenação natural por epoch second e depois nanos. equals é direto: mesmo segundo e mesmos nanos.

Distância

Duration d = Duration.between(start, end);                     // a Duration
long millis = ChronoUnit.MILLIS.between(start, end);
long days = ChronoUnit.DAYS.between(start, end);               // 24h-equivalent days

Para timestamps de máquina, todos são exatos — não há ambiguidade de calendário. ChronoUnit.MONTHS.between(start, end) em Instants lança exceção porque meses não têm comprimento constante quando medidos em segundos: não há fuso horário, então o calculador não tem como saber qual mês contém esses segundos. Esse é o modo de falha correto.

Ponte com java.util.Date

Código antigo usa java.util.Date. As conversões são diretas:

Date legacy = Date.from(inst);                                 // Instant -> Date
Instant back = legacy.toInstant();                              // Date -> Instant

Date é internamente um wrapper em torno de um long de epoch-millisecond, então o ciclo de ida e volta é sem perdas módulo nanossegundos (Date tem precisão de milissegundos, Instant tem precisão de nanossegundos). O capítulo Legacy Date cobre a migração em detalhes.

Por que tudo internamente deveria ser Instant

A recomendação que emergiu de executar java.time em produção por dez anos:

  • Internamente, use Instant. Armazenamento, comparação, log, timestamps de mensagens, em qualquer lugar onde o valor flui de máquina para máquina.
  • Na fronteira — ao exibir para um usuário, ao aceitar entrada do usuário — converta para ZonedDateTime ou LocalDateTime usando o fuso horário correto para o contexto.

Isso separa "o que realmente aconteceu" de "como é rotulado." Um bug na fronteira (fuso horário errado) deixa seus valores internos corretos; um bug no sistema de tipos que deixa LocalDateTime fluir internamente tende a deixar você com timestamps que silenciosamente estão em fusos horários diferentes.

Um exemplo prático: um log de eventos simples

O programa abaixo registra uma sequência de eventos como Instants, computa durações entre eventos, demonstra a fronteira com calendário ao anexar um fuso horário para exibição, mostra a ponte com Date legado, e finalmente ilustra a regra "sem calendário" mostrando que ChronoUnit.MONTHS.between em dois Instants lança exceção.

java— editable, runs on the server

O que extrair da execução:

  • Instant.parse("2025-11-04T19:30:00Z") foi analisado apenas por causa do Z no final. Remova o Z e o parse falha — o tipo insiste em saber que a string está em UTC. Outros fusos horários precisam usar ZonedDateTime.parse (ou serem fornecidos via atZone).
  • A sequência de eventos usou Duration.between(...). Cada resultado foi uma contagem inteira limpa de milissegundos — sem confusão de fuso horário, sem horário de verão, sem aritmética de calendário. É por isso que o tempo do lado do servidor pertence ao Instant: a aritmética é apenas subtração de longs por baixo dos panos.
  • O bloco "mesmo instante, dois rótulos de fuso horário" imprimiu ZonedDateTimes que pareciam diferentes, mas eram o mesmo Instant. atZone(...) é puramente uma operação de exibição em Instant. Se você quiser fazer matemática de calendário (próximo mês, fim de semana), faça no ZonedDateTime, depois chame .toInstant() para voltar.
  • Date.from(inst) e legacy.toInstant() foram sem perdas módulo nanossegundos. Date carrega apenas milissegundos, então o ciclo de ida e volta por Date trunca a precisão sub-ms. Para a maioria dos logs, isso é aceitável; para captura de eventos de alta precisão, fique em Instant do início ao fim e nunca faça o ciclo por Date.
  • ChronoUnit.MONTHS.between(a, b) lançou UnsupportedTemporalTypeException. Esse é o modo de falha correto: meses não têm segundos de comprimento constante, e o JDK recusa-se a inventar uma resposta. Usar ZonedDateTime forneceu o fuso horário ausente, e a mesma chamada funcionou. O padrão é geral: operações com forma de calendário precisam de um fuso horário, e o sistema de tipos obriga você a fornecê-lo explicitamente.

O que vem a seguir

Instant é o momento. Os próximos dois capítulos cobrem os comprimentos de tempo entre momentos: Java Duration para medições de "X segundos, Y nanos", e Java Period para comprimentos de calendário de "X anos, Y meses, Z dias". Os dois juntos são como você expressa "uma hora depois" versus "um mês depois" sem que nenhum dos dois seja com perdas.

Prática

Prática
Uma linha de log inclui `created_at: 1700000000` (um segundo do epoch Unix). Você precisa de um valor `java.time` que possa comparar com `Instant.now()` e passar para um cálculo `Duration.between(...)`. Qual conversão está correta?
Uma linha de log inclui `created_at: 1700000000` (um segundo do epoch Unix). Você precisa de um valor `java.time` que possa comparar com `Instant.now()` e passar para um cálculo `Duration.between(...)`. Qual conversão está correta?
Was this page helpful?