Banyak kebutuhan bisnis sebenarnya hanya membutuhkan tanggal, bukan jam: tanggal lahir seseorang, tanggal jatuh tempo tagihan, hari libur nasional, atau tanggal terbit faktur. Menyimpan nilai seperti itu bersama jam dan zona waktu justru membuka peluang salah tafsir — apakah pukul berapa maksudnya, dan di zona siapa? LocalDate di package java.time menjawab kebutuhan tersebut dengan representasi bersih: tahun, bulan, dan tanggal saja. Pada Chapter ini kita pelajari cara membuat objek LocalDate, membaca komponen kalendernya, mengubahnya secara aman, dan membandingkan dua tanggal.
Membuat Objek LocalDate #
Cara paling langsung adalah factory method LocalDate.of(tahun, bulan, tanggal). Berbeda dari Calendar yang menghitung bulan mulai dari nol, parameter bulan pada LocalDate bernilai 1 sampai 12 — Januari adalah 1, Desember adalah 12 — sehingga niat kode terbaca apa adanya. Kita juga dapat membangun objek dari teks memakai LocalDate.parse(), yang secara bawaan menerima format ISO-8601 yyyy-MM-dd. Factory method LocalDate.now() tersedia untuk tanggal hari ini berdasarkan zona waktu sistem, tetapi contoh-contoh pada Section ini memakai nilai beku seperti 2026-01-15 agar keluarannya dapat diverifikasi ulang kapan pun.
Membaca Komponen Tanggal #
Semua komponen tanggal dapat dibaca melalui method get yang bernama eksplisit. Dua komponen menarik perhatian khusus: getMonth() dan getDayOfWeek() mengembalikan enum Month dan DayOfWeek, bukan angka mentah. Enum membuat kode bebas dari magic number, dan setiap anggota enum memiliki method getDisplayName() untuk menampilkan nama dalam bahasa apa pun:
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.MonthDay;
import java.time.Year;
import java.time.format.TextStyle;
import java.util.Locale;
public class MembuatLocalDate {
public static void main(String[] args) {
LocalDate peluncuran = LocalDate.of(2026, 1, 15); // tahun, bulan (1-12), tanggal
LocalDate dariTeks = LocalDate.parse("2026-01-15"); // format ISO-8601 bawaan
System.out.println("peluncuran : " + peluncuran);
System.out.println("dariTeks : " + dariTeks);
System.out.println("getYear() : " + peluncuran.getYear());
System.out.println("getMonthValue() : " + peluncuran.getMonthValue());
System.out.println("getMonth() : " + peluncuran.getMonth());
System.out.println("getDayOfMonth() : " + peluncuran.getDayOfMonth());
System.out.println("getDayOfWeek() : " + peluncuran.getDayOfWeek());
System.out.println("getDayOfYear() : " + peluncuran.getDayOfYear());
// Month dan DayOfWeek adalah enum: nama hari/bulan aman dari salah ketik.
DayOfWeek hari = peluncuran.getDayOfWeek();
Month bulan = peluncuran.getMonth();
Locale id = Locale.forLanguageTag("id");
System.out.println("nama hari : " + hari.getDisplayName(TextStyle.FULL, id));
System.out.println("nama bulan : " + bulan.getDisplayName(TextStyle.FULL, id));
System.out.println("isLeapYear() : " + peluncuran.isLeapYear());
System.out.println("lengthOfMonth() : " + peluncuran.lengthOfMonth());
System.out.println("lengthOfYear() : " + peluncuran.lengthOfYear());
// Tipe pendamping untuk kasus yang tidak butuh tanggal lengkap.
System.out.println("MonthDay : " + MonthDay.of(Month.DECEMBER, 25));
System.out.println("Year : " + Year.of(2026) + ", kabisat? " + Year.isLeap(2026));
}
}Output yang Dihasilkan:
peluncuran : 2026-01-15
dariTeks : 2026-01-15
getYear() : 2026
getMonthValue() : 1
getMonth() : JANUARY
getDayOfMonth() : 15
getDayOfWeek() : THURSDAY
getDayOfYear() : 15
nama hari : Kamis
nama bulan : Januari
isLeapYear() : false
lengthOfMonth() : 31
lengthOfYear() : 365
MonthDay : --12-25
Year : 2026, kabisat? falseMari kita telaah baris-baris kode di atas:
- Baris 10-11: dua jalur pembuatan —
of(2026, 1, 15)dengan bulan bernilai1untuk Januari, danparse("2026-01-15")yang membaca format ISO-8601. - Baris 14-19: method
getbernama jelas menggantikanCalendar.get(Calendar.YEAR)beserta konstantanya. - Baris 22-26: enum
DayOfWeekdanMonthdiambil dari objek, lalugetDisplayName(TextStyle.FULL, Locale.forLanguageTag("id"))mencetak nama berbahasa Indonesia —KamisdanJanuari. - Baris 28-30:
isLeapYear(),lengthOfMonth(), danlengthOfYear()menjawab pertanyaan kalender tanpa perhitungan manual. - Baris 33-34:
MonthDaymewakili pasangan bulan-tanggal (misalnya--12-25untuk 25 Desember) danYearmewakili tahun tunggal — tipe kecil yang berguna untuk validasi berulang tahunan.
Mengubah dan Membandingkan Tanggal #
LocalDate bersifat immutable: method withYear, plusDays, atau minusWeeks tidak mengubah objek asal, melainkan mengembalikan objek LocalDate baru. Method until(tanggalLain, ChronoUnit.DAYS) menghitung jarak antar dua tanggal, dan isBefore/isAfter/isEqual membandingkan dua tanggal tanpa operasi pengurangan manual:
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class ManipulasiLocalDate {
public static void main(String[] args) {
LocalDate mulai = LocalDate.of(2026, 1, 15);
// Semua operasi mengembalikan objek baru; objek mulai tidak pernah berubah.
LocalDate versiBaru = mulai.withYear(2030);
LocalDate tambah45 = mulai.plusDays(45);
LocalDate mundur2 = mulai.minusWeeks(2);
System.out.println("mulai : " + mulai);
System.out.println("withYear(2030) : " + versiBaru);
System.out.println("plusDays(45) : " + tambah45);
System.out.println("minusWeeks(2) : " + mundur2);
LocalDate tenggat = LocalDate.of(2026, 3, 20);
System.out.println("until DAYS : " + mulai.until(tenggat, ChronoUnit.DAYS));
System.out.println("until MONTHS : " + mulai.until(tenggat, ChronoUnit.MONTHS));
System.out.println("isBefore(tenggat) : " + mulai.isBefore(tenggat));
System.out.println("isAfter(tenggat) : " + mulai.isAfter(tenggat));
System.out.println("isEqual(parse ulang): " + mulai.isEqual(LocalDate.parse("2026-01-15")));
}
}Output yang Dihasilkan:
mulai : 2026-01-15
withYear(2030) : 2030-01-15
plusDays(45) : 2026-03-01
minusWeeks(2) : 2026-01-01
until DAYS : 64
until MONTHS : 2
isBefore(tenggat) : true
isAfter(tenggat) : false
isEqual(parse ulang): truePerhatikan hasil plusDays(45): penjumlahan tanggal mengikuti kalender sungguhan — dari 15 Januari 2026 melewati Februari yang berjumlah 28 hari (2026 bukan tahun kabisat), sehingga mendarat di 2026-03-01. Baris until menunjukkan jarak 64 hari atau 2 bulan penuh menuju 20 Maret 2026, dan ChronoUnit.MONTHS hanya menghitung bulan utuh, menyisakan lima hari yang tidak dihitung.
Validasi Kalender Saat Pembuatan #
LocalDate menolak kombinasi tanggal yang tidak masuk akal sejak pembuatan objek, bukan saat objek dipakai. Program berikut mencoba membuat tanggal 29 Februari 2026:
import java.time.LocalDate;
public class TanggalInvalid {
public static void main(String[] args) {
// 2026 bukan tahun kabisat, sehingga 29 Februari tidak pernah ada.
LocalDate salah = LocalDate.of(2026, 2, 29);
System.out.println("tanggal: " + salah);
}
}Program berhenti dengan exit code 1 dan JVM mencetak exception di stderr; baris pertamanya berbunyi java.time.DateTimeException: Invalid date 'February 29' as '2026' is not a leap year, diikuti stack trace yang menunjuk ke pemanggilan LocalDate.of. Kegagalan cepat semacam ini menggantikan debug di kemudian hari ketika data tanggal tidak sah sudah tersebar ke basis data.
Dengan tanggal dikuasai, kebutuhan berikutnya adalah jam: jam buka toko, waktu mulai kelas, atau batas pengumpulan tugas pada hari tertentu. Bab berikutnya menghadirkan LocalTime, representasi waktu dalam satu hari tanpa tanggal dan tanpa zona waktu.
Referensi #
- Oracle: Class LocalDate (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.
ManipulasiLocalDate.java
Unduh berkasimport java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class ManipulasiLocalDate {
public static void main(String[] args) {
LocalDate mulai = LocalDate.of(2026, 1, 15);
// Semua operasi mengembalikan objek baru; objek mulai tidak pernah berubah.
LocalDate versiBaru = mulai.withYear(2030);
LocalDate tambah45 = mulai.plusDays(45);
LocalDate mundur2 = mulai.minusWeeks(2);
System.out.println("mulai : " + mulai);
System.out.println("withYear(2030) : " + versiBaru);
System.out.println("plusDays(45) : " + tambah45);
System.out.println("minusWeeks(2) : " + mundur2);
LocalDate tenggat = LocalDate.of(2026, 3, 20);
System.out.println("until DAYS : " + mulai.until(tenggat, ChronoUnit.DAYS));
System.out.println("until MONTHS : " + mulai.until(tenggat, ChronoUnit.MONTHS));
System.out.println("isBefore(tenggat) : " + mulai.isBefore(tenggat));
System.out.println("isAfter(tenggat) : " + mulai.isAfter(tenggat));
System.out.println("isEqual(parse ulang): " + mulai.isEqual(LocalDate.parse("2026-01-15")));
}
}
MembuatLocalDate.java
Unduh berkasimport java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.MonthDay;
import java.time.Year;
import java.time.format.TextStyle;
import java.util.Locale;
public class MembuatLocalDate {
public static void main(String[] args) {
LocalDate peluncuran = LocalDate.of(2026, 1, 15); // tahun, bulan (1-12), tanggal
LocalDate dariTeks = LocalDate.parse("2026-01-15"); // format ISO-8601 bawaan
System.out.println("peluncuran : " + peluncuran);
System.out.println("dariTeks : " + dariTeks);
System.out.println("getYear() : " + peluncuran.getYear());
System.out.println("getMonthValue() : " + peluncuran.getMonthValue());
System.out.println("getMonth() : " + peluncuran.getMonth());
System.out.println("getDayOfMonth() : " + peluncuran.getDayOfMonth());
System.out.println("getDayOfWeek() : " + peluncuran.getDayOfWeek());
System.out.println("getDayOfYear() : " + peluncuran.getDayOfYear());
// Month dan DayOfWeek adalah enum: nama hari/bulan aman dari salah ketik.
DayOfWeek hari = peluncuran.getDayOfWeek();
Month bulan = peluncuran.getMonth();
Locale id = Locale.forLanguageTag("id");
System.out.println("nama hari : " + hari.getDisplayName(TextStyle.FULL, id));
System.out.println("nama bulan : " + bulan.getDisplayName(TextStyle.FULL, id));
System.out.println("isLeapYear() : " + peluncuran.isLeapYear());
System.out.println("lengthOfMonth() : " + peluncuran.lengthOfMonth());
System.out.println("lengthOfYear() : " + peluncuran.lengthOfYear());
// Tipe pendamping untuk kasus yang tidak butuh tanggal lengkap.
System.out.println("MonthDay : " + MonthDay.of(Month.DECEMBER, 25));
System.out.println("Year : " + Year.of(2026) + ", kabisat? " + Year.isLeap(2026));
}
}
TanggalInvalid.java
Unduh berkasimport java.time.LocalDate;
public class TanggalInvalid {
public static void main(String[] args) {
// 2026 bukan tahun kabisat, sehingga 29 Februari tidak pernah ada.
LocalDate salah = LocalDate.of(2026, 2, 29);
System.out.println("tanggal: " + salah);
}
}