在印度跑数据同步任务,时区差半小时的坑

我们有个每天凌晨 2 点跑的同步任务,把订单数据从业务库拉到分析库,量不算大,单次大概 40 万行,跑在孟买一台 4C8G 的机器上,Python 3.9 + cron。现象是:每月 1 号跑出来的报表,前一天的汇总总是少一截。不是全丢,是夜里 0 点到 2 点之间的数据没进去。

一开始盯着定时任务看,crontab -l 里写的是 0 2 * * *,机器时间用 timedatectl 看,是 Asia/Kolkata,+05:30,看着没问题。后来把任务里的时间窗口打印出来才明白:代码里写死了

start = datetime.utcnow().replace(hour=0, minute=0, second=0)
end = start + timedelta(days=1)

服务器本地时间是 IST,但 utcnow() 拿到的是 UTC,等于把窗口整体往前挪了 5 个半小时。凌晨 2 点跑的时候,窗口是 UTC 当天 0 点到 24 点,换算成 IST 就是 5:30 到次日 5:30,于是本地 0 点到 2 点的数据落在窗口外,被漏掉了。

改法不复杂,把窗口的边界统一显式带上时区:

from datetime import datetime, timedelta
from zoneinfo import ZoneInfo

ist = ZoneInfo("Asia/Kolkata")
end = datetime.now(ist).replace(hour=0, minute=0, second=0, microsecond=0)
start = end - timedelta(days=1)

然后入库时统一转 UTC 存,展示层再转回来。另外把 cron 改成 30 2 * * *,避开整点和上游备份窗口重叠。

为什么这么做:utcnow() 返回的是 naive datetime,没有时区信息,和带时区的数据比较时不会报错,只会静默按 UTC 算。IST 是 +05:30,不是整小时偏移,这种半时区最容易在「按天切窗口」的逻辑里出问题,因为很多人默认偏移是整数小时。代价是代码里到处要传 tzinfo,稍不注意混进一个 naive datetime,又回到老路。

会失效的地方:如果上游表的时间字段本身是字符串且没带偏移,转换前要先确认是本地时间还是 UTC,我们在一个历史表上就吃过这个亏,字段名叫 created_at,实际存的是 UTC 字符串。还有夏令时,印度没有 DST,这段代码照搬到有 DST 的地区,用 replace 加减天数会错一小时。

你们那边跑跨时区同步任务,时间窗口是按本地零点切还是按 UTC 切?有没有遇到过这种半时区偏移的问题?

0
赞同度 100% · 0 条评论 · 7 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。