Settlement batches don't know about daylight saving time

Settlement batches don’t know about daylight saving time

结算批次并不了解夏令时

Reconciliation broke twice a year for months before we found the pattern. Our settlement cutoff was defined as 23:00 local time, converted to UTC at deploy time and hardcoded. Worked fine for six months, then the clocks changed and the cutoff silently shifted by an hour relative to the acquirer’s actual batch close. 在发现其中的规律之前,我们的对账系统曾连续数月在每年两次的时间点出现故障。我们的结算截止时间定义为当地时间 23:00,在部署时转换为 UTC 时间并硬编码在代码中。这套逻辑运行了半年,直到时钟调整,截止时间相对于收单机构的实际批次关闭时间悄悄偏移了一个小时。

The result: on the Sunday DST kicked in, roughly 40 minutes of transactions that should have landed in Monday’s settlement file landed in Sunday’s instead. Nothing errored. Both files were valid, both totals reconciled internally, but our ledger attributed a chunk of Monday’s revenue to Sunday. 结果是:在夏令时生效的那个周日,本应计入周一结算文件的约 40 分钟交易被错误地计入了周日。系统没有任何报错。两个文件都是有效的,内部总额也都能对上,但我们的账本却将周一的一部分收入归到了周日。

Finance caught it three weeks later during month-end close, not us. Fix was boring: store cutoff as a timezone-aware rule (America/New_York, 23:00, DST-adjusted), not a fixed UTC offset baked in at deploy. Also added a daily check comparing our computed batch boundary against the acquirer’s actual file timestamp, alerting on drift over 5 minutes. 财务部门在三周后的月末结账时才发现这个问题,而不是我们。修复方案很枯燥:将截止时间存储为“时区感知”规则(例如:America/New_York, 23:00, 自动调整夏令时),而不是在部署时写死的固定 UTC 偏移量。此外,我们还增加了一个每日检查机制,将我们计算出的批次边界与收单机构的实际文件时间戳进行比对,一旦偏差超过 5 分钟就会发出警报。

What surprised me is how long a boundary bug like this can survive. It doesn’t fail loudly, it just quietly reassigns transactions across a date line twice a year, and every individual number still looks correct in isolation. Anyone else hardcode a UTC offset somewhere they later regretted? 令我惊讶的是,这种边界错误竟然能潜伏这么久。它不会引发明显的故障,只是每年两次悄无声息地将交易跨日期线重新分配,而且单独看每一个数字似乎都是正确的。还有其他人因为在某处硬编码了 UTC 偏移量而感到后悔吗?