Instant — une classe immuable du paquetage java.time, représentant un point sur la ligne de temps en UTC avec une précision à la nanoseconde. Contrairement à LocalDateTime, Instant ne contient pas de date et d'heure dans un format lisible par l'humain — c'est une représentation machine d'un instant. Selon la spécification Oracle Java 17 (2024), Instant est conçu pour l'échange machine de timestamps et est un analogue de System.currentTimeMillis(), mais avec une précision à la nanoseconde.
Points Clés
Instant est une classe qui modélise un point unique sur la ligne de temps. Sa représentation interne se compose de deux champs : long seconds (le nombre de secondes depuis 1970-01-01T00:00:00Z) et int nanos (nanosecondes dans la seconde actuelle, de 0 à 999999999).
La plage de valeurs d'Instant est de -31557014167219200 à 31556889864403199 secondes depuis l'époque, couvrant environ 292 millions d'années dans les deux directions. C'est suffisant pour toutes les tâches pratiques, y compris les calculs astronomiques.
Selon Baeldung (2024), Instant est un pont entre les types lisibles par l'humain (LocalDateTime, ZonedDateTime) et les formats machine (timestamp en millisecondes). Instant est utilisé pour la journalisation, la mise en cache, la synchronisation et toutes les tâches où un moment absolu dans le temps est important.
La classe implémente les interfaces Comparable (pour comparer des instants) et Temporal (pour utilisation dans l'API commune java.time). Instant est immuable — toutes les méthodes renvoient un nouvel objet.
Avant Java 8, java.util.Date et System.currentTimeMillis() étaient utilisés pour travailler avec des instants temporels. Les deux approches ont des inconvénients. Date est mutable, non thread-safe, stocke le temps en millisecondes depuis l'époque, mais ses noms de méthodes sont obsolètes (getYear() renvoie 116 pour 2016).
Long (un timestamp simple) est rapide et compact, mais n'a pas de support intégré pour les nanosecondes, ne s'affiche pas dans un format lisible et nécessite une analyse manuelle lors du débogage. L'approche Long ne distingue pas non plus les types de données — un développeur pourrait passer une valeur incorrecte.
Instant résout tous ces problèmes. Il est immuable, contient des informations explicites de précision (secondes + nanosecondes), se sérialise au format ISO-8601 « 2026-07-21T15:00:00Z » et possède une API riche pour les conversions. Selon SonarSource (2024), Instant est le remplacement recommandé de Date dans tous les nouveaux projets.
Le moment actuel est obtenu via Instant.now(). Contrairement à LocalDateTime.now(), Instant.now() renvoie toujours l'heure en UTC, ignorant le fuseau horaire de l'appareil. Cela le rend idéal pour les timestamps de serveur.
À partir de valeurs existantes : Instant.ofEpochSecond(long epochSecond) — depuis les secondes depuis l'époque, Instant.ofEpochMilli(long epochMilli) — depuis les millisecondes, Instant.parse(CharSequence) — depuis une chaîne ISO-8601 (« 2026-07-21T15:00:00Z »).
Pour la lecture : getEpochSecond() — le nombre de secondes depuis l'époque, toEpochMilli() — le nombre de millisecondes, getNano() — les nanosecondes. La méthode toString() renvoie une chaîne au format 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 est converti en ZonedDateTime via atZone(ZoneId). Par exemple, Instant.now().atZone(ZoneId.of(« Europe/Moscow »)) renvoie un ZonedDateTime pour Moscou. Sans zone, la conversion est impossible — Instant ne contient pas d'informations calendaires.
Pour convertir Instant en LocalDateTime : atZone(ZoneId).toLocalDateTime(). Cette approche est explicite et ne perd pas d'informations. Conversion inverse : LocalDateTime.atZone(ZoneId).toInstant().
Pour la compatibilité avec java.util.Date : Date.from(instant) et date.toInstant(). C'est une conversion bidirectionnelle qui préserve la précision jusqu'aux millisecondes (Date ne supporte pas les nanosecondes). Pour java.sql.Timestamp, utilisez Timestamp.from(instant) avec le support des nanosecondes.
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()
La caractéristique clé d'Instant est qu'il est complètement indépendant des fuseaux horaires. Instant.now() renvoie le même résultat sur n'importe quel appareil, n'importe où dans le monde. Ceci est réalisé en fixant le temps en UTC.
Un fuseau horaire n'est nécessaire que pour afficher Instant à un humain. Pour cela, atZone(ZoneId) est utilisé. ZoneId.systemDefault() renvoie le fuseau horaire de l'appareil défini dans le système d'exploitation. ZoneOffset.UTC est la constante pour UTC.
Dans les systèmes distribués, il est recommandé de stocker et transmettre tous les timestamps en Instant (ou OffsetDateTime avec ZoneOffset.UTC). La conversion vers l'heure locale est effectuée uniquement sur le client avant l'affichage à l'utilisateur. Cela évite toute confusion avec les fuseaux horaires.
Dans les applications Android distribuées, la synchronisation temporelle est critique pour une mise en cache correcte, les notifications et l'édition collaborative. Instant est le choix naturel pour cette tâche grâce à son ancrage en UTC.
Lors de la comparaison de timestamps provenant de différents appareils, il faut considérer que les horloges système peuvent diverger. Il est recommandé d'utiliser l'heure du serveur comme référence. Le serveur renvoie Instant en UTC, et le client le compare avec l'Instant local uniquement pour les calculs relatifs.
Pour calculer la différence entre deux instants, utilisez Duration.between(Instant start, Instant end). Cette méthode renvoie une Duration qui peut être convertie en heures, minutes, secondes. Les méthodes isAfter() et isBefore() permettent de comparer des instants.
fun isCacheExpired(
cachedAt: Instant,
ttlMinutes: Long
): Boolean {
val elapsed = Duration.between(cachedAt, Instant.now())
return elapsed.toMinutes() >= ttlMinutes
}
Le premier exemple est la journalisation d'événements avec un timestamp. Instant est sauvegardé dans la base de données Room et envoyé au serveur. Le timestamp est journalisé en UTC pour une interprétation sans ambiguïté.
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) }
}
}
Le deuxième exemple est la détermination du temps écoulé depuis un événement. Nous utilisons Duration.between pour afficher « il y a 5 minutes », « il y a 2 heures » — un format courant dans les messageries et les réseaux sociaux.
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"
}
}
Le troisième exemple est la synchronisation de données entre le serveur et le client. Nous utilisons Instant pour suivre l'heure de la dernière mise à jour.
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
}
}
La première erreur est d'utiliser Instant.now().toString() pour l'affichage à l'utilisateur. Instant produit au format UTC « 2026-07-21T15:00:00Z », qui est illisible pour les humains. Convertissez toujours Instant via atZone() vers le fuseau horaire local avant de l'afficher.
La deuxième erreur est de perdre les nanosecondes lors de la conversion vers java.util.Date. Date ne supporte que les millisecondes. Si Instant a des nanosecondes, elles seront supprimées dans Date.from(instant). Utilisez Instant.truncatedTo(ChronoUnit.MILLIS) pour spécifier explicitement la précision.
La troisième erreur est la confusion entre toEpochMilli() et getEpochSecond(). toEpochMilli() renvoie le nombre de millisecondes depuis l'époque (long), tandis que getEpochSecond() renvoie le nombre de secondes (long). Confondre ces méthodes peut entraîner une erreur de 1000x.
La quatrième erreur est de supposer qu'Instant.now() est synchronisé sur tous les appareils. Les horloges système peuvent différer de minutes, voire d'heures. Pour les opérations critiques en termes de temps (authentification, paiements), utilisez l'Instant du serveur comme source de vérité.
Foire Aux Questions
System.currentTimeMillis() renvoie un long — le nombre de millisecondes depuis l'époque sans lien avec le fuseau horaire. Instant fournit la même fonctionnalité mais avec une précision à la nanoseconde et une API riche pour les conversions, comparaisons et la compatibilité avec java.time.
Room ne supporte pas Instant directement. Utilisez TypeConverter qui convertit Instant en Long (toEpochMilli) et inversement (Instant.ofEpochMilli). Pour la précision à la nanoseconde, sauvegardez deux champs : époque-secondes et nanosecondes.
Oui, Instant est immuable et implémente correctement equals() et hashCode(). Deux Instants avec la même valeur seront égaux. Cela en fait une clé fiable pour HashMap et autres collections, contrairement à java.util.Date qui est mutable.
Utilisez Duration.between(start, end) pour obtenir une Duration ou ChronoUnit.SECONDS.between(start, end) pour la différence en secondes (long). Duration fournit les méthodes toMinutes(), toHours(), toDays() et toNanos().
Instant est conçu comme un point absolu sur la ligne de temps. Sans spécifier de fuseau horaire ou UTC, l'analyse est impossible car Instant ne contient pas d'informations calendaires. Le suffixe « Z » désigne un décalage zéro (UTC) et est obligatoire pour le format ISO-8601.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi