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 mandatoryLa 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 resolutionLa 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 rangetoEpochMilli è 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 * 3600sNessun 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 secondsPer 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 daysPer 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 -> InstantDate è 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
ZonedDateTimeoLocalDateTimeusando 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.
Cosa trarre dall'esecuzione:
Instant.parse("2025-11-04T19:30:00Z")è stato analizzato solo grazie allaZfinale. Rimuovere laZe il parsing fallisce — il tipo insiste nel sapere che la stringa è UTC. Le altre zone devono passare attraversoZonedDateTime.parse(o essere fornite tramiteatZone).- 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 aInstant: l'aritmetica è solo sottrazione dilongsotto il cofano. - Il blocco "same instant, two zone labels" ha stampato
ZonedDateTimeche sembravano diversi ma erano lo stessoInstant.atZone(...)è puramente un'operazione di visualizzazione suInstant. Se vuoi fare matematica del calendario (prossimo mese, fine settimana), falla suZonedDateTime, poi chiama.toInstant()per tornare. Date.from(inst)elegacy.toInstant()erano senza perdita modulo nanosecondi.Dateporta solo millisecondi, quindi l'andata e ritorno attraversoDatetronca la precisione sub-ms. Per la maggior parte dei log questo va bene; per la cattura di eventi ad alta precisione, rimani inInstantend-to-end e non fare mai l'andata e ritorno attraversoDate.ChronoUnit.MONTHS.between(a, b)ha lanciatoUnsupportedTemporalTypeException. Questa è la modalità di fallimento corretta: i mesi non hanno lunghezza costante in secondi, e il JDK si rifiuta di inventare una risposta. Passare attraversoZonedDateTimeha 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.