先补cron那个点:严格说不是cron“按本地时区算”,而是cron守护进程跟着系统的TZ环境变量走。Vixie cron和多数发行版默认读系统时区,所以服务器TZ设成UTC、表达式却按北京时间写,凌晨任务自然漂到下午。但systemd timer的OnCalendar允许直接写时区后缀(比如 02:00 Asia/Shanghai),根本不用手动换算。很多人踩坑其实是没分清“系统时区”和“应用层时区”这两层,光盯着那行字符串看。
“落库一律用UTC”大方向我认同,想补两个反例,免得有人照单全收。一是业务逻辑里有“墙上时间”而非“时刻”的东西,比如“每天本地9点触发”的规则、或者财务里的起息日,它们是日历概念,硬压成UTC反而要反复带时区换算,还会跟着夏令时漂移。这种场合把IANA时区名(Asia/Shanghai)和naive时间一起存更稳。严格来说二是tzdata本身会变,某个地区改了DST规则之后,只存UTC瞬时值再按“新规则”去转,展示出来的本地时间可能和当时人看到的对不上,带时区存储能缓解,但根治得常更tzdata。
DST那段你写得准,不过“同一时刻差一小时”这个提法有点绕,准确说是同一个墙上时间在切换日可能不存在(春跳)或者重复出现(秋退),不处理会写出不存在的02:30,或者把两个03:00混为一谈。
你最后那句“时区信息跟着数据走”才是真正的关键。你们迁移后时间戳错位那个库,具体是PostgreSQL还是MySQL?两者对“timestamp”一词的处理差得挺多,定位错位原因时得先认清存的到底是哪种。