時間の長さを2:30:45のようにフォーマットするには?

コロンを使用したデジタル時計形式で時間の長さを表示する

はじめに

ビデオプレーヤー、ストップウォッチ、カウントダウンタイマーは、馴染みのある形式で時間を表示します。「2:30:45」を見ると、すぐに2時間30分45秒を意味することが理解できます。このデジタル時計形式はコロンを使用して時間単位を区切り、ラベルやスペースを使用しません。

この形式を手動で構築するには、数字をゼロで埋めたり、異なる時間長を処理したりする必要があります。5分30秒のビデオは「5:30」と表示され、2時間のビデオは「2:00:00」と表示されます。エッジケースを含めると、ロジックは複雑になります。

function formatDuration(seconds) {
  const h = Math.floor(seconds / 3600);
  const m = Math.floor((seconds % 3600) / 60);
  const s = Math.floor(seconds % 60);

  if (h > 0) {
    return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
  }
  return `${m}:${String(s).padStart(2, '0')}`;
}

Intl.DurationFormat APIは、digitalスタイルオプションを使用してこれを自動的に処理します。どのような時間長に対しても正確な形式を生成します。

const duration = { hours: 2, minutes: 30, seconds: 45 };
new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
// "2:30:45"

タイマー表示にデジタルスタイルを使用する

digitalスタイルは、デジタル時計のように時間を表示します。フォーマッタを作成するときに、styleオプションを"digital"に設定します。

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

表示したい時間単位を含む時間オブジェクトを渡します。フォーマッタは単位間にコロンを追加し、必要に応じて値をゼロで埋めます。

const duration = { hours: 1, minutes: 5, seconds: 30 };
formatter.format(duration);
// "1:05:30"

分が「5」ではなく「05」と表示されることに注目してください。時間が存在する場合、フォーマッタは自動的に分を2桁に埋めます。これにより、リストやテーブルでの整列が維持されます。

時間のない時間長の場合は、時間オブジェクトからそのプロパティを省略します。

const shortDuration = { minutes: 5, seconds: 30 };
formatter.format(shortDuration);
// "5:30"

分が最大の単位である場合、埋め込みは不要になります。形式はコンパクトでありながら明確さを保ちます。

デジタルフォーマットが異なる時間長をどのように処理するか

デジタルフォーマットは、duration オブジェクトに含める単位に基づいて出力を調整します。これは、ビデオプレーヤーが動画の長さに合わせて表示を適応させる方法と一致しています。

短い時間は分と秒のみを表示します。

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

const short = { minutes: 3, seconds: 42 };
formatter.format(short);
// "3:42"

より長い時間には時間が含まれます。

const long = { hours: 2, minutes: 15, seconds: 8 };
formatter.format(long);
// "2:15:08"

時間が表示される場合、より小さい単位はすべて2桁にパディングされます。時間がない場合、分はパディングなしで表示されますが、秒はパディングされたままです。

このパディングルールは、スペースを無駄にすることなく一貫した配置を作成します。短い動画のリストでは、コロンが揃った「5:30」、「12:45」、「8:02」が表示されます。長い動画のリストでは、「1:05:30」、「2:12:45」、「3:08:02」と一貫したフォーマットで表示されます。

インターフェースに必要な単位のみを含めてください。1時間を超えることのないカウントダウンタイマーは、時間を完全に省略できます。

const countdown = { minutes: 42, seconds: 15 };
formatter.format(countdown);
// "42:15"

ゼロパディングと表示オプションの制御

デジタルフォーマットはデフォルトのパディングルールを使用しますが、個々の時間単位のオプションを指定することでこれらを上書きできます。

各単位の表示方法は、単位固有のオプションを設定することで制御できます。numeric値はパディングなしで数字を表示します。2-digit値は2桁のパディングを強制します。

const duration = { hours: 1, minutes: 5, seconds: 3 };

new Intl.DurationFormat('en', {
  style: 'digital',
  hours: 'numeric',
  minutes: '2-digit',
  seconds: '2-digit'
}).format(duration);
// "1:05:03"

これはデフォルトの動作です。異なるフォーマットが必要な場合にのみ、これらのオプションを指定する必要があります。

時間を常に2桁で表示するよう強制します。

new Intl.DurationFormat('en', {
  style: 'digital',
  hours: '2-digit',
  minutes: '2-digit',
  seconds: '2-digit'
}).format(duration);
// "01:05:03"

このフォーマットは、一貫した幅でレイアウトのシフトを防ぐ同期表示に適しています。

よりコンパクトな表示のために秒からパディングを削除します。

const shortDuration = { minutes: 5, seconds: 3 };

new Intl.DurationFormat('en', {
  style: 'digital',
  seconds: 'numeric'
}).format(shortDuration);
// "5:3"

このフォーマットはあまり一般的ではありません。なぜなら、ユーザーはデジタル表示では秒がパディングされることを期待するからです。

デジタル形式に小数部分の秒を追加する

一部のアプリケーションではミリ秒やマイクロ秒を表示する必要があります。デジタル形式では fractionalDigits オプションを使用して小数部分の秒をサポートしています。

秒の後に表示する小数点以下の桁数を設定します。

const duration = {
  minutes: 5,
  seconds: 30,
  milliseconds: 123
};

new Intl.DurationFormat('en', {
  style: 'digital',
  fractionalDigits: 3
}).format(duration);
// "5:30.123"

フォーマッタは秒の後に小数点を追加し、指定された桁数の小数部分を表示します。

ストップウォッチは通常、秒の100分の1単位を表示します。

const lap = {
  minutes: 1,
  seconds: 23,
  milliseconds: 450
};

new Intl.DurationFormat('en', {
  style: 'digital',
  fractionalDigits: 2
}).format(lap);
// "1:23.45"

フォーマッタは指定された精度に丸めます。450ミリ秒の値は45/100秒になります。

マイクロ秒の精度が必要な場合は、durationオブジェクトにマイクロ秒を含め、より高い小数点以下の桁数を設定します。

const precise = {
  seconds: 42,
  milliseconds: 123,
  microseconds: 456
};

new Intl.DurationFormat('en', {
  style: 'digital',
  fractionalDigits: 6
}).format(precise);
// "42.123456"

デジタル形式と他のスタイルを使い分ける場合

インターフェースがタイマー、ストップウォッチ、メディアプレーヤーに似ている場合は、デジタル形式を選択してください。ユーザーはこれらのコンテキストでこの形式を期待しています。

ビデオプレーヤーのコントロールにはデジタル形式を使用します。

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

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

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

formatVideoTime(6345); // "1:45:45"
formatVideoTime(125);  // "2:05"

カウントダウンタイマーやストップウォッチにはデジタル形式を使用します。

function formatStopwatch(milliseconds) {
  const minutes = Math.floor(milliseconds / 60000);
  const seconds = Math.floor((milliseconds % 60000) / 1000);
  const ms = milliseconds % 1000;

  return new Intl.DurationFormat('en', {
    style: 'digital',
    fractionalDigits: 2
  }).format({ minutes, seconds, milliseconds: ms });
}

formatStopwatch(125450); // "2:05.45"

文章内で期間を表示する場合や、ラベルが明確さを向上させる場合は、他のスタイルを使用してください。航空券予約サイトでは、コンテキストがタイマーではないため、「8:15:00」ではなく「8時間15分」と表示します。

// 文章や説明に適している
new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "1 hr, 46 min and 40 sec"

// タイマー表示に適している
new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
// "1:46:40"

古いブラウザ向けにデジタルフォーマットを手動で構築する

Intl.DurationFormat APIは2025年3月に利用可能になりました。古いブラウザでは、デジタルフォーマットを手動で構築します。

手動アプローチでは、合計秒数から時間単位を計算し、値にゼロパディングを行う必要があります。

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

  if (hours > 0) {
    const paddedMinutes = String(minutes).padStart(2, '0');
    const paddedSeconds = String(seconds).padStart(2, '0');
    return `${hours}:${paddedMinutes}:${paddedSeconds}`;
  } else {
    const paddedSeconds = String(seconds).padStart(2, '0');
    return `${minutes}:${paddedSeconds}`;
  }
}

formatDigitalDuration(6345); // "1:45:45"
formatDigitalDuration(125);  // "2:05"

この関数は合計秒数を3600で割って時間を取得します。残りを60で割ると分が得られます。最終的な残りは秒です。

padStart()メソッドは、値が10未満の場合に先頭にゼロを追加します。これにより「5」が「05」になり、一貫したフォーマットが確保されます。

合計秒数ではなく期間オブジェクトとして保存されている場合は、値を直接抽出します。

function formatDurationObject(duration) {
  const h = duration.hours || 0;
  const m = duration.minutes || 0;
  const s = duration.seconds || 0;

  if (h > 0) {
    return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
  }
  return `${m}:${String(s).padStart(2, '0')}`;
}

formatDurationObject({ hours: 1, minutes: 5, seconds: 30 }); // "1:05:30"
formatDurationObject({ minutes: 5, seconds: 30 }); // "5:30"

ミリ秒を含めることで小数部分の秒を追加し、小数点精度でフォーマットします。

function formatWithMilliseconds(duration) {
  const m = duration.minutes || 0;
  const s = duration.seconds || 0;
  const ms = duration.milliseconds || 0;

  const paddedSeconds = String(s).padStart(2, '0');
  const fractional = String(ms).padStart(3, '0').slice(0, 2);

  return `${m}:${paddedSeconds}.${fractional}`;
}

formatWithMilliseconds({ minutes: 1, seconds: 23, milliseconds: 450 });
// "1:23.45"

使用前にAPIのサポート状況を確認します。

function formatDuration(duration) {
  if (typeof Intl.DurationFormat !== 'undefined') {
    return new Intl.DurationFormat('en', { style: 'digital' }).format(duration);
  } else {
    return formatDurationObject(duration);
  }
}

このアプローチにより、ネイティブAPIが利用可能な場合はそれを使用しながら、すべてのブラウザで一貫した出力を提供します。

ビデオプレーヤーとタイマーの一般的なパターン

ビデオプレーヤーは現在の時間と合計時間の両方をフォーマットする必要があります。両方の値を処理するために再利用可能なフォーマッターを作成しましょう。

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

function formatVideoPosition(currentSeconds, totalSeconds) {
  const current = secondsToDuration(currentSeconds);
  const total = secondsToDuration(totalSeconds);

  return `${videoFormatter.format(current)} / ${videoFormatter.format(total)}`;
}

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

  return hours > 0
    ? { hours, minutes, seconds: secs }
    : { minutes, seconds: secs };
}

formatVideoPosition(125, 6345); // "2:05 / 1:45:45"

フレームごとに更新するカウントダウンタイマーの場合、フォーマッターを一度作成して再利用します。

const timerFormatter = new Intl.DurationFormat('en', {
  style: 'digital',
  fractionalDigits: 2
});

function updateTimer(remainingMs) {
  const duration = {
    minutes: Math.floor(remainingMs / 60000),
    seconds: Math.floor((remainingMs % 60000) / 1000),
    milliseconds: remainingMs % 1000
  };

  document.getElementById('timer').textContent = timerFormatter.format(duration);
}

ラップタイムを表示するストップウォッチの場合、小数点以下の秒数で経過時間をフォーマットします。

const lapFormatter = new Intl.DurationFormat('en', {
  style: 'digital',
  fractionalDigits: 3
});

function formatLapTime(startMs, endMs) {
  const elapsedMs = endMs - startMs;

  return lapFormatter.format({
    minutes: Math.floor(elapsedMs / 60000),
    seconds: Math.floor((elapsedMs % 60000) / 1000),
    milliseconds: elapsedMs % 1000
  });
}

const lap1Start = performance.now();
// ... 時間が経過 ...
const lap1End = performance.now();

formatLapTime(lap1Start, lap1End); // "1:23.456"

これらのパターンは、クリーンで再利用可能なコードを維持しながら、最も一般的な時間フォーマットのシナリオを処理します。