高精度时间

W3C 工作草案,

关于本文件的更多信息
当前版本:
https://www.w3.org/TR/2026/WD-hr-time-3-20260225/
最新发布版本:
https://www.w3.org/TR/hr-time-3/
编辑草案:
https://w3c.github.io/hr-time/
历史版本:
历史记录:
https://www.w3.org/standards/history/hr-time-3/
反馈:
public-web-perf@w3.org 邮件主题行为 “[hr-time-3] … message topic …” (邮件归档)
GitHub
规范内问题
测试套件:
https://wpt.live/hr-time/
编辑者:
Yoav Weiss (Shopify)
前任编辑:
(Google LLC)
(Google LLC)
(Microsoft Corp.)

摘要

本规范定义了一个 API,可提供时间原点与亚毫秒分辨率的当前时间,并且不会受到系统时钟偏移或调整影响。

本文档状态

本节描述了本文档在发布时的状态。 当前所有 W3C 发布物和本技术报告的最新修订版 可见于W3C 标准与草案索引

本文档由Web 性能工作组推荐路径 发布为工作草案。 作为工作草案发布 并不代表 W3C及其成员的认可。

此为草案文件, 随时可能被其他文件 更新、替代或废弃。 除作为进行中工作外,不宜引用本文件。

GitHub Issues为本规范讨论的首选方式。

本文档受 2025年8月18日 W3C 流程文件约束。

本文档由依据 W3C 专利政策运作的团体产生。 W3C 维护着 所有相关专利披露的公开列表, 该页面还包括披露专利的方法说明。 任何知晓某专利且认为其包含 必要声明 的个人, 须根据 W3C 专利政策第 6 条 披露相关信息。

1. 引言

本节为非规范性内容。

ECMAScript 语言规范 [ECMA-262] 定义了 Date 对象, 它表示自 1970 年 1 月 1 日 UTC 起的毫秒数型时间值。 对于大部分用途,这样的时间定义已足够,因为这些值可表示自 1970 年 1 月 1 日 UTC 起约 285,616 年内任意时间点的毫秒精度。

实际上,这些时间定义会受到时钟漂移和系统时钟调整的影响。时间值不一定总是单调递增,后续数值可能降低,或停滞不变。

例如,以下脚本计算的 duration 可能为正、为负或为零:

var mark_start = Date.now();
doTask(); // Some task
var duration = Date.now() - mark_start;

对于某些任务,这种时间定义并不充分,因为:

本规范并不建议更改 Date.now() [ECMA-262] 的行为, 因它对于获取日历当前值十分有用,且有着长期使用历史。 DOMHighResTimeStamp 类型, Performance.now() 方法, 以及 Performance.timeOrigin 属性通过提供具备亚毫秒分辨率的单调递增时间值,解决了上述问题。

提供亚毫秒分辨率并非本规范的强制要求。实现可出于隐私与安全原因选择限制其暴露的定时器分辨率,而不暴露亚毫秒定时。依赖亚毫秒分辨率的用例在这种情况下可能无法满足。

1.1. 应用场景

本节为非规范性内容。

本规范定义了几种不同能力:它通过稳定、单调的时钟提供时间戳、支持上下文间比较,并支持亚毫秒分辨率(如实现允许的话)。

性能测量需要稳定的单调时钟,因为无关的时钟漂移会扭曲测量结果,甚至使其失效。例如,要精确测量导航到 Document、获取资源或脚本执行的耗时,需要具备亚毫秒分辨率的单调递增时钟。

在不同上下文间比较时间戳也非常重要,例如 Worker 与主线程之间协作,或对这些工作进行分析以获取统一事件时间线视图时。

对亚毫秒定时器的需求主要包括以下场景:

1.2. 示例

本节为非规范性内容。

开发者可能希望构建整个应用的时间线,包括 WorkerSharedWorker 上下文产生的事件,这些上下文拥有不同的 时间原点。 要在同一个时间线展示这些事件,应用可借助 Performance.timeOrigin 属性对 DOMHighResTimeStamp 进行换算。

// ---- worker.js -----------------------------
// Shared worker script
onconnect = function(e) {
  var port = e.ports[0];
  port.onmessage = function(e) {
    // Time execution in worker
    var task_start = performance.now();
    result = runSomeWorkerTask();
    var task_end = performance.now();
  }

  // Send results and epoch-relative timestamps to another context
  port.postMessage({
    'task': 'Some worker task',
    'start_time': task_start + performance.timeOrigin,
    'end_time': task_end + performance.timeOrigin,
    'result': result
  });
}

// ---- application.js ------------------------
// Timing tasks in the document
var task_start = performance.now();
runSomeApplicationTask();
var task_end = performance.now();

// developer provided method to upload runtime performance data
reportEventToAnalytics({
  'task': 'Some document task',
  'start_time': task_start,
  'duration': task_end - task_start
});

// Translating worker timestamps into document’s time origin
var worker = new SharedWorker('worker.js');
worker.port.onmessage = function (event) {
  var msg = event.data;

  // translate epoch-relative timestamps into document’s time origin
  msg.start_time = msg.start_time - performance.timeOrigin;
  msg.end_time = msg.end_time - performance.timeOrigin;

  reportEventToAnalytics(msg);
}

2. 时间概念

2.1. 时钟

时钟追踪时间的流逝,可报告 算法执行时的不安全当前时间。 时钟种类很多。Web 平台上的所有时钟都试图保证 1 毫秒时钟与真实世界 1 毫秒等值,但它们遇到无法完全精确时的处理方式不同。

wall clock不安全当前时间

总是尽可能贴近用户直观时间。由于计算机有时会变慢、变快或丢失时间,wall clock 有时需调整,这意味着不安全当前时间可能减少,因此不可靠于性能测量或事件顺序记录。Web 平台与 [ECMA-262]time 共享同一 wall clock

monotonic clock不安全当前时间

永远不会减少,因此不会被系统时钟调整影响。单调时钟仅存在于一次用户代理(user agent)运行中,无法用于比较不同运行中的事件。

由于单调时钟无法调整为用户时间,应将其用于测量而非用户可见时间。与用户通信任何时间值时应使用 wall clock。

用户代理可在浏览器重启、进入隔离浏览(如隐私模式)或创建无法与现有 settings object 通信的环境设置对象时,为Unix 纪元的估算单调时间生成新值。因此开发者不应将共享时间戳当作能跨全部上下文保持单调性的绝对时间;实际上单调性只适用于能互发消息的上下文,例如postMessage(message, options)BroadcastChannel等。

在某些场景如标签页被置后台时,用户代理可选择节流该上下文中的定时器与定时回调,甚至全部冻结。这类节流不应影响单调时钟返回时间的分辨率与精度。

2.2. 时刻与时长

每个时钟不安全当前时间会返回一个不安全时刻粗化时间 会把这些不安全时刻 转换成粗化时刻,简记为 时刻。不同钟表产生的不安全时刻时刻不可比较。

时刻不安全时刻代表时间点,不能直接存为数字。实现通常将时刻实现为自某个参考点的时长,但规范应以时刻形式描述。

时长 是同一时钟上两个时刻之间的距离。两端都不能是不安全时刻,这样时长及其差值可以缓解 [§ 9.1 时钟分辨率] 所述问题。 时长可用毫秒、秒等单位表示。所有时钟都以相同速度计时,时长不再与特定时钟关联,在同一时钟上的两时刻计算得的时长可加到第二钟的某时刻上生成该钟的另一个时刻

ab 的时长, 如下所示:

  1. 断言:ab 来自同一时钟
  2. 断言:ab 均为粗化时刻
  3. 返回 ab 的用时长表示的时间量。若 b 早于 a,则为负时长。

时长可隐式用作 DOMHighResTimeStamp。 若需 隐式将时长转为时间戳, 给定时长 d,返回该 d 的毫秒数。

3. 规范作者工具

如需在单页面内计时(即单个 环境设置对象的上下文), 可使用 settingsObject当前相对时间戳, 它定义为 时长settingsObject时间原点settingsObject当前单调时间。该值可利用 时长隐式转换DOMHighResTimeStamp 并直接暴露给 JavaScript。

如需在单个 UA 执行内计时,且 环境设置对象时间原点 不适合作为比较基准时,可通过环境设置对象的 当前单调时间 创建 时刻。某 环境设置对象 settingsObject当前单调时间 定义为以下步骤结果:

  1. unsafeMonotonicTime单调时钟不安全当前时间
  2. 返回以 unsafeMonotonicTimesettingsObject跨源隔离能力 作为参数调用 粗化时间 的结果。

单调时钟时刻 不能直接通过 JavaScript 或 HTTP 表示。 可取而代之的是,暴露两个此类 时刻 之间的 时长

跨多次 UA 执行计时时,可利用 当前粗化壁钟时间 或(若需更高精度且为 跨源隔离 上下文)环境设置对象的 当前壁钟时间 创建 时刻当前粗化壁钟时间 是以 壁钟不安全当前时间 调用 粗化时间 得到的结果。

环境设置对象 settingsObject当前壁钟时间 计算如下:

  1. unsafeWallTime壁钟不安全当前时间
  2. 返回以 unsafeWallTimesettingsObject跨源隔离能力 作为参数调用 粗化时间 的结果。

若使用自 壁钟时刻,需确保设计兼容用户前移或后移时钟的情形。

壁钟的 时刻 在 JavaScript 中可这样表示: 传入 Unix 纪元 到该时刻的毫秒数,作为 Date 构造函数参数;或传入自 Unix 纪元 起的纳秒数给 Temporal.Instant 构造器。[Temporal]

避免在不同计算机间发送类似的表示方法,否则可能暴露用户的时钟偏差,这是一种追踪向量。 更推荐采用类似单调时钟时刻方式,即发送两时刻间的时长。

3.1. 示例

DOM 事件发生时间可通过如下方式报告:

  1. eventtimeStamp 属性初始化为 this相关设置对象当前相对时间戳

错误报告的存活时长可通过如下方式计算:

  1. report生成时间初始化为 settings当前单调时间

随后:

  1. data 是包含如下键/值的映射:
    age
    report生成时间context相关设置对象当前单调时间 之间的毫秒数,四舍五入为最接近的整数。
    ...

多天到期的归因报告可通过如下方式处理:

  1. source 为新建的归因源结构,成员包括:
    ...
    source time
    context当前壁钟时间
    expiry
    基于 value["expiry"] 解析时长字符串

数日后:

  1. context当前壁钟时间 小于 source 的 source time + expiry,则发送报告。

4. 时间原点

Unix 纪元壁钟上 对应 1970 年 1 月 1 日 00:00:00 UTC 的 时刻

任何可互相通信的 环境设置对象组, 拥有一个 Unix 纪元的估算单调时间, 它是 单调时钟上的一个 时刻, 其值通过以下步骤初始化:

  1. wall time壁钟不安全当前时间
  2. monotonic time单调时钟不安全当前时间
  3. epoch time = monotonic time - (wall time - Unix 纪元)
  4. Unix 纪元的估算单调时间 初始化为 以 epoch time 作为参数调用 粗化时间 的结果。
上述“可互通信的 settings-object 组”还需更详细规定。与 familiar with 类似,但包含 Worker

性能测量报告从相关 环境设置对象 初始化早期某 时刻 到当前的 时长。 此 时刻 存储于该 settings object 的 时间原点中。

若要 获取时间原点时间戳,给定 全局对象 global,返回 时长, 按如下步骤:

  1. timeOriginglobal相关设置对象时间原点

    Window 上下文,该值表示 导航开始时刻。 在 WorkerServiceWorker 内容中,该值表示 worker 启动时刻。 [SERVICE-WORKERS]

  2. 返回 时长, 从 Unix 纪元的估算单调时间timeOrigin

获取时间原点时间戳 返回的值大致等于 globalUnix 纪元 之后其 时间原点 的时刻。该值与在时间原点执行 Date.now() 可能不同, 因为前者相对于不受系统/用户时钟调整、时钟漂移等影响的 单调时钟 记录。

粗化时间 算法, 给定某 时钟上的 不安全时刻 timestamp 及可选布尔 crossOriginIsolatedCapability(默认为 false),步骤如下:
  1. time resolution 为 100 微秒,或更高的 实现定义值。
  2. crossOriginIsolatedCapability 为 true,则设 time resolution 为 5 微秒,或更高的 实现定义值。
  3. 实现定义方式粗化并(可能)抖动 timestamp,保证其分辨率不超出 time resolution
  4. 返回 timestamp 作为时刻
相对高精度时间, 给定某 单调时钟不安全时刻 time全局对象 global,按如下步骤返回 时长
  1. coarse time 为调用 粗化时间 ,以 timeglobal相关设置对象跨源隔离能力 为参数。
  2. 返回 相对高精度粗化时间,参数是 coarse timeglobal
相对高精度粗化时间 给定 单调时钟 上的 时刻 coarseTime全局对象 global,返回 global相关设置对象时间原点coarseTime时长

当前高精度时间, 给定 全局对象 current global,须返回 以 不安全共享当前时间current global 为参数调用 相对高精度时间 的结果。

粗化共享当前时间 给定可选布尔 crossOriginIsolatedCapability(默认为 false),须返回以 不安全共享当前时间crossOriginIsolatedCapability 调用 粗化时间 的结果。

不安全共享当前时间 应返回 单调时钟的不安全当前时间

5. DOMHighResTimeStamp 类型别名

DOMHighResTimeStamp 类型用于存储以毫秒为单位的时长。根据上下文,它可以表示自某个基准时刻(如时刻)、时间原点Unix 纪元后持续的时长对应的时刻。

typedef double DOMHighResTimeStamp;

DOMHighResTimeStamp 应当能以足够精度(毫秒)表示时间,既能进行测量,又能防止计时攻击——详见 § 9.1 时钟分辨率

DOMHighResTimeStampdouble, 因此它只能以有限分辨率表示基于 epoch 的时间——即 Unix 纪元 到某时刻的毫秒数。以 2023 年为例,该分辨率约为 0.2 微秒。

6. EpochTimeStamp 类型别名

typedef unsigned long long EpochTimeStamp;

EpochTimeStamp 表示自Unix 纪元起 到 壁钟上的某一时刻的整数毫秒数(不计闰秒)。具体如何解释毫秒数量,由引用本类型的规范规定。

7. Performance 接口

[Exposed=(Window,Worker)]
interface Performance : EventTarget {
    DOMHighResTimeStamp now();
    readonly attribute DOMHighResTimeStamp timeOrigin;
    [Default] object toJSON();
};

7.1. now() 方法

now() 方法必须返回 当前高精度时间时长), 以 this相关全局对象为参数计算的毫秒数。

通过同一个时间原点Performance 实例调用 now() 方法返回的时间值,需使用同一 单调时钟。 只要时间值基于同一个时间原点, 则任何两次调用 now() 方法获取的时间值的时间差绝不能为负。

7.2. timeOrigin 属性

timeOrigin 属性必须返回 获取时间原点时间戳 (以 相关全局对象 为参数)的时长的毫秒数。

访问 Performance.timeOrigin 返回的时间值必须使用 单调时钟 (该时钟由所有 时间原点 共享), 并以 [ECMA-262] time 为基点。 ——见 [§ 9 安全性考虑]。

7.3. toJSON() 方法

调用 toJSON() 时,应执行 [WEBIDL]默认 toJSON 步骤

8. WindowOrWorkerGlobalScope mixin 的扩展

8.1. performance 属性

performance 属性定义在接口 mixin WindowOrWorkerGlobalScope 上,可从全局对象 访问与性能有关的属性与方法。

partial interface mixin WindowOrWorkerGlobalScope {
  [Replaceable] readonly attribute Performance performance;
};

9. 安全性考虑

本节为非规范性内容。

9.1. 时钟分辨率

许多应用在测量和调度时都需要精确计时。例如,协调页面上的动画、声音等活动需要高分辨率时间来提升用户体验。测量同样能帮助开发者追踪关键代码组件的性能,发现性能回退等。

但同样的精确计时能力也有被攻击者用于恶意目的的风险,攻击者可能借此推测、推断本不应可见或可访问的数据。例如,缓存攻击、指纹统计分析与微架构攻击都涉及利用浏览器或应用各类高分辨率计时信息来分辨用户子集、标识具体用户、揭示同进程的无关数据 —— 见 [CACHE-ATTACKS][SPECTRE]

本规范定义的 API 可提供亚毫秒分辨率时间,比分辨率仅有毫秒的 EpochTimeStamp 更精确。但即便无该 API,攻击者也可通过反复执行与统计分析获得高分辨率估算值。

为了防止该新 API 显著提升此类攻击的精度与速度, DOMHighResTimeStamp 类型的最小分辨率应当足够粗糙以防御攻击。

必要时,用户代理应在 粗化时间处理模型中将 time resolution 设为更高,以减少因体系结构/软件约束或其它考虑给隐私与安全带来的风险。

为减轻上述攻击,用户代理可使用任何其认为必要的技术。这些技术的部署可能依赖于浏览器体系结构、用户设备、内容类型及其跨源读取数据的能力等各种因素。

可能的缓解手段包括:

要彻底防止此类定时侧信道攻击实际上几乎不可能:要么所有操作都在和任何敏感信息取值无关的固定时间内完成,要么应用必须完全隔离计时原语(时钟、定时器、计数器等)。这两者都因实现复杂度及对应用/浏览器性能与响应性的损害而不现实。

时钟分辨率仍是研究中的难题,尚无业界公认或适用于所有浏览器的权威建议。可追踪 Issue 79

9.2. 时钟漂移

本规范还定义了能提供时间原点零时刻亚毫秒分辨率的 API,它要求并暴露给应用一个单调时钟,并需覆盖所有浏览器上下文。单调时钟不必与物理时间一致,但推荐与[ECMA-262]time定义进行校准,以免增加新的指纹信息——例如应用可轻松获得此时间,若暴露新的逻辑时钟将带来新信息。

即便如此,单调时钟仍可能暴露更高分辨率的时钟漂移。应用可在同一上下文多次记录日时与单调时间值(Date.now()now()), 观察两者的漂移——譬如因自动或用户时钟调校导致的漂移。有了 timeOrigin 属性,攻击者还可将 时间原点 (由单调时钟报告) 与 时间原点 的当前 day-time 估算(即 `performance.timeOrigin` 与 `Date.now() - performance.now()` 之差)相比较,并潜在可在长时间跨度内观测时钟间漂移。

实践中,同样的时间漂移可由应用跨多次导航观测:应用可在每个上下文记录逻辑时间,并利用客户端或服务器的同步机制推断用户时钟变动。同理,底层协议如 TCP 时间戳无需多次访问即可向服务器泄漏同样的高精度信息。因此,本 API 提供的信息不应暴露关于用户的新显著熵或此前未知的信息。

10. 隐私性考虑

本节为非规范性内容。

当前针对 Document时间原点定义会暴露 跨源重定向带来的总耗时(直到请求到达文档源为止)。 这暴露了跨源信息, 但如何在不对性能指标造成重大影响的前提下缓解,尚无定论。

相关讨论见 Navigation Timing Issue 160

11. 致谢

感谢 Arvind Jain、Angelos D. Keromytis、Boris Zbarsky、Jason Weber、Karen Anderson、Nat Duca、Philippe Le Hegaret、Ryosuke Niwa、 Simha Sethumadhavan、Todd Reifsteck、Tony Gentilcore、Vasileios P. Kemerlis、Yoav Weiss 和 Yossef Oren 对本规范的贡献。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语结合表达。 在本规范的规范性部分中, “MUST”“MUST NOT”“REQUIRED”“SHALL”“SHALL NOT”“SHOULD”“SHOULD NOT”“RECOMMENDED”“MAY”“OPTIONAL” 等关键字应按 RFC 2119 的说明解释。 但为可读性考虑, 本规范中这些词不会全部大写出现。

除明确标记为非规范性、示例和注释的部分, 其余本规范文本均为规范性内容。 [RFC2119]

本规范中的示例以“例如(for example)”开头,或用 class="example" 将其与规范性文本区分,例如:

这是一个信息性示例。

信息性注释以“Note”开头, 并用 class="note" 与规范性文本区分,例如:

注,这是一个信息性注释。

一致性算法

作为算法一部分、祈使句表述的要求 (例如:“去除所有前导空格字符” 或 “返回 false 并中止这些步骤”) 应结合算法引言所用关键词 (例如“must”、“should”、“may”等) 一同解释。

以算法或具体步骤表述的一致性要求, 可用任意方式实现,只要最终结果等价即可。 本规范中定义的算法旨在易于理解,不要求高性能。 鼓励实现者优化。

索引

本规范定义的术语

引自其他规范的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 活标准. URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[HR-TIME-3]
Yoav Weiss. 高精度时间. 2024年11月7日. WD. URL: https://www.w3.org/TR/hr-time-3/
[HTML]
Anne van Kesteren 等. HTML 标准. 活标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 活标准. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于 RFC 的关键字以标示要求级别. 1997年3月. 最佳现行实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 活标准. URL: https://webidl.spec.whatwg.org/

信息性引用

[CACHE-ATTACKS]
Yossef Oren 等. The Spy in the Sandbox - Practical Cache Attacks in Javascript. 2015年3月1日. URL: https://arxiv.org/abs/1502.07373
[SERVICE-WORKERS]
Monica CHINTALA; Yoshisato Yanagisawa. Service Workers Nightly. 2026年2月23日. CRD. URL: https://www.w3.org/TR/service-workers/
[SPECTRE]
Paul Kocher 等. Spectre Attacks: Exploiting Speculative Execution. 2018年1月. URL: https://spectreattack.com/spectre.pdf
[Temporal]
Temporal. Stage 3 提案. URL: https://tc39.es/proposal-temporal/

IDL 索引

typedef double DOMHighResTimeStamp;

typedef unsigned long long EpochTimeStamp;

[Exposed=(Window,Worker)]
interface Performance : EventTarget {
    DOMHighResTimeStamp now();
    readonly attribute DOMHighResTimeStamp timeOrigin;
    [Default] object toJSON();
};

partial interface mixin WindowOrWorkerGlobalScope {
  [Replaceable] readonly attribute Performance performance;
};

问题索引

上述“可互通信的 settings-object 组”还需更详细规定。与 familiar with 类似,但包含 Worker