指南

根据访客的时区本地化日期和时间格式

以服务器所在时区显示的时间戳,几乎对任何人来说都读起来不对,除了碰巧和服务器处于同一时区的人,而对于访客来自世界各地的网站来说,这样的人几乎一个也没有。

获取访客的时区

IP 查询直接返回一个 timezone 字段,为您提供本地化页面上任何时间戳所需的标识符。

GET /v1/ip?ip=203.0.113.77
{
  "status": "ok",
  "ip": "203.0.113.77",
  "version": 4,
  "found": true,
  "country": "Brazil",
  "country_code": "BR",
  "region": "Rio de Janeiro",
  "city": "Rio de Janeiro",
  "postcode": "20040",
  "lat": -22.9068,
  "lon": -43.1729,
  "timezone": "America/Sao_Paulo",
  "asn": 5566,
  "org": "Example ISP"
}

转换已存储的时间戳

像往常一样,在数据库中以 UTC 存储每个时间戳,只在显示时,在服务器上使用您查询到的时区标识符将其转换为访客的本地时区。由于本站所有内容都在服务器端渲染,没有客户端 JavaScript,因此转换和格式化都在页面发送之前完成,而不是之后在浏览器中进行。

local_time = convert_to_timezone(stored_utc_timestamp, "America/Sao_Paulo")

第二个示例:订单确认时间戳

显示“下单时间”和“预计送达时间”的订单确认页面,是一个很好的例子,说明这一点的重要性不仅限于简单的页头时钟。将两个时间戳都通过同一个访客时区进行转换,而不是因疏忽把其中一个留在服务器时间,可以保持两个数字的一致性,避免出现一个被转换而另一个没有、导致预计送达时间看起来早于下单时间的令人困惑的情况。

格式化,而不仅仅是转换

时区和格式相关,但属于不同的选择。对于所在时区通常使用 24 小时制和日月年日期顺序的访客来说,与之匹配的格式才真正有用,而不仅仅是把小时数调整后,仍用一种看起来很陌生的格式写出来。如果您想做的不只是调整小时数,可以将同一次 IP 查询中的 country_code 与一个小型格式查找表结合使用。

需要避免的常见错误

不要把转换实现为一次性计算出的固定小时偏移量,然后应用到以后的每个时间戳上。一个时区相对于 UTC 的实际偏移量会随着夏令时在一年中发生变化,因此,如果代码使用存储的偏移数值,而不是使用合适的日期时间库通过时区标识符本身进行转换,那么在一个季节正确转换的时间戳,到另一个季节可能会相差一小时。

边缘情况:非整小时偏移

并非每个时区与 UTC 的偏移都是整小时。有些时区的偏移是 30 或 45 分钟,而不是整整一小时。依靠能够理解完整 IANA 时区标识符的日期库,而不是简化的仅含小时的偏移值,就能正确处理这种情况,您无需编写任何特例代码。如果您想在转换后的时间旁边明确显示偏移量,时区查询文档中的 utc_offset 和 abbreviation 字段会很有用。

在会话期间缓存时区

每个访客会话只查询一次时区,并在该次访问期间的每个页面上渲染的每个时间戳中重复使用它,而不是为显示的每个日期再次调用 API,因为时区本身不会在会话中途改变。

本地化整个网站的成本

每个新会话进行一次查询,就能覆盖该次访问期间显示的所有时间戳的本地化,无论页面上出现多少个日期,都只算一次请求。这使得即使是内容繁多的网站,也能轻松保持在每个密钥附带的每天 2,500 次免费请求之内。

要在整个网站上正确处理本地时间和日期格式,归根结底只需每个会话一次查询,之后再进行一致的服务器端渲染。IPv4 查询文档时区查询文档介绍了获取时区的两种方法。