Kodeloka
  1. Kodeloka
  2. Kelas PHP
  3. Materi

1 · Fondasi · Modul 5 dari 25

Tanggal, Waktu (DateTimeImmutable), Zona Waktu UTC, dan Internasionalisasi (intl)

Mengelola waktu dengan aman menggunakan DateTimeImmutable (dan metode mikrodetik PHP 8.4), memisahkan penyimpanan waktu UTC di basis data dari tampilan zona waktu lokal (Asia/Jakarta), menghitung rentang waktu dengan DateInterval, serta memformat angka dan tanggal menggunakan ekstensi intl.

1. Mengapa Wajib Menggunakan DateTimeImmutable dan Bukan DateTime?

Di PHP, terdapat dua kelas tanggal utama yang mengimplementasikan DateTimeInterface:

  1. DateTime (Mutable / Berbahaya): Ketika Anda memanggil $tanggal->modify('+7 days'), objek $tanggal aslinya ikut berubah di tempat! Jika objek $tanggalMulai tersebut sudah dikirimkan ke fungsi lain, seluruh bagian program yang memegang referensi ke $tanggalMulai tiba-tiba melihat tanggal yang sudah bergeser 7 hari tanpa sadar!
  2. DateTimeImmutable (Immutable / Standar Terbaik): Setiap pemanggilan metode pengubah seperti $tanggal->modify('+7 days') atau $tanggal->setTimezone(...) selalu mengembalikan instans objek baru dan membiarkan objek aslinya utuh 100%!
<?php

declare(strict_types=1);

$mulaiLangganan = new DateTimeImmutable('2026-10-16 08:00:00', new DateTimeZone('UTC'));

// modify() pada DateTimeImmutable menghasilkan objek baru tanpa mengubah $mulaiLangganan!
$akhirLangganan = $mulaiLangganan->modify('+30 days');

echo $mulaiLangganan->format('Y-m-d'); // Tetap "2026-10-16"
echo $akhirLangganan->format('Y-m-d'); // "2026-11-15"
💡 Fitur Baru di PHP 8.4 pada DateTimeImmutable

Sejak PHP 8.4, tersedia metode statis langsung DateTimeImmutable::createFromTimestamp(int|float $timestamp) (yang mendukung angka bulat detik maupun float dengan pecahan mikrodetik!), serta metode getMicrosecond() dan setMicrosecond(int $microsecond)!

2. Arsitektur Zona Waktu: Simpan dalam UTC, Tampilkan Sesuai Zona Pengguna!

Pengguna situs Anda di Indonesia tersebar di tiga zona waktu (WIB / Asia/Jakarta UTC+7, WITA / Asia/Makassar UTC+8, dan WIT / Asia/Jayapura UTC+9), belum lagi pengguna di luar negeri.

Jika Anda menyimpan jam lokal mentah tanpa zona waktu di basis data, urutan kejadian antara pengguna di Jakarta dan Makassar akan menjadi kacau! Aturan emas di sistem produksi:

  1. Di Server dan Basis Data: Selalu simpan waktu dalam zona waktu universal UTC (atau gunakan tipe kolom TIMESTAMPTZ di PostgreSQL).
  2. Di Lapisan Tampilan (View / API): Konversikan objek DateTimeImmutable dari UTC ke zona waktu pengguna menggunakan ->setTimezone(new DateTimeZone($zonaPengguna)) tepat saat hendak ditampilkan!
<?php

declare(strict_types=1);

// 1. Waktu kejadian disimpan sebagai UTC di database
$waktuKejadianUtc = new DateTimeImmutable('2026-10-16 07:30:00', new DateTimeZone('UTC'));

// 2. Konversi ke WIB (Asia/Jakarta), WITA (Asia/Makassar), dan WIT (Asia/Jayapura) saat ditampilkan:
$wib  = $waktuKejadianUtc->setTimezone(new DateTimeZone('Asia/Jakarta'));
$wita = $waktuKejadianUtc->setTimezone(new DateTimeZone('Asia/Makassar'));
$wit  = $waktuKejadianUtc->setTimezone(new DateTimeZone('Asia/Jayapura'));

echo $wib->format('Y-m-d H:i T') . "\n";  // "2026-10-16 14:30 WIB"
echo $wita->format('Y-m-d H:i T') . "\n"; // "2026-10-16 15:30 WITA"
echo $wit->format('Y-m-d H:i T') . "\n";  // "2026-10-16 16:30 WIT"

Menghitung Selisih Waktu dengan diff() dan DateInterval

<?php

declare(strict_types=1);

$tenggat = new DateTimeImmutable('2026-12-31 23:59:59', new DateTimeZone('Asia/Jakarta'));
$sekarang = new DateTimeImmutable('2026-10-16 14:30:00', new DateTimeZone('Asia/Jakarta'));

$selisih = $sekarang->diff($tenggat);
// %a mencetak total jumlah hari penuh di antara kedua tanggal
echo $selisih->format('Tersisa %a hari (%m bulan, %d hari, %h jam)');

3. Memformat Mata Uang dan Tanggal Bahasa Indonesia dengan Ekstensi intl

Fungsi DateTimeImmutable::format('l, d F Y') selalu mencetak nama hari dan bulan dalam bahasa Inggris ("Friday, 16 October 2026"). Untuk mencetak tanggal Bahasa Indonesia yang baku ("Jumat, 16 Oktober 2026") dan mata uang Rupiah yang presisi, gunakan kelas IntlDateFormatter dan NumberFormatter dari ekstensi standar intl:

<?php

declare(strict_types=1);

// 1. Memformat Mata Uang Rupiah dengan NumberFormatter (ext-intl)
$pemformatUang = new NumberFormatter('id_ID', NumberFormatter::CURRENCY);
$pemformatUang->setAttribute(NumberFormatter::FRACTION_DIGITS, 0);
echo $pemformatUang->formatCurrency(1_450_000, 'IDR') . "\n"; // "Rp1.450.000"

// 2. Memformat Tanggal Lengkap Bahasa Indonesia dengan IntlDateFormatter
$pemformatTanggal = new IntlDateFormatter(
    locale: 'id_ID',
    dateType: IntlDateFormatter::FULL,
    timeType: IntlDateFormatter::SHORT,
    timezone: 'Asia/Jakarta',
);

$waktu = new DateTimeImmutable('2026-10-16 07:30:00', new DateTimeZone('UTC'));
echo $pemformatTanggal->format($waktu) . "\n";
// Keluaran: "Jumat, 16 Oktober 2026 14.30"
⚠️ Hasil bisa berbeda

Bentuk keluaran IntlDateFormatter (misalnya ada tidaknya kata "pukul" antara tanggal dan jam, dan spasi pada Rp1.450.000) bergantung pada versi ICU/CLDR yang terpasang di server; jalankan kodenya di lingkungan Anda untuk melihat hasil persisnya.

Latihan Praktik: Konverter Jadwal Webinar Lintas Zona Waktu Nusantara

Buatlah berkas jadwal-webinar.php:

  1. Buat fungsi buatJadwalWebinar(string $waktuMulaiWib, int $durasiMenit): array yang menerima string waktu WIB (mis. '2026-11-10 19:00:00'), membuat DateTimeImmutable dengan zona 'Asia/Jakarta', dan menghitung waktu selesainya dengan ->modify("+{$durasiMenit} minutes").
  2. Kembalikan jadwal lengkap dalam format UTC (untuk disimpan ke database) serta tampilan waktu dalam WIB, WITA, dan WIT!