Library java.time sangat baik, namun kode yang memakainya hanya dapat dikompilasi ke JVM. Saat kita membangun aplikasi Kotlin Multiplatform (KMP) yang menargetkan Native, JS, dan Android secara bersamaan, diperlukan library tanggal yang dapat berjalan di semua target tersebut. JetBrains merilis kotlinx-datetime untuk memenuhi kebutuhan ini. Bab ini memperkenalkan tipe utama library ini, perbedaan API dengan java.time, dan strategi pemilihan antara keduanya.
Apa Itu kotlinx-datetime #
kotlinx-datetime adalah library multiplatform resmi JetBrains yang menyediakan subset java.time dalam desain ulang yang konsisten lintas target. Library ini tidak menggantikan java.time di JVM — keduanya dapat berdampingan. Pada JVM, kotlinx-datetime menjadi thin wrapper di atas java.time; pada target Native dan JS, library ini memakai implementasi sendiri.
Tabel berikut membandingkan tipe utama yang tersedia:
| Tipe kotlinx-datetime | Padanan java.time | Catatan |
|---|---|---|
Instant | java.time.Instant | Hampir identik, sama-sama UTC |
LocalDate | java.time.LocalDate | Mirip, beberapa method berbeda nama |
LocalDateTime | java.time.LocalDateTime | Tersedia, namun jarang dipakai di KMP |
DateTimePeriod | (gabungan Period + Duration) | Pengukur waktu fleksibel |
DayOfWeek | java.time.DayOfWeek | Enum sama |
Month | java.time.Month | Enum sama |
Yang tidak tersedia di kotlinx-datetime: ZonedDateTime, OffsetDateTime, DateTimeFormatter dengan pola kompleks, ChronoUnit, dan sebagian besar fitur lanjutan java.time. Pengembang sengaja menjaga API tetap minimal agar dapat diimplementasikan secara konsisten di semua target.
Konsep API dan Padanan java.time #
Bila kita membandingkan cuplikan kotlinx-datetime dengan padanan java.time-nya, perbedaannya tipis namun konsisten. Cuplikan berikut menunjukkan padanan dalam sintaks java.time (yang dapat dikompilasi di lingkungan JVM murni). Pada proyek KMP nyata, ganti impor java.time.* dengan kotlinx.datetime.*:
// kotlinx-datetime:
import kotlinx.datetime.*
val sekarang: Instant = Clock.System.now()
val hariIni: LocalDate = sekarang.toLocalDateTime(TimeZone.currentSystemDefault()).date
val besok: LocalDate = hariIni.plus(DatePeriod(days = 1))
val antara: Int = hariIni.until(besok, DateTimeUnit.DAY)// java.time:
import java.time.*
val sekarang: Instant = Instant.now()
val hariIni: LocalDate = LocalDate.now()
val besok: LocalDate = hariIni.plusDays(1)
val antara: Long = ChronoUnit.DAYS.between(hariIni, besok)Perbedaan inti yang patut dicatat: kotlinx-datetime memakai Clock.System untuk mendapatkan waktu saat ini (memungkinkan injeksi test clock), DatePeriod sebagai padanan Period, dan DateTimeUnit sebagai padanan ChronoUnit. Pemisahan Clock sebagai parameter eksplisit adalah pola yang disarankan karena memudahkan testing.
Contoh Padanan java.time yang Dapat Dijalankan #
Karena library kotlinx-datetime tidak terpasang di lingkungan ini, kode berikut memakai java.time sebagai padanan untuk mendemonstrasikan konsep API yang setara. Komentar pada tiap blok menandai nama padanan di kotlinx-datetime:
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
// Catatan: file ini mendemonstrasikan konsep API kotlinx-datetime
// dengan menggunakan java.time sebagai padanan yang dapat dikompilasi.
// Pada proyek Kotlin Multiplatform nyata, kita akan mengimpor
// kotlinx.datetime.* sebagai pengganti java.time.*
fun main() {
// 1. Instant kotlinx-datetime: titik waktu UTC, sama konsepnya
// dengan java.time.Instant.
val instan: Instant = Instant.parse("2026-01-15T08:30:00Z")
println("=== Instant ===")
println("instant : $instan")
// 2. LocalDate kotlinx-datetime: tanggal tanpa zona, serupa
// dengan java.time.LocalDate.
val tanggal: LocalDate = LocalDate.of(2026, 1, 15)
println()
println("=== LocalDate ===")
println("tanggal : $tanggal")
println("tahun/bulan/hr : ${tanggal.year}/${tanggal.monthValue}/${tanggal.dayOfMonth}")
// 3. LocalDateTime kotlinx-datetime: tanggal + jam tanpa zona.
val jadwal: LocalDateTime = LocalDateTime.of(2026, 1, 15, 8, 30)
println()
println("=== LocalDateTime ===")
println("jadwal : $jadwal")
// 4. Konversi antara LocalDate <-> Instant pada zona tertentu.
// Pada kotlinx-datetime: LocalDate.atStartOfDayIn(tz)
// Di sini dipakai padanan java.time.
val zonaJakarta: ZoneId = ZoneId.of("Asia/Jakarta")
val instanAwalHari: Instant = tanggal.atStartOfDay(zonaJakarta).toInstant()
println()
println("=== Konversi LocalDate -> Instant ===")
println("instanAwalHari : $instanAwalHari")
// 5. Konversi Instant -> LocalDate pada zona tertentu.
// Pada kotlinx-datetime: instant.toLocalDateTime(tz).date
val ldtJakarta = instan.atZone(zonaJakarta)
val tanggalJakarta: LocalDate = ldtJakarta.toLocalDate()
println()
println("=== Konversi Instant -> LocalDate ===")
println("tanggalJakarta : $tanggalJakarta")
// 6. Format ISO-8601 (sama dengan java.time untuk sebagian besar tipe)
val fmtIso: DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE
println()
println("=== Format ISO ===")
println("ISO LocalDate : ${fmtIso.format(tanggal)}")
println("toString instant: ${instan.toString()}")
}Output yang Dihasilkan:
=== Instant ===
instant : 2026-01-15T08:30:00Z
=== LocalDate ===
tanggal : 2026-01-15
tahun/bulan/hr : 2026/1/15
=== LocalDateTime ===
jadwal : 2026-01-15T08:30
=== Konversi LocalDate -> Instant ===
instanAwalHari : 2026-01-14T17:00:00Z
=== Konversi Instant -> LocalDate ===
tanggalJakarta : 2026-01-15
=== Format ISO ===
ISO LocalDate : 2026-01-15
toString instant: 2026-01-15T08:30:00ZMari kita telaah baris-baris kode di atas:
- Baris 13 (
Instant.parse("2026-01-15T08:30:00Z")): padanan langsungkotlinx.datetime.Instant.parse(...). Constructor dan parser ISO-8601 identik di kedua library, sehingga kode pertukaran data tetap kompatibel. - Baris 19 (
LocalDate.of(2026, 1, 15)): padanankotlinx.datetime.LocalDate(2026, 1, 15)— di kotlinx-datetime,LocalDatedipakai sebagai constructor function, bukan method statisof. - Baris 31 (
tanggal.atStartOfDay(zonaJakarta).toInstant()): padanankotlinx.datetime.LocalDate.atStartOfDayIn(TimeZone). Pada JVM,ZoneIddanTimeZonekotlinx-datetime saling terhubung; konversi tidak mengubah offset. - Baris 38 (
instan.atZone(zonaJakarta).toLocalDate()): padanankotlinx.datetime.Instant.toLocalDateTime(TimeZone).date. Pola ini menampilkan tanggal lokal dari instan UTC. - Baris 45 (
fmtIso.format(tanggal)): padanankotlinx.datetime.LocalDate.toString(). kotlinx-datetime sengaja tidak menyediakanDateTimeFormatterdengan pola custom di semua target; formatting kustom didelegasikan kejava.timesaat berjalan di JVM.
Kapan Memilih kotlinx-datetime vs java.time #
| Skenario | Pilihan |
|---|---|
| Aplikasi server JVM murni | java.time (lebih lengkap) |
| Aplikasi Android dengan codebase KMP | kotlinx-datetime (lintas target) |
| Logika domain lintas-platform (model data) | kotlinx-datetime |
| Tampilan zona kompleks (DST, zone rules) | java.time di sisi JVM |
Format custom (dd MMM yyyy) | java.time.DateTimeFormatter |
Strategi yang umum dipakai di proyek KMP: simpan tipe data sebagai kotlinx-datetime.Instant dan kotlinx-datetime.LocalDate di modul commonMain. Di modul jvmMain, konversi ke java.time saat diperlukan (mis. untuk formatter kompleks atau interaksi dengan library Java).
Referensi Lanjutan #
- kotlinx-datetime
— halaman GitHub
kotlinx-datetimeuntuk abstraksi tanggal-jam lintas platform - Kotlin Multiplatform
— dokumentasi Kotlin Multiplatform untuk pustaka lintas platform seperti
kotlinx-datetime
Selanjutnya #
Setelah memahami lanskap library tanggal, kita akan masuk ke pemakaian konkret tipe LocalDate dan LocalTime untuk merepresentasikan tanggal dan jam tanpa zona. Bab LocalDate dan LocalTime akan membahas konstruksi, parsing, dan akses properti harian.