标准答案

  1. timestamp without time zone 只保存日期和时间字段,不携带也不自动推断时区;timestamptz 表示一个绝对时刻,PostgreSQL 会按当前 session time zone 输入、存储和展示。
  2. 创建时间、支付时间、日志时间和过期时间通常应使用 timestamptz,并在接口中使用带 Z 或明确 offset 的格式;timestamptz 不会保留用户原始时区名称。
  3. 营业时间、每天 09:00 这类“墙上时间”不是绝对时刻,通常要把本地日期时间和 IANA 时区名称一起保存,再根据规则计算实际发生时间。
  4. 连接的 session timezone、驱动映射、JSON 序列化和前端展示要作为一条链测试,明确输入没有 offset、输入带 Z、跨时区展示和夏令时切换的行为。
  5. 持续时间和超时判断应基于明确的时间戳差值或应用的单调时钟;不要用格式化字符串排序,也不要把服务器本地时区当作业务时区。

题目解析

PostgreSQL 的 timestamptz 名称容易造成误解:它不是把“原始时区”存进字段,而是保存可比较的绝对时刻,并按照会话时区显示。因此如果业务还需要知道用户选择的时区,必须另存时区标识。

本地时间和绝对时间解决不同问题。航班起飞、支付完成和日志排序需要绝对时刻;门店营业时间、生日和按当地时间执行的提醒需要本地时间加时区规则,不能用一个字段强行覆盖两种语义。

序列化是常见的跨层错误来源。数据库、驱动、API 和前端只要有一层把无 offset 字符串当成本地时间解析,就可能在不同服务器或夏令时切换后产生数小时偏移。

常见误区

  • 误区:所有时间都使用 timestamp without time zone。改正:先区分绝对事件时间和本地墙上时间,跨区域事件通常使用 timestamptz。
  • 误区:把数据库存 UTC 当成完整的时区方案。改正:还要明确输入解析、session timezone、接口 offset、展示时区和是否需要保存 IANA 时区名称。
  • 误区:直接比较格式化后的时间字符串。改正:先解析成带时区的时间类型或时间戳,再按绝对时刻比较;涉及持续时间时使用单调时钟或明确的 interval 语义。

作者信息