设计时区转换 API - 为真实应用构建稳健端点
构建时区转换 API 听起来简单,但很快就会遇到 IANA 数据库管理、夏令时间隙处理、错误语义和缓存策略等问题。本文涵盖了决定端点在政策和 tzdata 更新后能否保持可靠的关键设计决策。
最基本的格式选择是 12 小时制(AM/PM)还是 24 小时制。12 小时制在美国、加拿大、澳大利亚和菲律宾的日常使用中占主导地位。24 小时制是欧洲大陆、日本、韩国和中国的标准。英国处于中间位置:口语英语使用 12 小时制,但火车时刻表和官方文件使用 24 小时制。
正确的软件设计是遵从用户的区域设置,而非选择一个全局默认值。服务器硬编码“14:30”对美国用户来说看起来不对;“2:30 PM”对习惯 24 小时制的人来说则显得刻板。尊重操作系统或浏览器的区域设置,让格式化层来选择,而不是在业务逻辑中固化选择。
ISO 8601(如 2026-05-15T16:30:00+09:00)非常适合机器间的数据交换,但不应直接呈现给最终用户。将其用于 API 载荷、数据库存储和日志输出,然后在显示层转换为本地化的人类可读格式。混淆机器格式和人类格式的角色是导致用户对时间戳投诉的最常见原因。
有一个例外:面向工程师的技术仪表盘和调试界面。ISO 8601 中明确的时区偏移量在比较跨地区事件时消除了歧义。你的受众是技术人员还是普通用户,决定了 ISO 8601 是否适合出现在面向用户的路径中。
JavaScript 的 Intl.DateTimeFormat API 利用操作系统的国际化数据提供区域感知的格式化。new Intl.DateTimeFormat('ja-JP', { hour: '2-digit', minute: '2-digit' }).format(date) 返回“16:30”,而将区域设置改为 'en-US' 则得到“4:30 PM”。无需第三方库,且数据随操作系统更新保持最新。
注意两个陷阱。第一,dateStyle/timeStyle 与单独的选项(year/month/day/hour/minute)不能在同一次调用中混用。第二,浏览器在输出细节上存在细微差异(如 AM/PM 周围是否有空格),因此快照测试可能不稳定。如果需要稳定的测试结果,使用字符串包含断言或对输出进行标准化。
社交和聊天应用通常显示“3 分钟前”或“昨天”而非绝对时间。直觉很简单,但由此产生几个设计决策。一分钟以内应该多精细地显示(大多数服务将一分钟内折叠为“刚刚”)?多少天之后切换为绝对日期(Twitter 在 24 小时后切换,Facebook 在 7 天后)?当用户跨越时区时,“昨天”如何计算?
Intl.RelativeTimeFormat 处理结果字符串的本地化,但不负责选择合适的单位(秒、分、时、天、周、月、年)。你仍需要一小段逻辑根据差值的大小选择正确的单位。一些库(date-fns、Day.js relativeTime 插件)为你封装了这些,但底层的设计决策仍需你自己做出。
12 小时制在正午和午夜有一个著名的歧义。按照惯例,12:00 PM 是正午,12:00 AM 是午夜,但这在逻辑上不一致(PM 代表 post meridiem 即“午后”,然而正午本身却被标记为 PM)。航空业通过使用 24 小时制来规避这个问题(00:00 表示午夜,12:00 表示正午),美国法律文件通常明确写“12:00 noon”或“12:00 midnight”。
软件中的防御性做法是内部以 24 小时整数(0-23)存储时间,仅在显示时转换。接受用户 12 小时制输入时,要明确验证 AM/PM 的解释,并在测试用例中包含 12:00 AM 和 12:00 PM。正午/午夜边界的 bug 容易引入,也容易在随意测试中被遗漏。
这篇文章对您有帮助吗?