如何切换 12 小时和 24 小时时间格式

使用 JavaScript 根据用户偏好以 AM/PM 或 24 小时制显示时间

引言

世界各地的时间显示方式各不相同。美国人通常看到 2:30 PM,而德国人则看到 14:30,这两者其实表示同一时刻。如果你只用一种格式显示时间,就默认所有用户都遵循同样的习惯。

以不熟悉的格式显示时间会带来使用障碍。习惯 14:30 的用户看到 2:30 PM 时需要在脑海中换算时间;而习惯 2:30 PM 的用户看到 14:30 也会遇到同样的问题。每次在应用中看到时间时,这种认知负担都会重复出现。

JavaScript 提供了 Intl.DateTimeFormat API,可自动处理时间格式化。本教程将解释时间格式为何不同、该 API 如何控制 12 小时与 24 小时制的显示,以及何时需要覆盖本地默认设置。

为什么不同地区的时间格式不同

不同地区形成了各自的时间显示习惯,这些习惯反映了文化传统、教育体系和历史沿革。没有一种格式是全球通用的。

在美国、加拿大、澳大利亚和菲律宾,12 小时制配合 AM/PM 标识是标准做法。下午的时间会显示为 2:30 PM。

在大多数欧洲、拉丁美洲和亚洲国家,24 小时制是标准。相同的时间会显示为 14:30,无需 AM/PM 标识。

有些地区会根据场景使用两种格式。例如,英国在交通时刻表中采用 24 小时制,但日常交流中则用 12 小时制。

在显示时间时,你需要根据用户所在地区和使用场景,匹配他们的预期格式。

12 小时制和 24 小时制的含义

12 小时制将一天分为两个 12 小时周期。小时从 12 到 11,之后重新开始。该系统使用 AM(上午,ante meridiem)表示午夜到中午,PM(下午,post meridiem)表示中午到午夜。午夜为 12:00 AM,中午为 12:00 PM。

12:00 AM → midnight
1:00 AM → 1 hour after midnight
11:59 AM → 1 minute before noon
12:00 PM → noon
1:00 PM → 1 hour after noon
11:59 PM → 1 minute before midnight

24 小时制的小时数从 0 连续计数到 23。午夜为 00:00,全天结束于 23:59。无需 AM/PM 标识,因为每个小时都有唯一编号。

00:00 → midnight
01:00 → 1 hour after midnight
11:59 → 1 minute before noon
12:00 → noon
13:00 → 1 hour after noon
23:59 → 1 minute before midnight

不同的地区默认使用不同的时间格式。Intl.DateTimeFormat API 遵循这些默认值,但在需要时允许你进行覆盖。

使用 hour12 选项控制时间格式

hour12 选项用于控制是否采用 12 小时制。设置为 true 可显示带 AM/PM 的 12 小时制,设置为 false 则为 24 小时制。

const date = new Date('2025-03-15T14:30:00');

const format12 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hour12: true
});

const format24 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hour12: false
});

console.log(format12.format(date));
// Output: "2:30 PM"

console.log(format24.format(date));
// Output: "14:30"

hour12 选项会覆盖地区的默认行为。即使美式英语通常采用 12 小时制,设置 hour12: false 也会强制使用 24 小时制。

你必须在选项中包含 hour,或使用 timeStyle,才能使 hour12 选项生效。如果输出中没有时间组件,该选项不会产生影响。

使用 hourCycle 选项实现精细控制

hourCycle 选项比 hour12 提供更精细的小时计数控制。它接受四个值:"h11""h12""h23""h24"

const date = new Date('2025-03-15T00:30:00'); // 12:30 AM

const h11 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hourCycle: 'h11'
});

const h12 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hourCycle: 'h12'
});

const h23 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hourCycle: 'h23'
});

const h24 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hourCycle: 'h24'
});

console.log(h11.format(date));
// Output: "0:30 AM"

console.log(h12.format(date));
// Output: "12:30 AM"

console.log(h23.format(date));
// Output: "00:30"

console.log(h24.format(date));
// Output: "24:30"

每个小时周期值在相同时间下会产生不同的输出。差异在午夜和中午时最为明显。

了解四种 hourCycle 值

这四个 hourCycle 值定义了每个周期内小时的编号方式。

"h12" 值采用 12 小时制,小时从 1 到 12。午夜显示为 12:00 AM,中午显示为 12:00 PM。这是美国常用的标准 12 小时制。

"h11" 值采用 12 小时制,小时从 0 到 11。午夜显示为 0:00 AM,中午显示为 0:00 PM。这种格式较少见,但在某些场景下会出现。

"h23" 值采用 24 小时制,小时从 0 到 23。午夜显示为 00:00,全天结束于 23:59。这是欧洲和亚洲大多数地区使用的标准 24 小时制。

"h24" 值采用 24 小时制,小时从 1 到 24。午夜从前一天的视角显示为 24:00。这种格式较为罕见,但在某些技术场景中会用到。

大多数应用程序要么使用 "h12" 作为 12 小时制,要么使用 "h23" 作为 24 小时制。

比较各小时周期下的午夜

午夜最能体现小时周期值之间的差异。

const midnight = new Date('2025-03-15T00:00:00');

const cycles = ['h11', 'h12', 'h23', 'h24'];

cycles.forEach(cycle => {
  const formatter = new Intl.DateTimeFormat('en-US', {
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric',
    hourCycle: cycle
  });

  console.log(`${cycle}: ${formatter.format(midnight)}`);
});

// Output:
// h11: 0:00:00 AM
// h12: 12:00:00 AM
// h23: 00:00:00
// h24: 24:00:00

h12h23 值分别为各自格式提供了最常见的表示方式。

比较各小时周期下的中午

中午同样展示了小时周期的不同。

const noon = new Date('2025-03-15T12:00:00');

const cycles = ['h11', 'h12', 'h23', 'h24'];

cycles.forEach(cycle => {
  const formatter = new Intl.DateTimeFormat('en-US', {
    hour: 'numeric',
    minute: 'numeric',
    second: 'numeric',
    hourCycle: cycle
  });

  console.log(`${cycle}: ${formatter.format(noon)}`);
});

// Output:
// h11: 0:00:00 PM
// h12: 12:00:00 PM
// h23: 12:00:00
// h24: 12:00:00

同样,h12h23 会生成标准表示,而 h11 则将中午小时数表示为 0。

hour12 与 hourCycle 的交互方式

当同时指定 hour12hourCycle 时,hour12 选项优先生效,hourCycle 选项会被忽略。

const date = new Date('2025-03-15T14:30:00');

const formatter = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hour12: true,
  hourCycle: 'h23'  // This is ignored
});

console.log(formatter.format(date));
// Output: "2:30 PM" (12-hour format from hour12: true)

hour12: true 设置会强制采用 12 小时制,覆盖原本 hourCycle: 'h23' 设置下的 24 小时制。

实际应用中,建议仅使用 hour12 进行简单控制,或使用 hourCycle 进行精确控制,但不要同时使用两者。

遵循本地化默认设置

如果同时省略 hour12hourCycle,格式化器会采用地区的默认时间格式。

const date = new Date('2025-03-15T14:30:00');

const usFormatter = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric'
});

const deFormatter = new Intl.DateTimeFormat('de-DE', {
  hour: 'numeric',
  minute: 'numeric'
});

const jpFormatter = new Intl.DateTimeFormat('ja-JP', {
  hour: 'numeric',
  minute: 'numeric'
});

console.log(usFormatter.format(date));
// Output: "2:30 PM" (US default: 12-hour)

console.log(deFormatter.format(date));
// Output: "14:30" (German default: 24-hour)

console.log(jpFormatter.format(date));
// Output: "14:30" (Japanese default: 24-hour)

美国地区默认采用 12 小时制,而德国和日本地区默认采用 24 小时制。遵循这些默认设置可为各地区用户提供最熟悉的体验。

覆盖本地化默认设置

你可以通过显式设置 hour12hourCycle 来覆盖地区的默认时间格式。

const date = new Date('2025-03-15T14:30:00');

// Force German locale to use 12-hour format
const de12 = new Intl.DateTimeFormat('de-DE', {
  hour: 'numeric',
  minute: 'numeric',
  hour12: true
});

// Force US locale to use 24-hour format
const us24 = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric',
  hour12: false
});

console.log(de12.format(date));
// Output: "2:30 PM"

console.log(us24.format(date));
// Output: "14:30"

当你需要强制采用特定时间格式而不考虑用户地区时,这种方法非常有效。

何时遵循本地化默认设置

大多数应用应遵循本地化默认值。各地区用户期望看到其熟悉的时间格式,覆盖这些默认值会导致混淆。

为本地区用户显示时间时,应让格式化器采用地区默认时间格式。

const formatter = new Intl.DateTimeFormat(navigator.language, {
  hour: 'numeric',
  minute: 'numeric'
});

const time = new Date('2025-03-15T14:30:00');
console.log(formatter.format(time));
// Output varies by user's locale
// For en-US: "2:30 PM"
// For de-DE: "14:30"
// For ja-JP: "14:30"

这种方式可自动适应每位用户的期望,无需额外配置。

何时覆盖本地化默认设置

当您的应用需要在所有用户之间保持一致性时,应覆盖本地化默认设置。交通系统、军事应用和技术工具通常需要无论本地化如何都采用 24 小时制。

// Always use 24-hour format for flight times
const formatter = new Intl.DateTimeFormat(navigator.language, {
  hour: 'numeric',
  minute: 'numeric',
  hour12: false
});

const departureTime = new Date('2025-03-15T14:30:00');
console.log(`Departure: ${formatter.format(departureTime)}`);
// Output: "Departure: 14:30" (for all locales)

这样可以确保在显示用户需要精确参考的时间(如时刻表、日志或时间戳)时的一致性。

当用户明确选择偏好时,您也可以覆盖默认设置。如果您的应用允许用户在 12 小时制和 24 小时制之间切换,应优先采用用户的选择,而不是本地化默认值。

function formatTime(date, userPrefers24Hour) {
  const formatter = new Intl.DateTimeFormat(navigator.language, {
    hour: 'numeric',
    minute: 'numeric',
    hour12: !userPrefers24Hour
  });

  return formatter.format(date);
}

const time = new Date('2025-03-15T14:30:00');

console.log(formatTime(time, false));
// Output: "2:30 PM"

console.log(formatTime(time, true));
// Output: "14:30"

这种模式既尊重了用户选择,同时也能对时间显示的其他方面(如分隔符和文本方向)进行本地化。

hour12 与 timeStyle 的结合使用

hour12 选项可与 timeStyle 预设样式配合使用。这样可以在使用便捷预设样式的同时灵活控制时间格式。

const date = new Date('2025-03-15T14:30:45');

const short12 = new Intl.DateTimeFormat('en-US', {
  timeStyle: 'short',
  hour12: true
});

const short24 = new Intl.DateTimeFormat('en-US', {
  timeStyle: 'short',
  hour12: false
});

const medium12 = new Intl.DateTimeFormat('en-US', {
  timeStyle: 'medium',
  hour12: true
});

const medium24 = new Intl.DateTimeFormat('en-US', {
  timeStyle: 'medium',
  hour12: false
});

console.log(short12.format(date));
// Output: "2:30 PM"

console.log(short24.format(date));
// Output: "14:30"

console.log(medium12.format(date));
// Output: "2:30:45 PM"

console.log(medium24.format(date));
// Output: "14:30:45"

这种方式通过结合预设样式和显式小时格式控制,简化了时间格式化流程。

为多语言环境格式化时间

当您的应用服务于多个地区的用户时,应根据每个本地化的习惯格式化时间。

const date = new Date('2025-03-15T14:30:00');

const locales = [
  { code: 'en-US', name: 'United States' },
  { code: 'en-GB', name: 'United Kingdom' },
  { code: 'de-DE', name: 'Germany' },
  { code: 'ja-JP', name: 'Japan' }
];

locales.forEach(locale => {
  const formatter = new Intl.DateTimeFormat(locale.code, {
    hour: 'numeric',
    minute: 'numeric'
  });

  console.log(`${locale.name}: ${formatter.format(date)}`);
});

// Output:
// United States: 2:30 PM
// United Kingdom: 14:30
// Germany: 14:30
// Japan: 14:30

每个本地化会自动采用其首选格式,您无需了解各地区具体采用哪种格式。

检查实际采用的小时制

您可以通过调用 resolvedOptions() 检查格式化器实际采用的小时制。

const formatter = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: 'numeric'
});

const options = formatter.resolvedOptions();
console.log(options.hourCycle);
// Output: "h12"

console.log(options.hour12);
// Output: true

该方法会返回格式化器在解析所有本地化默认值和显式选项后实际采用的设置。返回对象在包含时间组件时会同时包含 hourCyclehour12 属性。

在模板中格式化时间

无论在何处向用户展示时间信息,都可以使用格式化后的时间。这包括将时间插入 HTML、在日志中显示时间戳或展示日程安排。

const formatter = new Intl.DateTimeFormat(navigator.language, {
  hour: 'numeric',
  minute: 'numeric'
});

const meetingTime = new Date('2025-03-15T14:30:00');
const deadlineTime = new Date('2025-03-15T17:00:00');

document.getElementById('meeting').textContent = formatter.format(meetingTime);
document.getElementById('deadline').textContent = formatter.format(deadlineTime);

格式化后的字符串与其他字符串值一样,可以插入到文本内容、属性或任何向用户展示信息的场景中。

需要注意的事项

使用 hour12 选项可在 12 小时制和 24 小时制之间切换。设置为 true 时为带 AM/PM 的 12 小时制,设置为 false 时为 24 小时制。

使用 hourCycle 选项可精确控制小时编号。"h12" 值为标准 12 小时制,"h23" 值为标准 24 小时制。

大多数应用应遵循本地化默认设置。用户期望时间以其所在地区的常用格式显示。仅当全局一致性比地区习惯更重要时才应覆盖默认值。

hour12timeStyle 预设样式结合使用,可以在采用便捷预设样式的同时灵活控制小时格式。