跨时区数仓怎么划分业务日期?夏令时怎么处理?
面试怎么回答
我先确定指标使用哪个时间字段和业务时区,再保存 UTC 时刻、原始时区与业务日期。查询某个本地日时,把当天和次日零点分别转成 UTC,用左闭右开区间过滤,避免固定偏移和固定二十四小时误差。账户时区变更需要有效期,验收重点覆盖夏令时、日界事件和历史重跑对账。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
业务说要看“美国昨天的收入”,这句话还不足以写出 SQL:美国有多个时区,“昨天”也可能指广告平台、用户所在地或公司财务日。时间字段格式一样,不代表统计边界相同。
同时保存事件发生的绝对时刻与业务时区,按命名时区生成业务日期;日边界分别转换成 UTC,才能覆盖夏令时和跨区对账。
先问清楚这个日期服务谁
收入归属可能按事件发生时间,结算按账单时间,广告消耗按账户时区。同一个订单出现在不同日期并不必然是数据错误,要先把指标对应的时间字段、时区和结算截止点写清楚。
建议把 event_time、ingest_time、business_date 分开,避免一个 dt 同时表示入仓日和业务日。业务日期还可以带时区标识或口径版本,让后续对账知道当时采用了哪套归属规则。
UTC 时刻与本地墙上时间不是同一种信息
绝对时刻用于排序、关联和延迟计算;本地日期时间需要时区规则才能映射为绝对时刻。只收到一个没有偏移的本地时间字符串时,遇到夏令时回拨,就可能无法唯一判断是哪一次出现。
以 PostgreSQL 为例,timestamptz 记录时刻并按会话时区显示,不会替你保留最初输入的时区名称。业务需要追溯原始时区,就额外保存 IANA 名称,例如 America/Los_Angeles,而不是依赖查询客户端的默认设置。
业务日应先在当地建立两端边界
要查询洛杉矶某一天,先得到该时区当天零点和次日零点,再分别换算成 UTC,过滤条件使用左闭右开区间。不要把 UTC 起点直接加固定二十四小时当作所有本地日的终点。
夏令时切换日可能只有二十三或二十五小时。命名时区包含这些历史规则,固定 UTC 偏移只适用于已经确定的偏移场景。若底层按 UTC 日期分区,一个业务日还可能跨两个分区,需要同时考虑裁剪范围。

一个典型场景:只有几天对账总差一截
比如业务长期按固定偏移生成本地日期,多数日期与平台账单一致,到了夏令时附近突然出现缺口。最初判断是同步延迟,补跑后差额仍集中在日界附近的一小时。
常规规则只检查整月总量,跨日错分会互相抵消。改用命名时区后,再按日界两侧的事件主键复核,才能证明是日期归属问题。测试集要覆盖切换前后、回拨的重复本地时间,以及延迟进入下一分区的事件。
时区变更也要作为口径变更管理
如果客户把账户时区从一个区域改到另一个区域,需要明确新规则从何时生效,历史账单是否重算。直接更新维表的当前时区再重跑全部历史,会悄悄改变过去每天的收入。
我会给账户时区保留有效期,分别提供按原结算口径和按新分析口径的结果。跨系统对账时带上业务日对应的 UTC 起止水位,不只交换一个日期字符串;这样双方才能确认比较的确实是同一批事件。