JavaScriptの Intl とは、数値・通貨・日付・相対時間・リスト・並べ替え・文字分割などを、ロケール(言語と地域の指定)に合わせて書式化するための組み込みオブジェクト(国際化API)です。 (1234567).toLocaleString("ja-JP") の裏側で動いているのも Intl.NumberFormat で、オプションを渡すだけで「¥1,234,568」「1235万」「令和8年10月1日」「3 日前」のような表示が、自前の文字列処理なしで作れます。

この記事は「やりたいこと → コード → 出力」の逆引き一覧が中心です。118個のレシピを Node.js 22.22(ICU 78.2)と Chromium 141(Playwright)で同じコードで実行して出力を並べ、環境で異なる点(Intl.DurationFormat の有無、¥ と ¥、空白の種類)、インスタンスを使い回したときの速度差(Node で約60倍)、toLocaleString との関係、実際のエラー文つきのつまずきポイントまでまとめました。表の出力はすべて実行結果です。

Intl 逆引き早見表(やりたいこと → コード → 出力)

数値・通貨(Intl.NumberFormat)

ロケールは "ja-JP"、コード欄の { ... } は new Intl.NumberFormat("ja-JP", { ... }).format(値) の省略形です。

やりたいこと コード 出力(Node 22 / Chromium 141 とも)
3桁区切り new Intl.NumberFormat("ja-JP").format(1234567.891) 1,234,567.891
区切りなし { useGrouping: false } → 1234567 1234567
円(JPY) { style: "currency", currency: "JPY" } → 1234567.5 ¥1,234,568
「円」を付ける { style: "currency", currency: "JPY", currencyDisplay: "name" } 1,234,567円
通貨コードで { ..., currencyDisplay: "code" } JPY 1,234,567
ドル(USD) { style: "currency", currency: "USD" } → 1234567.891 $1,234,567.89
ドルを言葉で { ..., currency: "USD", currencyDisplay: "name" } 1,234,567.89米ドル
負数を括弧で(会計) new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", currencySign: "accounting" }).format(-1234.5) ($1,234.50)
万・億 { notation: "compact" } → 12345678 1235万
万・億 小数1桁 { notation: "compact", maximumFractionDigits: 1 } 1234.6万
億 { notation: "compact" } → 123456789 1.2億
円 + 万・億 { style: "currency", currency: "JPY", notation: "compact" } ¥1235万
英語の K / M new Intl.NumberFormat("en-US", { notation: "compact" }).format(12345678) 12M
英語の million { notation: "compact", compactDisplay: "long" }(en-US) 12 million
パーセント { style: "percent" } → 0.2567 26%
パーセント 小数1桁 { style: "percent", maximumFractionDigits: 1 } → 0.2567 25.7%
単位 km { style: "unit", unit: "kilometer" } → 12.5 12.5 km
単位を言葉で { style: "unit", unit: "kilometer", unitDisplay: "long" } 12.5 キロメートル
単位 kg / GB unit: "kilogram" → 3.2 / unit: "gigabyte" → 256 3.2 kg / 256 GB
単位 時間 { style: "unit", unit: "hour", unitDisplay: "long" } → 3 3 時間
単位 km/h・℃ unit: "kilometer-per-hour" → 60 / unit: "celsius" → 36.5 60 km/h / 36.5°C
小数2桁固定 { minimumFractionDigits: 2, maximumFractionDigits: 2 } → 1234.5 1,234.50
小数の既定(最大3桁) {} → 3.14159265 3.142
有効桁3桁 { maximumSignificantDigits: 3 } → 1234567 / 0.00123456 1,230,000 / 0.00123
ゼロ埋め { minimumIntegerDigits: 3, useGrouping: false } → 7 007
符号を常に付ける { signDisplay: "always" } → 1234 +1,234
0以外に符号 { signDisplay: "exceptZero" } → 0 / 5 / -5 0 / +5 / -5
切り捨て { maximumFractionDigits: 0, roundingMode: "trunc" } → 2.9 2
整数なら小数を消す { minimumFractionDigits: 2, trailingZeroDisplay: "stripIfInteger" } → 100 100
範囲(〜) new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" }).formatRange(1000, 2500) ¥1,000 ~ ¥2,500
部品に分解 .formatToParts(-1234)(JPY) minusSign:- currency:¥ integer:1 group:, integer:234
指数表記 new Intl.NumberFormat("en-US", { notation: "scientific" }).format(123456) 1.235E5
BigInt .format(12345678901234567890n) 12,345,678,901,234,567,890
文字列の数値(精度維持) { maximumFractionDigits: 20 } → "0.1234567890123456789" 0.1234567890123456789
漢数字 new Intl.NumberFormat("ja-JP-u-nu-hanidec").format(2026) 二,〇二六

日付・時刻(Intl.DateTimeFormat)

D = new Date("2026-10-01T00:00:00Z")(日本時間 2026/10/1 9:00)、D2 は同日 10:30、D3 は 10/3 です。すべて timeZone: "Asia/Tokyo" を付けています(理由は後述)。

やりたいこと コード(new Intl.DateTimeFormat("ja-JP", { timeZone: "Asia/Tokyo", ... }).format(D)) 出力
標準の日付 オプションなし 2026/10/1
ゼロ埋めの日付 { dateStyle: "medium" } 2026/10/01
yyyy/MM/dd(個別指定) { year: "numeric", month: "2-digit", day: "2-digit" } 2026/10/01
yyyy-MM-dd new Intl.DateTimeFormat("sv-SE", { timeZone: "Asia/Tokyo" }) 2026-10-01
yyyy-MM-dd HH:mm:ss "sv-SE" + { dateStyle: "short", timeStyle: "medium" } → D2 2026-10-01 10:30:00
日本語の日付 { dateStyle: "long" } 2026年10月1日
曜日つき { dateStyle: "full" } 2026年10月1日木曜日
曜日だけ { weekday: "short" } / { weekday: "long" } 木 / 木曜日
月日と曜日 { month: "long", day: "numeric", weekday: "short" } 10月1日(木)
時刻 { timeStyle: "short" } / "medium" / "long" / "full" 9:00 / 9:00:00 / 9:00:00 JST / 9時00分00秒 日本標準時
日時 { dateStyle: "medium", timeStyle: "short" } 2026/10/01 9:00
24時間・2桁 { hour: "2-digit", minute: "2-digit", hourCycle: "h23" } 09:00
午前・午後 { hour: "numeric", minute: "2-digit", hour12: true }(15:05) 午後3:05
英語の日時 "en-US" + { dateStyle: "full", timeStyle: "short" } Thursday, October 1, 2026 at 9:00 AM
和暦 "ja-JP-u-ca-japanese" + { dateStyle: "long" } 令和8年10月1日
和暦(短い) "ja-JP-u-ca-japanese"、オプションなし R8/10/1
和暦(オプション指定) { calendar: "japanese", era: "long", year: "numeric", month: "long", day: "numeric" } 令和8年10月1日
元年 和暦 + dateStyle: "long" → 2019-05-01 令和元年5月1日
タイムゾーン名 { hour: "2-digit", minute: "2-digit", timeZoneName: "longOffset" } 09:00 GMT+09:00
別のタイムゾーン { timeZone: "America/New_York", dateStyle: "medium", timeStyle: "long" } 2026/09/30 20:00:00 GMT-4
時間の範囲 { dateStyle: "medium", timeStyle: "short" } → .formatRange(D, D2) 2026/10/01 9時00分~10時30分
日付の範囲 { dateStyle: "long" } → .formatRange(D, D3) 2026/10/01~2026/10/03
英語の範囲 "en-US" + { dateStyle: "medium" } → .formatRange(D, D3) Oct 1 – 3, 2026
ミリ秒 { minute: "2-digit", second: "2-digit", fractionalSecondDigits: 3 } 00:05.123
部品に分解 { dateStyle: "medium" } → .formatToParts(D) year:2026 literal:/ month:10 literal:/ day:01

相対時間・リスト・複数形・並べ替え・分割・期間・表示名

やりたいこと コード 出力
「昨日」 new Intl.RelativeTimeFormat("ja", { numeric: "auto" }).format(-1, "day") 昨日
「3 日前」 new Intl.RelativeTimeFormat("ja").format(-3, "day") 3 日前
「2 時間後」「来月」 .format(2, "hour") / { numeric: "auto" } + .format(1, "month") 2 時間後 / 来月
英語の相対時間 new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-1, "day") yesterday
「A、B、C」 new Intl.ListFormat("ja").format(["りんご", "みかん", "バナナ"]) りんご、みかん、バナナ
「AまたはB」 new Intl.ListFormat("ja", { type: "disjunction" }).format([...]) りんご、みかん、またはバナナ
英語の and / or new Intl.ListFormat("en").format(["apples", "oranges", "bananas"]) apples, oranges, and bananas
複数形の判定 new Intl.PluralRules("en").select(1) / .select(2) one / other
序数(1st, 2nd) new Intl.PluralRules("en", { type: "ordinal" }).select(3) few(→ 3rd)
日本語の並べ替え ["みかん", "りんご", "バナナ", "いちご"].sort(new Intl.Collator("ja").compare) いちご, バナナ, みかん, りんご
数値順に並べる ["item10", "item2", "item1"].sort(new Intl.Collator("ja", { numeric: true }).compare) item1, item2, item10
大小・アクセント無視 new Intl.Collator("en", { sensitivity: "base" }).compare("a", "Á") 0(等しい)
見た目の文字数 [...new Intl.Segmenter("ja", { granularity: "grapheme" }).segment("👨‍👩‍👧‍👦です")].length 3(.length は 13)
単語に分割 new Intl.Segmenter("ja", { granularity: "word" }) + isWordLike 今日, は, 良い, 天気, です, ね
文に分割 new Intl.Segmenter("ja", { granularity: "sentence" }) 今日は晴れ。 / 明日は雨です。
期間(1時間30分5秒) new Intl.DurationFormat("ja", { style: "long" }).format({ hours: 1, minutes: 30, seconds: 5 }) 1 時間 30 分 5 秒(Chromium 141。Node 22 は未対応)
期間(digital) new Intl.DurationFormat("ja", { style: "digital" }).format({ ... }) 1:30:05
国名 new Intl.DisplayNames(["ja"], { type: "region" }).of("US") アメリカ合衆国
言語名 new Intl.DisplayNames(["ja"], { type: "language" }).of("en-GB") イギリス英語
通貨名・文字体系 { type: "currency" } → .of("USD") / { type: "script" } → .of("Hira") 米ドル / ひらがな
ロケールの分解 new Intl.Locale("ja-JP-u-ca-japanese") → .language / .region / .calendar ja / JP / japanese
対応している値の一覧 Intl.supportedValuesOf("currency").length / ("timeZone").length 162(Node)・159(Chromium)/ 418

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript Intlの使い方|数値・通貨・日付フォーマット逆引き一覧」

検証環境と Intl の全体像

この記事の出力は、Node.js v22.22.2(V8 12.4、ICU 78.2、CLDR 48、tz 2025c)と Chromium 141.0.7390.37(Playwright 1.56)で同じコードを実行して得たものです。 Intl の各クラスは「ロケール + オプションでインスタンスを作り、format() に値を渡す」という同じ形をしています。

flowchart LR
  A[やりたいこと] --> B{種類は?}
  B -->|数値・通貨・単位・%| N["Intl.NumberFormat"]
  B -->|日付・時刻・和暦・TZ| D["Intl.DateTimeFormat"]
  B -->|3日前・来月| R["Intl.RelativeTimeFormat"]
  B -->|A、B、C| L["Intl.ListFormat"]
  B -->|1時間30分| U["Intl.DurationFormat"]
  B -->|並べ替え| C["Intl.Collator"]
  B -->|文字数・単語| S["Intl.Segmenter"]
  B -->|国名・言語名| G["Intl.DisplayNames"]
  N & D & R & L & U --> F["format / formatToParts / formatRange"]
クラス 役割 Chrome Firefox Safari Node.js
Intl.NumberFormat 数値・通貨・単位・% 24 29 10 0.12
同 v3(formatRange・roundingMode・trailingZeroDisplay 等) 範囲・丸めの細かい制御 106 116 15.4 19
Intl.DateTimeFormat 日付・時刻・和暦・タイムゾーン 24 29 10 0.12
同 formatRange 日時の範囲 76 91 14.1 12.9
Intl.RelativeTimeFormat 「3 日前」 71 65 14 12
Intl.ListFormat 「A、B、C」 72 78 14.1 12
Intl.PluralRules 複数形・序数の判定 63 58 13 10
Intl.Collator 言語に合わせた並べ替え 24 29 10 0.12
Intl.Segmenter 文字・単語・文の分割 87 125 14.1 16
Intl.DisplayNames 国名・言語名・通貨名 81 86 14.1 14
Intl.DurationFormat 期間(1時間30分) 129 136 16.4 23
Intl.Locale ロケールの分解・正規化 74 75 14 12
Intl.supportedValuesOf() 対応値の一覧 99 93 15.4 18

対応バージョンは MDN の互換性データ(@mdn/browser-compat-data 8.1.3、2026-09-24)から取りました。Intl.DurationFormat 以外は、現在サポートされている主要ブラウザと Node.js 18 以降でそのまま使えます。

ロケールは BCP 47 形式("ja-JP"、"en-US")で指定し、-u- 以降に拡張キーを付けると暦(ca-japanese)や数字の体系(nu-hanidec)、数値順の並べ替え(kn-true)を切り替えられます。配列で ["xx-YY", "ja-JP"] のように渡すと、使える最初のものが選ばれます(検証では ja-JP が選ばれました)。"ja_JP" のようにアンダースコアで書くと RangeError: Incorrect locale information provided になるので、ハイフンに直して渡します("ja-jp" と小文字で書いた場合は ja-JP に正規化されます)。

数値の書式(Intl.NumberFormat)

3桁区切り・小数桁・有効桁

既定では小数は最大3桁(maximumFractionDigits: 3)で四捨五入され、3桁区切りが付きます。 桁数を固定したいときは minimumFractionDigits と maximumFractionDigits を同じ値にし、「意味のある桁」で丸めたいときは maximumSignificantDigits を使います(1234567 → 1,230,000、0.00123456 → 0.00123)。ロケールを de-DE にすると区切り記号が入れ替わり 1.234.567,891 になります。

適用された既定値は new Intl.NumberFormat("ja-JP").resolvedOptions() で確認でき、Node 22・Chromium 141 とも minimumFractionDigits: 0、maximumFractionDigits: 3、useGrouping: "auto"、roundingMode: "halfExpand"、signDisplay: "auto" でした。

通貨(JPY / USD と currencyDisplay)

style: "currency" では小数桁の既定が通貨ごとに決まり、JPY は0桁、USD は2桁です。 そのため 1234.56 円は ¥1,235 と丸められます(resolvedOptions() で JPY は [0, 0]、USD は [2, 2] でした)。記号の出し方は currencyDisplay で切り替えます。

const yen = (opt) => new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY", ...opt });
console.log(yen().format(1234567.5));                          // ¥1,234,568
console.log(yen({ currencyDisplay: "name" }).format(1234567)); // 1,234,567円
console.log(yen({ minimumFractionDigits: 2 }).format(1234.5)); // ¥1,234.50
console.log(new Intl.NumberFormat("en-US", { style: "currency", currency: "JPY" }).format(1234567.5)); // ¥1,234,568
console.log(new Intl.NumberFormat("de-DE", { style: "currency", currency: "EUR" }).format(1234567.891)); // 1.234.567,89 €
currencyDisplay JPY(ja-JP) USD(ja-JP) 用途
"symbol"(既定) ¥1,234,567 $1,234,567.89 一般的な表示
"narrowSymbol" ¥1,234,567 $1,234,567.89 地域を省いた短い記号(CA$ → $ など)
"code" JPY 1,234,567 USD 1,234,567.89 複数通貨が混ざる一覧
"name" 1,234,567円 1,234,567.89米ドル 日本語の文章中

JPY の記号は、ロケール ja-JP では全角の ¥(U+FFE5)、en-US では半角の ¥(U+00A5)になります。 半角で統一したいときは formatToParts で currency 部分を差し替えるか、currencyDisplay: "name" で「円」を付ける方法があります。通貨コードは3文字なら何でも受け付けられ、currency: "YEN" のような存在しないコードでもエラーにならず YEN 1.00 と出ます(コードの綴りはテストで確認しておくと安心です)。

万・億の compact 表記

notation: "compact" を ja-JP で使うと「万」「億」単位になります。 既定の丸めは「小数0桁」と「有効桁2桁」のうち精度が高いほう(roundingPriority: "morePrecision")なので、12345678 は 1235万、123456789 は 1.2億 と、桁によって小数の有無が変わります。小数桁をそろえたいときは maximumFractionDigits を明示します。

const compact = (opt) => new Intl.NumberFormat("ja-JP", { notation: "compact", ...opt });
console.log([1000, 9999, 10000, 15000, 12345678, 123456789].map((n) => compact().format(n)));
// [ '1000', '9999', '1万', '1.5万', '1235万', '1.2億' ]
console.log(compact({ maximumFractionDigits: 1 }).format(12345678));   // 1234.6万
console.log(compact({ style: "currency", currency: "JPY" }).format(12345678)); // ¥1235万
console.log(new Intl.NumberFormat("en-US", { notation: "compact" }).format(12345678)); // 12M
console.log(new Intl.NumberFormat("en-US", { notation: "compact", compactDisplay: "long" }).format(12345678)); // 12 million

パーセントと単位

style: "percent" は「割合(0.256)」を受け取って100倍します。 すでに百分率になった 25.6 を渡すと 2,560% になります。単位は style: "unit" と unit で指定し、unit には "km" ではなく CLDR の名前("kilometer")を使います。使える名前は Intl.supportedValuesOf("unit") で一覧できます(-per- でつなぐと kilometer-per-hour のような合成単位になります)。

よく使う unit short(既定) long(ja-JP)
kilometer / meter / centimeter 12.5 km 12.5 キロメートル
kilogram / gram 3.2 kg 3.2 キログラム
byte / kilobyte / megabyte / gigabyte / terabyte 256 GB 256 ギガバイト
second / minute / hour / day 5 秒 / 3 時間 / 5 日(ja-JP は short でも漢字) 3 時間
kilometer-per-hour(-per- で合成) 60 km/h 時速 60 キロメートル
celsius / percent 36.5°C 摂氏 36.5 度

符号・丸め・末尾ゼロ

roundingMode の既定は "halfExpand"(いわゆる四捨五入)で、Intl は数値を最短の10進表現として丸めます。 そのため (1.005).toFixed(2) は2進数の誤差で 1.00 になりますが、Intl では 1.01 になります(検証済み。文字列 "1.005" を渡しても 1.01)。

const nf = (opt) => new Intl.NumberFormat("ja-JP", opt);
console.log([0, 5, -5].map((n) => nf({ signDisplay: "exceptZero" }).format(n)));  // [ '0', '+5', '-5' ]
console.log(nf({ maximumFractionDigits: 0, roundingMode: "trunc" }).format(2.9)); // 2
console.log((1.005).toFixed(2), new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format(1.005)); // 1.00 1.01
console.log([NaN, Infinity, -0].map((n) => nf().format(n)));                      // [ 'NaN', '∞', '-0' ]
オプション 取りうる値 既定
signDisplay auto / always / exceptZero / negative / never auto
roundingMode ceil / floor / expand / trunc / halfCeil / halfFloor / halfExpand / halfTrunc / halfEven halfExpand
roundingIncrement 1 / 2 / 5 / 10 / 20 / 25 / 50 / 100 ...(maximumFractionDigits と併用) 1
roundingPriority auto / morePrecision / lessPrecision auto(compact は morePrecision)
trailingZeroDisplay auto / stripIfInteger auto

範囲(formatRange)と部品(formatToParts)

formatRange(a, b) は「¥1,000 ~ ¥2,500」のような範囲を、formatToParts() は記号・整数・区切りなどの部品を返します。 部品に分けると、通貨記号だけ小さく表示する、¥ を ¥ に置き換える、といった加工が文字列操作なしでできます。

const yen = new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" });
console.log(yen.formatRange(1000, 2500));   // ¥1,000 ~ ¥2,500(~ は U+FF5E、前後は半角空白)
console.log(yen.formatRange(1000, 1000));   // 約¥1,000(同じ値は「約」になる)
console.log(new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).formatRange(3, 5)); // $3.00 – $5.00(– は U+2013)
console.log(yen.formatToParts(-1234));
// [ { type: 'minusSign', value: '-' }, { type: 'currency', value: '¥' },
//   { type: 'integer', value: '1' }, { type: 'group', value: ',' }, { type: 'integer', value: '234' } ]

// 通貨記号を半角にし、<small> で囲む例
const html = yen.formatToParts(1234567)
  .map((p) => (p.type === "currency" ? `<small>¥</small>` : p.value)).join("");
console.log(html);                          // <small>¥</small>1,234,567

NumberFormat オプション早見表

オプション 主な値 既定 用途
style decimal / currency / percent / unit decimal 書式の種類
currency ISO 4217 コード(JPY、USD) なし(currency スタイルでは必須) 通貨
currencyDisplay symbol / narrowSymbol / code / name symbol 記号の出し方
currencySign standard / accounting standard 負数を括弧に
unit / unitDisplay CLDR 単位名 / short / narrow / long なし / short 単位
notation standard / scientific / engineering / compact standard 万・億、指数
compactDisplay short / long short 12M / 12 million
useGrouping always / auto / min2 / true / false auto 3桁区切り
minimumIntegerDigits 1〜21 1 ゼロ埋め
minimumFractionDigits / maximumFractionDigits 0〜100 0 / 3(通貨は通貨ごと) 小数桁
minimumSignificantDigits / maximumSignificantDigits 1〜21 指定時 1 / 21 有効桁
numberingSystem latn / hanidec / arab など ロケール依存 数字の体系

日付・時刻の書式(Intl.DateTimeFormat)

dateStyle / timeStyle の一覧

よく使う形は dateStyle と timeStyle の組み合わせで作れます。 4段階(full / long / medium / short)の出力を ja-JP でそろえました。

指定 ja-JP の出力(2026-10-01 9:00 JST) en-US の出力
dateStyle: "full" 2026年10月1日木曜日 Thursday, October 1, 2026
dateStyle: "long" 2026年10月1日 October 1, 2026
dateStyle: "medium" 2026/10/01 Oct 1, 2026
dateStyle: "short" 2026/10/01 10/1/26
timeStyle: "full" 9時00分00秒 日本標準時 9:00:00 AM Japan Standard Time
timeStyle: "long" 9:00:00 JST 9:00:00 AM GMT+9
timeStyle: "medium" 9:00:00 9:00:00 AM
timeStyle: "short" 9:00 9:00 AM
dateStyle: "medium", timeStyle: "short" 2026/10/01 9:00 Oct 1, 2026, 9:00 AM

個別指定(yyyy/MM/dd・曜日・24時間)

year / month / day / weekday / hour / minute / second を個別に指定すると、必要な要素だけを出せます。 dateStyle と個別指定を同時に渡すと TypeError: Invalid option : option になるので、どちらか一方にします。"sv-SE"(スウェーデン語)ロケールは yyyy-MM-dd の形になるため、ISO 風の日付が欲しいときの定番です。

const D = new Date("2026-10-01T00:00:00Z");
const ja = (opt) => new Intl.DateTimeFormat("ja-JP", { timeZone: "Asia/Tokyo", ...opt }).format(D);
console.log(ja({ year: "numeric", month: "2-digit", day: "2-digit" }));    // 2026/10/01
console.log(ja({ month: "long", day: "numeric", weekday: "short" }));      // 10月1日(木)
console.log(ja({ hour: "2-digit", minute: "2-digit", hourCycle: "h23" })); // 09:00
console.log(new Intl.DateTimeFormat("sv-SE", { dateStyle: "short", timeStyle: "medium", timeZone: "Asia/Tokyo" })
  .format(new Date("2026-10-01T01:30:00Z")));                              // 2026-10-01 10:30:00

hourCycle は "h23"(0〜23時)を使います。"h24" にすると深夜0時が 24:00 になります(検証済み)。hour12: false も 00:00 になりました。

和暦(ja-JP-u-ca-japanese)

和暦はロケールに -u-ca-japanese を付けるか、calendar: "japanese" を渡すだけで出せます。 改元の境目も ICU のデータで正しく切り替わります。

const w = (d, opt = {}) => new Intl.DateTimeFormat("ja-JP-u-ca-japanese", { timeZone: "Asia/Tokyo", ...opt }).format(d);
console.log(w(new Date("2026-10-01T00:00:00Z")));                         // R8/10/1
console.log(w(new Date("2026-10-01T00:00:00Z"), { dateStyle: "long" }));  // 令和8年10月1日
console.log(w(new Date("2019-05-01T00:00:00Z"), { dateStyle: "long" }));  // 令和元年5月1日
console.log(w(new Date("2019-04-30T00:00:00Z"), { dateStyle: "long" }));  // 平成31年4月30日

era を個別指定すると "long" で 令和8年10月1日、"short" で 令和8/10/1、"narrow" で R8/10/1 になります。

タイムゾーン

timeZone を省略すると実行環境のタイムゾーンが使われます。 サーバー(多くは UTC)とブラウザ(JST)で日付がずれる原因になるため、表示用のフォーマッタには timeZone: "Asia/Tokyo" を付けておくと安心です。名前の出し方は timeZoneName で6種類(short / long / shortOffset / longOffset / shortGeneric / longGeneric)あり、IANA 名の一覧は Intl.supportedValuesOf("timeZone") で取れます(検証環境では418件)。

const D = new Date("2026-10-01T00:00:00Z");
for (const z of ["short", "long", "shortOffset", "longOffset"]) {
  console.log(z, new Intl.DateTimeFormat("ja-JP", { hour: "numeric", timeZoneName: z, timeZone: "Asia/Tokyo" }).format(D));
}
// short 9時 JST / long 9時 日本標準時 / shortOffset 9時 GMT+9 / longOffset 9時 GMT+09:00
console.log(new Intl.DateTimeFormat("ja-JP", { dateStyle: "medium", timeStyle: "long", timeZone: "America/New_York" }).format(D));
// 2026/09/30 20:00:00 GMT-4

日時の範囲(formatRange)

formatRange(from, to) は、同じ日なら日付を1回だけ出し、時刻だけを「~」でつなぎます。 自前で「開始 ~ 終了」を組むと日付の重複やロケールごとの記号に悩みますが、formatRange ならロケールに合った形になります。

const D = new Date("2026-10-01T00:00:00Z"), D2 = new Date("2026-10-01T01:30:00Z"), D3 = new Date("2026-10-03T00:00:00Z");
console.log(new Intl.DateTimeFormat("ja-JP", { dateStyle: "medium", timeStyle: "short", timeZone: "Asia/Tokyo" }).formatRange(D, D2));
// 2026/10/01 9時00分~10時30分
console.log(new Intl.DateTimeFormat("ja-JP", { dateStyle: "long", timeZone: "Asia/Tokyo" }).formatRange(D, D3));
// 2026/10/01~2026/10/03
console.log(new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeZone: "Asia/Tokyo" }).formatRange(D, D3));
// Oct 1 – 3, 2026

DateTimeFormat オプション早見表

オプション 主な値 既定 用途
dateStyle / timeStyle full / long / medium / short なし まとめて指定(個別指定と併用不可)
year numeric / 2-digit 個別指定なしのとき numeric 年
month numeric / 2-digit / long / short / narrow numeric 月(long で「10月」「October」)
day numeric / 2-digit numeric 日
weekday long / short / narrow なし 曜日
era long / short / narrow なし 元号・紀元
hour / minute / second numeric / 2-digit なし(timeStyle 指定時は自動) 時刻
fractionalSecondDigits 1〜3 なし ミリ秒
hourCycle / hour12 h11 / h12 / h23 / h24 / true / false ロケール依存 12時間・24時間
timeZone IANA 名(Asia/Tokyo、UTC) 実行環境の値 タイムゾーン
timeZoneName short / long / shortOffset / longOffset / shortGeneric / longGeneric なし JST / GMT+9 など
calendar gregory / japanese / buddhist など18種 gregory 暦
numberingSystem latn / hanidec など latn 数字の体系

相対時間・リスト・複数形

「3 日前」「来月」を出す(Intl.RelativeTimeFormat)

RelativeTimeFormat は「数値と単位」から相対表現を作ります。 numeric: "auto" にすると -1 day が「昨日」、1 month が「来月」のような言葉になり、既定("always")では「1 日前」のように常に数字で出ます。単位は second / minute / hour / day / week / month / quarter / year(複数形 days なども可)で、それ以外は RangeError: Invalid unit argument for Intl.RelativeTimeFormat.prototype.format() 'days ago' になります。

const rtf = new Intl.RelativeTimeFormat("ja", { numeric: "auto" });
console.log(rtf.format(-1, "day"), rtf.format(-3, "day"), rtf.format(1, "month")); // 昨日 3 日前 来月
console.log(new Intl.RelativeTimeFormat("ja").format(-1, "day"));                 // 1 日前(numeric: "always" が既定)
console.log(rtf.formatToParts(-3, "day"));
// [ { type: 'integer', value: '3', unit: 'day' }, { type: 'literal', value: ' 日前' } ]

差の大きさから単位を自動で選ぶ関数は、次の「使い回しヘルパー」に入れています(30 秒前 / 5 分前 / 3 時間前 / 昨日 / 3 日前 / 2 週間後 / 2 か月前 / 来年 を確認済み)。

「A、B、C」「AまたはB」(Intl.ListFormat)

ListFormat は配列を、言語ごとの読点や「and / or」でつなぎます。 引数は文字列の配列(または Set などの反復可能オブジェクト)で、文字列をそのまま渡すと1文字ずつ分解され a、,、b のようになります。

console.log(new Intl.ListFormat("ja").format(["りんご", "みかん", "バナナ"]));                          // りんご、みかん、バナナ
console.log(new Intl.ListFormat("ja", { type: "disjunction" }).format(["りんご", "みかん", "バナナ"])); // りんご、みかん、またはバナナ
console.log(new Intl.ListFormat("ja").format(new Set(["a", "b"])));                                 // a、b
type 意味 ja en
conjunction(既定) AとB りんご、みかん、バナナ apples, oranges, and bananas
disjunction AまたはB りんご、みかん、またはバナナ apples, oranges, or bananas
unit 単位の列挙 3 ft 7 in(narrow) 3 ft, 7 in(long)

複数形と序数(Intl.PluralRules)

PluralRules は「1 item / 2 items」のような複数形の分岐と、「1st / 2nd / 3rd」の序数の分岐を言語ごとに判定します。 日本語の分類は other だけなので(resolvedOptions().pluralCategories は ["other"])、主に英語など複数形がある言語の文言で使います。

const ord = new Intl.PluralRules("en", { type: "ordinal" });
const suffix = { one: "st", two: "nd", few: "rd", other: "th" };
console.log([1, 2, 3, 4, 11, 22].map((n) => n + suffix[ord.select(n)]).join(", ")); // 1st, 2nd, 3rd, 4th, 11th, 22nd
const items = (n) => `${n} ${new Intl.PluralRules("en").select(n) === "one" ? "item" : "items"}`;
console.log(items(1), items(2));                                                     // 1 item 2 items

並べ替え・文字分割・表示名

日本語の並べ替えと数値順(Intl.Collator)

Array.prototype.sort() は引数なしだと文字コード順なので、日本語は Intl.Collator("ja").compare を渡します。 numeric: true で「item2 → item10」の数値順、sensitivity で大小やアクセントの扱いを変えられます。localeCompare も内部では同じ比較ですが、大量に並べるときは Collator を1つ作って渡すほうが効率的です。

console.log(["みかん", "りんご", "バナナ", "いちご"].sort());                                // [ 'いちご', 'みかん', 'りんご', 'バナナ' ](コード順)
console.log(["みかん", "りんご", "バナナ", "いちご"].sort(new Intl.Collator("ja").compare)); // [ 'いちご', 'バナナ', 'みかん', 'りんご' ]
console.log(["item10", "item2", "item1"].sort(new Intl.Collator("ja", { numeric: true }).compare)); // [ 'item1', 'item2', 'item10' ]
console.log(new Intl.Collator("ja").compare("あ", "ア"));                                   // 0(既定ではひらがなとカタカナを同じ扱い)
sensitivity 区別するもの "a" と "A" "a" と "á"
base 文字の基本形だけ 等しい 等しい
accent アクセント 等しい 異なる
case 大小 異なる 等しい
variant(既定) すべて 異なる 異なる

文字数・単語・文の分割(Intl.Segmenter)

絵文字や結合文字を含む文字列の「見た目の文字数」は Segmenter で数えます。 granularity を "grapheme" にすると "👨‍👩‍👧‍👦です" は 3(.length は 13)、"word" にすると 今日は良い天気ですね。 が 今日 / は / 良い / 天気 / です / ね に分かれ(isWordLike が true のものだけ)、"sentence" にすると 今日は晴れ。 / 明日は雨です。 のように文で分かれます。英語でも "It's 3.5 km." を It's / 3.5 / km のように単語単位で切り出せます。

国名・言語名・通貨名(Intl.DisplayNames)

DisplayNames はコードから各言語の名前を引けます。 対応している type は region / language / currency / script / calendar / dateTimeField です。コードの形式が合わないと RangeError: invalid_argument になります("japan" のような綴りではなく "JP")。

console.log(new Intl.DisplayNames(["ja"], { type: "region" }).of("US"));      // アメリカ合衆国
console.log(new Intl.DisplayNames(["ja"], { type: "language" }).of("en-GB")); // イギリス英語
console.log(new Intl.DisplayNames(["ja"], { type: "calendar" }).of("japanese")); // 和暦
console.log(["year", "month", "weekOfYear", "day"].map((f) => new Intl.DisplayNames(["ja"], { type: "dateTimeField" }).of(f))); // [ '年', '月', '週', '日' ]

期間の書式(Intl.DurationFormat)と対応状況

Intl.DurationFormat は { hours: 1, minutes: 30 } のような期間オブジェクトを「1 時間 30 分」「1:30:05」に書式化します。 MDN では 2025年3月に Baseline(主要ブラウザで利用可)となり、対応は Chrome 129 / Edge 129 / Firefox 136 / Safari 16.4 / Node.js 23 / Deno 1.46 からです。検証した Node 22 では typeof Intl.DurationFormat が undefined(呼ぶと TypeError: Intl.DurationFormat is not a constructor)で、Chromium 141 では function でした。Chromium 141 の出力は style: "long" で 1 時間 30 分 5 秒、"digital" で 1:30:05、英語の "long" で 1 hour, 30 minutes, 5 seconds、"narrow" で 2d 3h です。

未対応環境では、NumberFormat の unit と ListFormat の type: "unit" を組み合わせると同じ出力が作れます。 次の関数は Node 22 でも Chromium 141 でも同じ結果になりました。

// Intl.DurationFormat があれば使い、なければ NumberFormat(unit) + ListFormat で組み立てる
function formatDuration(duration, locale = "ja", style = "long") {
  if (typeof Intl.DurationFormat === "function") {
    return new Intl.DurationFormat(locale, { style }).format(duration);
  }
  const parts = Object.entries(duration)
    .filter(([, v]) => v !== 0)
    .map(([unit, v]) => new Intl.NumberFormat(locale, {
      style: "unit", unit: unit.replace(/s$/, ""), unitDisplay: style,
    }).format(v));
  return new Intl.ListFormat(locale, { style, type: "unit" }).format(parts);
}
console.log(formatDuration({ hours: 1, minutes: 30, seconds: 5 }));          // 1 時間 30 分 5 秒
console.log(formatDuration({ hours: 1, minutes: 30, seconds: 5 }, "en"));    // 1 hour, 30 minutes, 5 seconds
console.log(formatDuration({ days: 2, hours: 3 }, "en", "narrow"));          // 2d 3h

style は long / short / narrow / digital で、{ hours: "numeric", minutes: "2-digit" } のように単位ごとの表示や fractionalDigits も指定できます。期間オブジェクトのキーは years / months / weeks / days / hours / minutes / seconds / milliseconds / microseconds / nanoseconds です。

Node 22 と Chromium 141 で同じコードを動かした結果

118個のレシピのうち112個は Node 22 と Chromium 141 で完全に同じ出力で、違ったのは Intl.DurationFormat に関する5個と Intl.supportedValuesOf("currency") の件数(162 と 159)だけでした。 どちらも V8 + ICU で動いているため書式はほぼ一致しますが、V8 の版(Node 22 は 12.4、Chromium 141 はより新しい)と ICU/CLDR の版は別々に更新されます。Node.js は 22.x の途中でも ICU を更新する(今回の 22.22 は ICU 78.2)ので、「同じ Node 22 でもパッチ版で文字列が変わる」ことがあります。 テストで日時や通貨の文字列を固定値と比べるときは、この前提を知っておくと原因が追いやすくなります。

Chromium 側は Playwright の page.evaluate() に同じ式の文字列を渡して評価し、locale / timezoneId を指定しない既定のコンテキストで実行しました。

記号と空白の違い(文字コードで確認)

見た目が同じでも、通貨記号や空白の文字コードがロケールや書式で異なります。 文字列比較のテストが「一致しない」ときは、まずここを疑うと早く解決します([...s].map((c) => c.codePointAt(0).toString(16)) で確認できます)。

書式 出力 注目する文字
JPY(ja-JP) ¥1 ¥ は U+FFE5(全角)
JPY(en-US) ¥1 ¥ は U+00A5(半角)
currencyDisplay: "code" JPY 1,234,567 コードと数字の間は U+00A0(改行しない空白)
EUR(de-DE) 1.234,50 € € の前は U+00A0
3桁区切り(fr-FR) 1 234 567,891 区切りは U+202F(狭い改行しない空白)
単位(ja-JP) 12.5 km 通常の空白 U+0020
範囲(ja-JP) ¥1,000 ~ ¥2,500 ~ は U+FF5E、前後は U+0020
範囲(en-US) $3.00 – $5.00 – は U+2013(en ダッシュ)
時刻(en-US) 9:00 AM この環境では U+0020。ICU の版によっては U+202F が使われていたことがある

テストでは、formatToParts() で部品ごとに比べる、あるいは s.replace(/\s+/g, " ") のように空白を正規化してから比べると、ICU の版が変わっても安定します(JavaScript の \s は U+00A0 や U+202F にも一致します)。

ロケールとタイムゾーンを省略すると環境で結果が変わる

ロケールを省略すると Node.js は環境変数 LANG(または LC_ALL)、ブラウザは利用者の言語設定で既定ロケールが決まり、タイムゾーンは TZ 環境変数や OS 設定に従います。 同じコードを環境変数だけ変えて実行した結果です。

実行方法 既定ロケール 既定 TZ D.toLocaleString() (1234567.891).toLocaleString()
TZ=Asia/Tokyo node(検証環境の既定) en-US Asia/Tokyo 10/1/2026, 9:00:00 AM 1,234,567.891
TZ=UTC node en-US UTC 10/1/2026, 12:00:00 AM 1,234,567.891
TZ=America/New_York node en-US America/New_York 9/30/2026, 8:00:00 PM 1,234,567.891
LANG=ja_JP.UTF-8 node ja-JP Asia/Tokyo 2026/10/1 9:00:00 1,234,567.891
LANG=de_DE.UTF-8 TZ=Europe/Berlin node de-DE Europe/Berlin 1.10.2026, 02:00:00 1.234.567,891
Chromium(既定のコンテキスト) en-US Asia/Tokyo 10/1/2026, 9:00:00 AM 1,234,567.891
Chromium(locale: "ja-JP", timezoneId: "Asia/Tokyo") ja-JP Asia/Tokyo 2026/10/1 9:00:00 1,234,567.891

同じ D が「10/1」にも「9/30」にもなっています。表示用のフォーマッタには、ロケールと timeZone を必ず明示するのがいちばん簡単な対策です。Playwright では browser.newContext({ locale: "ja-JP", timezoneId: "Asia/Tokyo" }) でブラウザ側の既定を固定できます。

インスタンスを使い回すと何倍速いか

new Intl.NumberFormat(...) の生成は、format() の呼び出しよりはるかに重い処理です。 同じ書式を1万〜10万回使う場合の計測結果です(performance.now() で計測、単位はミリ秒。環境により値は変わります)。

処理 Node 22(10万回) Node 22(1万回) Chromium 141(1万回)
NumberFormat を毎回 new して format 3,130 317 246
n.toLocaleString("ja-JP", opt) を毎回 2,988 302 233
NumberFormat を1つ作って format を使い回す 52 6 5
DateTimeFormat を毎回 new して format 6,718 658 615
d.toLocaleString("ja-JP", opt) を毎回 6,579 629 593
DateTimeFormat を1つ作って使い回す 306 14 14

Node 22 の10万回では、NumberFormat で約60倍、DateTimeFormat で約22倍の差になりました。toLocaleString は呼ぶたびに内部でフォーマッタを作るのと同じコストなので、一覧表示やループの中では Intl のインスタンスを使い回します(計測は performance.now() で、for ループ内で毎回 new する版と、ループ外で1つ作る版を同じ入力配列で比べました)。

コピペ用:キャッシュ付きフォーマッタ(fmt.mjs)

ロケールとオプションをキーにして Map に保存しておけば、どこから呼んでもインスタンスが使い回されます。 相対時間の単位を自動で選ぶ relative() も入れています。

// fmt.mjs — Intl のインスタンスをキャッシュして使い回すヘルパー(Node 22 / ブラウザ共通)
const cache = new Map();

/** 同じロケール・オプションなら同じインスタンスを返す */
function get(Ctor, locale, options = {}) {
  const key = `${Ctor.name}|${locale}|${JSON.stringify(options)}`;
  let f = cache.get(key);
  if (!f) {
    f = new Ctor(locale, options);
    cache.set(key, f);
  }
  return f;
}

export const num = (n, options, locale = "ja-JP") =>
  get(Intl.NumberFormat, locale, options).format(n);
export const yen = (n) => num(n, { style: "currency", currency: "JPY" });
export const compact = (n) => num(n, { notation: "compact", maximumFractionDigits: 1 });

const TZ = "Asia/Tokyo";
export const dateTime = (d, options = { dateStyle: "medium", timeStyle: "short" }, locale = "ja-JP") =>
  get(Intl.DateTimeFormat, locale, { timeZone: TZ, ...options }).format(d);
export const wareki = (d) => dateTime(d, { dateStyle: "long" }, "ja-JP-u-ca-japanese");
export const dateRange = (from, to) =>
  get(Intl.DateTimeFormat, "ja-JP", { timeZone: TZ, dateStyle: "medium", timeStyle: "short" }).formatRange(from, to);

/** 「3 日前」「来月」など、差の大きさから単位を自動で選ぶ */
export function relative(target, now = new Date(), locale = "ja") {
  const rtf = get(Intl.RelativeTimeFormat, locale, { numeric: "auto" });
  const diffSec = Math.round((target - now) / 1000);
  const units = [["year", 31536000], ["month", 2592000], ["week", 604800],
                 ["day", 86400], ["hour", 3600], ["minute", 60], ["second", 1]];
  for (const [unitName, sec] of units) {
    if (Math.abs(diffSec) >= sec || unitName === "second") {
      return rtf.format(Math.round(diffSec / sec), unitName);
    }
  }
}

export const list = (items, type = "conjunction", locale = "ja") =>
  get(Intl.ListFormat, locale, { type }).format(items);
export const sortJa = (arr) => [...arr].sort(get(Intl.Collator, "ja", { numeric: true }).compare);
// 使い方(node で実行した出力をコメントに記載)
import { yen, compact, wareki, dateRange, relative, list, sortJa } from "./fmt.mjs";
const now = new Date("2026-10-01T00:00:00Z"); // 日本時間 2026/10/01 9:00
console.log(yen(1234567.5), compact(12345678), wareki(now));   // ¥1,234,568 1234.6万 令和8年10月1日
console.log(dateRange(now, new Date("2026-10-01T01:30:00Z"))); // 2026/10/01 9時00分~10時30分
for (const t of [-30, -5 * 60, -3 * 3600, -86400, -3 * 86400, 14 * 86400, -60 * 86400, 400 * 86400]) {
  console.log(relative(new Date(now.getTime() + t * 1000), now));
}
// 30 秒前 / 5 分前 / 3 時間前 / 昨日 / 3 日前 / 2 週間後 / 2 か月前 / 来年
console.log(list(["りんご", "みかん"], "disjunction"), sortJa(["item10", "りんご", "item2", "いちご"]));
// りんごまたはみかん [ 'item2', 'item10', 'いちご', 'りんご' ]

toLocaleString との関係とオプション早見表

toLocaleString() 系のメソッドは、内部で対応する Intl クラスを作って format() を呼ぶのと同じ結果になります。 引数も同じ (locales, options) なので、Intl のオプション表はそのまま toLocaleString に使えます。検証では (1234.5).toLocaleString("ja-JP", opt) === new Intl.NumberFormat("ja-JP", opt).format(1234.5) が true でした。

メソッド 同じ結果になる Intl 既定で出る要素
number.toLocaleString(loc, opt) new Intl.NumberFormat(loc, opt).format(number) 3桁区切り、小数最大3桁
bigint.toLocaleString(loc, opt) new Intl.NumberFormat(loc, opt).format(bigint) 同上
date.toLocaleString(loc, opt) new Intl.DateTimeFormat(loc, opt).format(date) 年月日 + 時分秒(2026/10/1 9:00:00)
date.toLocaleDateString(loc, opt) 同上(日付の既定) 年月日(2026/10/1)
date.toLocaleTimeString(loc, opt) 同上(時刻の既定) 時分秒(9:00:00)
array.toLocaleString(loc, opt) 各要素の toLocaleString を , でつなぐ 1,234.5,2026/10/1 9:00:00
string.localeCompare(other, loc, opt) new Intl.Collator(loc, opt).compare(string, other) 言語に合わせた比較
string.toLocaleUpperCase(loc) (対応する Intl クラスはなし。言語ごとの大小変換) トルコ語の i など
Temporal.*.toLocaleString(loc, opt) new Intl.DateTimeFormat(loc, opt).format(temporalObject) 型に応じた日付・時刻
使い分け 選ぶもの 理由
1回だけ、画面の1か所 toLocaleString 短く書ける
ループ・一覧・サーバーの大量処理 Intl のインスタンスを使い回す 生成コストを払うのが1回で済む(上の計測で約20〜60倍)
範囲(formatRange)や部品(formatToParts)が必要 Intl toLocaleString にはない
適用された既定値を知りたい Intl の resolvedOptions() toLocaleString にはない

よくあるつまずきと直し方

つまずきの多くは「省略した指定が環境依存になる」か「オプション名・値の形式が違う」のどちらかです。 実際のエラー文と合わせてまとめます。

症状 原因 直し方
サーバーとブラウザで日付が1日ずれる timeZone 未指定で実行環境の TZ(UTC など)が使われる timeZone: "Asia/Tokyo" を付ける
本番だけ 10/1/2026 と英語形式になる ロケール未指定で LANG やブラウザ設定に従う 第1引数に "ja-JP" を必ず渡す
3.14159 が 3.142 になる maximumFractionDigits の既定が 3 maximumFractionDigits を指定する
¥1,235 と小数が消える JPY の既定は小数0桁 minimumFractionDigits: 2 などを指定
RangeError: maximumFractionDigits value is out of range. minimumFractionDigits: 3, maximumFractionDigits: 1 のように最小 > 最大 最小 ≤ 最大にする
TypeError: Currency code is required with currency style. style: "currency" で currency 未指定 currency: "JPY" を付ける
YEN 1.00 のような表示 存在しない通貨コードはエラーにならない ISO 4217 のコード(JPY)を使う
RangeError: Invalid unit argument for Intl.NumberFormat() 'km' 単位は CLDR 名 unit: "kilometer"
2,560% になる percent は割合を100倍する 0.256 を渡す
RangeError: Invalid time zone specified: Asia/Tokio IANA 名の綴り Asia/Tokyo(Intl.supportedValuesOf("timeZone") で確認)
TypeError: Invalid option : option dateStyle と year などの併用 どちらか一方にする
RangeError: Invalid time value Invalid Date を渡した Number.isNaN(d.getTime()) で先に確認
RangeError: Incorrect locale information provided "ja_JP" のようなアンダースコア "ja-JP"
RangeError: Invalid unit argument ... 'days ago' RelativeTimeFormat の単位に余計な語 "day" または "days"
a、,、b と1文字ずつ区切られる ListFormat に文字列を渡した 配列を渡す
テストで "9:00 AM" が一致しない 空白が U+00A0 や U+202F の場合がある formatToParts で比べる、または空白を正規化
1235万 と 1.2億 で小数の有無が違う compact の既定は「小数0桁」と「有効2桁」の精度が高いほう maximumFractionDigits: 1 をそろえる
// 直す前:環境に依存する
const before = (d) => d.toLocaleDateString();                 // TZ=UTC のサーバーでは前日になることがある

// 直した後:ロケール・タイムゾーン・桁数を明示し、インスタンスを使い回す
const dateFmt = new Intl.DateTimeFormat("ja-JP", { dateStyle: "medium", timeZone: "Asia/Tokyo" });
const after = (d) => (Number.isNaN(d.getTime()) ? "-" : dateFmt.format(d));
console.log(after(new Date("2026-10-01T00:00:00Z")), after(new Date("2026-13-01"))); // 2026/10/01 -

よくある質問

Q. toLocaleString と Intl.NumberFormat、どちらを使えばよいですか? A. 結果は同じなので、1回だけなら toLocaleString、一覧やループでは Intl.NumberFormat のインスタンスを使い回す、と使い分けます。10万回の計測では、毎回 toLocaleString を呼ぶより使い回すほうが約60倍速くなりました。範囲(formatRange)や部品(formatToParts)が必要なときも Intl を使います。

Q. 「1,234円」のように数字と「円」を別々に装飾したいです。 A. currencyDisplay: "name" で 1,234円 にするか、formatToParts() で integer / group / currency などの部品に分けて、必要な部分だけタグで囲みます。この記事の「範囲と部品」の例では currency 部分を <small>¥</small> に置き換えました。

Q. Node.js で Intl.DurationFormat が undefined になります。 A. Intl.DurationFormat は Node.js 23 以降(ブラウザは Chrome 129 / Firefox 136 / Safari 16.4 以降)で使えます。Node 22 では NumberFormat の unit と ListFormat の type: "unit" を組み合わせた関数で同じ出力(1 時間 30 分 5 秒)が作れます。本文の formatDuration() をそのまま使えます。

Q. ブラウザとサーバーで表示が違うのはなぜですか? A. 主な原因は3つです。(1)ロケールやタイムゾーンを省略して環境の既定が使われている、(2)ICU/CLDR の版が違い記号や空白の文字が異なる、(3)Intl.DurationFormat のように対応していないクラスがある。ロケールと timeZone を明示し、テストでは formatToParts や空白の正規化で比べると安定します。

まとめ

  • Intl は「ロケール + オプションでインスタンスを作り、format() に値を渡す」という同じ形で、数値・通貨・日付・相対時間・リスト・期間・並べ替え・分割・表示名を扱える
  • 3桁区切りは NumberFormat("ja-JP")、円は style: "currency", currency: "JPY"、万・億は notation: "compact"、単位は style: "unit"
  • 日付は dateStyle / timeStyle か個別指定、和暦は "ja-JP-u-ca-japanese"、範囲は formatRange、部品は formatToParts
  • ロケールと timeZone は必ず明示する。省略すると LANG / TZ / ブラウザ設定で結果が変わる(検証では同じ日時が 10/1 にも 9/30 にもなった)
  • インスタンスを使い回すと Node 22 で約20〜60倍速い。toLocaleString は毎回フォーマッタを作るのと同じコスト
  • Node 22 と Chromium 141 の出力は118個中112個が一致。違いは Intl.DurationFormat の有無(Node 23 以降で対応)と通貨一覧の件数だけ
  • ¥(U+FFE5)と ¥(U+00A5)、U+00A0 / U+202F の空白はテストで一致しない原因になるので、formatToParts や正規化で比べる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScript Intlの使い方|数値・通貨・日付フォーマット逆引き一覧」

株式会社RJCでは、標準APIの細かな挙動を実際に動かして確かめ、プロジェクトの中で仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?