如何将时长格式化为 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 样式

digital 样式会将时长格式化为数字时钟的样式。创建格式化器时,将 style 选项设置为 "digital"

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

只需传入包含所需时间单位的 duration 对象,格式化器会自动在各单位间添加冒号,并在需要时补零。

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

可以看到,分钟会显示为“05”而不是“5”。当存在小时数时,格式化器会自动将分钟补齐为两位数,这样在列表或表格中可以保持对齐。

如果时长没有小时,只需在 duration 对象中省略该属性即可。

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"

当出现小时时,所有较小的单位都补齐为两位数。没有小时时,分钟不补零,但秒仍然补零。

这种补零规则可以保证对齐一致且不浪费空间。短视频列表会显示“5:30”、“12:45”、“8:02”,冒号对齐。长视频列表会显示“1:05:30”、“2:12:45”、“3:08:02”,格式统一。

只需包含界面所需的时间单位。例如,倒计时从不超过一小时,可以完全省略小时。

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

控制零补齐和显示选项

数字格式有默认的补零规则,但你可以为各个时间单位单独指定选项进行覆盖。

通过设置单位选项控制每个时间单位的显示方式。numeric 值表示不补零直接显示数字,2-digit 值则强制补齐为两位数。

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"

这就是默认行为。只有在需要不同格式时才需指定这些选项。

强制小时始终显示为两位数。

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"

格式化器会在秒后添加小数点,并显示指定数量的小数位。

秒表通常显示百分之一秒。

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

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

格式化器会根据指定精度进行四舍五入。450 毫秒会变为 45 个百分之一秒。

如需微秒级精度,请在 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 分”而不是“8:15:00”,因为此场景不是计时器。

// Good for prose and descriptions
new Intl.DurationFormat('en', { style: 'short' }).format(duration);
// "1 hr, 46 min and 40 sec"

// Good for timer displays
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”,以实现格式统一。

对于以 duration 对象而非总秒数存储的时长,请直接提取各个值。

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();
// ... time passes ...
const lap1End = performance.now();

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

这些模式可处理最常见的时长格式化场景,同时保持代码简洁且易于复用。