Instant — neměnná třída z balíčku java.time, představující bod na časové ose v UTC s přesností na nanosekundy. Na rozdíl od LocalDateTime, Instant neobsahuje datum a čas v člověkem čitelném formátu — jde o strojovou reprezentaci okamžiku. Podle specifikace Oracle Java 17 (2024) je Instant navržen pro strojovou výměnu časových razítek a je analogií System.currentTimeMillis(), ale s nanosekundovou přesností.
Hlavní body
Instant — je třída modelující jediný bod na časové ose. Její vnitřní reprezentace se skládá ze dvou polí: long seconds (počet sekund od 1970-01-01T00:00:00Z) a int nanos (nanosekundy v rámci aktuální sekundy, od 0 do 999999999).
Rozsah hodnot Instant — od -31557014167219200 do 31556889864403199 sekund od epochy, což pokrývá přibližně 292 milionů let v obou směrech. To je dostatečné pro všechny praktické úkoly, včetně astronomických výpočtů.
Podle Baeldung (2024) je Instant mostem mezi člověkem čitelnými typy (LocalDateTime, ZonedDateTime) a strojovými formáty (timestamp v milisekundách). Instant se používá pro logování, ukládání do mezipaměti, synchronizaci a všechny úkoly, kde je důležitý absolutní časový okamžik.
Třída implementuje rozhraní Comparable (pro porovnávání okamžiků) a Temporal (pro použití v obecném API java.time). Instant je neměnný — všechny metody vracejí nový objekt.
Před Javou 8 se pro práci s časovými okamžiky používaly java.util.Date a System.currentTimeMillis(). Oba přístupy mají nevýhody. Date je měnitelný, není thread-safe, ukládá čas v milisekundách od epochy, ale názvy metod jsou zastaralé (getYear() vrací 116 pro rok 2016).
Long (jednoduchý timestamp) je rychlý a kompaktní, ale nemá vestavěnou podporu pro nanosekundy, nezobrazuje se v čitelné podobě a vyžaduje ruční parsování při ladění. Přístup Long také nerozlišuje datový typ — vývojář může předat nesprávnou hodnotu.
Instant řeší všechny tyto problémy. Je neměnný, obsahuje explicitní informaci o přesnosti (sekundy + nanosekundy), serializuje se do formátu ISO-8601 „2026-07-21T15:00:00Z“ a má bohaté API pro převody. Podle SonarSource (2024) je Instant doporučenou náhradou za Date ve všech nových projektech.
Aktuální okamžik se získá pomocí Instant.now(). Na rozdíl od LocalDateTime.now() vrací Instant.now() vždy čas v UTC, ignoruje časové pásmo zařízení. To ho činí ideálním pro serverová časová razítka.
Z existujících hodnot: Instant.ofEpochSecond(long epochSecond) — ze sekund od epochy, Instant.ofEpochMilli(long epochMilli) — z milisekund, Instant.parse(CharSequence) — z řetězce ISO-8601 („2026-07-21T15:00:00Z”).
Pro čtení se používá getEpochSecond() — počet sekund od epochy, toEpochMilli() — počet milisekund, getNano() — nanosekundy. Metoda toString() vrací řetězec ve formátu 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 se převádí na ZonedDateTime pomocí atZone(ZoneId). Například Instant.now().atZone(ZoneId.of("Europe/Moscow")) vrátí ZonedDateTime pro Moskvu. Bez zóny je převod nemožný — Instant neobsahuje kalendářní informace.
Na LocalDateTime se Instant převádí pomocí atZone(ZoneId).toLocalDateTime(). Tento způsob je explicitní a neztrácí informace. Opačný převod — LocalDateTime.atZone(ZoneId).toInstant().
Pro kompatibilitu s java.util.Date: Date.from(instant) a date.toInstant(). Toto je obousměrný převod zachovávající přesnost až na milisekundy (Date nepodporuje nanosekundy). Pro práci s java.sql.Timestamp se používá Timestamp.from(instant) s podporou nanosekund.
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()
Klíčová vlastnost Instant — je zcela nezávislý na časových pásmech. Instant.now() vrací stejný výsledek na jakémkoli zařízení kdekoli na světě. Toho je dosaženo fixací času v UTC.
Časové pásmo je potřeba pouze pro zobrazení Instant člověku. K tomu slouží atZone(ZoneId). ZoneId.systemDefault() vrací časové pásmo zařízení nastavené v operačním systému. ZoneOffset.UTC — konstanta pro UTC.
V distribuovaných systémech se doporučuje ukládat a přenášet všechna časová razítka v Instant (nebo OffsetDateTime s ZoneOffset.UTC). Převod na místní čas se provádí pouze na straně klienta před zobrazením uživateli. Tím se předchází zmatkům s časovými pásmy.
V distribuovaných Android aplikacích je synchronizace času kritická pro správné fungování ukládání do mezipaměti, notifikací a společné editace. Instant — přirozená volba pro tento úkol díky vazbě na UTC.
Při porovnávání časových razítek z různých zařízení je třeba vzít v úvahu, že systémové hodiny se mohou lišit. Doporučuje se používat čas serveru jako referenci. Server vrací Instant v UTC, klient porovnává s místním Instant pouze pro relativní výpočty.
Pro výpočet rozdílu mezi dvěma okamžiky se používá Duration.between(Instant start, Instant end). Tato metoda vrací Duration — dobu trvání, kterou lze převést na hodiny, minuty, sekundy. Metody isAfter() a isBefore() umožňují porovnávat okamžiky.
fun isCacheExpired(
cachedAt: Instant,
ttlMinutes: Long
): Boolean {
val elapsed = Duration.between(cachedAt, Instant.now())
return elapsed.toMinutes() >= ttlMinutes
}
První příklad — zaznamenávání událostí s časovým razítkem. Instant se ukládá do databáze Room a odesílá na server. Časové razítko se loguje v UTC pro jednoznačnou interpretaci.
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) }
}
}
Druhý příklad — určení času, který uplynul od události. Používáme Duration.between pro zobrazení „před 5 minutami“, „před 2 hodinami“ — formátu běžného v messengerech a sociálních sítích.
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"
}
}
Třetí příklad — synchronizace dat mezi serverem a klientem. Používáme Instant pro sledování času poslední aktualizace.
class SyncManager {
private var lastSyncAt: Instant? = null
fun sync() {
val syncStart = Instant.now()
// požadavek serveru s lastSyncAt
lastSyncAt = syncStart
}
fun shouldSync(intervalMinutes: Long): Boolean {
val last = lastSyncAt ?: return true
return Duration.between(last, Instant.now())
.toMinutes() >= intervalMinutes
}
}
První chyba — použití Instant.now().toString() pro zobrazení uživateli. Instant se zobrazuje v UTC formátu „2026-07-21T15:00:00Z“, který je pro člověka nečitelný. Vždy převeďte Instant pomocí atZone() do místního časového pásma před zobrazením.
Druhá chyba — ztráta nanosekund při převodu na java.util.Date. Date podporuje pouze milisekundy. Pokud má Instant nanosekundy, budou ztraceny při Date.from(instant). Použijte Instant.truncatedTo(ChronoUnit.MILLIS) pro explicitní určení přesnosti.
Třetí chyba — záměna toEpochMilli() a getEpochSecond(). toEpochMilli() vrací počet milisekund od epochy (long), zatímco getEpochSecond() vrací počet sekund (long). Záměna těchto metod může vést k 1000násobné chybě.
Čtvrtá chyba — předpoklad, že Instant.now() je synchronizován na všech zařízeních. Systémové hodiny se mohou lišit o minuty, ba i hodiny. Pro časově kritické operace (autentizace, platby) používejte serverový Instant jako zdroj pravdy.
Často kladené otázky
System.currentTimeMillis() vrací long — počet milisekund od epochy bez vazby na časové pásmo. Instant poskytuje stejnou funkcionalitu, ale s nanosekundovou přesností a bohatým API pro převody, porovnávání a kompatibilitu s java.time.
Room přímo nepodporuje Instant. Použijte TypeConverter, který převádí Instant na Long (toEpochMilli) a zpět (Instant.ofEpochMilli). Pro nanosekundovou přesnost ukládejte dvě pole: epocha-sekundy a nanosekundy.
Ano, Instant je neměnný a správně implementuje equals() a hashCode(). Dva Instant se stejnou hodnotou budou stejné. To z něj činí spolehlivý klíč pro HashMap a další kolekce, na rozdíl od měnitelného java.util.Date.
Použijte Duration.between(start, end) pro získání Duration nebo ChronoUnit.SECONDS.between(start, end) pro rozdíl v sekundách (long). Duration poskytuje metody toMinutes(), toHours(), toDays() a toNanos().
Instant je navržen jako absolutní bod na časové ose. Bez uvedení časového pásma nebo UTC je parsování nemožné, protože Instant neobsahuje kalendářní informace. Přípona „Z“ označuje nulový offset (UTC) a je povinná pro formát ISO-8601.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také