首页 › programming › 别自己写时区代码:程序员第一保命课

别自己写时区代码:程序员第一保命课

导语:一行时间代码,埋住全世界

在编程界流传着一个共识级的“恐怖故事”:如果你觉得自己能力强到可以手写时间处理逻辑,那说明你还没被时间坑过。日历应用里消失的一天、跨国会议里错乱的时刻、凌晨三点突然翻车的定时任务——十有八九都是时间和时区惹的祸。

今天要深读的这期视频,是这个领域公认的“传道之作”:Computerphile 频道的《The Problem with Time & Timezones》(时间与时区的问题),主讲人是英国科技圈名嘴 Tom Scott。这期视频发布于2013年,十几年过去依然被程序员们反复挖出来安利,在 Reddit 的 r/programming 和无数新人培训材料里常年出镜——可以说是一代程序员的时间观启蒙课。

它解决的是一个看似简单、实则极深的问题:为什么全世界最好的程序员,联手也造不出一个完美的时间处理函数? 答案不在数学,而在政治和人性。这期视频把答案讲得透彻又好玩,最后给出的三条“保命建议”,值得写在每一个工程团队的规范里。

视频背景卡片

项目 内容
视频标题 The Problem with Time & Timezones
频道 Computerphile(计算机科普名频道,订阅量百万级)
主讲 Tom Scott(英国科普作者/工程师)
发布时间 2013年
时长 约20分钟
播放量 约200万级(常青经典,历年累计,未实时核验)
核心主题 为什么时间是编程中最难处理的抽象,以及工程上的正确姿势

核心内容拆解

要点一:一场跨城演讲,撞出时间的墙

视频的开场非常“Tom Scott”——他没有上来讲概念,而是从自己的一次行程说起:接了一场外地演讲的邀约,查火车时刻、对会议时间、算出发点,这套动作人类做起来毫不费力。但只要你试着把这个过程写进日程软件,让程序在全球任意两个城市之间自动完成“几点出发、几点到场”,立刻就会撞上一堵墙。

(延伸分析)这个开场其实暗藏了一个计算机科学的深刻观察:我们日常接触的抽象大多是“规整”的——文件系统有统一的接口,TCP 有严谨的协议规范,唯独时间,这个天天挂在每个人嘴边的东西,绑定的是地理、历史和立法。规整的东西机器擅长,不规整的东西机器就傻。Tom Scott 用最生活化的例子,一脚踹开了这期视频的主题:不是程序员蠢,是时间这个抽象本身就是漏的。

要点二:时区不是数学题,是政治题

这是全片最核心、被引用最多的论点:时区不是科学家定义的,是政府定义的,而政府会随时改变主意。 Tom Scott 强调,任何一个时区规则——夏令时的起止日期、与 UTC 的偏移量、甚至整个国家换一个时区——都可能因为一纸政令在几周内生效。你的代码里写的每一条“固定规则”,都可能在某个凌晨被现实作废。

(延伸)现实远比视频里的举例更魔幻:2011年底,萨摩亚为了加强与主要贸易伙伴的联系,决定跨过国际日期变更线,全国直接跳过了12月30日这一天——日历上这天根本不存在;更早的年代,荷兰曾长期使用比格林尼治快约20分钟的“阿姆斯特丹时间”;印度用的是 +5:30 这种半小时时区,澳大利亚中部甚至有 +9:15 的四十五分钟偏移。每一次“看似不合理”的规则背后都是一段历史。中国全国统一使用北京时间(UTC+8),地理上却横跨多个时区——对写代码的人来说,这意味着“中国的用户”时区判断出奇地简单,而“美国的用户”能让你把光阴活活算错半天。

要点三:Unix 时间戳与 UTC——唯一靠谱的存法

视频给出的第一条工程建议,后来成了全行业的共识:存储一律用 UTC 时间戳,本地时区只发生在“展示”那一层。Tom Scott 强调,如果你在数据库里存“2026-09-15 14:30:00”这种带本地时间的字符串,恭喜你,这个数据已经烂了——因为它丢失了“这是哪个时区的时间”这一关键信息,将来无论怎么解析都是猜。

(延伸)这个原则在主流数据库和语言里早有体现:PostgreSQL 的 timestamptz 类型本质上就是把输入归一成 UTC 再存;Java 的 Date 对象内部也只是一个毫秒数。顺带提醒一个容易被忽略的远期风险——2038问题:还在用32位有符号整数的 time_t,将在2038年1月19日溢出归零。64位服务器基本免疫了,但大量还在服役的嵌入式设备、老旧网元、工控模块,都值得在今天查一遍。时间债,是会在七年后连本带息讨回来的。

要点四:夏令时,才是真正的 Boss 战

如果说时区偏移只是麻烦,夏令时(DST)就是灾难级的存在。视频里 Tom Scott 用英国冬令时/夏令时做例子:一年两次,全英国在凌晨把时钟拨动一小时。对程序来说这意味着:秋天的那一小时会在本地时间上出现两次——凌晨的一点半真实地存在了两遍,日志顺序、任务调度、计费记录全都能被它搅乱;春天则相反,有一段本地时间根本不存在。更坑的是,南北半球夏令时方向相反,赤道附近不少国家干脆不实行。

(延伸)这个坑在不同语言生态里留下了大量“伤疤”:Java 旧的 Date/Calendar API 混乱到业界忍无可忍,Java 8 终于引入了全新的 java.time(JSR-310),它的设计脱胎于 Joda-Time 库,作者 Stephen Colebourne 亲自操刀重构;JavaScript 的 Date 连“月份从0开始”这种反人类设计都保留了三十年,社区库 moment.js 已于2020年宣布进入维护模式,接棒的下一代标准 Temporal 提案在 TC39 磨了多年仍在推进。一门语言的时间 API 质量,几乎可以当成工程品位的试金石。

要点五:IANA tz database——一群志愿者在给全世界对表

视频里另一个重要科普:你的手机、电脑、服务器之所以“知道”每个国家当前的时区规则,靠的是 IANA 维护的时区数据库(tz database,又称 Olsen database)。它由志愿者社区维护,各国规则一变,就发新版本,再经由操作系统更新、手机推送分发到全世界。Tom Scott 特别点出了危险场景:政府宣布时区变更到正式生效可能只有几周,如果你的设备没及时更新 tzdata,日程就会集体错乱。

(延伸)由此可以推出一条朴素但常被违反的纪律:永远不要在代码里硬编码时区规则。“印度是 +5:30”、“美国东部夏令时是 -4”这类硬编码,本质上是把你自己的知识和 tzdata 的一次快照焊死在了一起,政府一改规则就集体翻车。正确姿势是引用系统时区标识(如 Asia/Shanghai),让底层数据库的更新自然流经你的系统。服务器的 tzdata 包,应该像安全补丁一样纳入例行升级管理——它不是普通依赖,它是“现实世界规则的驱动程序”。

要点六:逃生通道——能不碰就不碰,要碰就交标准库

视频收尾,Tom Scott 给出了那条被无数博客转述的“保命三原则”,大意是:第一,如果业务上可以不处理时区,那就别处理;第二,必须处理时,交给成熟的系统库和时区数据库,永远别自己发明;第三,存储用 UTC,展示才转本地。没有花哨的框架,没有炫技的算法,就这么朴素的三句话——因为时间处理这个领域, complexity 不在你这边,而在现实世界那一边。

(延伸)落到今天各语言的标准姿势:Java 用 java.time,Python 3.9+ 内置 zoneinfo(PEP 615),Go 用 time.LoadLocation,前端等 Temporal 落地期间用 date-fns 这类库兜底;服务间传时间,统一用 ISO 8601 格式的 UTC 字符串(如 2026-09-15T06:30:00Z),末尾那个 Z 就是全世界的通用暗号。接口契约里把这一个小字符写清楚,能省掉未来无数个深夜的电话会议。

知识结构图

flowchart TD
    T["编程中的时间问题"] --> A["存储层"]
    T --> B["计算层"]
    T --> C["展示层"]
    A --> A1["只存UTC时间戳"]
    A --> A2["禁存本地时间字符串"]
    B --> B1["比较与调度先归一"]
    B --> B2["提防夏令时重与缺"]
    C --> C1["最后一步才转本地"]
    C --> C2["只用系统时区库"]
    B2 --> D["IANA时区数据库"]
    C2 --> D
    D --> E["政府随时改规则"]
    E --> F["设备靠更新续命"]
    F --> G["硬编码规则必翻车"]

实战启示

第一,给你的项目做一次“时间审计”。 检查三件事:数据库时间字段是不是 timestamptz 或等价的 UTC 存储?服务间接口是不是 ISO 8601 带明确的 UTC 标识?有没有任何一处代码硬编码了“+8”或“+5:30”?三问有一个答不上来,就值得排期修复——这类债平时不响,一响就是在跨年、跨夏令时切换的凌晨。

第二,把“拨钟测试”写进测试用例。 把系统时间拨到夏令时切换边界、年末最后一天、闰年2月29日再跑一遍核心逻辑。日历、计费、调度类系统尤其要这么做。真实世界里,国内外不少大厂都吃过“那天根本不存在”或“那天出现了两次”的亏。

第三,通信同行请把视角拉到“同步”层面。 这期视频讲的是“读时间”的坑,而通信行业还要管“对时间”:网管系统告警时间戳建议全网统一 UTC 落库,跨国排障时才不会各说各话;NTP/PTP 同步体系里,闰秒是真实发生过的事故源——2012年6月30日的闰秒曾让多家知名互联网站集体故障,Google 因此长期采用“闰秒平滑”(leap smear)方案把这一秒摊进全天;而 5G TDD 基站对空口同步有微秒量级的硬要求,时间哪是一行代码,是一张网的地基。

第四,老设备别忘查 2038。 32位时间戳溢出问题对服务器已是过去时,对仍在服役的嵌入式模块、老旧网元、工控盒子却是进行时。列个清单,比2037年集体救火便宜得多。

延伸学习路径

  1. IANA tz database 官方页(https://www.iana.org/time-zones)—— 看看这个“世界规则驱动程序”长什么样、更新多频繁
  2. Oracle 官方 java.time 教程(https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html)—— 现代“年龄/时刻/时段”三分模型的最佳范本,思想可迁移到任何语言
  3. TC39 Temporal 提案(https://tc39.es/proposal-temporal/)—— JavaScript 时间处理的未来形态,值得提前了解
  4. PEP 615(https://peps.python.org/pep-0615/)—— Python zoneinfo 的设计文档,顺带理解系统 tzdata 的接入方式
  5. timeanddate.com 的 Time Zone News 板块(https://www.timeanddate.com/news/time/)—— 全球时区变更的“新闻直播”,比任何教程都更能让你体会“时区是政治题”

一句话总结

时间代码你永远写不对——但你可以站在 tzdata 和标准库的肩膀上,把“错”留给全世界的志愿者去修正。

参考来源

  1. The Problem with Time & Timezones - Computerphile — 本文分析的核心视频
  2. IANA Time Zone Database — 时区数据库官方页面
  3. Java SE java.time API 文档 — 现代 Java 时间 API
  4. TC39 Temporal Proposal — JavaScript 时间处理下一代标准
  5. PEP 615 – Support for the IANA Time Zone Database in the Standard Library — Python 时区支持设计文档

注:视频播放量、时长等页面数据为撰写时的通行说法,以 YouTube 页面实时数据为准。


本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入