Instant — java.timeパッケージの不変クラスで、UTCのタイムライン上のポイントをナノ秒精度で表します。LocalDateTimeとは異なり、Instantは人間が読める形式の日付と時間を含みません—瞬間の機械表現です。Oracle Java 17 (2024)仕様によると、Instantはタイムスタンプの機械交換用に設計されており、System.currentTimeMillis()の類似品ですが、ナノ秒精度です。
重要なポイント
Instantはタイムライン上の単一のポイントをモデル化するクラスです。その内部表現は2つのフィールドで構成されています:long seconds(1970-01-01T00:00:00Zからの秒数)とint nanos(現在の秒内のナノ秒、0~999999999)。
Instant値の範囲はエポックから-31557014167219200から31556889864403199秒で、両方向に約2億9200万年をカバーします。これは天文学的计算を含むあらゆる実用的なタスクに十分です。
Baeldung (2024)によると、Instantは人間が読めるタイプ(LocalDateTime、ZonedDateTime)と機械形式(ミリ秒のタイムスタンプ)の間の橋渡しです。Instantはロギング、キャッシング、同期、そして時間の絶対的な瞬間が重要なすべてのタスクに使用されます。
このクラスはComparable(瞬間の比較用)とTemporal(共通のjava.time APIでの使用用)インターフェースを実装しています。Instantは不変です—すべてのメソッドは新しいオブジェクトを返します。
Java 8以前は、時間の瞬間を扱うためにjava.util.DateとSystem.currentTimeMillis()が使用されていました。どちらのアプローチにも欠点があります。Dateは可変で、スレッドセーフではなく、エポックからのミリ秒で時間を保存しますが、メソッド名が古くなっています(getYear()は2016年に116を返します)。
Long(単純なタイムスタンプ)は高速でコンパクトですが、ナノ秒の組み込みサポートがなく、読み取り可能な形式で表示されず、デバッグ時に手動解析が必要です。Longアプローチはデータ型も区別しません—開発者が誤った値を渡す可能性があります。
Instantはこれらすべての問題を解決します。不変で、明示的な精度情報(秒+ナノ秒)を含み、ISO-8601形式“2026-07-21T15:00:00Z”にシリアライズされ、変換のための豊富なAPIを持ちます。SonarSource (2024)によると、Instantはすべての新しいプロジェクトでDateの推奨代替品です。
現在の瞬間はInstant.now()で取得します。LocalDateTime.now()とは異なり、Instant.now()はデバイスのタイムゾーンを無視して常にUTCの時刻を返します。これによりサーバータイムスタンプに最適です。
既存の値から:Instant.ofEpochSecond(long epochSecond) — エポックからの秒から、Instant.ofEpochMilli(long epochMilli) — ミリ秒から、Instant.parse(CharSequence) — ISO-8601文字列(“2026-07-21T15:00:00Z”)から。
読み取り用:getEpochSecond() — エポックからの秒数、toEpochMilli() — ミリ秒数、getNano() — ナノ秒。toString()メソッドは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はatZone(ZoneId)を介してZonedDateTimeに変換されます。たとえば、Instant.now().atZone(ZoneId.of(“Europe/Moscow”))はモスクワのZonedDateTimeを返します。ゾーンなしでは変換は不可能です—Instantにはカレンダー情報が含まれていません。
InstantをLocalDateTimeに変換するには:atZone(ZoneId).toLocalDateTime()。このアプローチは明示的で情報を失いません。逆変換:LocalDateTime.atZone(ZoneId).toInstant()。
java.util.Dateとの互換性:Date.from(instant)およびdate.toInstant()。これはミリ秒までの精度を保持する双方向変換です(Dateはナノ秒をサポートしません)。java.sql.Timestampの場合は、ナノ秒サポート付きでTimestamp.from(instant)を使用します。
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()
Instantの重要な特徴は、タイムゾーンから完全に独立していることです。Instant.now()は世界中のどのデバイスでも同じ結果を返します。これはUTCに時刻を固定することで実現されます。
タイムゾーンはInstantを人間に表示するためにのみ必要です。これにはatZone(ZoneId)を使用します。ZoneId.systemDefault()はオペレーティングシステムで設定されたデバイスのタイムゾーンを返します。ZoneOffset.UTCはUTCの定数です。
分散システムでは、すべてのタイムスタンプをInstant(またはZoneOffset.UTC付きのOffsetDateTime)で保存および送信することをお勧めします。現地時間への変換は、ユーザーに表示する前にクライアントでのみ実行されます。これによりタイムゾーンの混乱を防ぎます。
分散Androidアプリケーションでは、正しいキャッシング、通知、共同編集のために時刻同期が重要です。InstantはUTCに固定されているため、このタスクに自然な選択肢です。
異なるデバイスのタイムスタンプを比較する場合、システムクロックがずれている可能性があることを考慮する必要があります。サーバー時刻を基準として使用することをお勧めします。サーバーはUTCでInstantを返し、クライアントは相対計算のためにのみローカルのInstantと比較します。
2つの瞬間の差を計算するには、Duration.between(Instant start, Instant end)を使用します。このメソッドは時間、分、秒に変換可能なDurationを返します。isAfter()とisBefore()メソッドで瞬間を比較できます。
fun isCacheExpired(
cachedAt: Instant,
ttlMinutes: Long
): Boolean {
val elapsed = Duration.between(cachedAt, Instant.now())
return elapsed.toMinutes() >= ttlMinutes
}
最初の例は、タイムスタンプ付きのイベントロギングです。InstantはRoomデータベースに保存され、サーバーに送信されます。タイムスタンプは明確な解釈のためにUTCで記録されます。
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) }
}
}
2番目の例は、イベントからの経過時間の決定です。Duration.betweenを使用して“5分前”、“2時間前”を表示します—メッセンジャーやソーシャルネットワークで一般的な形式です。
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"
}
}
3番目の例は、サーバーとクライアント間のデータ同期です。Instantを使用して最後の更新時刻を追跡します。
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
}
}
最初の間違いは、ユーザーに表示するためにInstant.now().toString()を使用することです。InstantはUTC形式“2026-07-21T15:00:00Z”で出力され、人間には読めません。表示する前に必ずatZone()でローカルのタイムゾーンに変換してください。
2番目の間違いは、java.util.Dateに変換するときにナノ秒を失うことです。Dateはミリ秒のみをサポートします。Instantにナノ秒がある場合、Date.from(instant)で破棄されます。精度を明示的に指定するにはInstant.truncatedTo(ChronoUnit.MILLIS)を使用してください。
3番目の間違いは、toEpochMilli()とgetEpochSecond()の混同です。toEpochMilli()はエポックからのミリ秒数(long)を返し、getEpochSecond()は秒数(long)を返します。これらのメソッドを混同すると1000倍のエラーになる可能性があります。
4番目の間違いは、Instant.now()がすべてのデバイスで同期されていると想定することです。システムクロックは数分から数時間異なる可能性があります。時間に敏感な操作(認証、支払い)では、真実のソースとしてサーバーのInstantを使用してください。
よくある質問
System.currentTimeMillis()はlongを返します—タイムゾーンのバインドなしでエポックからのミリ秒数です。Instantは同じ機能を提供しますが、ナノ秒精度と変換、比較、java.timeとの互換性のための豊富なAPIを備えています。
RoomはInstantを直接サポートしていません。InstantをLong(toEpochMilli)に変換し、逆に戻す(Instant.ofEpochMilli)TypeConverterを使用してください。ナノ秒精度の場合は、エポック秒とナノ秒の2つのフィールドを保存します。
はい、Instantは不変で、equals()とhashCode()を正しく実装しています。同じ値を持つ2つのInstantは等しくなります。これにより、可変なjava.util.Dateとは異なり、HashMapやその他のコレクションの信頼性の高いキーになります。
Durationを取得するにはDuration.between(start, end)を、秒単位(long)の差にはChronoUnit.SECONDS.between(start, end)を使用します。DurationはtoMinutes()、toHours()、toDays()、toNanos()メソッドを提供します。
Instantはタイムライン上の絶対ポイントとして設計されています。タイムゾーンまたはUTCを指定しないと、Instantにカレンダー情報が含まれていないため解析は不可能です。“Z”サフィックスはゼロオフセット(UTC)を示し、ISO-8601形式では必須です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。