Instant: What It Is, Timestamp and Application in Development

Author: IT Sectr Published: 2026-07-13 Reading time: 9 min

Instant — an immutable class from the java.time package representing a point on the timeline in UTC with nanosecond precision. Unlike LocalDateTime, Instant does not contain date and time in a human-readable format — it is a machine representation of a moment. According to the Oracle Java 17 (2024) specification, Instant is designed for machine exchange of timestamps and is an analogue of System.currentTimeMillis(), but with nanosecond precision.

Key Takeaways

  • Instant — a moment in time in UTC with nanosecond precision, immutable and thread-safe.
  • Stores time as the number of seconds from the epoch (1970-01-01T00:00:00Z) plus nanoseconds.
  • Instant.now() returns the current moment in UTC regardless of the device’s time zone.
  • For conversion to a human-readable format, atZone(ZoneId) is used, returning a ZonedDateTime.
  • Instant is the preferred type for passing timestamps in APIs and distributed systems.

What is Instant?

Instant is a class that models a single point on the timeline. Its internal representation consists of two fields: long seconds (the number of seconds from 1970-01-01T00:00:00Z) and int nanos (nanoseconds within the current second, from 0 to 999999999).

The range of Instant values is from -31557014167219200 to 31556889864403199 seconds from the epoch, covering approximately 292 million years in both directions. This is sufficient for any practical tasks, including astronomical calculations.

According to Baeldung (2024), Instant is a bridge between human-readable types (LocalDateTime, ZonedDateTime) and machine formats (timestamp in milliseconds). Instant is used for logging, caching, synchronization, and all tasks where an absolute moment in time is important.

The class implements the Comparable (for comparing moments) and Temporal (for use in the common java.time API) interfaces. Instant is immutable — all methods return a new object.

Instant vs Date vs Long

Before Java 8, java.util.Date and System.currentTimeMillis() were used to work with moments in time. Both approaches have drawbacks. Date is mutable, not thread-safe, stores time in milliseconds from the epoch, but its method names are outdated (getYear() returns 116 for 2016).

Long (a simple timestamp) is fast and compact, but has no built-in nanosecond support, is not displayed in a readable format, and requires manual parsing during debugging. The Long approach also does not distinguish data types — a developer could pass an incorrect value.

Instant solves all these problems. It is immutable, contains explicit precision information (seconds + nanoseconds), serializes to ISO-8601 format “2026-07-21T15:00:00Z”, and has a rich API for conversions. According to SonarSource (2024), Instant is the recommended replacement for Date in all new projects.

Creating and Reading Instant

The current moment is obtained via Instant.now(). Unlike LocalDateTime.now(), Instant.now() always returns the time in UTC, ignoring the device’s time zone. This makes it ideal for server timestamps.

From existing values: Instant.ofEpochSecond(long epochSecond) — from seconds since epoch, Instant.ofEpochMilli(long epochMilli) — from milliseconds, Instant.parse(CharSequence) — from an ISO-8601 string (“2026-07-21T15:00:00Z”).

For reading: getEpochSecond() — the number of seconds since epoch, toEpochMilli() — the number of milliseconds, getNano() — nanoseconds. The toString() method returns a string in ISO-8601 format.

kotlin
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()

Converting Instant to Other Formats

Instant is converted to ZonedDateTime via atZone(ZoneId). For example, Instant.now().atZone(ZoneId.of(“Europe/Moscow”)) returns a ZonedDateTime for Moscow. Without a zone, conversion is impossible — Instant does not contain calendar information.

To convert Instant to LocalDateTime: atZone(ZoneId).toLocalDateTime(). This approach is explicit and does not lose information. Reverse conversion: LocalDateTime.atZone(ZoneId).toInstant().

For compatibility with java.util.Date: Date.from(instant) and date.toInstant(). This is a bidirectional conversion that preserves precision up to milliseconds (Date does not support nanoseconds). For java.sql.Timestamp, use Timestamp.from(instant) with nanosecond support.

kotlin
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 and Time Zones

The key feature of Instant is that it is completely independent of time zones. Instant.now() returns the same result on any device anywhere in the world. This is achieved by fixing the time in UTC.

A time zone is only needed to display Instant to a human. For this, atZone(ZoneId) is used. ZoneId.systemDefault() returns the device’s time zone set in the operating system. ZoneOffset.UTC is the constant for UTC.

In distributed systems, it is recommended to store and transmit all timestamps in Instant (or OffsetDateTime with ZoneOffset.UTC). Conversion to local time is performed only on the client before displaying to the user. This prevents time zone confusion.

Time Synchronization via Instant

In distributed Android applications, time synchronization is critical for correct caching, notifications, and collaborative editing. Instant is the natural choice for this task due to its UTC anchoring.

When comparing timestamps from different devices, you need to consider that system clocks may diverge. It is recommended to use server time as a reference. The server returns Instant in UTC, and the client compares it with the local Instant only for relative calculations.

To calculate the difference between two moments, use Duration.between(Instant start, Instant end). This method returns a Duration that can be converted to hours, minutes, seconds. The isAfter() and isBefore() methods allow comparing moments.

kotlin
fun isCacheExpired(
    cachedAt: Instant,
    ttlMinutes: Long
): Boolean {
    val elapsed = Duration.between(cachedAt, Instant.now())
    return elapsed.toMinutes() >= ttlMinutes
}

Practical Examples in Android

The first example is logging events with a timestamp. Instant is saved to the Room database and sent to the server. The timestamp is logged in UTC for unambiguous interpretation.

kotlin
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) }
    }
}

The second example is determining the time elapsed since an event. We use Duration.between to display “5 minutes ago”, “2 hours ago” — a format common in messengers and social networks.

kotlin
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"
    }
}

The third example is data synchronization between server and client. We use Instant to track the last update time.

kotlin
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
    }
}

Common Mistakes

The first mistake is using Instant.now().toString() for display to the user. Instant outputs in UTC format “2026-07-21T15:00:00Z”, which is unreadable for humans. Always convert Instant via atZone() to the local time zone before displaying.

The second mistake is losing nanoseconds when converting to java.util.Date. Date only supports milliseconds. If Instant has nanoseconds, they will be discarded in Date.from(instant). Use Instant.truncatedTo(ChronoUnit.MILLIS) to explicitly specify precision.

The third mistake is confusion between toEpochMilli() and getEpochSecond(). toEpochMilli() returns the number of milliseconds since epoch (long), while getEpochSecond() returns the number of seconds (long). Mixing these methods can result in a 1000x error.

The fourth mistake is assuming that Instant.now() is synchronized across all devices. System clocks can differ by minutes or even hours. For time-critical operations (authentication, payments), use server Instant as the source of truth.

Frequently Asked Questions

How is Instant different from System.currentTimeMillis()?

System.currentTimeMillis() returns a long — the number of milliseconds since epoch without time zone binding. Instant provides the same functionality but with nanosecond precision and a rich API for conversions, comparisons, and compatibility with java.time.

How to save Instant in Room Database?

Room does not support Instant directly. Use TypeConverter that converts Instant to Long (toEpochMilli) and back (Instant.ofEpochMilli). For nanosecond precision, save two fields: epoch-seconds and nanoseconds.

Can Instant be used as a key in HashMap?

Yes, Instant is immutable and correctly implements equals() and hashCode(). Two Instants with the same value will be equal. This makes it a reliable key for HashMap and other collections, unlike mutable java.util.Date.

How to get the difference between two Instants?

Use Duration.between(start, end) to get a Duration or ChronoUnit.SECONDS.between(start, end) for the difference in seconds (long). Duration provides toMinutes(), toHours(), toDays(), and toNanos() methods.

Why does Instant.parse() require a Z suffix or offset?

Instant is designed as an absolute point on the timeline. Without specifying a time zone or UTC, parsing is impossible because Instant does not contain calendar information. The “Z” suffix denotes zero offset (UTC) and is mandatory for the ISO-8601 format.

Summary

  • Instant — an immutable class for an absolute moment in time in UTC with nanosecond precision, preferred for timestamps in distributed systems.
  • Unlike java.util.Date, Instant is immutable, thread-safe, and has nanosecond precision. Unlike Long — it is explicitly typed and does not allow confusion with other numeric values.
  • To display to the user, Instant is converted via atZone(ZoneId) to ZonedDateTime, otherwise the time will be shown in UTC.
  • For API transmission, use Instant.toString() (ISO-8601) or toEpochMilli() for compatibility with older formats.
  • When working with Room, save Instant as Long via TypeConverter using the toEpochMilli() method.
  • To calculate elapsed time, use Duration.between(), which correctly handles nanoseconds.
  • Do not rely on device clock synchronization — for critical operations, use server Instant as the source of truth.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also