<!-- title: 别存本地时间：一条时区神课深读 -->
<!-- domain: programming -->

# 别存本地时间：一条时区神课深读

## 概述

程序员社区流传一个说法：世界上有两类难题，一类是缓存失效和命名，另一类是时区。而在 YouTube 上，把“时区到底难在哪”讲得最透彻的，是 Computerphile 频道的一条经典视频《The Problem with Time & Timezones》。这条视频没有炫技动画，就是一个白板加一段演示，却常年被程序员反复挖出来重看，长期位居该频道的播放量前列（据频道页面）。它值得读的原因很简单：它讲的不是某个框架的用法，而是几乎所有日历、日程、订单、日志系统都会踩的那颗雷——为什么“存本地时间”是一个看起来无害、实际上必然翻车的决定。本文带你完整重走这条视频的论证路线，再补上视频没来得及展开的几层。

## 视频基本信息

| 项目 | 内容 |
|------|------|
| 英文标题 | The Problem with Time & Timezones |
| 中文标题 | 时间与时区的问题 |
| 频道 | Computerphile |
| 主讲人 | Tom Scott（英国知名科普创作者） |
| 发布时间 | 2017 年（据频道页面） |
| 播放量 | 数百万级，长期位居频道前列（据频道页面，未逐日核对） |
| 链接 | youtube.com/watch?v=-5wpm-gesOY |

## 一条老视频为什么常看常新

先说结论：这条视频的核心论点，七年过去不但没有过时，反而越来越“真”。因为视频里警告的那类事故，今天仍在每个新框架、新数据库、新业务的身上重演——新的开发者换了一茬又一茬，雷还是那颗雷。

Tom Scott 的讲法非常克制。他不堆术语，而是从一个最朴素的场景出发：你写了一个日程应用，用户说“每天早上九点提醒我”。你怎么存这个时间？绝大多数人的第一反应是：存"09:00"这个字符串，或者存一个时间戳。视频用不到二十分钟，把这两个看似合理的答案逐一拆掉。

## 核心主张拆解：四记重拳

### 第一拳：拨钟日不存在“物理时间”

以英国为例，每年三月末的某个凌晨，时钟会从一点直接跳到两点（进入夏令时）；十月末的某个凌晨，时钟又会从两点退回一点（退出夏令时）。这意味着：

- 春季拨快的那个夜晚，本地时间"01:30"**根本不存在**——你的系统收到了这个时间，它是合法字符串，但世界上没有那一分钟；
- 秋季拨慢的那个夜晚，本地时间"01:30"**出现了两次**——你说的是第一个一点半，还是第二个？

视频里最扎心的演示就在这里：一个按“本地时间加一天”做循环日程的程序，跨过拨钟日之后，会议会整体漂移一小时。用户看到的是“我的提醒凭什么变了”，而代码看起来一行都没错。

### 第二拳：时区不是偏移量，是一张会变的规则表

很多人以为“时区 = UTC+8”这样一组偏移数字。视频明确指出这是幻觉：时区的本质是**一整套由各国政府随时可以修改的规则**——什么时候进入夏令时、什么时候退出、甚至整个时区划到哪里，都是行政决定，不是物理决定。

最极端的例子是萨摩亚：2011 年 12 月，这个国家为了和澳大利亚、新西兰的贸易伙伴对齐工作日，整条时区从 UTC−11 直接切到 UTC+13，**全国日历上整个跳过了 12 月 30 日这一天**——前一天是周四，一觉醒来是周六。工程师对“连续性”的一切假设，在行政命令面前不值一提。类似的规则调整，几乎每年都在全球某处发生。

### 第三拳：全世界的软件靠一张“民间规则表”续命

政府改规则，软件怎么办？视频引出了这个体系里真正的英雄：IANA 时区数据库（tz database，俗称 tzdata / Olson 数据库）。这是一份由志愿者和研究者维护的规则表，收录了全球几百个时区的现行规则和历史变更，政府一改规则，这里就跟进更新，然后操作系统、浏览器、编程语言运行时在全球范围内分发更新。

换句话说：你以为你的代码在“算”时区，其实是 tzdata 在“查”时区。你的程序里唯一该做的，是**别把规则硬编码进自己的代码**，而是随时查最新的规则表。

### 第四拳：解法——但比“存 UTC”更细一层

视频给出的答案分两种情况，这也是它比网上大多数“一句话教程”高明的地方：

1. **记录“已经发生的时间点”**（日志、下单时间、消息发送时间）：一律存 UTC 时间戳，展示时再按用户时区换算。
2. **记录“未来要发生的日程”**（会议、航班、提醒）：不能只存 UTC！因为未来的 UTC 时刻依赖“到时候政府怎么定规则”——如果某个国家明年改了夏令时政策，你现在算出的 UTC 值就是错的。正确做法是存**当地时间 + 时区名**（例如“09:00，Europe/London"），到展示或触发时再换算。

第二个结论是这条视频真正的“神来之笔”，也是被最多观众漏掉的一层。它推翻了“存 UTC 就万事大吉”的粗暴口诀。

## 知识与理论溯源

视频里的每一条主张，背后都有扎实的工程与历史依据：

- **IANA tz database**：最早由 Arthur David Olson 在上世纪八十年代发起，后由 Paul Eggert 长期主持维护，现由 IANA（互联网号码分配机构）托管发布，每年发布多个版本。所有主流操作系统、浏览器和编程语言都以它为事实标准。
- **美国 2007 年夏令时规则变更**：2005 年《能源政策法案》决定从 2007 年起调整美国夏令时的起止日期，导致全球无数日历软件、调度系统批量打补丁，被称为一次小型的“千年虫”演习。这是“政府改规则、软件全体的灾难”最著名的实证。
- **萨摩亚跳日事件**：2011 年 12 月 30 日被整日跳过，有真实国际日期变更线调整的官方记录佐证，是视频论证“规则可以随意改”的杀手锏案例。
- **怪时区家族**：尼泊尔用 UTC+5:45，澳大利亚豪勋爵岛的夏令时只拨**半小时**，查塔姆群岛是 +12:45。这些真实存在的偏移，宣告了“时区=整点偏移”假设的死刑。
- **ISO 8601 / RFC 3339**：`2026-09-26T08:00:00Z` 末尾的"Z"代表祖鲁时间（UTC），这是机器间传输时间戳的标准格式。视频强调的“存 UTC”在工程上的落地形态就是它。
- **Windows 存本地时间的传统**：Windows 的系统时钟历史上按本地时间走（而非 UTC），这也是视频顺带吐槽的点——它是双系统引导时间错乱等经典问题的根源。
- **社区“错误认知清单”**：程序员社区流传的“Falsehoods Programmers Believe About Time"（程序员相信的关于时间的错误观念）系列清单，罗列了几十条类似幻觉，与这条视频互为印证。

## 批判性审查

**证据充分的部分**：时区规则可被政府随时修改（tzdb 的更新日志公开可查）、夏令时边界的“不存在/重复”时间问题（任何一本权威时间处理文档都会确认）、“存 UTC + 边缘换算”的行业共识、以及“未来日程必须存当地时间+时区”的结论——这些是跨语言、跨平台的工程共识，没有争议。

**被简化或需要补充的部分**：

1. **“永远存 UTC”是被观众简化的版本**。视频其实自己论证了例外（未来日程），但很多读者只记住了口诀。生日、营业时间、合同生效日这类“墙上时间”业务，存 UTC 反而会出错——你记录的本来就是“当地那个时刻”，不是某个物理瞬间。
2. **视频没讲系统里还有“第二台钟”**。计算耗时（性能统计、超时控制）必须用单调时钟（monotonic clock），不能拿系统时间做减法——否则 NTP 校时或闰秒一来，你的耗时算出负数。这是同一主题下视频没覆盖的一层。
3. **“时区名”和“固定偏移”不是一回事**。`+08:00` 是一个数字，`Asia/Shanghai` 是一整套含历史和夏令时规则的条目。接口设计里混用两者，是比存本地时间更隐蔽的坑。
4. **闰秒问题的结局正在改写**。UTC 历史上靠“加一秒”追平地球自转误差，而 2022 年国际计量大会（CGPM）已通过决议，计划在 2035 年前取消闰秒机制；科技巨头则早已用“跳秒抹平”（smearing）技术自行消化。视频年代较早，未涉及这个后续——这是“看过视频还能多懂一层”的典型增量。
5. **历史数据的可靠性不均**。tzdb 对 1970 年以前的历史时区数据只做到“尽力而为”，做历史数据分析的人需要知道这条边界。

## 认知提升与实用启示

**第一条：给数据库立规矩。** 所有“时间点”字段一律用 UTC 时间戳（PostgreSQL 用 `timestamptz`，它内部就是存 UTC），接口传输一律用 ISO 8601 带"Z"的格式，明文禁止存"2026-09-26 09:00:00"这类裸本地字符串。这条规矩写进团队规范，能消掉未来八成的时区事故。

**第二条：日历类业务，存“当地时间 + IANA 时区名”。** 你要保存的是用户当时指的那个墙上时间，以及他所在时区的完整规则条目。触发和展示时再换算。永远记录用户当时的时区选择，因为用户会搬家、会旅行。

**第三条：把边界时间塞进测试集。** 每个处理时间的函数，都应该被“不存在的时间”（春令时凌晨）、“重复的时间”（秋令时凌晨）和“萨摩亚跳日”三种输入拷打一遍。测耗时一律用单调时钟。这三个测试用例的成本不到半小时，防住的是半夜被告警叫醒的凌晨。

## 视频论证结构图

```mermaid
flowchart TD
    A[日程App存了本地时间] --> B[春季拨快<br/>有些时间不存在]
    A --> C[秋季拨慢<br/>同一时刻出现两次]
    B --> D[只存偏移量并不够]
    C --> D
    D --> E[政府随时改规则<br/>时区是张会变的表]
    E --> F[萨摩亚整日跳过<br/>规则可被整条推翻]
    E --> G[IANA时区数据库<br/>全球软件同步更新]
    F --> H[解法一<br/>时间点存UTC]
    G --> H
    G --> I[解法二<br/>未来日程存当地时间加时区]
    H --> J[展示层再换算<br/>别硬编码规则]
    I --> J
    J --> K[例外提醒<br/>生日类业务别换算]
```

## 延伸阅读

1. **IANA 时区数据库官方页**（iana.org/time-zones）——视频主角本尊，可查每一次规则变更的发布记录。
2. **MDN 的 Date 文档**（developer.mozilla.org）——理解 JavaScript 里这套坑的现代解法（`Intl` API 如何替代手写偏移）。

## 参考来源

1. [The Problem with Time & Timezones — Computerphile](https://www.youtube.com/watch?v=-5wpm-gesOY) — 本文解读的这条视频本体，播放与发布数据以频道页面为准
2. [Computerphile 频道主页](https://www.youtube.com/@Computerphile) — 视频所属频道，计算机科普领域头部频道
3. [IANA Time Zone Database](https://www.iana.org/time-zones) — 视频中提到的全球时区规则表官方发布页
4. [MDN Web Docs: Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) — 时间存储与换算的工程实践参考
5. [ISO 8601 — Wikipedia](https://en.wikipedia.org/wiki/ISO_8601) — 国际日期时间表示标准，"Z"后缀的含义

---
> 本文由「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学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
