W3docs

Java Instant

Rappresenta un momento sulla linea temporale in UTC con Instant — ideale per timestamp e tempo macchina.

Instant è un singolo momento sulla linea temporale globale, memorizzato come nanosecondi dall'epoca Unix (1970-01-01T00:00:00Z). Non ha zona, non ha calendario, nessuna nozione di "che giorno è" — solo un conteggio. Quel conteggio è in UTC per costruzione, quindi due Instant da macchine diverse in zone diverse si confrontano direttamente: quello con il numero più basso è il precedente.

Questo è il tipo per i timestamp macchina. Righe di log. Timestamp dei messaggi. "Quando ha ricevuto la richiesta il server." Audit trail. Qualsiasi cosa che deve essere ordinabile globalmente e che non dovrebbe mai essere ambigua riguardo al giorno che rappresenta — perché non esiste alcun "giorno", solo secondi.

Creazione

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

La forma stringa termina con una Z letterale (per "Zulu time", militare per UTC). Instant.parse("2025-11-04T19:30:00") (senza Z) è un errore di parsing — il tipo rifiuta di indovinare quale zona intendi.

Due factory che userai spesso:

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

La maggior parte dei formati di timestamp esterni (Unix time(2), syslog, interi JSON created_at) sono secondi o millisecondi dall'epoca. Le factory ofEpochSecond / ofEpochMilli sono il ponte standard.

Risoluzione

Instant ha precisione al nanosecondo (1 secondo = 1.000.000.000 ns). Sulla maggior parte dei sistemi l'orologio sottostante ha una risoluzione inferiore — il millisecondo è tipico, il microsecondo su Linux moderno. Instant.now() restituisce un valore alla risoluzione disponibile; i nanosecondi inutilizzati sono zero.

Accessori:

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 è la conversione con perdita: i nanosecondi vengono troncati ai millisecondi. Per le righe di log e i timestamp JSON questo va generalmente bene; per i record di eventi ad alta frequenza, usa getEpochSecond + getNano separatamente.

Nessun calendario

Instant.getDayOfMonth() non esiste. Né getYear, getHour, né alcun altro accessore del calendario. Il tipo genuinamente non lo sa — le informazioni del calendario richiedono una zona, e Instant non ne ha una. Se vuoi chiedere "che ora era a New York quando è successo", devi prima allegare una zona:

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

atZone(zone) è il ponte nella direzione opposta rispetto a ZonedDateTime.toInstant(). I due insieme ti danno l'andata e ritorno completo: momento ↔ etichetta con zona. Vedi Java ZonedDateTime per il lato calendario di quella coppia.

Aritmetica

Stessa 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

Nessun plusDays su Instant (nel senso del calendario). Ha plus(amount, ChronoUnit), e ChronoUnit.DAYS funziona perché il JDK definisce un Day come esattamente 24 ore di secondi per Instant. Non è ciò che è un giorno del calendario quando è in gioco il DST, che è la ragione principale per cui Instant non finge di esserlo.

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

Per operazioni a forma di calendario ("un mese dopo nella zona dell'utente"), passa attraverso ZonedDateTime:

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

Confronto

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

Instant implementa Comparable<Instant> con ordinamento naturale per secondo dell'epoca poi nanosecondi. equals è diretto: stesso secondo e stessi nanosecondi.

Distanza

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

Per i timestamp macchina questi sono tutti esatti — non c'è ambiguità del calendario. ChronoUnit.MONTHS.between(start, end) su Instants lancia un'eccezione perché i mesi non hanno lunghezza costante se misurati in secondi: non c'è zona, quindi il calcolatore non ha modo di sapere quale mese contiene quei secondi. Questa è la modalità di fallimento corretta.

Ponte java.util.Date

Il vecchio codice usa java.util.Date. Le conversioni sono dirette:

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

Date è internamente un wrapper attorno a un long di millisecondi-dall'epoca, quindi l'andata e ritorno è senza perdita modulo nanosecondi (Date ha precisione al millisecondo, Instant al nanosecondo). Il capitolo Legacy Date tratta la migrazione in dettaglio.

Perché tutto internamente dovrebbe essere Instant

La raccomandazione emersa dall'esecuzione di java.time in produzione per dieci anni:

  • Internamente, usa Instant. Storage, confronto, logging, timestamp dei messaggi, ovunque il valore fluisce da macchina a macchina.
  • Al confine — quando si visualizza a un utente, quando si accetta l'input dell'utente — converti in ZonedDateTime o LocalDateTime usando la zona giusta per il contesto.

Questo separa "cosa è veramente successo" da "come è etichettato". Un bug al confine (zona sbagliata) lascia i tuoi valori interni corretti; un bug nel sistema di tipi che lascia fluire LocalDateTime internamente tende a lasciarti con timestamp che sono silenziosamente in zone diverse.

Un esempio pratico: un piccolo log di eventi

Il programma seguente registra una sequenza di eventi come Instant, calcola le durate inter-evento, dimostra il confine del calendario allegando una zona per la visualizzazione, mostra il ponte legacy Date, e infine illustra la regola "nessun calendario" mostrando che ChronoUnit.MONTHS.between su due Instant lancia un'eccezione.

java— editable, runs on the server

Cosa trarre dall'esecuzione:

  • Instant.parse("2025-11-04T19:30:00Z") è stato analizzato solo grazie alla Z finale. Rimuovere la Z e il parsing fallisce — il tipo insiste nel sapere che la stringa è UTC. Le altre zone devono passare attraverso ZonedDateTime.parse (o essere fornite tramite atZone).
  • La sequenza di eventi ha usato Duration.between(...). Ogni risultato era un conteggio di millisecondi interi pulito — nessuna confusione di zona, nessun DST, nessuna aritmetica del calendario. Ecco perché il timing lato server appartiene a Instant: l'aritmetica è solo sottrazione di long sotto il cofano.
  • Il blocco "same instant, two zone labels" ha stampato ZonedDateTime che sembravano diversi ma erano lo stesso Instant. atZone(...) è puramente un'operazione di visualizzazione su Instant. Se vuoi fare matematica del calendario (prossimo mese, fine settimana), falla su ZonedDateTime, poi chiama .toInstant() per tornare.
  • Date.from(inst) e legacy.toInstant() erano senza perdita modulo nanosecondi. Date porta solo millisecondi, quindi l'andata e ritorno attraverso Date tronca la precisione sub-ms. Per la maggior parte dei log questo va bene; per la cattura di eventi ad alta precisione, rimani in Instant end-to-end e non fare mai l'andata e ritorno attraverso Date.
  • ChronoUnit.MONTHS.between(a, b) ha lanciato UnsupportedTemporalTypeException. Questa è la modalità di fallimento corretta: i mesi non hanno lunghezza costante in secondi, e il JDK si rifiuta di inventare una risposta. Passare attraverso ZonedDateTime ha fornito la zona mancante, e la stessa chiamata ha funzionato. Il pattern è generale: le operazioni a forma di calendario hanno bisogno di una zona, e il sistema di tipi ti obbliga a fornirne una esplicitamente.

Cosa c'è dopo

Instant è il momento. I prossimi due capitoli trattano le durate di tempo tra momenti: Java Duration per misurazioni "X secondi, Y nanosecondi", e Java Period per durate del calendario "X anni, Y mesi, Z giorni". I due insieme sono il modo per esprimere "un'ora dopo" vs "un mese dopo" senza che nessuno dei due sia con perdita.

Pratica

Pratica
Una riga di log include `created_at: 1700000000` (un secondo di epoca Unix). Hai bisogno di un valore `java.time` che puoi confrontare con `Instant.now()` e passare a un calcolo `Duration.between(...)`. Quale conversione è corretta?
Una riga di log include `created_at: 1700000000` (un secondo di epoca Unix). Hai bisogno di un valore `java.time` che puoi confrontare con `Instant.now()` e passare a un calcolo `Duration.between(...)`. Quale conversione è corretta?
Was this page helpful?