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 的原因。