JavaScriptでフォーマットされた日付の個別の部分を取得する方法

formatToParts()を使用して、ロケールでフォーマットされた日付の各コンポーネントに個別にアクセスする

はじめに

format()メソッドは、「2025年1月15日」や「15/01/2025」のような完全にフォーマットされた文字列を返します。これはシンプルな表示には適していますが、個別の部分を異なるスタイルで表示することはできません。月名を太字にしたり、年を異なる色にしたり、特定のコンポーネントにカスタムマークアップを適用したりすることはできません。

JavaScriptは、この問題を解決するためにformatToParts()メソッドを提供しています。単一の文字列を返す代わりに、フォーマットされた日付の各部分を表すオブジェクトの配列を返します。各部分には、monthdayyearなどのタイプと、January152025などの値があります。これらの部分を処理して、カスタムスタイルを適用したり、複雑なレイアウトを構築したり、フォーマットされた日付をリッチなユーザーインターフェースに統合したりできます。

フォーマットされた文字列のカスタマイズが困難な理由

「2025年1月15日」のようなフォーマットされた文字列を受け取った場合、月がどこで終わり、日がどこから始まるかを簡単に識別することはできません。ロケールによってコンポーネントの配置順序が異なります。ロケールによって異なる区切り文字が使用されます。これらの文字列を確実に解析するには、Intl APIですでに実装されているフォーマットルールを複製する複雑なロジックが必要です。

月名を太字で表示するカレンダーアプリケーションを考えてみましょう。format()を使用する場合、次のことが必要になります。

  1. どの文字が月名を表すかを検出する
  2. コンポーネント間のスペースと句読点を考慮する
  3. ロケール間で異なる月のフォーマットを処理する
  4. 日付を壊さないように文字列を慎重に解析する

このアプローチは脆弱でエラーが発生しやすいです。ロケールのフォーマットルールが変更されると、パース処理が壊れてしまいます。

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);
// Output: "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" }
// ]

hourminutesecond型は時刻コンポーネントを表します。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);
// Output: "<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);
// Output: "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);
// Output: "<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: 月曜日やMonのような曜日
  • era: BC、AD、BCEのような紀元表示
  • year: 2025のような年
  • month: 1月や01のような月名または月番号
  • 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 will include { type: "timeZoneName", value: "PST" } or similar

秒の小数部付きの日付:

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 will include { 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));
// Output: "January <span class="text-blue-600 font-bold">18</span>, 2025"

const monday = new Date(2025, 0, 13);
console.log(formatDateWithHighlight(monday));
// Output: "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));
// Output: "<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()を使用してください。