Kenapa ini penting
Hampir semua aplikasi berurusan dengan waktu: tanggal transaksi, jadwal janji temu, batas pembayaran, waktu kirim pesan, umur pengguna. Masalahnya, waktu itu rumit. Indonesia saja punya tiga zona (WIB, WITA, WIT). Server kamu mungkin berjalan di zona UTC di Singapura atau Amerika, sementara penggunamu di Jakarta. Kalau kamu ceroboh, transaksi yang dibuat pukul 23.30 WIB tanggal 31 bisa tercatat tanggal 1 bulan berikutnya, dan laporan keuangan bulananmu jadi berantakan.
Di modul ini kamu belajar objek Date bawaan JavaScript: apa sebenarnya yang disimpan di dalamnya, apa itu timestamp, dan bagaimana zona waktu memengaruhi tampilan. Kamu juga akan mengenal Temporal API, pengganti Date yang jauh lebih baik dan sedang dalam proses masuk ke semua browser.
Konsep inti
Apa yang disimpan Date?
Ini kunci paling penting: objek Date hanya menyimpan satu angka, yaitu jumlah milidetik sejak 1 Januari 1970 pukul 00:00:00 UTC (disebut Unix epoch). Angka ini disebut timestamp. Date tidak menyimpan zona waktu. Zona waktu baru dipakai saat kamu menampilkan atau membaca bagian-bagiannya (jam, tanggal) dengan method lokal.
Analogi: timestamp itu seperti satu momen yang sama di seluruh dunia, misalnya saat pertandingan final dimulai. Orang di Jakarta melihat jam dinding menunjukkan 19.00, di Makassar 20.00, di London 12.00. Momennya satu, tampilannya berbeda.
UTC vs WIB
- UTC (Coordinated Universal Time) adalah acuan waktu dunia, tanpa daylight saving.
- WIB = UTC+7, WITA = UTC+8, WIT = UTC+9. Indonesia tidak memakai daylight saving time.
- Nama zona resmi (IANA) yang dipakai JavaScript:
Asia/Jakarta,Asia/Makassar,Asia/Jayapura.
Cara membuat Date
| Cara | Contoh | Catatan |
|---|---|---|
| Sekarang | new Date() |
Waktu saat kode jalan |
| Timestamp | new Date(1792740600000) |
Milidetik sejak epoch |
| String ISO lengkap | new Date("2026-10-23T14:30:00+07:00") |
Paling aman, offset jelas |
| String tanggal saja | new Date("2026-10-23") |
Dianggap UTC tengah malam |
| String tanggal+jam tanpa offset | new Date("2026-10-23T14:30") |
Dianggap waktu lokal |
| Komponen | new Date(2026, 9, 23) |
Bulan mulai dari 0! Waktu lokal |
| Komponen UTC | new Date(Date.UTC(2026, 9, 23)) |
Interpretasi UTC |
Mendapatkan timestamp: Date.now() (sekarang) atau tanggal.getTime().
Tanggal tidak valid
new Date("besok") tidak melempar error, tetapi menghasilkan Invalid Date yang getTime()-nya NaN. Selalu validasi dengan Number.isNaN(d.getTime()).
Temporal API: masa depan
Date punya banyak kekurangan: bulan mulai dari 0, object bisa diubah (mutable), dukungan zona waktu lemah, parsing string tidak konsisten. Temporal adalah API baru dari TC39 (komite standar JavaScript) yang sudah mulai dikirim browser, dengan tipe yang jelas: Temporal.PlainDate (tanggal tanpa jam), Temporal.ZonedDateTime (waktu + zona), Temporal.Instant (momen), Temporal.Duration (durasi). Semuanya immutable.
Status dukungan per data MDN (browser-compat-data) Oktober 2026: Chrome & Edge 144+, Firefox 139+, Deno 2.7+ sudah mendukung; Safari baru di Technology Preview; tabel MDN mencatat Node.js 26, tetapi saat diuji dengan Node 26.0.0, typeof Temporal masih "undefined". Artinya Temporal belum Baseline (belum aman dipakai tanpa cadangan). Untuk produksi saat ini: tetap kuasai Date, atau pakai polyfill @js-temporal/polyfill / temporal-polyfill, dan selalu cek dukungan dengan typeof Temporal !== "undefined".
Contoh kode
// 1. Date hanya menyimpan timestamp
const momen = new Date("2026-10-23T14:30:00+07:00"); // 14.30 WIB
console.log(momen.getTime()); // → 1792740600000
console.log(momen.toISOString()); // → 2026-10-23T07:30:00.000Z (UTC, huruf Z = UTC)
// 2. Momen yang sama, tampilan berbeda per zona
const zona = ["Asia/Jakarta", "Asia/Makassar", "Asia/Jayapura", "UTC"];
for (const tz of zona) {
const jam = momen.toLocaleTimeString("id-ID", { timeZone: tz, hour: "2-digit", minute: "2-digit" });
console.log(tz.padEnd(14), jam);
}
// → Asia/Jakarta 14.30
// → Asia/Makassar 15.30
// → Asia/Jayapura 16.30
// → UTC 07.30
// 3. Jebakan parsing string
const a = new Date("2026-10-23"); // dianggap UTC
const b = new Date("2026-10-23T00:00"); // dianggap waktu lokal komputer
console.log(a.toISOString()); // → 2026-10-23T00:00:00.000Z
console.log(b.toISOString()); // → 2026-10-22T17:00:00.000Z (jika komputer di WIB)
// 4. Bulan mulai dari 0
const c = new Date(2026, 9, 23); // 9 = Oktober!
console.log(c.getMonth(), c.getDate()); // → 9 23
// 5. Timestamp & selisih sederhana
const mulai = Date.now();
let total = 0;
for (let i = 0; i < 1e6; i++) total += i;
console.log(`Loop selesai dalam ${Date.now() - mulai} ms`); // → Loop selesai dalam 3 ms (bervariasi)
// 6. Validasi tanggal
function buatTanggal(input) {
const d = new Date(input);
if (Number.isNaN(d.getTime())) throw new Error(`Tanggal tidak valid: ${input}`);
return d;
}
try {
buatTanggal("tanggal 32 bulan 13");
} catch (e) {
console.log(e.message); // → Tanggal tidak valid: tanggal 32 bulan 13
}
// 7. Zona waktu default lingkungan
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone); // → Asia/Jakarta (tergantung komputer)
console.log(momen.getTimezoneOffset()); // → -420 (menit; WIB = UTC+7 → -420, tandanya terbalik!)
// 8. Simpan di database/JSON selalu dalam UTC ISO
const transaksi = { id: 1, dibuatPada: momen.toISOString() };
console.log(JSON.stringify(transaksi)); // → {"id":1,"dibuatPada":"2026-10-23T07:30:00.000Z"}
const dibaca = new Date(JSON.parse(JSON.stringify(transaksi)).dibuatPada);
console.log(dibaca.getTime() === momen.getTime()); // → true
// 9. Temporal (jalankan hanya jika tersedia)
if (typeof Temporal !== "undefined") {
const hariIni = Temporal.Now.plainDateISO("Asia/Jakarta");
console.log(hariIni.add({ days: 30 }).toString()); // → 2026-11-22 (misalnya)
} else {
console.log("Temporal belum didukung di lingkungan ini"); // → (di Node 26.0.0)
}
Contoh 7 memperlihatkan keanehan getTimezoneOffset(): WIB adalah UTC+7, tetapi hasilnya -420. Method ini mengembalikan "berapa menit yang harus ditambahkan ke waktu lokal agar menjadi UTC", jadi tandanya terbalik. Hal kecil seperti ini sering menjebak.
Prinsip emas penanganan waktu: simpan dan kirim dalam UTC (ISO 8601 atau timestamp), tampilkan dalam zona pengguna. Logika bisnis (misalnya "transaksi bulan Oktober") harus didefinisikan dengan zona yang jelas.
Kesalahan umum
- Lupa bulan mulai dari 0.
new Date(2026, 10, 1)adalah 1 November. Buat konstanta atau selalu kurangi 1 dari bulan manusia. - Mengira
new Date("2026-10-23")itu tengah malam WIB. Itu tengah malam UTC, yaitu 07.00 WIB. Untuk tengah malam lokal pakainew Date(2026, 9, 23)atau string dengan offset+07:00. - Menyimpan tanggal sebagai string tampilan seperti
"23/10/2026". Formatnya ambigu (format AS membacanya bulan/tanggal). Simpan ISO UTC. - Tidak memvalidasi input.
Invalid Datetidak error, ia menyebar diam-diam menjadiNaNdi perhitungan. CekgetTime(). - Langsung memakai Temporal di produksi tanpa cek dukungan atau polyfill. Safari dan sebagian lingkungan belum siap.
Latihan
- Mudah, Buat Date untuk 17 Agustus 2026 pukul 10.00 WIB memakai string ISO dengan offset, lalu cetak
toISOString()-nya. - Sedang, Tulis function
jamDiKota(date)yang mengembalikan object{ WIB, WITA, WIT }berisi jam dalam formatHH.MMuntuk momen tersebut. - Menantang, Tulis function
apakahTanggalValid(tahun, bulan, tanggal)(bulan manusia 1-12) yang mengembalikanfalseuntuk tanggal seperti 31 Februari. Petunjuk: Date otomatis "menggulung" tanggal yang kelebihan.
Lihat solusi
// 1
const hut = new Date("2026-08-17T10:00:00+07:00");
console.log(hut.toISOString()); // → 2026-08-17T03:00:00.000Z
// 2
function jamDiKota(date) {
const f = (tz) => date.toLocaleTimeString("id-ID", { timeZone: tz, hour: "2-digit", minute: "2-digit" });
return { WIB: f("Asia/Jakarta"), WITA: f("Asia/Makassar"), WIT: f("Asia/Jayapura") };
}
console.log(jamDiKota(hut)); // → { WIB: '10.00', WITA: '11.00', WIT: '12.00' }
// 3
function apakahTanggalValid(tahun, bulan, tanggal) {
const d = new Date(tahun, bulan - 1, tanggal);
return d.getFullYear() === tahun && d.getMonth() === bulan - 1 && d.getDate() === tanggal;
}
console.log(apakahTanggalValid(2026, 2, 31)); // → false (digulung jadi 3 Maret)
console.log(apakahTanggalValid(2028, 2, 29)); // → true (2028 tahun kabisat)
Ringkasan
Datehanya menyimpan timestamp: milidetik sejak 1 Januari 1970 UTC.- Zona waktu hanya berpengaruh saat membaca/menampilkan; WIB = UTC+7,
Asia/Jakarta. - String tanggal saja dianggap UTC; tanggal+jam tanpa offset dianggap lokal; bulan di konstruktor mulai dari 0.
- Simpan dalam UTC (ISO 8601), tampilkan dalam zona pengguna, validasi dengan
getTime(). - Temporal sudah di Chrome/Edge 144+ dan Firefox 139+, tetapi belum Baseline (Safari belum); cek dukungan atau pakai polyfill.