2時間30分のような時間間隔をフォーマットする方法

自動ローカライゼーションでユーザーの言語に合わせて期間を表示

はじめに

何かにかかる時間を表示する際は、ユーザーが理解できる形式で期間を表示する必要があります。動画は2時間30分の再生時間を表示し、ワークアウトアプリは運動時間を記録し、プロジェクト管理ツールはタスク完了時間を表示します。ローカライゼーションを行わない場合、次のようなコードを書くことになります。

const hours = 2;
const minutes = 30;
const timeSpan = `${hours}h ${minutes}m`;

これは言語に関係なく、すべてのユーザーに対して「2h 30m」を生成します。フランス語ユーザーは「2 h 30 min」を期待しているのに「2h 30m」が表示されます。ドイツ語ユーザーは「2 Std. 30 Min」ではなく英語の略語が表示されます。スペイン語ユーザーは単位間の接続詞「y」が表示されません。

JavaScriptはIntl.DurationFormat APIを提供しており、ユーザーの言語と文化的慣習に従って時間間隔をフォーマットできます。このレッスンでは、期間フォーマッターの作成方法、期間オブジェクトの構築方法、あらゆるロケールに対して時間間隔を正しく表示する方法について説明します。

時間間隔とは

時間間隔は時間の長さを表し、時点を表すものではありません。150分という数値は期間です。2025年3月15日午後2時30分は日時です。

この区別は重要です。なぜなら、日付にはカレンダー、タイムゾーン、歴史的なルールが関係するからです。時間間隔はカレンダーのコンテキストなしに経過時間を測定します。期間は特定の瞬間とは独立して存在するため、期間にタイムゾーンを追加することはできません。

時間間隔にはIntl.DurationFormatを使用してください。日時にはIntl.DateTimeFormatを使用してください。「2時間前」のような相対的な表現にはIntl.RelativeTimeFormatを使用してください。

期間フォーマッターを作成する

Intl.DurationFormatコンストラクタは、ロケールとオプションオブジェクトを受け取ります。ロケールは出力言語を決定します。オプションは書式スタイルと単位の表示を制御します。

const formatter = new Intl.DurationFormat('en', { style: 'long' });

期間オブジェクトを指定してformat()を呼び出すと、書式設定された文字列が生成されます。期間オブジェクトには、時間単位の数値プロパティが含まれます。

const duration = { hours: 2, minutes: 30 };
formatter.format(duration);
// "2 hours and 30 minutes"

APIは、ロケールに基づいて、略語、接続詞、語順、スペーシングを自動的に処理します。

期間オブジェクトの構築

期間オブジェクトは、時間単位のプロパティを持つプレーンなJavaScriptオブジェクトです。表示したい単位のみを含めます。

const duration1 = { hours: 2, minutes: 30 };
const duration2 = { minutes: 5, seconds: 45 };
const duration3 = { hours: 1, minutes: 15, seconds: 30 };

APIは次の時間単位をサポートしています:yearsmonthsweeksdayshoursminutessecondsmillisecondsmicrosecondsnanoseconds

すべての単位を含める必要はありません。表示したくない単位は省略してください。

const formatter = new Intl.DurationFormat('en', { style: 'long' });

formatter.format({ hours: 2, minutes: 30 });
// "2 hours and 30 minutes"

formatter.format({ minutes: 30 });
// "30 minutes"

formatter.format({ hours: 2 });
// "2 hours"

書式スタイルの選択

styleオプションは、出力の密度を制御します。4つのスタイルが利用可能です:longshortnarrowdigital

longスタイルは完全な単語を使用します。文章やメインコンテンツエリアに使用してください。

const duration = { hours: 2, minutes: 30 };

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours and 30 minutes"

shortスタイルは一般的な略語を使用します。スペースが限られているが可読性が重要な場合に使用してください。

new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "2 hr and 30 min"

narrowスタイルは最小限の文字を使用します。モバイルインターフェースやデータテーブルなどのコンパクトな表示に使用してください。

new Intl.DurationFormat('en', { style: 'narrow' }).format(duration);
// "2h 30m"

digitalスタイルは、コロンを使用したタイマーのような出力を生成します。メディアプレーヤーやカウントダウン表示に使用してください。

new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
// "2:30:00"

デジタル形式では、最大単位から最小単位まですべての単位を含める必要があります。時間と分を表示する場合は、秒も含める必要があります。

異なる言語での時間間隔の表示

同じ時間間隔でも、各言語で異なる形式で表示されます。APIはすべてのローカライゼーションを自動的に処理します。

const duration = { hours: 2, minutes: 30 };

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours and 30 minutes"

new Intl.DurationFormat('fr', { style: 'long' }).format(duration);
// "2 heures et 30 minutes"

new Intl.DurationFormat('de', { style: 'long' }).format(duration);
// "2 Stunden und 30 Minuten"

new Intl.DurationFormat('es', { style: 'long' }).format(duration);
// "2 horas y 30 minutos"

new Intl.DurationFormat('ja', { style: 'long' }).format(duration);
// "2時間30分"

各ロケールで異なる単語と接続詞が使用されていることに注目してください。フランス語では「et」、ドイツ語では「und」、スペイン語では「y」が使用され、日本語では接続詞は使用されません。APIはすべてのロケールに対してこれらのルールを認識しています。

短縮形式と狭小形式も正しくローカライズされます。

new Intl.DurationFormat('fr', { style: 'short' }).format(duration);
// "2 h et 30 min"

new Intl.DurationFormat('de', { style: 'narrow' }).format(duration);
// "2 Std. 30 Min."

ユーザーのロケールに応じた時間間隔の表示

ロケールをハードコーディングする代わりに、ブラウザからユーザーの優先言語を使用します。navigator.languageプロパティは、ユーザーの最優先言語設定を返します。

const userLocale = navigator.language;
const formatter = new Intl.DurationFormat(userLocale, { style: 'short' });

const duration = { hours: 2, minutes: 30 };
formatter.format(duration);
// Output varies by user's locale
// For en-US: "2 hr and 30 min"
// For de-DE: "2 Std. und 30 Min."
// For fr-FR: "2 h et 30 min"

これにより、手動でロケールを選択することなく、各ユーザーの期待に応じて時間間隔が表示されます。

ミリ秒から期間オブジェクトへの変換

時間計算では、多くの場合ミリ秒が生成されます。適切な係数で除算することで、ミリ秒を期間オブジェクトに変換します。

const milliseconds = 9000000; // 2 hours 30 minutes

const hours = Math.floor(milliseconds / 3600000);
const minutes = Math.floor((milliseconds % 3600000) / 60000);
const seconds = Math.floor((milliseconds % 60000) / 1000);

const duration = { hours, minutes, seconds };

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours, 30 minutes and 0 seconds"

特に表示したい場合を除き、ゼロ値は省略してください。

const duration = {};
if (hours > 0) duration.hours = hours;
if (minutes > 0) duration.minutes = minutes;
if (seconds > 0) duration.seconds = seconds;

new Intl.DurationFormat('en', { style: 'long' }).format(duration);
// "2 hours and 30 minutes"

2つの日付から時間間隔を計算

タイムスタンプを減算して2つの日付間の期間を計算し、結果を期間オブジェクトに変換します。

const startTime = new Date('2025-10-15T10:00:00');
const endTime = new Date('2025-10-15T12:30:00');

const diffMs = endTime - startTime;

const hours = Math.floor(diffMs / 3600000);
const minutes = Math.floor((diffMs % 3600000) / 60000);

const duration = { hours, minutes };

new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "2 hr and 30 min"

このアプローチは、ミリ秒を生成するあらゆる時間計算に適用できます。

動画プレーヤーの再生時間の表示

動画プレーヤーはコントロール部分に再生時間を表示します。コンパクトな表示には、デジタル形式または狭小形式を使用します。

function formatVideoDuration(totalSeconds) {
  const hours = Math.floor(totalSeconds / 3600);
  const minutes = Math.floor((totalSeconds % 3600) / 60);
  const seconds = Math.floor(totalSeconds % 60);

  const duration = hours > 0
    ? { hours, minutes, seconds }
    : { minutes, seconds };

  const locale = navigator.language;
  return new Intl.DurationFormat(locale, { style: 'digital' }).format(duration);
}

formatVideoDuration(9000); // "2:30:00"
formatVideoDuration(330);  // "5:30"

これにより、必要な場合にのみ時間が含まれ、短い動画では「5:30」、長いコンテンツでは「2:30:00」と表示されます。

ワークアウト時間のフォーマット

フィットネスアプリは運動時間を記録します。セッションサマリーにはlongスタイルを使用し、コンパクトなリスト表示にはnarrowスタイルを使用します。

function formatWorkoutDuration(startTime, endTime, locale) {
  const diffMs = endTime - startTime;

  const hours = Math.floor(diffMs / 3600000);
  const minutes = Math.floor((diffMs % 3600000) / 60000);

  const duration = hours > 0
    ? { hours, minutes }
    : { minutes };

  return new Intl.DurationFormat(locale, { style: 'long' }).format(duration);
}

const workoutStart = new Date('2025-10-15T07:00:00');
const workoutEnd = new Date('2025-10-15T09:30:00');

formatWorkoutDuration(workoutStart, workoutEnd, 'en');
// "2 hours and 30 minutes"

formatWorkoutDuration(workoutStart, workoutEnd, 'es');
// "2 horas y 30 minutos"

プロジェクトタスク時間のフォーマット

プロジェクト管理ツールは、タスクにかかる時間を表示します。ダッシュボード表示にはshortスタイルを使用します。

function formatTaskDuration(minutes, locale) {
  const hours = Math.floor(minutes / 60);
  const mins = minutes % 60;

  const duration = {};
  if (hours > 0) duration.hours = hours;
  if (mins > 0) duration.minutes = mins;

  return new Intl.DurationFormat(locale, { style: 'short' }).format(duration);
}

formatTaskDuration(150, 'en');
// "2 hr and 30 min"

formatTaskDuration(45, 'en');
// "45 min"

formatTaskDuration(150, 'de');
// "2 Std. und 30 Min."

異なる時間単位のフォーマット

時間範囲は時間と分に限定されません。サポートされている単位の任意の組み合わせをフォーマットできます。

const formatter = new Intl.DurationFormat('en', { style: 'long' });

formatter.format({ days: 3, hours: 2 });
// "3 days and 2 hours"

formatter.format({ minutes: 45, seconds: 30 });
// "45 minutes and 30 seconds"

formatter.format({ hours: 1, minutes: 30, seconds: 45 });
// "1 hour, 30 minutes and 45 seconds"

APIは、単位の任意の組み合わせに対して適切な接続詞と区切り文字を処理します。

秒のみの時間範囲のフォーマット

時間が1分未満の場合は、秒のみを含めます。

const formatter = new Intl.DurationFormat('en', { style: 'short' });

formatter.format({ seconds: 45 });
// "45 sec"

formatter.format({ seconds: 5 });
// "5 sec"

非常に短い時間の場合は、ミリ秒を含めることができます。

formatter.format({ seconds: 5, milliseconds: 500 });
// "5 sec and 500 ms"

パフォーマンスのためにフォーマッターインスタンスを再利用

新しいフォーマッターの作成には、ロケールデータの読み込みとオプションの処理が必要です。同じロケールとスタイルで複数の時間範囲をフォーマットする場合は、フォーマッターを一度作成して再利用します。

const formatter = new Intl.DurationFormat('en', { style: 'short' });

const durations = [
  { hours: 1, minutes: 30 },
  { hours: 2, minutes: 15 },
  { minutes: 45 }
];

durations.map(d => formatter.format(d));
// ["1 hr and 30 min", "2 hr and 15 min", "45 min"]

このパターンは、ループや繰り返しレンダリングで多数の時間をフォーマットする際のパフォーマンスを向上させます。

ブラウザサポート

Intl.DurationFormat APIは2025年3月にBaselineになりました。Chrome、Edge、Firefox、Safariの最新バージョンで動作します。古いブラウザはこのAPIをサポートしていません。

APIを使用する前にサポートを確認してください。

if (typeof Intl.DurationFormat !== 'undefined') {
  const formatter = new Intl.DurationFormat('en', { style: 'short' });
  return formatter.format(duration);
} else {
  return `${duration.hours}h ${duration.minutes}m`;
}

これにより、古いブラウザ向けのフォールバックを提供しながら、利用可能な場合はネイティブAPIを使用できます。