Aplikasi jarang mendapat tanggal dalam bentuk objek LocalDate yang sudah rapi — data datang sebagai teks dari formulir pengguna, berkas CSV, payload JSON, atau parameter URL. Mengubah teks menjadi objek date-time disebut parsing, dan kegagalannya adalah hal biasa, bukan kejadian luar biasa: pengguna mengetik 15-01-2026 pada kolom yang berharap 15/01/2026, atau mengisi tanggal 31 pada bulan Februari. Pada Chapter ini kita pelajari parsing dengan format ISO bawaan, pola kustom, peran Locale, cara menangkap DateTimeParseException, serta mode validasi ketat yang menolak tanggal mustahil alih-alih mengoreksinya diam-diam.
Parsing Format ISO Bawaan #
Setiap tipe inti java.time memiliki method parse() satu argumen yang membaca format ISO-8601 bawaan tipe tersebut — format yang sama dengan keluaran toString() miliknya. Bentuk ini ideal untuk pertukaran antar sistem karena tidak memerlukan formatter sama sekali:
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
public class ParsingIso {
public static void main(String[] args) {
// Tanpa formatter: parse memakai format ISO-8601 bawaan tiap tipe.
LocalDate tanggal = LocalDate.parse("2026-01-15");
LocalTime jam = LocalTime.parse("08:30:15");
LocalDateTime rapat = LocalDateTime.parse("2026-01-15T08:30");
Instant momen = Instant.parse("2026-01-15T01:00:00Z");
ZonedDateTime acara = ZonedDateTime.parse("2026-01-15T08:00+07:00[Asia/Jakarta]");
System.out.println("LocalDate : " + tanggal);
System.out.println("LocalTime : " + jam);
System.out.println("LocalDateTime : " + rapat);
System.out.println("Instant : " + momen);
System.out.println("ZonedDateTime : " + acara);
}
}Output yang Dihasilkan:
LocalDate : 2026-01-15
LocalTime : 08:30:15
LocalDateTime : 2026-01-15T08:30
Instant : 2026-01-15T01:00:00Z
ZonedDateTime : 2026-01-15T08:00+07:00[Asia/Jakarta]Mari kita telaah baris-baris kode di atas:
- Baris 9 (
LocalDate.parse("2026-01-15")): tanggal ISO selalutahun-bulan-tanggaldengan tanda hubung dan dua digit untuk bulan serta tanggal. - Baris 10 (
LocalTime.parse("08:30:15")): waktu ISO memakai jam 24 jam dengan titik dua; detik boleh dilewati (08:30) dan pecahan detik boleh menyusul. - Baris 11 (
LocalDateTime.parse("2026-01-15T08:30")): hurufTmenjadi jembatan antara bagian tanggal dan bagian waktu. - Baris 12 (
Instant.parse("2026-01-15T01:00:00Z")):Instantmensyaratkan akhiranZ— teks tanpaZmembuat parsing gagal karena tanda tersebut menegaskan zona UTC. - Baris 13 (
ZonedDateTime.parse(...)): bentuk paling kaya memuat waktu, offset+07:00, dan nama zona dalam kurung siku.
Parsing dengan Pola Kustom #
Untuk format non-ISO, kita membuat DateTimeFormatter dengan ofPattern() — huruf-huruf polanya sama persis dengan yang kita pakai pada bab pemformatan, hanya arah kerjanya dibalik. Teks harus cocok pola secara persis: pemisah, jumlah digit, hingga nama bulan yang ditulis dalam bahasa sesuai Locale formatter:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
public class ParsingKustom {
public static void main(String[] args) {
Locale id = Locale.forLanguageTag("id");
// Pola kustom untuk format sehari-hari.
DateTimeFormatter polaTgl = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate dariTeks = LocalDate.parse("15/01/2026", polaTgl);
System.out.println("dd/MM/yyyy : " + dariTeks);
// Pola dengan nama bulan harus cocok dengan Locale formatter.
DateTimeFormatter polaBulan = DateTimeFormatter.ofPattern("dd MMMM yyyy").withLocale(id);
System.out.println("bulan Indonesia : " + LocalDate.parse("15 Januari 2026", polaBulan));
DateTimeFormatter polaEn = DateTimeFormatter.ofPattern("MMMM d, yyyy", Locale.US);
System.out.println("bulan Inggris : " + LocalDate.parse("January 15, 2026", polaEn));
// Teks yang tidak sesuai pola melempar DateTimeParseException.
try {
LocalDate salah = LocalDate.parse("15-01-2026", polaTgl);
System.out.println("tidak terjadi: " + salah);
} catch (DateTimeParseException e) {
System.out.println("gagal parse : " + e.getMessage());
System.out.println("posisi galat : " + e.getErrorIndex());
}
}
}Output yang Dihasilkan:
dd/MM/yyyy : 2026-01-15
bulan Indonesia : 2026-01-15
bulan Inggris : 2026-01-15
gagal parse : Text '15-01-2026' could not be parsed at index 2
posisi galat : 2Baris 12-14 mem-parse pola dd/MM/yyyy — teks 15/01/2026 terbaca menjadi 15 Januari 2026. Baris 17-18 menunjukkan pola MMMM yang menuntut nama bulan sesuai Locale: teks 15 Januari 2026 hanya lolos pada formatter ber Locale Indonesia. Baris 20-21 memakai kelebihan beban ofPattern(pola, locale) — bentuk singkat yang setara dengan withLocale. Baris 24-31 adalah pola penanganan kegagalan: blok try-catch menangkap DateTimeParseException, class exception unchecked turunan DateTimeException yang dilempar parser tanpa dinyatakan pada tanda tangan method.
Dari keluaran terlihat dua informasi diagnostik yang berharga. Method getMessage() memuat teks lengkap Text '15-01-2026' could not be parsed at index 2 — parser berhasil membaca 15 lalu berhenti karena karakter pada posisi 2 bukan tanda miring yang diharapkan pola. Method getErrorIndex() mengembalikan angka 2 itu secara terpisah, siap dipakai untuk menyorot kolom yang salah pada antarmuka pengguna.
ResolverStyle dan Validasi Ketat #
Pertanyaan tersulit dalam parsing bukan teks salah format, melainkan teks yang formatnya benar tetapi isinya mustahil: 31/02/2026. Kelasnya ResolverStyle yang mengatur perilaku ini lewat tiga mode: SMART (bawaan) mengoreksi nilai tidak sahih ke nilai terdekat yang sahih, STRICT menolaknya, dan LENIENT membiarkan luapan satuan dihitung maju. Perhatikan perbandingan dua mode pertama:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
public class ParsingKetat {
public static void main(String[] args) {
// Mode bawaan (SMART): tanggal tidak sahih disesuaikan diam-diam.
DateTimeFormatter longgar = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println("SMART 31/02/2026 : " + LocalDate.parse("31/02/2026", longgar));
// Mode ketat (STRICT): tanggal tidak sahih ditolak lewat exception.
// Pola memakai uuuu (tahun tanpa era) karena STRICT menuntutnya.
DateTimeFormatter ketat = DateTimeFormatter.ofPattern("dd/MM/uuuu")
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDate hasil = LocalDate.parse("31/02/2026", ketat);
System.out.println("tidak terjadi: " + hasil);
} catch (Exception e) {
System.out.println("STRICT 31/02/2026 ditolak");
System.out.println("pesan: " + e.getMessage());
}
}
}Output yang Dihasilkan:
SMART 31/02/2026 : 2026-02-28
STRICT 31/02/2026 ditolak
pesan: Text '31/02/2026' could not be parsed: Invalid date 'FEBRUARY 31'Pada mode SMART, 31/02/2026 diam-diam menjadi 2026-02-28 — tanggal terakhir yang sahih pada Februari 2026. Mode STRICT menolak teks yang sama dengan pesan Invalid date 'FEBRUARY 31' sehingga pemanggil tahu pasti ada masalah pada data masukan.
Satu detail pada pola mode ketat: uuuu dipakai, bukan yyyy. Huruf y pada pola berarti year-of-era — nilainya baru bermakna setelah era (Masehi atau Sebelum Masehi) diketahui — sedangkan mode STRICT menolak meresolusi tanggal bila era tidak tersedia. Huruf u adalah tahun kalender langsung tanpa ketergantungan era, sehingga aman dipakai bersama STRICT.
Dengan parsing, seluruh pintu masuk data terkunci: teks apa pun kini dapat berubah menjadi objek java.time yang sahih atau ditolak dengan jelas. Berikutnya kita masuk ke dapur pengolahan: menambah, mengurangi, dan melompat antar tanggal dengan aritmetika date-time.
Referensi #
- Oracle: Class DateTimeFormatter (Java SE 25 API)
- Oracle: Class DateTimeParseException (Java SE 25 API)
- Oracle: Enum ResolverStyle (Java SE 25 API)
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
ParsingIso.java
Unduh berkasimport java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
public class ParsingIso {
public static void main(String[] args) {
// Tanpa formatter: parse memakai format ISO-8601 bawaan tiap tipe.
LocalDate tanggal = LocalDate.parse("2026-01-15");
LocalTime jam = LocalTime.parse("08:30:15");
LocalDateTime rapat = LocalDateTime.parse("2026-01-15T08:30");
Instant momen = Instant.parse("2026-01-15T01:00:00Z");
ZonedDateTime acara = ZonedDateTime.parse("2026-01-15T08:00+07:00[Asia/Jakarta]");
System.out.println("LocalDate : " + tanggal);
System.out.println("LocalTime : " + jam);
System.out.println("LocalDateTime : " + rapat);
System.out.println("Instant : " + momen);
System.out.println("ZonedDateTime : " + acara);
}
}
ParsingKetat.java
Unduh berkasimport java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
public class ParsingKetat {
public static void main(String[] args) {
// Mode bawaan (SMART): tanggal tidak sahih disesuaikan diam-diam.
DateTimeFormatter longgar = DateTimeFormatter.ofPattern("dd/MM/yyyy");
System.out.println("SMART 31/02/2026 : " + LocalDate.parse("31/02/2026", longgar));
// Mode ketat (STRICT): tanggal tidak sahih ditolak lewat exception.
// Pola memakai uuuu (tahun tanpa era) karena STRICT menuntutnya.
DateTimeFormatter ketat = DateTimeFormatter.ofPattern("dd/MM/uuuu")
.withResolverStyle(ResolverStyle.STRICT);
try {
LocalDate hasil = LocalDate.parse("31/02/2026", ketat);
System.out.println("tidak terjadi: " + hasil);
} catch (Exception e) {
System.out.println("STRICT 31/02/2026 ditolak");
System.out.println("pesan: " + e.getMessage());
}
}
}
ParsingKustom.java
Unduh berkasimport java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
public class ParsingKustom {
public static void main(String[] args) {
Locale id = Locale.forLanguageTag("id");
// Pola kustom untuk format sehari-hari.
DateTimeFormatter polaTgl = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate dariTeks = LocalDate.parse("15/01/2026", polaTgl);
System.out.println("dd/MM/yyyy : " + dariTeks);
// Pola dengan nama bulan harus cocok dengan Locale formatter.
DateTimeFormatter polaBulan = DateTimeFormatter.ofPattern("dd MMMM yyyy").withLocale(id);
System.out.println("bulan Indonesia : " + LocalDate.parse("15 Januari 2026", polaBulan));
DateTimeFormatter polaEn = DateTimeFormatter.ofPattern("MMMM d, yyyy", Locale.US);
System.out.println("bulan Inggris : " + LocalDate.parse("January 15, 2026", polaEn));
// Teks yang tidak sesuai pola melempar DateTimeParseException.
try {
LocalDate salah = LocalDate.parse("15-01-2026", polaTgl);
System.out.println("tidak terjadi: " + salah);
} catch (DateTimeParseException e) {
System.out.println("gagal parse : " + e.getMessage());
System.out.println("posisi galat : " + e.getErrorIndex());
}
}
}