Date & Time #
Sebelum Java 8, bekerja dengan tanggal dan waktu adalah mimpi buruk. java.util.Date bisa mewakili tanggal sekaligus waktu tapi tidak keduanya secara terpisah. Calendar verbose dan rawan bug. Keduanya mutable — artinya objek tanggal bisa diubah dari mana saja, menyebabkan bug yang sulit dilacak. Java 8 memperkenalkan java.time — API baru yang didesain ulang dari awal, immutable, thread-safe, dan jauh lebih intuitif. Artikel ini membahas semua kelas utama di java.time: cara membuat, memanipulasi, membandingkan, memformat, dan mengonversi tanggal dan waktu — termasuk penanganan zona waktu dan pengukuran durasi.
Gambaran Umum java.time #
Paket java.time punya satu prinsip utama: setiap kelas hanya mewakili satu konsep. Tidak ada kelas “serba bisa” seperti Date lama yang ambigu.
| Kelas | Mewakili | Contoh |
|---|---|---|
LocalDate | Tanggal saja, tanpa waktu | 2025-08-17 |
LocalTime | Waktu saja, tanpa tanggal | 14:30:00 |
LocalDateTime | Tanggal + waktu, tanpa zona | 2025-08-17T14:30:00 |
ZonedDateTime | Tanggal + waktu + zona waktu | 2025-08-17T14:30:00+07:00[Asia/Jakarta] |
Instant | Titik waktu dalam UTC (epoch) | 2025-08-17T07:30:00Z |
Duration | Selisih waktu dalam jam/menit/detik | PT4H30M (4 jam 30 menit) |
Period | Selisih tanggal dalam hari/bulan/tahun | P1Y2M3D (1 tahun 2 bulan 3 hari) |
Semua kelas di java.time bersifat immutable — setiap operasi seperti plusDays() atau minusHours() tidak mengubah objek asli, melainkan mengembalikan objek baru. Ini membuatnya aman dipakai di lingkungan multithreading tanpa sinkronisasi tambahan.
flowchart LR
A["LocalDate\n(tanggal saja)"] --> D["LocalDateTime\n(tanggal + waktu)"]
B["LocalTime\n(waktu saja)"] --> D
D --> E["ZonedDateTime\n(+ zona waktu)"]
E --> F["Instant\n(UTC epoch)"]Hindarijava.util.Date,java.util.Calendar, danjava.text.SimpleDateFormatdi kode baru. Ketiganya mutable, tidak thread-safe, dan penuh jebakan.java.timeadalah penggantinya sejak Java 8.
LocalDate #
LocalDate mewakili tanggal — tahun, bulan, hari — tanpa komponen waktu sama sekali. Gunakan ini untuk tanggal lahir, tanggal jatuh tempo, tanggal event, atau data apa pun yang tidak perlu informasi jam.
Membuat LocalDate #
import java.time.LocalDate;
import java.time.Month;
// Tanggal hari ini dari system clock
LocalDate hari ini = LocalDate.now();
System.out.println(hariIni); // 2025-05-07
// Tanggal spesifik — bulan menggunakan angka (1-12)
LocalDate kemerdekaan = LocalDate.of(1945, 8, 17);
LocalDate lebaran = LocalDate.of(2025, Month.MARCH, 30); // bisa pakai enum Month
// Parsing dari string ISO-8601
LocalDate dariString = LocalDate.parse("2025-12-31");
// Dari hari ke-N dalam setahun (day-of-year)
LocalDate hariKe100 = LocalDate.ofYearDay(2025, 100); // 10 April 2025
Membaca Komponen #
LocalDate tanggal = LocalDate.of(2025, 8, 17);
int tahun = tanggal.getYear(); // 2025
int bulan = tanggal.getMonthValue(); // 8
int hari = tanggal.getDayOfMonth(); // 17
Month namaBulan = tanggal.getMonth(); // AUGUST
DayOfWeek hariMinggu = tanggal.getDayOfWeek(); // SUNDAY
int hariDalamTahun = tanggal.getDayOfYear(); // 229
int hariDalamBulan = tanggal.lengthOfMonth(); // 31 (Agustus punya 31 hari)
boolean tahunKabisat = tanggal.isLeapYear(); // false
Manipulasi Tanggal #
Karena LocalDate immutable, semua metode manipulasi mengembalikan objek baru. Objek asli tidak berubah.
LocalDate awal = LocalDate.of(2025, 1, 15);
// Tambah dan kurangi
LocalDate semingguLagi = awal.plusWeeks(1); // 2025-01-22
LocalDate sebulanLalu = awal.minusMonths(1); // 2024-12-15
LocalDate setahunKemudian = awal.plusYears(1); // 2026-01-15
// Atur komponen spesifik (withXxx mengganti komponen tertentu)
LocalDate gantiBulan = awal.withMonth(6); // 2025-06-15
LocalDate gantiHari = awal.withDayOfMonth(1); // 2025-01-01
// Hari pertama dan terakhir dalam bulan
LocalDate pertama = awal.withDayOfMonth(1);
LocalDate terakhir = awal.withDayOfMonth(awal.lengthOfMonth());
Membandingkan Tanggal #
LocalDate a = LocalDate.of(2025, 1, 1);
LocalDate b = LocalDate.of(2025, 6, 15);
boolean aBefore = a.isBefore(b); // true
boolean aAfter = a.isAfter(b); // false
boolean aEqual = a.isEqual(b); // false
int selisih = a.compareTo(b); // negatif jika a lebih awal
// Cek apakah suatu tanggal berada dalam rentang
LocalDate sekarang = LocalDate.now();
boolean dalamRentang = !sekarang.isBefore(a) && !sekarang.isAfter(b);
LocalTime #
LocalTime mewakili waktu dalam sehari — jam, menit, detik, nanodetik — tanpa informasi tanggal atau zona waktu. Cocok untuk jam buka toko, jadwal rapat, atau waktu alarm.
Membuat LocalTime #
import java.time.LocalTime;
// Waktu sekarang
LocalTime sekarang = LocalTime.now();
// Waktu spesifik — parameter: jam, menit, detik (opsional), nanodetik (opsional)
LocalTime jamKantor = LocalTime.of(9, 0); // 09:00
LocalTime makan = LocalTime.of(12, 30, 0); // 12:30:00
LocalTime presisi = LocalTime.of(14, 0, 0, 500_000_000); // 14:00:00.5
// Nilai batas
LocalTime tengahMalam = LocalTime.MIDNIGHT; // 00:00
LocalTime tengahHari = LocalTime.NOON; // 12:00
// Parsing dari string
LocalTime dariString = LocalTime.parse("14:30:00");
Membaca dan Memanipulasi #
LocalTime t = LocalTime.of(14, 30, 45);
int jam = t.getHour(); // 14
int menit = t.getMinute(); // 30
int detik = t.getSecond(); // 45
// Tambah dan kurangi
LocalTime satuJamLagi = t.plusHours(1); // 15:30:45
LocalTime tigapuluhMenLalu = t.minusMinutes(30); // 14:00:45
// LocalTime wrap-around: lewat tengah malam kembali ke awal hari
LocalTime hampirTengahMalam = LocalTime.of(23, 50);
LocalTime setengahJamLagi = hampirTengahMalam.plusMinutes(30); // 00:20 (wrap!)
// Membandingkan
boolean lebihAwal = LocalTime.of(9, 0).isBefore(LocalTime.of(17, 0)); // true
LocalDateTime #
LocalDateTime adalah gabungan LocalDate dan LocalTime dalam satu objek. Ia mewakili momen spesifik — misalnya “Senin 17 Agustus 2025 pukul 08.00” — tapi tanpa informasi zona waktu. Ini cukup untuk sebagian besar kebutuhan internal aplikasi yang tidak perlu mengelola multi-timezone.
Membuat LocalDateTime #
import java.time.LocalDateTime;
// Sekarang
LocalDateTime sekarang = LocalDateTime.now();
// Spesifik
LocalDateTime acara = LocalDateTime.of(2025, 8, 17, 8, 0, 0);
// Gabung dari LocalDate + LocalTime
LocalDate tanggal = LocalDate.of(2025, 8, 17);
LocalTime waktu = LocalTime.of(8, 0);
LocalDateTime gabungan = LocalDateTime.of(tanggal, waktu);
// atau: tanggal.atTime(waktu)
// atau: tanggal.atTime(8, 0)
// Parsing
LocalDateTime dariString = LocalDateTime.parse("2025-08-17T08:00:00");
Manipulasi dan Ekstraksi #
LocalDateTime dt = LocalDateTime.of(2025, 8, 17, 14, 30);
// Semua operasi plus/minus dari LocalDate dan LocalTime tersedia
LocalDateTime besok = dt.plusDays(1); // 2025-08-18T14:30
LocalDateTime sejamLalu = dt.minusHours(1); // 2025-08-17T13:30
// Ekstrak komponen
LocalDate bagianTanggal = dt.toLocalDate(); // 2025-08-17
LocalTime bagianWaktu = dt.toLocalTime(); // 14:30
// Ganti komponen spesifik
LocalDateTime gantiBulan = dt.withMonth(12).withDayOfMonth(31);
// 2025-12-31T14:30
Konversi ke ZonedDateTime #
import java.time.ZoneId;
LocalDateTime dt = LocalDateTime.of(2025, 8, 17, 14, 30);
// Pasangkan dengan zona waktu untuk mendapat ZonedDateTime
ZonedDateTime diJakarta = dt.atZone(ZoneId.of("Asia/Jakarta"));
ZonedDateTime diLondon = dt.atZone(ZoneId.of("Europe/London"));
ZonedDateTime #
ZonedDateTime adalah LocalDateTime yang dilengkapi dengan informasi zona waktu. Gunakan ini saat aplikasimu beroperasi di lebih dari satu zona waktu — sistem reservasi, aplikasi meeting global, notifikasi terjadwal lintas negara.
Membuat ZonedDateTime #
import java.time.ZonedDateTime;
import java.time.ZoneId;
// Sekarang di zona waktu sistem
ZonedDateTime sekarang = ZonedDateTime.now();
// Sekarang di zona waktu tertentu
ZonedDateTime diJakarta = ZonedDateTime.now(ZoneId.of("Asia/Jakarta"));
ZonedDateTime diTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime diNY = ZonedDateTime.now(ZoneId.of("America/New_York"));
// Spesifik
ZonedDateTime meeting = ZonedDateTime.of(
2025, 8, 17, 14, 0, 0, 0,
ZoneId.of("Asia/Jakarta")
);
// Daftar semua zone ID yang tersedia
ZoneId.getAvailableZoneIds().stream()
.filter(z -> z.startsWith("Asia"))
.sorted()
.forEach(System.out::println);
Konversi Antar Zona Waktu #
ZonedDateTime jakartaTime = ZonedDateTime.of(
2025, 8, 17, 14, 0, 0, 0,
ZoneId.of("Asia/Jakarta")
);
// Asia/Jakarta = UTC+7
// Konversi ke zona lain — momen yang sama, representasi berbeda
ZonedDateTime londonTime = jakartaTime.withZoneSameInstant(ZoneId.of("Europe/London"));
ZonedDateTime tokyoTime = jakartaTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
System.out.println("Jakarta: " + jakartaTime); // 2025-08-17T14:00+07:00[Asia/Jakarta]
System.out.println("London: " + londonTime); // 2025-08-17T08:00+01:00[Europe/London]
System.out.println("Tokyo: " + tokyoTime); // 2025-08-17T16:00+09:00[Asia/Tokyo]
Informasi Offset dan Zona #
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Jakarta"));
ZoneId zona = zdt.getZone(); // Asia/Jakarta
ZoneOffset offset = zdt.getOffset(); // +07:00
// Konversi ke Instant (UTC)
Instant utc = zdt.toInstant();
// Konversi ke LocalDateTime (buang info zona)
LocalDateTime lokal = zdt.toLocalDateTime();
Instant #
Instant mewakili satu titik waktu di garis waktu universal — dihitung dalam detik dan nanodetik sejak Unix epoch (1 Januari 1970 00:00:00 UTC). Tidak ada konsep “tanggal” atau “jam” di sini, hanya angka. Gunakan Instant untuk timestamp log, audit trail, pengukuran durasi eksekusi, atau menyimpan waktu di database dalam format UTC.
Membuat dan Membaca Instant #
import java.time.Instant;
// Sekarang dalam UTC
Instant sekarang = Instant.now();
System.out.println(sekarang); // 2025-08-17T07:00:00.123Z
// Dari epoch second
Instant dariEpoch = Instant.ofEpochSecond(1_700_000_000L);
Instant dariMilli = Instant.ofEpochMilli(1_700_000_000_000L);
// Epoch second dan milli dari Instant
long epochSecond = sekarang.getEpochSecond();
long epochMilli = sekarang.toEpochMilli();
// Nilai batas
Instant awalZaman = Instant.EPOCH; // 1970-01-01T00:00:00Z
Instant minMax = Instant.MIN; // jauh di masa lalu
Manipulasi dan Konversi #
Instant t = Instant.now();
// Tambah dan kurangi — hanya dalam satuan detik/nanodetik/Duration
Instant satuMenitLagi = t.plusSeconds(60);
Instant satuJamLalu = t.minusSeconds(3600);
Instant denganDuration = t.plus(Duration.ofHours(2));
// Konversi Instant ke ZonedDateTime untuk kebutuhan tampilan
ZonedDateTime tampilan = t.atZone(ZoneId.of("Asia/Jakarta"));
System.out.println(tampilan); // 2025-08-17T14:00:00.123+07:00[Asia/Jakarta]
// Membandingkan
Instant a = Instant.ofEpochSecond(1000);
Instant b = Instant.ofEpochSecond(2000);
boolean aLebihAwal = a.isBefore(b); // true
long selisihDetik = b.getEpochSecond() - a.getEpochSecond(); // 1000
Duration dan Period #
Java punya dua kelas untuk mewakili selang waktu: Duration untuk selang berbasis waktu (jam, menit, detik), dan Period untuk selang berbasis kalender (hari, bulan, tahun). Keduanya tidak saling bisa ditukar — pilih berdasarkan konteks.
Duration — Selang Berbasis Waktu #
Duration cocok untuk mengukur berapa lama sesuatu berlangsung: durasi sebuah proses, jeda antar event, atau timeout koneksi.
import java.time.Duration;
// Buat Duration dari satuan
Duration duaJam = Duration.ofHours(2);
Duration tigapuluhMen = Duration.ofMinutes(30);
Duration seratusDetik = Duration.ofSeconds(100);
// Selisih antara dua waktu
LocalTime mulai = LocalTime.of(9, 0);
LocalTime selesai = LocalTime.of(17, 30);
Duration kerja = Duration.between(mulai, selesai); // PT8H30M
System.out.println(kerja.toHours()); // 8
System.out.println(kerja.toMinutes()); // 510
System.out.println(kerja.toSeconds()); // 30600
// Duration antara dua Instant (untuk mengukur eksekusi)
Instant awal = Instant.now();
// ... kode yang diukur ...
Instant akhir = Instant.now();
Duration eksekusi = Duration.between(awal, akhir);
System.out.println("Waktu eksekusi: " + eksekusi.toMillis() + " ms");
// Manipulasi
Duration diperpanjang = kerja.plusHours(1); // PT9H30M
Duration dipersingkat = kerja.minusMinutes(30); // PT8H
Duration digandakan = kerja.multipliedBy(2); // PT17H
Period — Selang Berbasis Kalender #
Period cocok untuk selisih antar tanggal yang perlu mempertimbangkan hari dalam bulan dan jumlah hari dalam tahun — misalnya umur seseorang, masa kontrak, atau jatuh tempo.
import java.time.Period;
// Buat Period dari satuan
Period sebulan = Period.ofMonths(1);
Period satu tahun = Period.ofYears(1);
Period dua setengah = Period.of(2, 6, 0); // 2 tahun 6 bulan
// Selisih antara dua tanggal
LocalDate lahir = LocalDate.of(1995, 7, 20);
LocalDate sekarang = LocalDate.now();
Period umur = Period.between(lahir, sekarang);
System.out.printf("Umur: %d tahun %d bulan %d hari%n",
umur.getYears(), umur.getMonths(), umur.getDays());
// Tambah Period ke tanggal
LocalDate mulaiKontrak = LocalDate.of(2025, 1, 1);
LocalDate akhirKontrak = mulaiKontrak.plus(Period.ofYears(2)); // 2027-01-01
LocalDate perpanjangan = akhirKontrak.plus(Period.ofMonths(6)); // 2027-07-01
// Mengapa Duration tidak bisa untuk ini?
// Duration.ofDays(365) tidak sama dengan Period.ofYears(1) di tahun kabisat
LocalDate tahunKabisat = LocalDate.of(2024, 1, 1);
LocalDate setahunLater = tahunKabisat.plus(Period.ofYears(1)); // 2025-01-01 ✓
// Duration tidak tahu soal kabisat — Period paham konteks kalender
Format dan Parsing #
DateTimeFormatter adalah alat untuk mengubah objek datetime menjadi string (format) dan mengubah string menjadi objek datetime (parsing). Ia thread-safe dan immutable, berbeda dengan SimpleDateFormat lama yang notorious karena tidak thread-safe.
Format Bawaan #
import java.time.format.DateTimeFormatter;
LocalDateTime dt = LocalDateTime.of(2025, 8, 17, 14, 30, 0);
// Format ISO bawaan (tidak perlu buat formatter)
System.out.println(dt); // 2025-08-17T14:30
System.out.println(dt.format(DateTimeFormatter.ISO_DATE)); // 2025-08-17
System.out.println(dt.format(DateTimeFormatter.ISO_TIME)); // 14:30
// Format lokal
System.out.println(dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// 2025-08-17T14:30:00
Format Kustom #
// Buat formatter dengan pola kustom
DateTimeFormatter indonesia = DateTimeFormatter.ofPattern("dd/MM/yyyy");
DateTimeFormatter lengkap = DateTimeFormatter.ofPattern("dd MMMM yyyy HH:mm");
DateTimeFormatter denganHari = DateTimeFormatter.ofPattern("EEEE, dd MMMM yyyy", new java.util.Locale("id", "ID"));
LocalDate tanggal = LocalDate.of(2025, 8, 17);
LocalDateTime waktu = LocalDateTime.of(2025, 8, 17, 8, 0);
System.out.println(tanggal.format(indonesia)); // 17/08/2025
System.out.println(waktu.format(lengkap)); // 17 August 2025 08:00
System.out.println(tanggal.format(denganHari)); // Minggu, 17 Agustus 2025
Parsing String ke Objek DateTime #
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
// ANTI-PATTERN: tidak handle DateTimeParseException
LocalDateTime hasil = LocalDateTime.parse("17/08/2025 14:30", fmt); // bisa throw exception
// BENAR: handle exception untuk input dari luar
import java.time.format.DateTimeParseException;
String input = "17/08/2025 14:30";
try {
LocalDateTime parsed = LocalDateTime.parse(input, fmt);
System.out.println("Berhasil: " + parsed); // 2025-08-17T14:30
} catch (DateTimeParseException e) {
System.out.println("Format tidak valid: " + e.getMessage());
}
// Parsing berbagai format
LocalDate tanggal = LocalDate.parse("2025-08-17"); // ISO default, tidak perlu formatter
LocalTime waktu = LocalTime.parse("14:30:00"); // ISO default
Pola Format yang Sering Dipakai #
| Simbol | Arti | Contoh |
|---|---|---|
yyyy | Tahun 4 digit | 2025 |
MM | Bulan 2 digit | 08 |
MMMM | Nama bulan penuh | August |
dd | Hari 2 digit | 17 |
EEEE | Nama hari penuh | Sunday |
HH | Jam (0–23) | 14 |
hh | Jam (1–12) | 02 |
mm | Menit | 30 |
ss | Detik | 00 |
a | AM/PM | PM |
z | Nama zona waktu | WIB |
Z | Offset zona | +0700 |
Kasus Nyata #
Menghitung Umur #
public static String hitungUmur(LocalDate tanggalLahir) {
LocalDate sekarang = LocalDate.now();
Period umur = Period.between(tanggalLahir, sekarang);
return String.format("%d tahun %d bulan %d hari",
umur.getYears(), umur.getMonths(), umur.getDays());
}
System.out.println(hitungUmur(LocalDate.of(1995, 7, 20)));
Cek Apakah Dalam Jam Operasional #
public static boolean dalamJamOperasional(LocalTime buka, LocalTime tutup) {
LocalTime sekarang = LocalTime.now();
return !sekarang.isBefore(buka) && sekarang.isBefore(tutup);
}
boolean buka = dalamJamOperasional(LocalTime.of(9, 0), LocalTime.of(17, 0));
System.out.println("Toko buka: " + buka);
Mengukur Durasi Eksekusi #
Instant mulai = Instant.now();
// ... proses yang ingin diukur ...
Thread.sleep(1500);
Instant selesai = Instant.now();
Duration durasi = Duration.between(mulai, selesai);
System.out.printf("Selesai dalam %d detik %d ms%n",
durasi.toSecondsPart(),
durasi.toMillisPart());
Konversi Timestamp Database ke Tampilan Lokal #
// Skenario: database menyimpan waktu dalam UTC (Instant)
Instant timestampDB = Instant.ofEpochSecond(1_724_000_000L);
// Tampilkan ke pengguna di Jakarta
ZoneId zonaJakarta = ZoneId.of("Asia/Jakarta");
ZonedDateTime tampilJakarta = timestampDB.atZone(zonaJakarta);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MMM yyyy, HH:mm z");
System.out.println(tampilJakarta.format(fmt));
// Contoh output: 18 Aug 2024, 21:33 WIB
Kapan Menggunakan Tiap Kelas #
Gunakan LOCALDATE jika:
✓ Menyimpan tanggal lahir, tanggal transaksi, deadline
✓ Tidak perlu informasi jam sama sekali
✓ Menghitung selisih hari/bulan/tahun antar tanggal
Gunakan LOCALTIME jika:
✓ Menyimpan jam buka/tutup, jadwal harian, alarm
✓ Tidak perlu informasi tanggal sama sekali
Gunakan LOCALDATETIME jika:
✓ Butuh tanggal DAN waktu, tapi aplikasi hanya di satu timezone
✓ Data internal sistem yang tidak perlu konversi zona
Gunakan ZONEDDATETIME jika:
✓ Aplikasi beroperasi di lebih dari satu negara/zona waktu
✓ Notifikasi terjadwal, meeting global, reservasi lintas negara
✓ Perlu konversi tampilan waktu berdasarkan lokasi pengguna
Gunakan INSTANT jika:
✓ Menyimpan timestamp log, audit trail, event sourcing
✓ Menyimpan ke database dalam format UTC
✓ Mengukur durasi eksekusi kode
Gunakan DURATION jika:
✓ Mengukur selisih waktu dalam jam/menit/detik
✓ Timeout, jeda antar event, durasi proses
Gunakan PERIOD jika:
✓ Mengukur selisih kalender dalam hari/bulan/tahun
✓ Menghitung umur, masa kontrak, jatuh tempo
Ringkasan #
- Gunakan
java.time, bukanDateatauCalendar— API lama mutable dan tidak thread-safe.java.timeimmutable, thread-safe, dan jauh lebih intuitif sejak Java 8.- Semua objek
java.timeimmutable —plusDays(),minusHours(), dan sejenisnya selalu mengembalikan objek baru. Objek asli tidak pernah berubah.LocalDateuntuk tanggal,LocalTimeuntuk waktu — jangan gunakanLocalDateTimejika hanya butuh salah satunya.ZonedDateTimesaat ada multi-timezone —LocalDateTimetidak menyimpan info zona. Kalau pengguna di zona berbeda membaca data yang sama, waktu yang ditampilkan bisa berbeda dari yang dimaksud.Instantuntuk timestamp universal — simpan waktu di database dalamInstant(UTC), konversi keZonedDateTimesaat perlu ditampilkan ke pengguna berdasarkan zona mereka.DurationvsPeriod—Durationberbasis waktu (detik/jam),Periodberbasis kalender (hari/bulan/tahun). Keduanya tidak saling bisa ditukar —Duration.ofDays(365)tidak sama denganPeriod.ofYears(1)di tahun kabisat.DateTimeFormatterthread-safe — bisa dijadikan konstantastatic finaldan dipakai bersama dari banyak thread. Berbeda denganSimpleDateFormatlama yang harus dibuat baru setiap kali.- Selalu handle
DateTimeParseException— saat mem-parsing input dari luar (form, API, file), selalu bungkus dalam try-catch karena format bisa tidak valid.