Harga, tanggal, dan jam ditulis beda di tiap negara, padahal nilainya sama. Aturannya: simpan nilai mentah, format di ujung. Bayangin buku kas Bu Sri: harga ditulis polos 700000, waktu check-in ditulis dalam UTC. Tiap tamu menerima salinan yang ditulis ulang dengan kebiasaannya sendiri: "Rp 700.000" untuk tamu Indonesia, "IDR 700,000" untuk tamu Amerika, jam 14.00 WIB yang sama ditulis 16.00 di Tokyo. Pelajaran Intl API sudah memperkenalkan pemformatnya; di sini kita masuk ke pilihan-pilihannya.
🧪 Tebakan cepat: Check-in Bu Sri adalah
2026-12-12T07:00:00Z(14.00 WIB). Tamu di Kairo memformatnya dengantimeZone: 'Africa/Cairo'. Jam berapa yang tertulis, dan apakah jamnya sama kalau tanggalnya 12 Juli? Jawabannya ada di bagian "Satu waktu, tiga jam dinding".
Simpan mentah, format di ujung
// Di database dan API: angka dan waktu UTC, bukan teks jadi
const booking = { pricePerNight: 350000, nights: 2, checkIn: '2026-12-12T07:00:00Z' };
// Di layar: format dengan locale tamu dan zona tempat kejadiannya
const total = new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 })
.format(booking.pricePerNight * booking.nights); // "Rp 700.000"
const when = new Intl.DateTimeFormat('id-ID', { dateStyle: 'full', timeStyle: 'short', timeZone: 'Asia/Jakarta' })
.format(new Date(booking.checkIn)); // "Sabtu, 12 Desember 2026 pukul 14.00"
- Rupiah disimpan sebagai bilangan bulat; teks "Rp 700.000" gak bisa dijumlahkan dan gak bisa ditulis ulang untuk locale lain.
- Waktu disimpan sebagai titik waktu UTC (ISO 8601 dengan
Zatau offset). Hati-hati waktu mem-parse:new Date('2026-12-12')dibaca sebagai tengah malam UTC, jadi laptop di Los Angeles menulis "11 Desember 2026".new Date('2026-12-12T14:00')tanpa offset dibaca sebagai jam lokal mesin: 07.00 UTC di laptop WIB, 14.00 UTC di server UTC. Tulis offset-nya:'2026-12-12T14:00:00+07:00'.
Mata uang: symbol, narrowSymbol, code, name
currencyDisplay memilih cara menulis mata uang. Harga kamar dalam rupiah, plus contoh nominal di mata uang lain (bukan kurs):
currencyDisplay |
id-ID, IDR 700000 |
en-US, IDR 700000 |
id-ID, USD 42.5 |
en-US, USD 42.5 |
en-US, EGP 2150 |
|---|---|---|---|---|---|
symbol (bawaan) |
Rp 700.000 | IDR 700,000 | US$42,50 | $42.50 | EGP 2,150.00 |
narrowSymbol |
Rp 700.000 | Rp 700,000 | $42,50 | $42.50 | E£2,150.00 |
code |
IDR 700.000 | IDR 700,000 | USD 42,50 | USD 42.50 | EGP 2,150.00 |
name |
700.000 Rupiah Indonesia | 700,000 Indonesian rupiahs | 42,50 Dolar Amerika Serikat | 42.50 US dollars | 2,150.00 Egyptian pounds |
symbolbergantung pada locale: "quot; polos berarti dolar AS dien-US, tapi diid-IDdolar AS ditulis "USquot;.narrowSymbolmemendekkan, tapi "quot; juga dipakai dolar Australia dan Singapura, jadi ambigu untuk daftar harga internasional.- Yen:
ja-JPmenulis "¥6,500" (Safari "¥6,500"), dannamemenulis "6,500円". Pound Mesir diar-EG: "٢٬١٥٠٫٠٠ ج.م.". - Desimal mengikuti mata uang: USD dan EGP 2 digit, JPY 0 digit di semua mesin.
- Rupiah: selalu
maximumFractionDigits: 0. Desimal bawaan IDR adalah 2 di Chrome 147, Firefox 146, dan Safari 26.4, tapi 0 di Node 22.22.1 (CLDR 48).minimumFractionDigits: 0saja gak cukup: untuk 349999,5 browser menulis "Rp 349.999,5", sedangkanmaximumFractionDigits: 0membulatkan jadi "Rp 350.000".
Notasi ringkas dan satuan
new Intl.NumberFormat('id-ID', { notation: 'compact' }).format(1200000); // "1,2 jt"
new Intl.NumberFormat('id-ID', { notation: 'compact', compactDisplay: 'long' }).format(1200000); // "1,2 juta"
new Intl.NumberFormat('id-ID', { style: 'unit', unit: 'kilometer', unitDisplay: 'long' }).format(26); // "26 kilometer"
new Intl.NumberFormat('id-ID', { style: 'unit', unit: 'celsius' }).format(12); // "12°C"
- 1.200.000 ditulis "1,2 jt" (
id-ID), "1.2M" (en-US), "120万" (ja-JP, 万 = 10.000), dan "١٫٢ مليون" (ar-EG). - Notasi ringkas membulatkan ke sekitar dua digit penting: total 3 malam Kamar Teras, 1.050.000, jadi "1,1 jt"; 1.275.000 jadi "1,3 jt". Cocok untuk "1,3 jt kali dilihat", jangan untuk harga yang dibayar.
style: 'unit'memakai nama satuan resmi:kilometer,celsius,meter,day. Dieng di 2.093 m: "2.093 m" (id-ID), "٢٬٠٩٣ مترًا" (ar-EG).unit: 'day'denganunitDisplay: 'long'untuk 2 memberi "2 hari", "2 days", dan bentuk dual "يومان" diar-EG. Satuannightgak ada:RangeError.
Tanggal: dateStyle atau field satu per satu
| Opsi | id-ID |
en-US |
ja-JP |
ar-EG |
|---|---|---|---|---|
dateStyle: 'short' |
12/12/26 | 12/12/26 | 2026/12/12 | ١٢/١٢/٢٠٢٦ |
dateStyle: 'medium' |
12 Des 2026 | Dec 12, 2026 | 2026/12/12 | ١٢/١٢/٢٠٢٦ |
dateStyle: 'long' |
12 Desember 2026 | December 12, 2026 | 2026年12月12日 | ١٢ ديسمبر ٢٠٢٦ |
{ weekday: 'short', day: 'numeric', month: 'short' } |
Sab, 12 Des | Sat, Dec 12 | 12月12日(土) | السبت، ١٢ ديسمبر |
dateStyledantimeStyleadalah paket jadi. Field (weekday,day,month,year,hour,minute) memilih bagian apa yang tampil; urutan dan tanda bacanya tetap dari locale.- Keduanya gak boleh dicampur:
{ dateStyle: 'medium', weekday: 'short' }melemparTypeError, dengan pesan yang beda di tiap mesin.
Rentang dan waktu relatif
const checkIn = new Date('2026-12-12T07:00:00Z'); // Sabtu 14.00 WIB
const checkOut = new Date('2026-12-14T05:00:00Z'); // Senin 12.00 WIB
new Intl.DateTimeFormat('id-ID', { day: 'numeric', month: 'short', timeZone: 'Asia/Jakarta' }).formatRange(checkIn, checkOut);
// "12\u201314 Des" (\u2013 adalah tanda pisah en dash)
// en-US: "Dec 12\u2009\u2013\u200914" di Chromium dan Node, "Dec 12 \u2013 14" di Firefox dan Safari
// ja-JP: "12/12~12/14", Firefox "12月12日~14日"; ar-EG: "١٢\u2013١٤ ديسمبر"
const rtf = new Intl.RelativeTimeFormat('id-ID', { numeric: 'auto' });
rtf.format(3, 'day'); // "dalam 3 hari"
rtf.format(1, 'day'); // "besok"
formatRangemenulis bagian yang sama sekali saja: "12" sampai "14 Des", bukan "12 Des sampai 14 Des". Lintas tahun ditulis lengkap: "30 Des 2026", tanda pisah, "2 Jan 2027".- Hasilnya memuat U+2013 dan, di Chromium, spasi tipis U+2009. Jangan membandingkannya dengan teks ketikan.
RelativeTimeFormatcuma menulis selisih yang kamu hitung. "Check-in 3 hari lagi" ditulis Intl sebagai "dalam 3 hari" (id-ID), "in 3 days", "3 日後", "خلال ٣ أيام". Hitung selisihnya dalam hari kalender di Asia/Jakarta (ambil tahun, bulan, tanggal lewatformatToPartsdengantimeZone), bukan jam dibagi 24.
Satu waktu, tiga jam dinding
const fmt = (zone) => new Intl.DateTimeFormat('id-ID', { weekday: 'short', day: 'numeric', month: 'short', hour: 'numeric', minute: '2-digit', timeZoneName: 'short', timeZone: zone }).format(checkIn);
fmt('Asia/Jakarta'); // "Sab, 12 Des, 14.00 WIB"
fmt('Asia/Tokyo'); // "Sab, 12 Des, 16.00 GMT+9" (ja-JP menulis "JST")
fmt('Africa/Cairo'); // "Sab, 12 Des, 09.00 GMT+2"
Di Kairo tertulis 09.00, karena bulan Desember Mesir memakai UTC+2. Mesir memakai waktu musim panas, jadi untuk 12 Juli offset-nya GMT+03:00 dan jamnya 10.00. Itu jawaban tebakannya. Jadi jangan pernah menyimpan "UTC+2" sebagai zona; simpan nama IANA (Africa/Cairo) dan biarkan Intl yang menghitung. "WIB" juga bukan nama zona: timeZone: 'WIB' melempar RangeError. Untuk jadwal check-in, tulis jam di zona homestay (Asia/Jakarta); jam di zona tamu boleh ditambahkan sebagai keterangan.
📌 Beda tiap mesin, diukur
Diukur di Chromium 147, Firefox 146, WebKit 26.4, dan Node 22.22.1. Yang gak ada di tabel ini sama di keempatnya.
| Yang diukur | Chromium 147 | Firefox 146 | WebKit 26.4 | Node 22.22.1 |
|---|---|---|---|---|
| Desimal bawaan IDR | 2 | 2 | 2 | 0 |
349999,5 IDR dengan minimumFractionDigits: 0 saja |
Rp 349.999,5 | Rp 349.999,5 | Rp349.999,5 | Rp 350.000 |
Simbol JPY di ja-JP |
¥ | ¥ | ¥ | ¥ |
Spasi di rentang en-US |
U+2009 | spasi biasa | spasi biasa | U+2009 |
Rentang ja-JP |
12/12~12/14 | 12月12日~14日 | 12/12~12/14 | 12/12~12/14 |
weekday: 'short' di ar-EG |
السبت | السبت | سبت | السبت |
Pesan TypeError untuk dateStyle plus weekday |
Invalid option : option | can't set option weekday when dateStyle is used | dateStyle and timeStyle may not be used with other DateTimeFormat options | Invalid option : option |
unit: 'day', long, ja-JP |
2 日 | 2 日 | 2日 | 2 日 |
⚠️ Jebakan yang sering ketemu
- Menyimpan teks jadi. "Rp 700.000" atau "12/12/2026" di database gak bisa dihitung dan gak bisa diterjemahkan. Simpan angka dan waktu UTC.
- Notasi ringkas untuk harga. "Rp 1,1 jt" bukan Rp 1.050.000.
narrowSymboldi daftar harga internasional. "quot; bisa dolar mana saja; pakaisymbolataucode.- Lupa
timeZone, atau menulis offset tetap. Jam ikut zona perangkat, dan offset Kairo berubah antara Desember dan Juli.
TL;DR: Simpan harga sebagai angka (700000) dan waktu sebagai UTC (2026-12-12T07:00:00Z), lalu format di layar. Mata uang memakai { style: "currency", currency: "IDR" } plus currencyDisplay (symbol "Rp 700.000" atau "IDR 700,000", narrowSymbol "Rp", code "IDR", name "Rupiah Indonesia"), dan rupiah selalu dengan maximumFractionDigits: 0, karena desimal bawaannya 2 di browser dan 0 di Node 22. Locale id-ID menulis 1000000 sebagai "1.000.000". Notasi ringkas ("1,2 jt", "1.2M", "120万") membulatkan, jadi bukan untuk harga. style: 'unit' untuk km, °C, dan hari. Tanggal memakai dateStyle atau field, gak boleh dicampur. formatRange menulis "12 sampai 14 Des" dengan tanda pisah U+2013, RelativeTimeFormat menulis "dalam 3 hari". Check-in yang sama adalah 14.00 di Asia/Jakarta, 16.00 di Asia/Tokyo, dan 09.00 di Africa/Cairo (10.00 di bulan Juli); simpan nama zona IANA, bukan offset.