Convert Unix Timestamp in Kotlin

For Kotlin on the JVM, use the modern java.time API:Instant for Unix timestamps, and ZonedDateTime for timezone-aware display. For Kotlin Multiplatform, use kotlinx-datetime.

java.time: Unix Timestamp to Instant

Kotlin (JVM)
import java.time.Instant

// Seconds → Instant
val seconds = 1704067200L
val instantFromSeconds = Instant.ofEpochSecond(seconds)
println(instantFromSeconds) // 2024-01-01T00:00:00Z

// Milliseconds → Instant
val millis = 1704067200000L
val instantFromMillis = Instant.ofEpochMilli(millis)
println(instantFromMillis) // 2024-01-01T00:00:00Z

// Seconds + nanoseconds
val precise = Instant.ofEpochSecond(1704067200L, 500_000_000L)
println(precise) // 2024-01-01T00:00:00.500Z

java.time: Instant to Unix Timestamp

Kotlin (JVM)
import java.time.Instant

// Current timestamps
val now = Instant.now()
println(now.epochSecond)   // seconds since epoch
println(now.toEpochMilli()) // milliseconds since epoch

// From a specific instant
val i = Instant.parse("2024-01-01T00:00:00Z")
val tsSeconds = i.epochSecond
val tsMillis = i.toEpochMilli()
println(tsSeconds) // 1704067200
println(tsMillis)  // 1704067200000

java.time: Instant, LocalDateTime, ZonedDateTime

Kotlin (JVM)
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime

val instant = Instant.parse("2024-01-01T00:00:00Z")

// Instant → ZonedDateTime (timezone-aware)
val utc: ZonedDateTime = instant.atZone(ZoneId.of("UTC"))
val newYork: ZonedDateTime = instant.atZone(ZoneId.of("America/New_York"))
println(utc)    // 2024-01-01T00:00Z[UTC]
println(newYork) // 2023-12-31T19:00-05:00[America/New_York]

// Instant → LocalDateTime (requires zone/offset)
val localUtc: LocalDateTime = LocalDateTime.ofInstant(instant, ZoneOffset.UTC)
val localInNy: LocalDateTime = instant.atZone(ZoneId.of("America/New_York")).toLocalDateTime()
println(localUtc)  // 2024-01-01T00:00
println(localInNy) // 2023-12-31T19:00

// LocalDateTime → Instant (must provide zone/offset)
val ldt = LocalDateTime.of(2024, 1, 1, 0, 0)
val backToInstantUtc = ldt.toInstant(ZoneOffset.UTC)
println(backToInstantUtc) // 2024-01-01T00:00:00Z

Timezone Handling

Store and transmit timestamps as Instant (UTC timeline), and only apply a timezone when converting to a calendar date/time for display. Prefer IANA timezone IDs like America/New_York over fixed offsets.

Kotlin (JVM)
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter

val instant = Instant.ofEpochSecond(1704067200L)

val systemZone = ZoneId.systemDefault()
val zdtSystem = instant.atZone(systemZone)
println(zdtSystem) // varies by device/server timezone

val zdtTokyo = instant.atZone(ZoneId.of("Asia/Tokyo"))
val fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z")
println(zdtTokyo.format(fmt)) // 2024-01-01 09:00:00 JST

kotlinx-datetime (Kotlin Multiplatform)

kotlinx-datetime provides Instant, LocalDateTime, and TimeZone for multiplatform code. Add it to Gradle and use the same conversion logic across targets.

Gradle (Kotlin DSL)
dependencies {
  implementation("org.jetbrains.kotlinx:kotlinx-datetime:<version>")
}
Kotlin (kotlinx-datetime)
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime

// Seconds/milliseconds → Instant
val i1 = Instant.fromEpochSeconds(1704067200L)
val i2 = Instant.fromEpochMilliseconds(1704067200000L)
println(i1) // 2024-01-01T00:00:00Z
println(i2) // 2024-01-01T00:00:00Z

// Instant → epoch seconds/millis
val now = Clock.System.now()
println(now.epochSeconds)
println(now.toEpochMilliseconds())

// Timezone-aware calendar conversion
val tz = TimeZone.of("America/New_York")
val ldt: LocalDateTime = i1.toLocalDateTime(tz)
println(ldt) // 2023-12-31T19:00

// LocalDateTime → Instant (must provide timezone)
val back: Instant = ldt.toInstant(tz)
println(back) // 2024-01-01T00:00:00Z

Android-Specific Considerations

Android supports java.time on API 26+. For lower API levels, enable core library desugaring so you can use Instant and ZoneId consistently.

Gradle (Android)
android {
  compileOptions {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
    isCoreLibraryDesugaringEnabled = true
  }
}

dependencies {
  coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:<version>")
}

Don't assume the device timezone is stable

The user can change timezone settings at runtime. Store instants (UTC) and re-render display values using the current timezone when needed.

Common Pitfalls

Seconds vs Milliseconds

Unix timestamps are often seconds, but many platform APIs produce milliseconds. Mixing them is the most common bug.

import java.time.Instant

val ts = 1704067200L

// ❌ Wrong: seconds passed as milliseconds
Instant.ofEpochMilli(ts) // 1970...

// ✅ Correct
Instant.ofEpochSecond(ts)

Using LocalDateTime for a Timestamp

LocalDateTime has no timezone. If you convert without specifying a timezone/offset, you get different results on different machines.

import java.time.Instant
import java.time.ZoneOffset

val i = Instant.ofEpochSecond(1704067200L)

// ✅ Convert via an explicit zone/offset
val ldtUtc = i.atOffset(ZoneOffset.UTC).toLocalDateTime()

DST and Ambiguous Local Times

Around DST transitions, some local times don't exist or occur twice. Prefer doing arithmetic on Instant and apply timezones only for display.

Frequently Asked Questions

Should I use java.time or kotlinx-datetime in Kotlin?

On the JVM (server, desktop, Android with desugaring), prefer java.time (Instant, ZonedDateTime). For Kotlin Multiplatform, use kotlinx-datetime so the same code works on JVM/JS/Native.

Does Kotlin use seconds or milliseconds for Unix timestamps?

Kotlin itself doesn’t define a unit. Many JVM APIs use seconds (Instant.ofEpochSecond) while Android/JavaScript often use milliseconds (System.currentTimeMillis, Date.now). Always confirm the unit before converting.

Why is my converted date off by hours?

You likely used the system default timezone (or a LocalDateTime without a timezone) when you meant UTC. Convert timestamps to Instant first, then format for a specific ZoneId/TimeZone.

What is the difference between Instant and LocalDateTime in Kotlin?

Instant is an absolute point in time (UTC timeline). LocalDateTime is just calendar date/time without timezone context. To convert between them you must provide a timezone or offset.

Can Unix timestamps be negative in Kotlin?

Yes. Negative epoch seconds represent instants before 1970-01-01T00:00:00Z. Both java.time.Instant and kotlinx.datetime.Instant support them.