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 mandatoryA 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 resolutionA 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 rangetoEpochMilli é 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 * 3600sNã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 secondsPara 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 daysPara 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 -> InstantDate é 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
ZonedDateTimeouLocalDateTimeusando 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.
O que extrair da execução:
Instant.parse("2025-11-04T19:30:00Z")foi analisado apenas por causa doZno final. Remova oZe o parse falha — o tipo insiste em saber que a string está em UTC. Outros fusos horários precisam usarZonedDateTime.parse(ou serem fornecidos viaatZone).- 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 aoInstant: a aritmética é apenas subtração delongs por baixo dos panos. - O bloco "mesmo instante, dois rótulos de fuso horário" imprimiu
ZonedDateTimes que pareciam diferentes, mas eram o mesmoInstant.atZone(...)é puramente uma operação de exibição emInstant. Se você quiser fazer matemática de calendário (próximo mês, fim de semana), faça noZonedDateTime, depois chame.toInstant()para voltar. Date.from(inst)elegacy.toInstant()foram sem perdas módulo nanossegundos.Datecarrega apenas milissegundos, então o ciclo de ida e volta porDatetrunca a precisão sub-ms. Para a maioria dos logs, isso é aceitável; para captura de eventos de alta precisão, fique emInstantdo início ao fim e nunca faça o ciclo porDate.ChronoUnit.MONTHS.between(a, b)lançouUnsupportedTemporalTypeException. Esse é o modo de falha correto: meses não têm segundos de comprimento constante, e o JDK recusa-se a inventar uma resposta. UsarZonedDateTimeforneceu 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.