I4.08.1store instants, display local设计
时间应以绝对标准时间存储,仅在呈现时转换为本地时区
别名: UTC 存储 · instant storage · 呈现时转本地 · timezone conversion at display
概念解释
事件发生在一个瞬间,这个瞬间在世界上只有一个。以绝对标准时间存储,仅在呈现时转成本地:库里记下的是这个瞬间(通常用协调世界时),屏幕上再按查看者的时区画出钟面。不要在库里记下「15:00」这种没有所属的本地数字当真相。协作时要不要把对方的本地画出来,是呈现策略;这里只谈哪一层是真相、哪一层是画出来的。
机制
本地钟面是一种投影。同一瞬间在北京是 23:00、在伦敦是 16:00,两个字符串,一个事实。若把本地字符串当存储,投影被写死成事实:查看者换了时区、规则改了夏令时、另一个人打开同一条记录,都无法从「15:00」还原瞬间,只能猜。绝对瞬间(UTC 或带明确偏移的时间戳)不依赖谁在看。呈现层拿瞬间和「现在这位查看者的区」做一次换算,钟面才是给这个人看的。
写本地、读的时候再猜区,是把换算提前做完并扔掉了换算前的输入。夏令时那一夜、历史规则变更、用户飞了一次,猜都会错。存储瞬间,等于把换算留到每次画出来的那一拍,用当时的区和当时的规则表。
边界
「每周二上午十点开会」不是一个瞬间,是一条本地钟面规则,不能只存一个 UTC 时刻就完——那会在夏令时边界上把十点变成九点。这类重复规则要另存时区标识和本地钟面,瞬间在每次展开时再算。纯本地、永不过区的闹钟(床头钟)可以只存本地钟面,前提是这条记录保证不离开这台设备、不参与同步。日志若要给审计对齐,呈现也可以继续用 UTC,不必强行转本地——「仅在呈现时转换」说的是允许转换,不是必须对每个人都画本地。
怎么落地
- 发生过的事件、消息、订单、审计点:存绝对瞬间;呈现时用查看者时区画钟面。
- 不要把「服务器机器的本地时区」写进库当默认,也不要存无偏移的 naive 本地时间当真相。
- 输入可以收本地钟面,但一进系统就连同当时所选时区换成瞬间再存。
- 验证:同一条记录在两个时区打开,钟面应不同、所指瞬间应相同。把库里的字段改成无区的「15:00」,换区后再打开,应无法证明还是同一个瞬间。床头闹钟可以只存本地;跨设备同步的日历不行。