国际化转型:时间

0 阅读4分钟

做国际化的时候,基本会遇到的一个问题就是时间的转换,其中包括时间的语言上的转化,还有就是时区的转化,这些是其中的一些痛点

先来了解一些基本概念

基本概念

  1. 相对时间

跟现在相对的时间,比如说十分钟前,十天前

  1. 绝对时间

没有参照物的时间

  1. 时区
  • 协调世界时

如果时间是以协调世界时(UTC)表示,则在时间后面直接加上一个“Z”(不加空格)。“Z”是协调世界时中0时区的标志。因此,“09:30 UTC”就写作“09:30Z”或是“0930Z”。“14:45:15 UTC”则为“14:45:15Z”或“144515Z”。

这个就是基准的时间

  • 时间偏移量

比如说我现在在中国,我的时区是Asia/Shanghai,就是 UTC+8,8 就是那个偏移量

  • 时区不规则

但是由于一些历史原因和其他原因,时区的形状不是规则的,会随国际线变化而变化

image.png

  • 夏令时和冬令时

夏令时(Daylight Saving Time,DST)和冬令时是现代社会应对日照时间变化而实施的时间调整机制。

夏令时的基本思想是在夏季时,将时间拨快一小时,以便更多的日照时间能被充分利用,尤其是在能源消耗较为密集的白天。冬令时则是指冬季恢复到标准时间,通常是将时间调整回正常的标准时间,以适应冬季较短的白昼。

很多国家会用这种方式来应对季节性日照变化、提高资源利用效率。

但对于开发来说,夏令时是一个非常典型的坑点:

  • 有些国家有夏令时,有些没有
  • 有些国家曾经用过,后来取消了
  • 夏令时切换的日期不统一
  • 某一天可能会出现“少一个小时”或“多一个小时”的情况

比如在夏令时切换当天:

  • 有的时间点会不存在
  • 有的时间点会出现两次

这就意味着,像 2025-03-30 02:30:00 这种本地时间,在某些时区里可能根本无效。

所以处理时间时,不能只看“年月日时分秒”字符串本身,还必须结合时区一起判断

具体思路

处理国际化时间问题,比较推荐的思路是:

1. 存储时统一用 UTC

这是最常见也最稳妥的方案。

不管用户在哪个国家,不管前端传过来的是什么时区,到了服务端之后,统一转换成 UTC 再存储

2025-03-18T06:30:00Z

而不是直接存:

2025-03-18 14:30:00

因为后者如果没有时区信息,后续几乎无法准确还原


2. 展示时转换为用户本地时区

存储用 UTC,展示时再根据用户所在时区转成本地时间。

这一步通常有几种策略:

  • 使用浏览器自动识别时区
  • 用户自己在设置里指定时区
  • 根据业务场景使用固定时区

这个不只是技术,也得看业务需要


3. 语言格式化交给国际化 API 或库处理

时间不仅要转换,还要“说得自然”。

比如在前端可以使用 Intl.DateTimeFormat 和 Intl.RelativeTimeFormat

格式化绝对时间

const date = new Date('2025-03-18T06:30:00Z')

const formatter = new Intl.DateTimeFormat('zh-CN', {
  dateStyle: 'full',
  timeStyle: 'long',
  timeZone: 'Asia/Shanghai'
})

console.log(formatter.format(date))

输出可能类似:

2025年3月18日星期二 中国标准时间 14:30:00

如果换成英文环境:

const formatterEn = new Intl.DateTimeFormat('en-US', {
  dateStyle: 'full',
  timeStyle: 'long',
  timeZone: 'America/New_York'
})

输出风格就会变成英文习惯


格式化相对时间

const rtf = new Intl.RelativeTimeFormat('zh-CN', { numeric: 'auto' })
console.log(rtf.format(-10, 'minute'))

输出:

10分钟前

英文:

const rtfEn = new Intl.RelativeTimeFormat('en-US', { numeric: 'auto' })
console.log(rtfEn.format(-10, 'minute'))

输出:

10 minutes ago

4. 拿到系统时区

浏览器对Intl.DateTimeFormat的支持很好,基本不存在拿不到系统时区的场景,web 项目可以通过这个那,如果是 rn 低版本的那可以要自己注入一个 polyfill 来提供支持了

try {
    // 尝试创建一个 DateTimeFormat 实例来获取时区
    const dtf = new Intl.DateTimeFormat();
    const timeZone = dtf.resolvedOptions().timeZone;
        if (timeZone) {
             return timeZone;
        }
    } catch (e) {
       // 继续尝试其他方法
    }

上面这些差不多就是在小红书实习的时候做的一个需求,我总结了一下,其实大概思路是这样,具体实现就不说了