JavaScriptでフォーマットされた日付の個々の部分を取得する方法
formatToParts()を使用してロケールでフォーマットされた日付の各コンポーネントに個別にアクセスする
はじめに
format()メソッドは「January 15, 2025」や「15/01/2025」のような完全にフォーマットされた文字列を返します。これは単純な表示には適していますが、個々の部分を異なるスタイルで装飾することはできません。月名を太字にしたり、年を異なる色で表示したり、特定の要素にカスタムマークアップを適用したりすることができません。
JavaScriptはこの問題を解決するためにformatToParts()メソッドを提供しています。単一の文字列を返す代わりに、フォーマットされた日付の各部分を表すオブジェクトの配列を返します。各部分にはmonth、day、yearなどの型と、January、15、2025などの値があります。これらの部分を処理して、カスタムスタイルを適用したり、複雑なレイアウトを構築したり、フォーマットされた日付をリッチなユーザーインターフェースに統合したりすることができます。
フォーマット済み文字列のカスタマイズが難しい理由
「January 15, 2025」のようなフォーマット済み文字列を受け取った場合、月がどこで終わり、日がどこから始まるかを簡単に識別することができません。ロケールによって要素の順序が異なります。一部のロケールでは異なる区切り文字を使用します。これらの文字列を確実に解析するには、Intl APIですでに実装されているフォーマットルールを複製する複雑なロジックが必要です。
月名を太字で表示するカレンダーアプリケーションを考えてみましょう。format()を使用する場合、以下のことが必要になります:
- どの文字が月名を表しているかを検出する
- 要素間のスペースや句読点を考慮する
- ロケール間で異なる月のフォーマットに対応する
- 日付を壊さないように文字列を慎重に解析する
このアプローチは脆弱でエラーが発生しやすいです。ロケールのフォーマットルールが変更されると、解析ロジックが破綻します。
formatToParts()メソッドは、コンポーネントを個別に提供することでこの問題を解消します。ロケールに関係なく、どの部分が何であるかを正確に示す構造化データを受け取ることができます。
formatToPartsを使用して日付コンポーネントを取得する
「formatToParts()」メソッドは戻り値を除いて「format()」と同じように動作します。同じオプションでフォーマッターを作成し、「format()」の代わりに「formatToParts()」を呼び出します。
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
これは以下のようなオブジェクトの配列を出力します:
[
{ type: "month", value: "January" },
{ type: "literal", value: " " },
{ type: "day", value: "15" },
{ type: "literal", value: ", " },
{ type: "year", value: "2025" }
]
各オブジェクトには、その部分が何を表すかを識別する「type」プロパティと、実際の文字列を含む「value」プロパティが含まれています。これらの部分はフォーマットされた出力と同じ順序で表示されます。
すべての値を結合することで、これを確認できます:
const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// 出力: "January 15, 2025"
連結された部分は「format()」を呼び出した場合とまったく同じ出力を生成します。
パートタイプを理解する
「type」プロパティは各コンポーネントを識別します。異なるフォーマットオプションは異なるパートタイプを生成します。
基本的な日付フォーマットの場合:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
// { type: "month", value: "January" },
// { type: "literal", value: " " },
// { type: "day", value: "15" },
// { type: "literal", value: ", " },
// { type: "year", value: "2025" }
// ]
「month」タイプは月名または数字を表します。「day」タイプは月の日を表します。「year」タイプは年を表します。「literal」タイプはフォーマッターによって挿入されるスペース、句読点、またはその他のテキストを表します。
曜日付きの日付の場合:
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
// { type: "weekday", value: "Wednesday" },
// { type: "literal", value: ", " },
// { type: "month", value: "January" },
// { type: "literal", value: " " },
// { type: "day", value: "15" },
// { type: "literal", value: ", " },
// { type: "year", value: "2025" }
// ]
「weekday」タイプは曜日を表します。
時間付きの日付の場合:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: "numeric"
});
const date = new Date(2025, 0, 15, 14, 30, 45);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
// { type: "month", value: "January" },
// { type: "literal", value: " " },
// { type: "day", value: "15" },
// { type: "literal", value: ", " },
// { type: "year", value: "2025" },
// { type: "literal", value: " at " },
// { type: "hour", value: "2" },
// { type: "literal", value: ":" },
// { type: "minute", value: "30" },
// { type: "literal", value: ":" },
// { type: "second", value: "45" },
// { type: "literal", value: " " },
// { type: "dayPeriod", value: "PM" }
// ]
「hour」、「minute」、「second」タイプは時間コンポーネントを表します。「dayPeriod」タイプは12時間形式でのAMまたはPMを表します。
日付の各部分にカスタムスタイルを適用する
formatToParts()の主な使用例は、異なるコンポーネントに異なるスタイルを適用することです。パーツ配列を処理して、特定のタイプをHTML要素でラップすることができます。
月名を太字にする例:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
if (part.type === "month") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// 出力: "<strong>January</strong> 15, 2025"
このアプローチはあらゆるマークアップ言語で機能します。パーツ配列を処理することで、HTML、JSX、またはその他の形式を生成できます。
年を異なるスタイルにする例:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
if (part.type === "year") {
return `<span class="text-gray-500">${part.value}</span>`;
}
return part.value;
})
.join("");
console.log(html);
// 出力: "January 15, <span class="text-gray-500">2025</span>"
このパターンは、異なるコンポーネントに異なる視覚的な強調が必要なカレンダー表示でよく使用されます。
複数のスタイルを使用したカスタム日付表示の構築
複雑なインターフェースでは、複数のスタイル規則を組み合わせることがよくあります。異なるパーツタイプに対して同時に異なるクラスや要素を適用できます。
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
switch (part.type) {
case "weekday":
return `<span class="weekday">${part.value}</span>`;
case "month":
return `<span class="month">${part.value}</span>`;
case "day":
return `<span class="day">${part.value}</span>`;
case "year":
return `<span class="year">${part.value}</span>`;
case "literal":
return `<span class="literal">${part.value}</span>`;
default:
return part.value;
}
})
.join("");
console.log(html);
// 出力: "<span class="weekday">Wednesday</span><span class="literal">, </span><span class="month">January</span><span class="literal"> </span><span class="day">15</span><span class="literal">, </span><span class="year">2025</span>"
この細かい制御により、各コンポーネントに対して正確なスタイリングが可能になります。CSSを使用して各クラスを異なるスタイルにすることができます。
カスタム日付レイアウトの作成
標準のロケールフォーマットとは異なるカスタムレイアウトに日付コンポーネントを並べ替えることができます。特定の部分を抽出し、任意の順序で構成できます。
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const day = parts.find(p => p.type === "day").value;
const month = parts.find(p => p.type === "month").value;
const year = parts.find(p => p.type === "year").value;
const customLayout = `
<div class="date-card">
<div class="day-large">${day}</div>
<div class="month-small">${month}</div>
<div class="year-small">${year}</div>
</div>
`;
console.log(customLayout);
これにより、日が目立つように表示され、その後に月と年が続く縦型カードレイアウトが作成されます。レイアウトが標準フォーマットと異なっていても、コンポーネントは適切にローカライズされたままです。
利用可能なすべてのパートタイプ
typeプロパティは、使用されるフォーマットオプションに応じて以下の値を持つことができます:
weekday:MondayやMonなどの曜日era:BC、AD、BCEなどの時代表示year:2025などの年month:JanuaryやO1などの月名または数字day:15などの月の日dayPeriod:AMやPM、またはその他のロケール固有の日周期hour:14や2などの時間minute:30などの分second:45などの秒fractionalSecond:ミリ秒またはその他の小数部分の秒timeZoneName:PSTやPacific Standard Timeなどのタイムゾーン名literal:フォーマットによって追加されるスペース、句読点、またはその他のテキストrelatedYear:代替カレンダーシステムにおけるグレゴリオ暦の年yearName:一部のカレンダーシステムにおける名前付きの年unknown:認識されないトークン
すべてのフォーマットオプションがすべてのパートタイプを生成するわけではありません。受け取るパーツは日付の値とフォーマッタの構成によって異なります。
時代表示のある日付:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
era: "short"
});
const date = new Date(-100, 0, 1);
const parts = formatter.formatToParts(date);
console.log(parts);
// [
// { type: "year", value: "101" },
// { type: "literal", value: " " },
// { type: "era", value: "BC" }
// ]
タイムゾーンのある日付:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
timeZoneName: "short"
});
const date = new Date(2025, 0, 15, 14, 30);
const parts = formatter.formatToParts(date);
console.log(parts);
// Partsには{ type: "timeZoneName", value: "PST" }などが含まれます
小数部分の秒がある日付:
const formatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
second: "numeric",
fractionalSecondDigits: 3
});
const date = new Date(2025, 0, 15, 14, 30, 45, 123);
const parts = formatter.formatToParts(date);
// Partsには{ type: "fractionalSecond", value: "123" }が含まれます
日付コンポーネントを条件付きでハイライトする
一部のアプリケーションでは、ビジネスロジックに基づいて特定の日付コンポーネントをハイライトします。formatToParts()を使用すると、適切なフォーマットを維持しながら、日付の値に基づいてスタイルを適用できます。
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
function formatDateWithHighlight(date) {
const parts = formatter.formatToParts(date);
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
const html = parts
.map(part => {
if (part.type === "day" && isWeekend) {
return `<span class="text-blue-600 font-bold">${part.value}</span>`;
}
return part.value;
})
.join("");
return html;
}
const saturday = new Date(2025, 0, 18);
console.log(formatDateWithHighlight(saturday));
// 出力: "January <span class="text-blue-600 font-bold">18</span>, 2025"
const monday = new Date(2025, 0, 13);
console.log(formatDateWithHighlight(monday));
// 出力: "January 13, 2025"
日付はロケールに適したフォーマットを受け取りながら、ビジネスロジックに基づいた条件付きスタイルが適用されます。
アクセシブルな日付表示の作成
formatToParts()を使用して、フォーマットされた日付にアクセシビリティ属性を追加できます。これにより、スクリーンリーダーが値を正確に読み上げることができます。
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
function formatAccessibleDate(date) {
const parts = formatter.formatToParts(date);
const formatted = parts.map(part => part.value).join("");
const isoDate = date.toISOString().split('T')[0];
return `<time datetime="${isoDate}">${formatted}</time>`;
}
const date = new Date(2025, 0, 15);
console.log(formatAccessibleDate(date));
// 出力: "<time datetime="2025-01-15">January 15, 2025</time>"
これにより、スクリーンリーダーが日付を適切に読み上げることができる一方で、表示はロケールに合わせたフォーマットで表示されます。
パーツがロケール固有のフォーマットを保持する方法
パーツ配列は、ロケール固有のフォーマットルールを自動的に維持します。異なるロケールでは、コンポーネントの順序や使用するフォーマットが異なりますが、formatToParts()はこれらの違いを処理します。
const usFormatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
console.log(usFormatter.formatToParts(date));
// [
// { type: "month", value: "January" },
// { type: "literal", value: " " },
// { type: "day", value: "15" },
// { type: "literal", value: ", " },
// { type: "year", value: "2025" }
// ]
const ukFormatter = new Intl.DateTimeFormat("en-GB", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(ukFormatter.formatToParts(date));
// [
// { type: "day", value: "15" },
// { type: "literal", value: " " },
// { type: "month", value: "January" },
// { type: "literal", value: " " },
// { type: "year", value: "2025" }
// ]
イギリスのフォーマットでは、日が月の前に配置されます。スタイリングコードはロケールに関係なく同じ方法でパーツ配列を処理し、フォーマットは自動的に適応します。
const jpFormatter = new Intl.DateTimeFormat("ja-JP", {
year: "numeric",
month: "long",
day: "numeric"
});
console.log(jpFormatter.formatToParts(date));
// [
// { type: "year", value: "2025" },
// { type: "literal", value: "年" },
// { type: "month", value: "1月" },
// { type: "day", value: "15" },
// { type: "literal", value: "日" }
// ]
日本語のフォーマットでは、異なる順序と「年」や「日」などの文字リテラルが使用されます。パーツ配列はこれらのロケール固有の規則を自動的に反映します。
formatToPartsとフレームワークコンポーネントの組み合わせ
Reactのようなモダンフレームワークでは、formatToParts()を使用して効率的にコンポーネントを構築できます。
function DateDisplay({ date, locale, options }) {
const formatter = new Intl.DateTimeFormat(locale, options);
const parts = formatter.formatToParts(date);
return (
<span className="date-display">
{parts.map((part, index) => {
if (part.type === "month") {
return <strong key={index}>{part.value}</strong>;
}
if (part.type === "year") {
return <span key={index} className="text-sm text-gray-500">{part.value}</span>;
}
return <span key={index}>{part.value}</span>;
})}
</span>
);
}
このコンポーネントは、どのロケールでも適切なフォーマットを維持しながら、異なる部分に異なるスタイルを適用します。
formatToPartsとformatを使い分ける場合
カスタマイズなしの単純なフォーマット済み文字列が必要な場合はformat()を使用してください。これは日付表示の一般的なケースです。
以下の場合はformatToParts()を使用してください:
- 日付の異なる部分に異なるスタイルを適用する
- フォーマット済みの日付でHTMLやJSXを構築する
- 特定のコンポーネントに属性やメタデータを追加する
- 日付コンポーネントをカスタムレイアウトに再配置する
- フォーマット済みの日付を複雑なレイアウトに統合する
- フォーマット済み出力をプログラムで処理する
formatToParts()メソッドは、単一の文字列ではなくオブジェクトの配列を作成するため、format()よりもわずかにオーバーヘッドが大きくなります。この差は一般的なアプリケーションでは無視できますが、1秒間に何千もの日付をフォーマットする場合は、format()の方がパフォーマンスが良くなります。
ほとんどのアプリケーションでは、パフォーマンスの懸念よりもスタイリングのニーズに基づいて選択してください。出力をカスタマイズする必要がない場合はformat()を使用してください。カスタムスタイルやマークアップが必要な場合はformatToParts()を使用してください。