Unix Timestamps Explained
Unix 时间戳详解
A Unix timestamp is the number of seconds that have elapsed since 00:00:00 UTC on 1 January 1970, not counting leap seconds. The instant 1740000000 corresponds to 17 February 2026, 09:20:00 UTC. That is the whole idea. Everything else — milliseconds, microseconds, signed-vs-unsigned 32-bit overflow, leap-second debates — is the interesting fallout of taking such a simple concept and pushing it through four decades of computing.
Unix 时间戳就是自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的秒数(不计入闰秒)。1740000000 这个瞬间对应 2026 年 2 月 17 日 09:20:00 UTC。整件事就是这么简单。其他所有事情 —— 毫秒、微秒、32 位有符号整数溢出、闰秒之争 —— 都是把这个极简概念推进了四十年计算史之后产生的有趣"余波"。
Why 1970?
为什么从 1970 年开始?
The Unix operating system was born in the early 1970s at Bell Labs, and the engineers needed a single "start of time" for their filesystem and system clock. 1 January 1970 was picked as a convenient recent round date — the first day of a decade, and the nearest year that nobody would have to type as a negative number for a long time. It was not the result of a deep astronomical or historical calculation. It was a pragmatic choice made in 1971 by Dennis Ritchie and Ken Thompson, and it has since been written into the foundations of nearly every modern operating system and language standard.
Unix 操作系统诞生于 1970 年代初的贝尔实验室,工程师们需要为文件系统和系统时钟设定一个统一的"时间起点"。1970 年 1 月 1 日就是在这个背景下被选中的 —— 一个方便、又足够新的整数年:十年之首,而且在相当长一段时间内系统都不用输出负数。它并不是来自什么天文学或历史学的严密推算,只是 Dennis Ritchie 和 Ken Thompson 在 1971 年做出的务实选择。后来这个起点被写进了几乎所有现代操作系统和语言标准的底层。
Programmers call this starting instant the "epoch". The C language, which inherited the convention from early Unix, exposes it through the time_t type and the time() function. Every major operating system and every mainstream programming language still uses the same epoch — 1 January 1970 00:00:00 UTC — to this day.
程序员把这个起点称为"epoch(纪元)"。继承了早期 Unix 传统的 C 语言通过 time_t 类型和 time() 函数暴露它。几乎所有主流操作系统和主流编程语言至今仍然使用同一个纪元 —— 1970 年 1 月 1 日 00:00:00 UTC。
Seconds, milliseconds, microseconds, nanoseconds
秒、毫秒、微秒、纳秒
A pure Unix timestamp counts whole seconds, which is fine for dates and logs. But the second is a surprisingly coarse unit for many real systems: at 44.1 kHz audio, one second of sound is 44,100 samples. A modern CPU running at 3 GHz executes millions of instructions in a single second. To handle these higher-resolution events, software reaches for smaller units:
最朴素的 Unix 时间戳只统计整秒,这对日期和日志已经够用。但对许多真实系统来说,"秒"这个单位其实相当粗糙:44.1 kHz 的音频里,1 秒对应 44,100 个采样点;3 GHz 的 CPU 在 1 秒内可以执行上百万条指令。为了处理更高分辨率的事件,软件会使用更小的单位:
- Milliseconds — 1,000 per second. Common in JavaScript (
Date.now()), web APIs, and most performance logging. - 毫秒 —— 每秒 1,000 毫秒。JavaScript(
Date.now())、Web API 以及大多数性能日志中常见。 - Microseconds — 1,000,000 per second. Used in some databases and trading systems.
- 微秒 —— 每秒 100 万微秒。某些数据库和交易系统中使用。
- Nanoseconds — 1,000,000,000 per second. The native unit of Go's
time.Timeand many high-resolution timers. - 纳秒 —— 每秒 10 亿纳秒。Go 的
time.Time和许多高精度定时器的原生单位。
Multiplying by 1,000 (or 1,000,000) is straightforward, but forgetting the factor is a classic source of bugs. A timestamp of 1740000000 means "February 2026" in seconds and "January 1970" in milliseconds. Whenever a timestamp moves between systems, the unit must be confirmed explicitly. There is no standard way to embed the unit in the number itself.
乘以 1,000(或 1,000,000)很容易,但忘记这个倍数是经典的 bug 来源。1740000000 在秒单位下是 2026 年 2 月,在毫秒单位下却是 1970 年 1 月。每次时间戳跨系统传递,都必须显式确认单位。数字本身并没有标准方式把单位嵌进去。
The 2038 problem
2038 年问题
The most famous deadline in computing is 19 January 2038, 03:14:07 UTC. At that instant, a 32-bit signed integer of seconds since the epoch overflows: 2^31 − 1 is 2,147,483,647, and one more second rolls it over to −2,147,483,648, which most programs interpret as 13 December 1901. Anything that still uses a signed 32-bit time_t — including a surprising amount of embedded firmware, file formats, and legacy C code — will misbehave at exactly that moment.
计算机史上最著名的"截止日期"是 2038 年 1 月 19 日 03:14:07 UTC。在那一瞬间,32 位有符号整数的秒级时间戳会溢出:2^31 − 1 等于 2,147,483,647,再加一秒就变成 −2,147,483,648,而多数程序会把它解释为 1901 年 12 月 13 日。任何仍在使用有符号 32 位 time_t 的系统 —— 包括数量惊人的嵌入式固件、文件格式和老旧 C 代码 —— 都会在那个瞬间开始出错。
The fix, already well underway in modern languages and 64-bit systems, is simply to use a wider type. A 64-bit signed integer counts seconds for roughly 292 billion years — about 21 times the current age of the universe. Linux switched its default time_t to 64-bit on most architectures years ago, and Apple did so for Cocoa in 2020. The remaining risk lives in 32-bit-only embedded devices and in old binaries that will never be recompiled.
在现代语言和 64 位系统上,修复方法就是用更宽的整型。64 位有符号整数能表示大约 2920 亿年的秒数 —— 约等于当前宇宙年龄的 21 倍。Linux 多年前就把多数架构下的 time_t 默认改成 64 位,苹果公司也在 2020 年对 Cocoa 做了同样处理。残余风险集中在仅支持 32 位的嵌入式设备,以及那些永远不会被重新编译的旧二进制。
If you maintain C or C++ code today, audit every use of time_t, ctime(), localtime(), and the classic struct tm for places where the result is silently cast back to a 32-bit integer. On the web, prefer Date.now() (a JS number, which is 64-bit float) for new code, and store timestamps as 64-bit integers server-side.
如果你今天在维护 C 或 C++ 代码,请审查所有 time_t、ctime()、localtime() 以及经典 struct tm 的使用位置,找出结果被悄悄截回 32 位整数的地方。在 Web 端,新代码请优先使用 Date.now()(JS 的 number 是 64 位浮点),服务器端用 64 位整数存储时间戳。
UTC vs local time, formats, and the time-zone trap
UTC 与本地时间:格式选择与时区陷阱
A Unix timestamp is always UTC. It does not carry a time zone. The instant 1740000000 is the same physical moment everywhere on Earth; how it is written for a human depends on where they are. Tokyo displays it as 18:20, London as 09:20, and New York as 04:20. Conflating a timestamp with a human-readable date is a common source of off-by-several-hours bugs.
Unix 时间戳永远是 UTC,它不携带时区信息。1740000000 这个瞬间在地球任何地方都是同一物理时刻,只是人类写法不同:东京是 18:20,伦敦是 09:20,纽约是 04:20。把时间戳和人类可读日期混为一谈,是典型的"差几小时" bug 来源。
The rule that prevents most of these bugs is: store in UTC, compute in UTC, only convert to a local time at the very last step — usually in the UI. A database that stores local times will eventually hit daylight saving transitions, server migrations between regions, or users logging in from another country, and at least one of those will silently produce wrong times. UTC, by contrast, never jumps and never shifts. It is the only sane canonical form.
避免这类 bug 的原则是:用 UTC 存储、用 UTC 计算,只在最后一步(通常是 UI 上)才转换为本地时间。如果数据库存的是本地时间,迟早会遇到夏令时切换、服务器跨区域迁移、或用户从其他国家登录,至少有一种情况会悄悄产生错误时间。而 UTC 永远不跳变、永远不偏移,是唯一稳妥的"规范形态"。
The IANA time zone database (often called "tzdata" or "zoneinfo") is the source of truth. It tracks every civil time zone change on Earth, including historical changes and the political decisions that move countries between offsets. Always reference a time zone by its IANA name (such as America/New_York or Asia/Shanghai) and keep your copy of the database up to date — most operating systems ship updates several times a year.
IANA 时区数据库(常称 "tzdata" 或 "zoneinfo")是权威来源,记录了地球上每一次民用时区变更,包括历史变更以及国家切换时区偏移的政治决定。永远用 IANA 名称(例如 America/New_York 或 Asia/Shanghai)来引用时区,并保持本地数据库最新 —— 多数操作系统一年会更新多次。
When a timestamp needs to be human-readable, three formats dominate. Unix time is a single integer with no time zone info, ideal for storage and machine-to-machine APIs. ISO 8601 looks like 2026-02-17T09:20:00Z or 2026-02-17T17:20:00+08:00: sortable as a string, unambiguous, includes the offset, and the default for JSON APIs. RFC 2822 — Tue, 17 Feb 2026 09:20:00 GMT — was designed for email headers and is still used in HTTP and email, but new APIs should avoid it. A useful convention: store the integer in the database, and add a separate ISO 8601 string in any API response. The integer is for math, the string is for humans, and never let the string be the only representation.
需要可读的时间字符串时,三种格式最常见。Unix 时间 是不带时区信息的单一整数,适合存储和机器对机器 API。ISO 8601 形如 2026-02-17T09:20:00Z 或 2026-02-17T17:20:00+08:00:按字符串排序即按时间排序、含义无歧义、附带偏移量,是 JSON API 的默认格式。RFC 2822 形如 Tue, 17 Feb 2026 09:20:00 GMT,原本为邮件头设计,至今仍用于 HTTP 和邮件,但新 API 应避免使用。一个有用的约定:数据库存整数,API 响应再附带一个 ISO 8601 字符串;整数用于数学,字符串用于展示,永远不要让字符串成为唯一表达形式。
Leap seconds and why they are controversial
闰秒及其争议
The Earth's rotation is not perfectly uniform. A "day" measured by atomic clocks is very slightly shorter than a "day" measured by the planet's actual rotation, so the international timekeeping community occasionally inserts a leap second to bring the two back into alignment. The last one was added on 31 December 2016, and a leap second makes that day have 23:59:60 instead of jumping directly to the next day.
地球自转并不是完全均匀的。原子钟所定义的"一天"比地球实际自转测出的"一天"略短,因此国际时间机构会偶尔插入一个闰秒,使两者重新对齐。最近一次是在 2016 年 12 月 31 日,那一天的 23:59:60 会被插入,而不是直接进入下一天。
Unix time, by convention, ignores leap seconds. A Unix timestamp increases by exactly 1 each second of civil time, and during a leap second it repeats the previous value. This is the source of the famous "smearing" techniques Google and other large operators use: instead of repeating a second, they spread the extra millisecond across a whole day so no clock is ever wrong by a full second. Most users will never notice either approach, but distributed databases that try to be exactly consistent across a leap second can find themselves stuck in unpleasant edge cases.
按惯例,Unix 时间忽略闰秒。Unix 时间戳在每个民用秒严格加 1,遇到闰秒时它会重复上一个值。这正是 Google 等大型运营商采用"时间抹平"(smearing)技术的根源:与其重复一秒,不如把多出的那 1 秒以毫秒为单位均匀摊到一整天里,这样不会有任何时钟整整错 1 秒。大多数用户察觉不到这些差异,但试图在闰秒时刻保持强一致的分布式数据库,常常会陷入令人不快的边界情况。
In 2022, the General Conference on Weights and Measures voted to abandon leap seconds by 2035, redefining the "day" as exactly 86,400 SI seconds. Until that takes effect, software that cares about long-term astronomical accuracy still has to handle the discontinuity; everyone else can safely pretend it does not exist.
2022 年,国际计量大会投票决定在 2035 年前取消闰秒,把"一天"重新定义为精确的 86,400 SI 秒。在新规生效前,关注长期天文精度的软件仍要处理这次不连续;其他应用完全可以假装闰秒不存在。
Storing time in databases
在数据库里存储时间
The two SQL types most people reach for are TIMESTAMP and DATETIME, and the differences matter. In MySQL, TIMESTAMP is stored as a Unix epoch and converted to UTC for storage, but it is restricted to the range 1970-01-01 03:14:07 UTC to 2038-01-19 03:14:07 UTC (the 32-bit limitation again). DATETIME stores the literal date and time without time zone awareness, covering a much wider range. PostgreSQL takes a different route: its timestamp with time zone type stores everything as UTC internally and never loses information regardless of input zone.
SQL 里最常用的两个类型是 TIMESTAMP 和 DATETIME,区别很重要。在 MySQL 中,TIMESTAMP 以 Unix 纪元形式存储并在内部转为 UTC,但范围限制在 1970-01-01 03:14:07 UTC 到 2038-01-19 03:14:07 UTC(又是 32 位限制)。DATETIME 则原样存日期时间,没有时区概念,覆盖范围大得多。PostgreSQL 走的是另一条路:它的 timestamp with time zone 在内部一律以 UTC 存储,无论输入是哪个时区都不会丢信息。
Whichever type you pick, the best practice is the same: store all timestamps in UTC, never in local time. If you ever need a "wall clock" representation later, compute it from the UTC value using the appropriate IANA zone. This single rule eliminates an entire category of bugs around daylight saving, server moves, and user-generated data.
无论选哪种类型,最佳实践都一样:所有时间戳一律存 UTC,绝不存本地时间。将来如果需要"挂钟"显示,就用对应的 IANA 时区从 UTC 计算出来。这一条规则能消除整一类与夏令时、服务器迁移和用户数据相关的 bug。
Try the tools
试试这些工具
Convert between Unix timestamps and human-readable dates with the timestamp converter. Switch between seconds and milliseconds, see the current epoch value, and copy the result to your clipboard in one click.
用 时间戳转换器 在 Unix 时间戳和可读日期之间互转。可在秒和毫秒之间切换,查看当前纪元值,并一键复制结果。