Date & Time #
Tanggal dan waktu adalah salah satu topik yang tampak sederhana tapi ternyata penuh jebakan — timezone, daylight saving time, format yang berbeda antar negara, dan representasi internal yang sering membingungkan. Objek Date bawaan JavaScript telah lama dikritik karena desainnya yang inkonsisten: bulan dimulai dari 0 bukan 1, metode yang memodifikasi objek secara mutable, dan penanganan timezone yang terbatas. Meski begitu, Date tetap menjadi fondasi yang perlu dipahami karena digunakan di seluruh ekosistem Node.js. Artikel ini membahas Date secara mendalam, Intl.DateTimeFormat untuk formatting yang benar per locale, dan pola-pola penanganan waktu yang aman di aplikasi produksi.
Objek Date Dasar #
// membuat Date
// saat ini
const sekarang = new Date();
console.log(sekarang); // 2024-01-15T10:30:00.000Z
// dari timestamp Unix (milidetik sejak 1 Jan 1970 UTC)
const dariTimestamp = new Date(1705312200000);
// dari string ISO 8601 — format paling aman untuk parsing
const dariISO = new Date("2024-01-15T10:30:00.000Z");
// dari string lain — HINDARI, hasilnya berbeda antar browser/Node.js
const dariString = new Date("January 15, 2024"); // ✗ tidak konsisten antar environment
// dari komponen — PERHATIAN: bulan dimulai dari 0!
const dariKomponen = new Date(2024, 0, 15, 10, 30, 0); // 15 Jan 2024, 10:30:00
// ↑ 0 = Januari!
// ANTI-PATTERN: lupa bulan dimulai dari 0
const salah = new Date(2024, 1, 15); // ✗ ini Februari, bukan Januari!
// BENAR: gunakan konstanta untuk kejelasan, atau gunakan ISO string
const benar = new Date("2024-01-15"); // ✓ ISO string tidak ambigu
// Date.now() — timestamp saat ini dalam milidetik (lebih cepat dari new Date())
const timestamp = Date.now();
// Date.UTC() — buat timestamp UTC dari komponen (bulan tetap 0-indexed)
const timestampUTC = Date.UTC(2024, 0, 15, 10, 30, 0);
Membaca Komponen Tanggal #
Date memiliki dua set metode: yang bekerja dalam timezone lokal (getFullYear, getMonth, dll) dan yang bekerja dalam UTC (getUTCFullYear, getUTCMonth, dll).
const d = new Date("2024-01-15T10:30:45.123Z");
// komponen dalam timezone LOKAL (hasilnya berbeda tergantung timezone server/client)
console.log(d.getFullYear()); // 2024 (atau mungkin berbeda jika UTC+X)
console.log(d.getMonth()); // 0 (Januari — ingat: 0-indexed!)
console.log(d.getDate()); // 15 (hari dalam bulan)
console.log(d.getDay()); // 1 (hari dalam minggu: 0=Minggu, 1=Senin)
console.log(d.getHours()); // tergantung timezone lokal
console.log(d.getMinutes()); // 30
console.log(d.getSeconds()); // 45
console.log(d.getMilliseconds()); // 123
console.log(d.getTime()); // timestamp dalam milidetik (selalu UTC)
console.log(d.getTimezoneOffset()); // offset timezone dalam menit (negatif untuk UTC+)
// komponen dalam UTC — konsisten di semua timezone
console.log(d.getUTCFullYear()); // 2024
console.log(d.getUTCMonth()); // 0
console.log(d.getUTCDate()); // 15
console.log(d.getUTCHours()); // 10 — selalu 10, tidak peduli timezone lokal
console.log(d.getUTCMinutes()); // 30
// nama hari dan bulan — gunakan Intl, bukan array manual
const namaHari = ["Minggu", "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu"];
const namaBulan = [
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
"Juli", "Agustus", "September", "Oktober", "November", "Desember"
];
// ANTI-PATTERN: array nama manual (tidak internasional)
console.log(namaHari[d.getDay()]); // ✗ hanya bekerja untuk satu bahasa
// BENAR: gunakan Intl.DateTimeFormat
const formatter = new Intl.DateTimeFormat("id-ID", { weekday: "long" });
console.log(formatter.format(d)); // "Senin" — otomatis per locale
Menulis Komponen Tanggal #
Objek Date bersifat mutable — metode set* mengubah objek secara langsung, bukan membuat objek baru. Ini sumber bug yang umum.
const d = new Date("2024-01-15T10:30:00Z");
// ANTI-PATTERN: modifikasi langsung objek yang dibagi
function tambahSatuHariSalah(tanggal: Date): Date {
tanggal.setDate(tanggal.getDate() + 1); // ✗ memodifikasi objek asli!
return tanggal;
}
const tgl = new Date("2024-01-15");
const besok = tambahSatuHariSalah(tgl);
console.log(tgl.toISOString()); // "2024-01-16..." — tgl juga berubah!
console.log(besok.toISOString()); // "2024-01-16..."
// BENAR: selalu buat salinan sebelum memodifikasi
function tambahHari(tanggal: Date, jumlah: number): Date {
const salinan = new Date(tanggal.getTime()); // buat salinan via timestamp
salinan.setDate(salinan.getDate() + jumlah);
return salinan;
}
const tgl2 = new Date("2024-01-15");
const besok2 = tambahHari(tgl2, 1);
console.log(tgl2.toISOString()); // "2024-01-15..." — tidak berubah ✓
console.log(besok2.toISOString()); // "2024-01-16..."
// set metode yang tersedia
const d2 = new Date();
d2.setFullYear(2025);
d2.setMonth(5); // Juni (0-indexed)
d2.setDate(20);
d2.setHours(14);
d2.setMinutes(30);
d2.setSeconds(0);
d2.setMilliseconds(0);
// set UTC
d2.setUTCFullYear(2025);
d2.setUTCMonth(5);
d2.setUTCHours(7); // 07:00 UTC = 14:00 WIB (UTC+7)
Formatting Tanggal #
toString dan toISOString #
const d = new Date("2024-01-15T10:30:00.000Z");
// toISOString — format ISO 8601, selalu UTC, format standar untuk API
console.log(d.toISOString());
// "2024-01-15T10:30:00.000Z"
// toLocaleDateString — format tanggal sesuai locale
console.log(d.toLocaleDateString("id-ID"));
// "15/1/2024" atau "15 Januari 2024" tergantung implementasi
// toLocaleTimeString — format waktu sesuai locale
console.log(d.toLocaleTimeString("id-ID"));
// toLocaleString — tanggal dan waktu sesuai locale
console.log(d.toLocaleString("id-ID"));
// toUTCString — format RFC 7231 (untuk HTTP headers)
console.log(d.toUTCString());
// "Mon, 15 Jan 2024 10:30:00 GMT"
// ANTI-PATTERN: format tanggal dengan konkatenasi string manual
function formatTanggalSalah(d: Date): string {
return d.getDate() + "/" + (d.getMonth() + 1) + "/" + d.getFullYear(); // ✗
// masalah: tidak ada zero-padding, tidak timezone-aware
}
// BENAR: gunakan Intl.DateTimeFormat
function formatTanggal(d: Date, locale: string = "id-ID"): string {
return new Intl.DateTimeFormat(locale, {
day: "2-digit",
month: "2-digit",
year: "numeric",
}).format(d);
}
console.log(formatTanggal(d)); // "15/01/2024"
Intl.DateTimeFormat — Formatting Profesional #
Intl.DateTimeFormat adalah cara yang benar untuk memformat tanggal — otomatis mengikuti konvensi per locale dan timezone.
const d = new Date("2024-01-15T10:30:00.000Z");
// format lengkap untuk Indonesia
const formatIndonesia = new Intl.DateTimeFormat("id-ID", {
weekday: "long", // "Senin"
day: "numeric", // "15"
month: "long", // "Januari"
year: "numeric", // "2024"
hour: "2-digit", // "17" (dalam WIB = UTC+7)
minute: "2-digit", // "30"
timeZone: "Asia/Jakarta",
});
console.log(formatIndonesia.format(d));
// "Senin, 15 Januari 2024 pukul 17.30"
// format singkat
const formatSingkat = new Intl.DateTimeFormat("id-ID", {
day: "2-digit",
month: "short",
year: "numeric",
timeZone: "Asia/Jakarta",
});
console.log(formatSingkat.format(d)); // "15 Jan 2024"
// waktu saja
const formatWaktu = new Intl.DateTimeFormat("id-ID", {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
timeZoneName: "short",
timeZone: "Asia/Jakarta",
});
console.log(formatWaktu.format(d)); // "17.30.00 WIB"
// format relatif — "3 hari yang lalu", "dalam 2 jam"
const formatRelatif = new Intl.RelativeTimeFormat("id-ID", {
numeric: "auto", // "kemarin" bukan "1 hari yang lalu"
});
console.log(formatRelatif.format(-1, "day")); // "kemarin"
console.log(formatRelatif.format(-3, "day")); // "3 hari yang lalu"
console.log(formatRelatif.format(2, "hour")); // "dalam 2 jam"
console.log(formatRelatif.format(-30, "minute")); // "30 menit yang lalu"
// helper: hitung dan format waktu relatif
function waktuRelatif(tanggal: Date, locale: string = "id-ID"): string {
const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
const selisihDetik = Math.round((tanggal.getTime() - Date.now()) / 1000);
const batas = [
{ unit: "year" as const, detik: 365 * 24 * 3600 },
{ unit: "month" as const, detik: 30 * 24 * 3600 },
{ unit: "week" as const, detik: 7 * 24 * 3600 },
{ unit: "day" as const, detik: 24 * 3600 },
{ unit: "hour" as const, detik: 3600 },
{ unit: "minute" as const, detik: 60 },
{ unit: "second" as const, detik: 1 },
];
for (const { unit, detik } of batas) {
const nilai = Math.round(selisihDetik / detik);
if (Math.abs(nilai) >= 1) {
return formatter.format(nilai, unit);
}
}
return "baru saja";
}
const tigaHariLalu = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
console.log(waktuRelatif(tigaHariLalu)); // "3 hari yang lalu"
Format Kustom #
Untuk format yang sangat spesifik (misalnya untuk nama file atau database), Intl.DateTimeFormat mungkin terlalu fleksibel. Gunakan formatToParts() untuk kontrol penuh:
function formatKustom(d: Date, timezone: string = "Asia/Jakarta"): {
tanggal: string; // "2024-01-15"
waktu: string; // "17:30:00"
dateTime: string; // "2024-01-15 17:30:00"
namaFile: string; // "20240115_173000"
} {
const parts = new Intl.DateTimeFormat("en-CA", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
timeZone: timezone,
}).formatToParts(d);
const get = (type: string) => parts.find((p) => p.type === type)?.value ?? "";
const tanggal = `${get("year")}-${get("month")}-${get("day")}`;
const waktu = `${get("hour")}:${get("minute")}:${get("second")}`;
return {
tanggal,
waktu,
dateTime: `${tanggal} ${waktu}`,
namaFile: `${get("year")}${get("month")}${get("day")}_${get("hour")}${get("minute")}${get("second")}`,
};
}
const d = new Date("2024-01-15T10:30:00.000Z");
console.log(formatKustom(d));
// { tanggal: "2024-01-15", waktu: "17:30:00", dateTime: "2024-01-15 17:30:00", namaFile: "20240115_173000" }
Aritmetika Tanggal #
// selalu buat salinan sebelum memodifikasi
function salinanDate(d: Date): Date {
return new Date(d.getTime());
}
// tambah/kurangi hari
function tambahHari(d: Date, jumlah: number): Date {
const hasil = salinanDate(d);
hasil.setDate(hasil.getDate() + jumlah);
return hasil;
}
// tambah/kurangi bulan — setMonth menangani overflow otomatis
function tambahBulan(d: Date, jumlah: number): Date {
const hasil = salinanDate(d);
hasil.setMonth(hasil.getMonth() + jumlah);
return hasil;
}
// PERHATIAN: tambahBulan bisa menghasilkan tanggal yang tidak diharapkan
const tgl31Jan = new Date("2024-01-31");
console.log(tambahBulan(tgl31Jan, 1).toISOString());
// "2024-03-02" — Feb tidak punya 31 hari, overflow ke Maret!
// untuk kasus ini, clamp ke akhir bulan
function tambahBulanAman(d: Date, jumlah: number): Date {
const hasil = salinanDate(d);
const hariAsli = d.getDate();
hasil.setMonth(hasil.getMonth() + jumlah);
// jika hari overflow, set ke hari terakhir bulan target
if (hasil.getDate() !== hariAsli) {
hasil.setDate(0); // setDate(0) = hari terakhir bulan sebelumnya
}
return hasil;
}
console.log(tambahBulanAman(tgl31Jan, 1).toISOString());
// "2024-02-29" — hari terakhir Februari 2024 (tahun kabisat)
// tambah/kurangi tahun
function tambahTahun(d: Date, jumlah: number): Date {
const hasil = salinanDate(d);
hasil.setFullYear(hasil.getFullYear() + jumlah);
return hasil;
}
// selisih antara dua tanggal
function selisihHari(a: Date, b: Date): number {
const ms = Math.abs(b.getTime() - a.getTime());
return Math.floor(ms / (1000 * 60 * 60 * 24));
}
function selisihBulan(a: Date, b: Date): number {
return (
(b.getFullYear() - a.getFullYear()) * 12 +
(b.getMonth() - a.getMonth())
);
}
function selisihTahun(a: Date, b: Date): number {
return b.getFullYear() - a.getFullYear();
}
// contoh
const lahir = new Date("1995-03-20");
const sekarang = new Date("2024-01-15");
console.log(`Umur: ${selisihTahun(lahir, sekarang)} tahun`); // 28
console.log(`Selisih: ${selisihHari(lahir, sekarang)} hari`); // 10527
// awal dan akhir periode
function awalHari(d: Date, timezone?: string): Date {
if (timezone) {
// gunakan Intl untuk mendapatkan tanggal dalam timezone tertentu
const parts = new Intl.DateTimeFormat("en-CA", {
year: "numeric", month: "2-digit", day: "2-digit",
timeZone: timezone,
}).formatToParts(d);
const get = (type: string) => parts.find((p) => p.type === type)?.value ?? "";
return new Date(`${get("year")}-${get("month")}-${get("day")}T00:00:00`);
}
const hasil = salinanDate(d);
hasil.setHours(0, 0, 0, 0);
return hasil;
}
function akhirHari(d: Date): Date {
const hasil = salinanDate(d);
hasil.setHours(23, 59, 59, 999);
return hasil;
}
function awalBulan(d: Date): Date {
const hasil = salinanDate(d);
hasil.setDate(1);
hasil.setHours(0, 0, 0, 0);
return hasil;
}
function akhirBulan(d: Date): Date {
const hasil = salinanDate(d);
hasil.setMonth(hasil.getMonth() + 1, 0); // setDate(0) = hari terakhir bulan ini
hasil.setHours(23, 59, 59, 999);
return hasil;
}
Timezone #
Timezone adalah sumber bug yang paling umum dengan Date. Seluruh objek Date menyimpan waktu dalam UTC secara internal — timezone lokal hanya digunakan saat menampilkan atau membaca komponen.
flowchart LR
A["Input string\n'2024-01-15T10:30:00Z'"] -- parse --> B["Internal\nUTC timestamp\n1705312200000ms"]
B -- "getHours()\nTimezone lokal" --> C["17 (WIB, UTC+7)\n10 (UTC)\n05 (New York, UTC-5)"]
B -- "toISOString()" --> D["'2024-01-15T10:30:00.000Z'\nSelalu UTC"]// masalah timezone yang paling umum: parsing string tanpa timezone
const tanpaTZ = new Date("2024-01-15");
// Di Node.js, string tanggal tanpa waktu diparsing sebagai UTC midnight!
console.log(tanpaTZ.toISOString()); // "2024-01-15T00:00:00.000Z"
// Tapi jika ditampilkan dalam WIB (UTC+7):
// getHours() = 7 — satu hari ke depan bagi pengguna di UTC-8!
// string dengan waktu tapi tanpa timezone diparsing sebagai LOCAL time
const denganWaktu = new Date("2024-01-15T00:00:00");
// Bergantung pada timezone server — tidak konsisten!
// BENAR: selalu sertakan timezone di string yang diparsing
const konsisten = new Date("2024-01-15T00:00:00Z"); // UTC
const wib = new Date("2024-01-15T00:00:00+07:00"); // WIB
const withTimezone = new Date("2024-01-15T07:00:00.000Z"); // UTC, sama dengan WIB midnight
// mendapatkan tanggal saat ini di timezone tertentu
function tanggalSaatIniDi(timezone: string): string {
return new Intl.DateTimeFormat("en-CA", {
year: "numeric",
month: "2-digit",
day: "2-digit",
timeZone: timezone,
}).format(new Date());
}
console.log(tanggalSaatIniDi("Asia/Jakarta")); // "2024-01-15"
console.log(tanggalSaatIniDi("America/New_York")); // mungkin "2024-01-14" di malam hari
// konversi waktu antar timezone
function konversiTimezone(d: Date, dariTZ: string, keTZ: string): string {
// tidak bisa benar-benar "konversi" Date ke timezone lain —
// Date selalu UTC. Kita hanya format tampilannya berbeda.
return new Intl.DateTimeFormat("en-CA", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
timeZone: keTZ,
}).format(d);
}
const sekarang = new Date("2024-01-15T10:30:00Z");
console.log(konversiTimezone(sekarang, "UTC", "Asia/Jakarta"));
// "2024-01-15, 17:30:00" (WIB = UTC+7)
console.log(konversiTimezone(sekarang, "UTC", "America/New_York"));
// "2024-01-15, 05:30:00" (EST = UTC-5)
Selalu simpan dan transfer waktu dalam format UTC (ISO 8601 dengan suffix Z) di database dan API. Konversi ke timezone lokal hanya dilakukan di lapisan presentasi — saat menampilkan ke pengguna. Ini mencegah bug yang sangat umum di mana waktu bergeser saat data berpindah antara server dengan timezone berbeda.Parsing Tanggal yang Aman #
new Date(string) berperilaku tidak konsisten untuk format selain ISO 8601. Untuk parsing yang robust, selalu validasi hasilnya.
// parsing aman — validasi setelah parse
function parseTanggal(input: string): Date | null {
const d = new Date(input);
// Date("invalid") menghasilkan "Invalid Date"
// isNaN pada timestamp-nya mendeteksi ini
if (isNaN(d.getTime())) return null;
return d;
}
// parsing format DD/MM/YYYY (format Indonesia umum)
function parseTanggalID(input: string): Date | null {
const match = input.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
if (!match) return null;
const [, hari, bulan, tahun] = match;
// buat dalam UTC agar tidak terpengaruh timezone
const d = new Date(Date.UTC(
parseInt(tahun),
parseInt(bulan) - 1, // konversi ke 0-indexed
parseInt(hari)
));
// validasi bahwa tanggal valid (misal: 31/02/2024 tidak ada)
if (
d.getUTCDate() !== parseInt(hari) ||
d.getUTCMonth() !== parseInt(bulan) - 1 ||
d.getUTCFullYear() !== parseInt(tahun)
) {
return null; // tanggal overflow — tidak valid
}
return d;
}
console.log(parseTanggalID("15/01/2024")); // Date object
console.log(parseTanggalID("31/02/2024")); // null — 31 Feb tidak ada
console.log(parseTanggalID("abc")); // null — format salah
// validasi rentang tanggal
function isRentangValid(mulai: Date, akhir: Date): boolean {
return mulai.getTime() <= akhir.getTime();
}
function isTanggalDiMasaDepan(d: Date): boolean {
return d.getTime() > Date.now();
}
function isUmurValid(tanggalLahir: Date, minUmur: number = 17): boolean {
const umur = selisihTahun(tanggalLahir, new Date());
return umur >= minUmur;
}
function selisihTahun(a: Date, b: Date): number {
return b.getFullYear() - a.getFullYear();
}
Pola Umum di Aplikasi #
Timestamp untuk Database #
// simpan sebagai ISO string (disarankan untuk portabilitas)
function sekarangISO(): string {
return new Date().toISOString();
// "2024-01-15T10:30:00.123Z"
}
// simpan sebagai Unix timestamp (integer, lebih compact)
function sekarangTimestamp(): number {
return Date.now(); // milidetik
}
function sekarangTimestampDetik(): number {
return Math.floor(Date.now() / 1000); // detik (untuk JWT exp, dll)
}
// konversi antara format
function isoKeTimestamp(iso: string): number {
return new Date(iso).getTime();
}
function timestampKeISO(ms: number): string {
return new Date(ms).toISOString();
}
Range Picker dan Periode #
interface Periode {
mulai: Date;
akhir: Date;
}
function periodeHariIni(timezone: string = "Asia/Jakarta"): Periode {
const sekarang = new Date();
// dapatkan tanggal hari ini di timezone yang ditentukan
const tanggalStr = tanggalSaatIniDi(timezone);
const mulai = new Date(`${tanggalStr}T00:00:00+07:00`);
const akhir = new Date(`${tanggalStr}T23:59:59.999+07:00`);
return { mulai, akhir };
}
function periodeMinggIni(): Periode {
const sekarang = new Date();
const hariIni = sekarang.getDay(); // 0=Minggu, 1=Senin...
const selisihKeSenin = hariIni === 0 ? -6 : 1 - hariIni;
const mulai = tambahHari(awalHari(sekarang), selisihKeSenin);
const akhir = tambahHari(mulai, 6);
akhir.setHours(23, 59, 59, 999);
return { mulai, akhir };
}
function periodeBulanIni(): Periode {
const sekarang = new Date();
return {
mulai: awalBulan(sekarang),
akhir: akhirBulan(sekarang),
};
}
function periode30HariTerakhir(): Periode {
const akhir = new Date();
const mulai = tambahHari(akhir, -30);
mulai.setHours(0, 0, 0, 0);
return { mulai, akhir };
}
function awalHari(d: Date): Date {
const hasil = new Date(d.getTime());
hasil.setHours(0, 0, 0, 0);
return hasil;
}
function tambahHari(d: Date, jumlah: number): Date {
const hasil = new Date(d.getTime());
hasil.setDate(hasil.getDate() + jumlah);
return hasil;
}
function awalBulan(d: Date): Date {
const hasil = new Date(d.getTime());
hasil.setDate(1);
hasil.setHours(0, 0, 0, 0);
return hasil;
}
function akhirBulan(d: Date): Date {
const hasil = new Date(d.getTime());
hasil.setMonth(hasil.getMonth() + 1, 0);
hasil.setHours(23, 59, 59, 999);
return hasil;
}
function tanggalSaatIniDi(timezone: string): string {
return new Intl.DateTimeFormat("en-CA", {
year: "numeric",
month: "2-digit",
day: "2-digit",
timeZone: timezone,
}).format(new Date());
}
Caching Formatter #
Membuat instance Intl.DateTimeFormat baru setiap kali format dipanggil cukup mahal. Cache instance yang sering digunakan:
// ANTI-PATTERN: buat formatter baru setiap pemanggilan
function formatTanggalBoros(d: Date): string {
return new Intl.DateTimeFormat("id-ID", { // ✗ alokasi baru setiap kali
day: "2-digit",
month: "long",
year: "numeric",
}).format(d);
}
// BENAR: cache formatter
const formatterCache = new Map<string, Intl.DateTimeFormat>();
function getFormatter(locale: string, options: Intl.DateTimeFormatOptions): Intl.DateTimeFormat {
const key = `${locale}:${JSON.stringify(options)}`;
if (!formatterCache.has(key)) {
formatterCache.set(key, new Intl.DateTimeFormat(locale, options));
}
return formatterCache.get(key)!;
}
// formatter yang sering digunakan — definisikan sekali
const fmt = {
tanggal: getFormatter("id-ID", { day: "2-digit", month: "long", year: "numeric", timeZone: "Asia/Jakarta" }),
waktu: getFormatter("id-ID", { hour: "2-digit", minute: "2-digit", timeZone: "Asia/Jakarta" }),
lengkap: getFormatter("id-ID", { day: "2-digit", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit", timeZone: "Asia/Jakarta" }),
};
console.log(fmt.tanggal.format(new Date())); // "15 Januari 2024"
console.log(fmt.waktu.format(new Date())); // "17.30"
Ringkasan #
- Bulan di
Datedimulai dari 0 — Januari = 0, Desember = 11. Ini sumber bug paling umum; selalu gunakan string ISO 8601 untuk membuat Date yang ambigu.Datebersifat mutable — selalu buat salinan dengannew Date(d.getTime())sebelum memodifikasi; jangan mutasi Date yang diterima sebagai parameter.- Simpan dan transfer waktu dalam UTC — gunakan
toISOString()untuk database dan API; konversi ke timezone lokal hanya di lapisan presentasi denganIntl.DateTimeFormat.- Gunakan
Intl.DateTimeFormatbukan format manual — menangani locale, timezone, dan konvensi tampilan secara otomatis; jauh lebih akurat dari konkatenasi string atau array nama bulan manual.- Cache instance
Intl.DateTimeFormatyang sering digunakan — membuat instance baru setiap pemanggilan cukup mahal; simpan diMapatau variabel modul.Intl.RelativeTimeFormatuntuk waktu relatif — “3 hari yang lalu”, “dalam 2 jam” — lebih akurat dan internasional dari implementasi manual.- Validasi hasil parsing —
new Date("string-tidak-valid")mengembalikan “Invalid Date” bukan error; selalu cekisNaN(d.getTime())setelah parsing.tambahBulanbisa overflow — 31 Januari + 1 bulan = 2 Maret, bukan 28/29 Februari. Gunakanclampke hari terakhir bulan target jika perilaku ini tidak diinginkan.Date.now()lebih cepat darinew Date().getTime()untuk timestamp — gunakan untuk performa kritis seperti benchmarking atau logging.
← Sebelumnya: Timers