Rapat daring antara tim di Jakarta dan tim di Tokyo punya satu momen mulai yang sama, tetapi dua angka jam yang berbeda: peserta Jakarta membuka laptop pukul 08:00, peserta Tokyo pukul 10:00. Nilai waktu dinding seperti LocalDateTime tidak cukup untuk mewakili kejadian semacam itu karena makna “pukul berapa” bergantung pada zona si pembaca. ZonedDateTime menyelesaikannya dengan menggabungkan tiga informasi dalam satu objek: tanggal-waktu lengkap, pengenal zona (ZoneId), dan offset aktual yang berlaku pada momen tersebut. Pada Chapter ini kita pelajari anatomi ZonedDateTime, konversi antarzona, dan posisinya sebagai jembatan antar tipe java.time lainnya.
Membuat Objek ZonedDateTime #
Tiga jalur pembuatan yang lazim: ZonedDateTime.of(tahun, bulan, tanggal, jam, menit, detik, nano, zoneId) mengisi seluruh komponen; kelebihan beban of(LocalDateTime, zoneId) menginterpretasikan nilai waktu dinding pada suatu zona; dan parse() membaca teks lengkap berisi waktu, offset, dan nama zona dalam kurung siku:
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class MembuatZonedDateTime {
public static void main(String[] args) {
ZoneId jakarta = ZoneId.of("Asia/Jakarta");
// Jalur 1: isi komponen lengkap beserta ZoneId.
ZonedDateTime rapat = ZonedDateTime.of(2026, 1, 15, 8, 0, 0, 0, jakarta);
// Jalur 2: dari LocalDateTime yang sudah ada + ZoneId.
ZonedDateTime acara = ZonedDateTime.of(LocalDateTime.of(2026, 1, 15, 20, 0), jakarta);
// Jalur 3: parse teks lengkap berisi waktu + offset + zona.
ZonedDateTime dariTeks = ZonedDateTime.parse("2026-01-15T08:00+07:00[Asia/Jakarta]");
System.out.println("rapat : " + rapat);
System.out.println("acara : " + acara);
System.out.println("dariTeks : " + dariTeks);
System.out.println("getZone() : " + rapat.getZone());
System.out.println("getOffset() : " + rapat.getOffset());
System.out.println("getDayOfWeek() : " + rapat.getDayOfWeek());
System.out.println("toInstant() : " + rapat.toInstant());
System.out.println("toLocalDateTime(): " + rapat.toLocalDateTime());
}
}Output yang Dihasilkan:
rapat : 2026-01-15T08:00+07:00[Asia/Jakarta]
acara : 2026-01-15T20:00+07:00[Asia/Jakarta]
dariTeks : 2026-01-15T08:00+07:00[Asia/Jakarta]
getZone() : Asia/Jakarta
getOffset() : +07:00
getDayOfWeek() : THURSDAY
toInstant() : 2026-01-15T01:00:00Z
toLocalDateTime(): 2026-01-15T08:00Mari kita telaah baris-baris kode di atas:
- Baris 9 (
ZonedDateTime.of(2026, 1, 15, 8, 0, 0, 0, jakarta)): pukul08:00diberi makna di zonaAsia/Jakarta; objek menyimpan tanggal, waktu, zona, sekaligus offset yang berlaku. - Baris 12 (
of(LocalDateTime..., jakarta)): nilai waktu dinding diinterpretasikan pada zona yang ditentukan — pola yang tepat saat data datang dariLocalDateTime. - Baris 15 (
parse): teks2026-01-15T08:00+07:00[Asia/Jakarta]adalah bentuktoString()bawaan — format ISO-8601 yang diperluas dengan offset dan nama zona dalam kurung siku. - Baris 22-23:
getZone()mengembalikan pengenal zonaAsia/Jakarta, sedangkangetOffset()mengembalikan offset aktual+07:00yang berlaku pada momen tersebut. - Baris 24-26:
toInstant()menerjemahkan momen ke garis waktu UTC (08:00dikurangi tujuh jam menjadi01:00Z),toLocalDateTime()mengambil kembali nilai waktu dindingnya.
Perhatikan anatomi teks 2026-01-15T08:00+07:00[Asia/Jakarta]: bagian pertama adalah tanggal-waktu lokal, bagian +07:00 adalah offset terhadap UTC, dan bagian dalam kurung siku adalah pengenal zona lengkap. Offset adalah angka yang sedang berlaku; zona adalah identitas aturan yang menentukan angka itu — pembedaan penting yang akan kita dalami pada bab berikutnya.
Konversi Antar Zona #
Dua method konversi tersedia dan keduanya membingungkan bila dipilih asal pilih. withZoneSameInstant(zonaBaru) mempertahankan momen yang sama lalu menghitung ulang jam dinding di zona tujuan — inilah yang kita maksud dengan “konversi waktu” dalam percakapan sehari-hari. Sebaliknya, withZoneSameLocal(zonaBaru) menyalin angka jam dinding apa adanya ke zona baru, sehingga momen yang diwakili menjadi berbeda:
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class KonversiZona {
public static void main(String[] args) {
ZonedDateTime diJakarta =
ZonedDateTime.of(2026, 1, 15, 8, 0, 0, 0, ZoneId.of("Asia/Jakarta"));
System.out.println("di Jakarta : " + diJakarta);
// withZoneSameInstant: momen yang sama, dibaca dari zona lain.
ZonedDateTime diTokyo = diJakarta.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("di Tokyo : " + diTokyo);
System.out.println("jam di Tokyo : " + diTokyo.getHour());
// Keduanya menunjuk momen yang sama di garis waktu.
System.out.println("Instant sama? " + diJakarta.toInstant().equals(diTokyo.toInstant()));
// withZoneSameLocal: waktu dinding disalin, momennya menjadi berbeda.
ZonedDateTime jamSama = diJakarta.withZoneSameLocal(ZoneId.of("Asia/Tokyo"));
System.out.println("jam sama : " + jamSama);
System.out.println("Instant sama? " + diJakarta.toInstant().equals(jamSama.toInstant()));
// Penjumlahan pada ZonedDateTime mengikuti waktu dinding setempat.
System.out.println("plus 2 jam : " + diTokyo.plusHours(2));
}
}Output yang Dihasilkan:
di Jakarta : 2026-01-15T08:00+07:00[Asia/Jakarta]
di Tokyo : 2026-01-15T10:00+09:00[Asia/Tokyo]
jam di Tokyo : 10
Instant sama? true
jam sama : 2026-01-15T08:00+09:00[Asia/Tokyo]
Instant sama? false
plus 2 jam : 2026-01-15T12:00+09:00[Asia/Tokyo]Baris 11-13 memakai withZoneSameInstant: rapat pukul 08:00 WIB adalah pukul 10:00 JST karena Tokyo berjalan dua jam lebih maju dari Jakarta. Baris 16 membuktikan kesamaan momen — toInstant() keduanya menghasilkan objek Instant yang sama. Baris 19-21 memakai withZoneSameLocal: angka 08:00 dipertahankan di Tokyo, dan pemeriksaan Instant mengembalikan false karena kedua nilai kini merujuk dua momen berbeda. Baris 24 menegaskan bahwa penjumlahan jam pada ZonedDateTime bekerja pada waktu dinding setempat.
Jembatan Antar Tipe Date-Time #
ZonedDateTime berdiri di pusat jaringan konversi java.time. Dari satu objek ZonedDateTime kita bisa mengambil Instant untuk kebutuhan mesin, LocalDateTime untuk waktu dinding, LocalDate dan LocalTime untuk komponennya, dan sebaliknya:
| Arah konversi | Method | Makna hasilnya |
|---|---|---|
ZonedDateTime → Instant | toInstant() | momen absolut di garis waktu UTC |
ZonedDateTime → LocalDateTime | toLocalDateTime() | waktu dinding di zonanya |
Instant → ZonedDateTime | instant.atZone(zona) | momen dibaca sebagai jam lokal |
LocalDateTime → ZonedDateTime | ldt.atZone(zona) | waktu dinding diberi makna zona |
Kita telah memakai ZoneId.of("Asia/Jakarta") berkali-kali tanpa membedah objeknya. Zona apa saja yang tersedia, apa bedanya Asia/Jakarta dengan +07:00, dan bagaimana Java mengetahui offset sebuah zona? Bab berikutnya membuka isi dunia ZoneId dan ZoneOffset.
Referensi #
- Oracle: Class ZonedDateTime (Java SE 25 API)
- Oracle: Package java.time (Java SE 25 API)
- dev.java: Date Time
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
KonversiZona.java
Unduh berkasimport java.time.ZoneId;
import java.time.ZonedDateTime;
public class KonversiZona {
public static void main(String[] args) {
ZonedDateTime diJakarta =
ZonedDateTime.of(2026, 1, 15, 8, 0, 0, 0, ZoneId.of("Asia/Jakarta"));
System.out.println("di Jakarta : " + diJakarta);
// withZoneSameInstant: momen yang sama, dibaca dari zona lain.
ZonedDateTime diTokyo = diJakarta.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("di Tokyo : " + diTokyo);
System.out.println("jam di Tokyo : " + diTokyo.getHour());
// Keduanya menunjuk momen yang sama di garis waktu.
System.out.println("Instant sama? " + diJakarta.toInstant().equals(diTokyo.toInstant()));
// withZoneSameLocal: waktu dinding disalin, momennya menjadi berbeda.
ZonedDateTime jamSama = diJakarta.withZoneSameLocal(ZoneId.of("Asia/Tokyo"));
System.out.println("jam sama : " + jamSama);
System.out.println("Instant sama? " + diJakarta.toInstant().equals(jamSama.toInstant()));
// Penjumlahan pada ZonedDateTime mengikuti waktu dinding setempat.
System.out.println("plus 2 jam : " + diTokyo.plusHours(2));
}
}
MembuatZonedDateTime.java
Unduh berkasimport java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class MembuatZonedDateTime {
public static void main(String[] args) {
ZoneId jakarta = ZoneId.of("Asia/Jakarta");
// Jalur 1: isi komponen lengkap beserta ZoneId.
ZonedDateTime rapat = ZonedDateTime.of(2026, 1, 15, 8, 0, 0, 0, jakarta);
// Jalur 2: dari LocalDateTime yang sudah ada + ZoneId.
ZonedDateTime acara = ZonedDateTime.of(LocalDateTime.of(2026, 1, 15, 20, 0), jakarta);
// Jalur 3: parse teks lengkap berisi waktu + offset + zona.
ZonedDateTime dariTeks = ZonedDateTime.parse("2026-01-15T08:00+07:00[Asia/Jakarta]");
System.out.println("rapat : " + rapat);
System.out.println("acara : " + acara);
System.out.println("dariTeks : " + dariTeks);
System.out.println("getZone() : " + rapat.getZone());
System.out.println("getOffset() : " + rapat.getOffset());
System.out.println("getDayOfWeek() : " + rapat.getDayOfWeek());
System.out.println("toInstant() : " + rapat.toInstant());
System.out.println("toLocalDateTime(): " + rapat.toLocalDateTime());
}
}