JavaScript Date.now():获取与转换 Unix 时间戳
聚焦 JavaScript Date.now() 的指南:获取当前 Unix 时间戳、转换秒与毫秒、把时间戳变成 Date 对象、用 Intl 格式化,并避免常见的解析陷阱。
JavaScript 内部如何存储时间
每个 JavaScript Date 在内部都是一个表示自 Unix 纪元(1970 年 1 月 1 日 00:00:00 UTC)以来毫秒数的 64 位浮点数。这个数就是 Date.getTime() 和 Date.now() 返回的值。Date 内部不存储时区——它始终是一个 UTC 毫秒计数。只有在为显示而格式化日期时时区信息才重要,这就是为什么同一个 Date 对象在洛杉矶可能显示为周一晚上,在东京显示为周二早上。本文聚焦于 Date.now() 和转换时间戳;若想更全面地了解 JavaScript 中每一项时间戳任务,请参阅下方链接的完整参考指南。
获取当前 Unix 时间戳
毫秒用 Date.now(),秒用 Math.floor(Date.now() / 1000)。两者都被广泛支持,且无需导入。重要的命名习惯是在变量名中包含单位:createdAtMs、expiresAtSeconds 或 unixSeconds。带单位的名称能防止未来的 API 使用者猜测一个无单位的时间戳字段意味着什么。
- Date.now() → 1700000000000 (milliseconds, 13 digits)
- Math.floor(Date.now() / 1000) → 1700000000 (seconds, 10 digits)
- +new Date() → same as Date.now() via unary coercion
- new Date().getTime() → same result, slightly more verbose
Date.now() vs performance.now()
Date.now() 用于挂钟时间戳:日志、API 负载、数据库字段、缓存过期,以及任何需要与真实日历时间对齐的东西。performance.now() 用于测量当前页面或进程内的经过时长。它是单调且高精度的,但不是 Unix 时间戳,且若无额外参考点便无法转换为真实日期。
- 当值将被存储、发送到 API 或作为日期显示时,使用 Date.now()
- 测量渲染、解析或请求耗时时,使用 performance.now()
- 不要把 performance.now() 作为事件时间存入数据库
- 不要为安全敏感的计时相减两个 Date.now() 值;系统时钟更改会影响它们
把时间戳转换为 Date
Date 构造函数接受毫秒。如果你的时间戳是秒(10 位),传入前请乘以 1000。忘记这一点是 JavaScript 中最常见的时间戳错误。一个快速检查是位数:当前 Unix 秒为 10 位,当前 Unix 毫秒为 13 位,而 Date 构造函数调用应当收到 13 位的毫秒形式。
- new Date(1700000000 * 1000) → Tue Nov 14 2023 22:13:20 UTC ✓ correct
- new Date(1700000000) → Tue Jan 20 1970 16:13:20 UTC ✗ missing × 1000
- new Date(1700000000 * 1000).toISOString() → '2023-11-14T22:13:20.000Z'
- new Date(1700000000 * 1000).toUTCString() → 'Tue, 14 Nov 2023 22:13:20 GMT'
用 Intl 进行时区感知格式化
用 Intl.DateTimeFormat 获得按区域设置和时区的输出。它自动处理夏令时,且无需外部库。对于仪表板、管理工具、状态页和面向用户的时间戳,这是最安全的内置途径,因为它让你显式选择区域设置和 IANA 时区。
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
- date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
- date.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })
- new Intl.DateTimeFormat('en-CA', { timeZone: tz }).formatToParts(date) → array of {type, value} for custom layouts
安全地解析日期字符串
解析是 JavaScript 变得出人意料的地方。带显式 Z 或偏移的 ISO 字符串是安全的,因为它们标识一个确切的瞬间。裸日期和非正式字符串更易读,但常依赖浏览器规则或运行时时区。对于 API 契约,优先使用带时区的 ISO 8601 字符串,或把单位写入字段名的数值 Unix 时间戳。
- Safe: new Date('2026-05-19T14:30:00Z') — explicit UTC
- Safe: new Date('2026-05-19T10:30:00-04:00') — explicit offset
- Risky: new Date('05/19/2026') — locale-dependent and ambiguous
- Risky: new Date('2026-05-19 10:30') — not a strict ISO timestamp in all runtimes
JavaScript 中常见的时间戳错误
- 写成 new Date(1700000000) 而非 new Date(1700000000 * 1000) —— 落在 1970 而非 2023
- getMonth() 返回 0–11,而非 1–12 —— 显示时始终用 date.getMonth() + 1
- new Date('2024-01-01') 解析为 UTC 午夜;new Date('2024/01/01') 解析为本地午夜
- 为「明天」加 86400000 毫秒会在夏令时切换处出错 —— 改用 setDate(d.getDate() + 1)
- 用 === 比较 Date 对象总会失败 —— 改为比较它们的 .getTime() 值
推荐的 JavaScript 时间戳清单
最简单的生产模式是存储一个规范的瞬间,只在边缘进行格式化。时间戳保持 UTC,名称中包含单位,只在界面或报表层转换为可读时区。
- 仅浏览器状态和 JavaScript Date 构造使用 Unix 毫秒
- 调用以秒记录 Unix 时间的 API 时使用 Unix 秒
- 当人可能检查负载时使用带 Z 或显式偏移的 ISO 8601
- 显示使用带显式 timeZone 的 Intl.DateTimeFormat
- 当本地日历日期重要时,围绕夏令时边界添加测试
JavaScript 时间戳常见问题
- Date.now() 返回秒还是毫秒?
- Date.now() 返回自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒。当 API 期望 Unix 秒时,除以 1000 并使用 Math.floor()。
- 为什么 new Date(1700000000) 显示 1970?
- Date 构造函数期望毫秒。1700000000 是秒时间戳,所以 JavaScript 把它读作纪元后仅 17 亿毫秒。请用 new Date(1700000000 * 1000)。
- JavaScript Date 会存储时区吗?
- 不会。Date 存储一个 UTC 毫秒计数。时区只在用 toString()、toLocaleString() 或 Intl.DateTimeFormat 等方法格式化时才被应用。
- 如何把 Unix 时间戳转换为 JavaScript Date?
- 向构造函数传入毫秒:10 位秒值用 new Date(seconds * 1000),13 位毫秒值用 new Date(ms)。忘记 × 1000 就是 2023 年的时间戳可能渲染为 1970 的原因。