目标
混杂格式加上无时区条目,会让同一份数据因提问的人不同而给出不同答案——活跃用户数随分析师时区漂移八小时。治理路径是机械的:先给列做形态普查,用锚定区域消歧日月顺序,统一归一为 UTC 时刻,并永远保留原始字符串列。这样假设可见、可审计、可在未来被纠正而无需重新导出源数据。
步骤要点
- 对时间戳列做形态普查:统计 iso、斜杠、横杠等格式数量,并用正则统计缺失时区偏移的条目数,量化清洗范围。
- 用锚定区域写消歧解析器:美式源默认月前日后,遇到 13/04 这类不可能的读法自动切换为日前月后。
- 归一时附加时区来源:带偏移的直接取绝对时刻,无时区的按遗留系统所在时区(如 America/New_York)转 Instant,输出两列——UTC 时刻加原始串。
- 用已知真值样本做往返验证:纽约 09:30 应变 13:30Z,再转回仍是 09:30,证明归一保留了原始墙钟含义。
- 在摄取边界用 zod 加守卫:新数据的时间戳必须带显式偏移,否则解析抛错,让遗留烂摊子只减不增。
排错提示
两个系统往同一列写了不同时区,就按 source-system id 分段清洗,各段套用各自时区假设,全局一刀切会让其中一批静默偏移数小时;DST 切换产生不存在的本地时间(春跳空档),各库解析结果不一,这些行标记人工复核而不是接受库默认;纯数字时间戳按数量级判断秒或毫秒并记录判定阈值,两种尺度在野外都存在。
适用边界
给遗留无时区数据补时区是假设,保留 createdAtRaw 才能让假设被重新审视;往返验证只能抓变换 bug,抓不住时区假设错误,那需要源系统的地面真值。
代码示例
before (one column, four realities):
2026-10-22T09:30:00Z (absolute, fine)
2026-10-22T09:30:00 (zone-less: which zone?)
03/04/2026 (US or EU reading?)
13/04/2026 (forces EU reading)
after (two columns, one truth + provenance):
createdAtUtc: 2026-10-22T13:30:00Z
createdAtRaw: "03/04/2026" (assumed US, America/New_York)
DAU by day, before: varies by 8h depending on analyst
DAU by day, after: identical for everyone