高分辨率时间

W3C 工作草案,

关于此文档的更多细节
此版本
https://w3org.cn/TR/2026/WD-hr-time-3-20260324/
最新发布版本
https://w3org.cn/TR/hr-time-3/
编辑草案
https://w3c.github.io/hr-time/
历史版本
历史
https://w3org.cn/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 Performance 工作组工作草案形式发布,使用 推荐轨道。以工作草案形式发布并不表示得到 W3C 及其成员的认可。

这是一个草案文档,可能随时会被其他文档更新、替换或废弃。将其作为进展中的工作之外的任何内容进行引用是不恰当的。

讨论本规范时,首选使用 GitHub Issues

本文件受 2025 年 8 月 18 日 W3C 流程文件的管辖。

本文档由遵守 W3C 专利政策 的小组制作。W3C 维护一份 与小组交付成果相关的专利披露公开列表;该页面还包括披露专利的说明。实际了解某项专利且认为该专利包含 必要权利要求 的个人必须按照 W3C 专利政策第 6 节披露信息。

1. 引言

本节是非规范性的。

ECMAScript 语言规范 [ECMA-262]Date 对象定义为自 1970 年 1 月 1 日 UTC 起的毫秒数时间值。对于大多数用途,这一定义足以满足需求,因为这些值在大约 285,616 年的范围内(自 1970 年 1 月 1 日 UTC 起)提供毫秒精度的时间点。

在实际使用中,这些时间定义会受到时钟漂移以及系统时钟调整的影响。时间值并不总是单调递增的,后续值可能下降或保持不变。

例如,下面的脚本可能会记录计算得到的 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. 使用场景

本节是非规范性的。

本规范定义了几种不同的能力:它提供基于稳定单调时钟的时间戳,可跨上下文进行比较,并可能具备亚毫秒分辨率。

在性能测量中需要稳定的单调时钟,因为不相关的时钟漂移会扭曲测量结果,使其毫无意义。例如,要准确测量导航到文档、资源获取或脚本执行的耗时,就需要一个具亚毫秒分辨率的单调递增时钟。

在不同上下文之间比较时间戳是必不可少的,例如在 Worker 与主线程之间同步工作,或对这类工作进行测量以创建统一的事件时间线视图。

最后,亚毫秒计时器的需求围绕以下使用场景

1.2. 示例

本节是非规范性的。

开发者可能希望为整个应用构建时间线,包括来自 WorkerSharedWorker 的事件,这些 worker 具有不同的 时间原点。要在同一时间线上显示这些事件,应用可以利用 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. 时钟

时钟(clock)用于跟踪时间的流逝,并能够报告算法步骤执行时的 不安全的当前时间。时钟种类繁多。Web 平台上的所有时钟都尝试实现每真实毫秒计数 1 毫秒的时钟时间,但它们在无法完全精确时的处理方式各不相同。

The wall clock’s unsafe current time

始终尽可能接近用户对时间的感知。由于计算机有时会运行慢、快或丢失时间,它的 wall clock 有时需要进行校准,这意味着 unsafe current time 可能会减小,从而导致它对性能测量或事件顺序记录不可靠。Web 平台共享了一个与 [ECMA-262] time 相同的 wall clock

The monotonic clock’s unsafe current time

永不减小,因此不会受到系统时钟调整的影响。monotonic clock 仅在单个 user agent 执行期间存在,因此无法用于比较可能在不同执行期间发生的事件。

由于 monotonic clock 不能被调整以匹配用户的时间感知,它应当仅用于测量,而非用户可见的时间。任何面向用户的时间通信都应使用 wall clock

当浏览器重新启动、启动隐身或类似的隔离浏览会话,或创建无法与任何现有设置对象通信的 environment settings object 时,用户代理可以为 Unix 纪元的估计单调时间 选取一个新值。由此,开发者不应将共享时间戳作为跨所有过去、现在与未来上下文的绝对时间使用;实际情况是,单调属性仅在能够通过提供的消息机制(例如 postMessage(message, options)BroadcastChannel 等)相互通信的上下文之间保持。

在某些情形(例如标签被置入后台)下,用户代理可能会选择对该上下文中的计时器和周期性回调进行限流,甚至完全冻结。这类限流不应影响单调时钟返回的时间的分辨率或准确性。

2.2. 时刻和持续时间

每个 clockunsafe current time 返回一个 不安全时刻Coarsen time 将这些 不安全时刻 转换为 粗化时刻(或简称 时刻)。不同时钟得到的 不安全时刻时刻 之间不可比较。

时刻不安全时刻 表示时间点,这意味着它们不能直接作为数字存储。实现通常会将一个 时刻 表示为相对于其他固定时间点的 持续时间,但规范应当直接使用 时刻 本身。

持续时间(duration)是同一 clock 上两个 时刻 之间的间隔。两端都不能是 不安全时刻,从而保证 durationduration 差值能够缓解 § 9.1 时钟分辨率 中的关注点。Durations 使用毫秒、秒等单位衡量。由于所有 clocks 都尝试以相同速率计数,durations 并不关联特定的 clock,并且由同一时钟上的两个 时刻 计算得到的 duration 可以加到另一时钟上的 时刻,从而得到该第二时钟上的另一个 时刻

duration from ab 的算法如下:

  1. 断言:ab 是由同一 clock 创建的。
  2. 断言:ab 均为 粗化时刻
  3. 返回从 ab 的时间量,作为一个 duration。如果 ba 之前,则返回负的 duration

Durations 可隐式用作 DOMHighResTimeStamp。要 隐式将持续时间转换为时间戳,给定一个 duration d,返回 d 中的毫秒数。

3. 规范作者的工具

在单个页面(即单个 environment settings object)内测量时间时,可使用 settingsObjectcurrent relative timestamp,定义为 duration from settingsObjecttime originsettingsObjectcurrent monotonic time。该值可通过 duration隐式转换DOMHighResTimeStamp 并直接暴露给 JavaScript。

在单次 UA 执行期间且 environment settings objecttime origin 不是合适的比较基准时,可使用 时刻,这些时刻基于 environment settings objectcurrent monotonic timeenvironment settings object settingsObjectcurrent monotonic time 可通过以下步骤获得:

  1. unsafeMonotonicTimemonotonic clockunsafe current time
  2. 返回调用 coarsen time 并传入 unsafeMonotonicTimesettingsObjectcross‑origin isolated capability 的结果。

时刻 来自 monotonic clock 不能直接在 JavaScript 或 HTTP 中表示。相反,公开两个此类 时刻 之间的 duration

在跨多个 UA 执行期间测量时间时,可使用 时刻,这些时刻基于 当前粗化墙上时间,或者(如果在 cross‑origin‑isolated 上下文 中需要更高精度)基于 environment settings objectcurrent wall timecurrent coarsened wall time 为调用 coarsen time 并传入 wall clockunsafe current time 的结果。

environment settings object settingsObjectcurrent wall time 通过以下步骤获得:

  1. unsafeWallTimewall clockunsafe current time
  2. 返回调用 coarsen time 并传入 unsafeWallTimesettingsObjectcross‑origin isolated capability 的结果。

在使用来自 wall clock时刻 时,请确保你的设计能够处理用户向前或向后调校系统时钟的情况。

时刻 来自 wall clock 可通过将自 Unix epoch 起的毫秒数传入 Date 构造函数,或将自 Unix epoch 起的纳秒数传入 Temporal.Instant 构造函数。[Temporal]

避免在计算机之间发送相似的时间表示,因为这样会泄露用户的时钟漂移,这是一种 tracking vector。相反,请使用类似于 monotonic clock时刻,在两 时刻 之间发送一个 duration

3.1. 示例

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

  1. eventtimeStamp 属性初始化为 this 所属的 relevant settings objectcurrent relative timestamp

错误报告的年龄可以使用以下方式计算:

  1. reportgeneration time 初始化为 settingscurrent monotonic time

随后

  1. data 为包含以下键/值对的映射:
    age
    该数值为 reportgeneration timecontextrelevant settings objectcurrent monotonic time 之间的毫秒数,四舍五入至最近的整数。
    ...

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

  1. source 为一个新的归因源结构,其项目如下:
    ...
    source time
    contextcurrent wall time
    expiry
    value["expiry"] 解析持续时间字符串

若干天后

  1. 如果 contextcurrent wall time 小于 source 的 source time 加上 source 的 expiry,则发送报告。

4. 时间原点

Unix 纪元(Unix epoch)是 时刻,对应于墙钟(wall clock)的 1970 年 1 月 1 日 00:00:00 UTC。

任何可能相互通信的一组 environment settings objects 都拥有一个 Unix 纪元的估计单调时间,即位于 monotonic clock 上的 时刻,其数值通过以下步骤初始化:

  1. wall timewall clockunsafe current time
  2. monotonic timemonotonic clockunsafe current time
  3. epoch timemonotonic time - (wall time - Unix epoch)
  4. estimated monotonic time of the Unix epoch 初始化为调用 coarsen time 并传入 epoch time 的结果。
上述可能相互通信的 settings‑objects 集合需要更明确的说明。它类似于 familiar with,但还包括 Worker

性能测量会报告一个自相关 environment settings object 初始化早期的 时刻duration。该 时刻 存储在该设置对象的 time origin 中。

获取时间原点时间戳,给定一个 global object global,执行以下步骤,返回一个 duration

  1. timeOriginglobalrelevant settings objecttime origin

    Window 上下文中,此值代表 导航开始 的时间。 在 WorkerServiceWorker 中,此值代表 worker 启动 的时间。[SERVICE-WORKERS]

  2. 返回从 Unix 纪元的估计单调时间timeOriginduration

获取时间原点时间戳 返回的值大致等同于 Unix epoch 之后 globaltime origin 发生的时间。它可能与在该时间原点执行的 Date.now() 的返回值不同,因为前者是相对于一个不受系统和用户时钟调整、时钟漂移等影响的 monotonic clock 记录的。

coarsen time 算法,给定某 不安全时刻 timestamp 所在的 clock,以及一个可选布尔值 crossOriginIsolatedCapability(默认 false),执行以下步骤:
  1. time resolution 为 100 微秒,或更高的 implementation‑defined 值。
  2. 如果 crossOriginIsolatedCapability 为 true,则将 time resolution 设为 5 微秒,或更高的 implementation‑defined 值。
  3. implementation‑defined 的方式对 timestamp 进行粗化并可能抖动,使其分辨率不超过 time resolution
  4. 返回 timestamp 作为 moment
相对高分辨率时间(relative high resolution time)在给定来自单调时钟(monotonic clock)的unsafe moment timeglobal object global 时,其等于以下步骤返回的duration
  1. coarse time 为调用 coarsen time,参数为 timeglobalrelevant settings objectcross-origin isolated capability 所得到的结果。
  2. 返回 relative high resolution coarse time 对于 coarse timeglobal
相对高分辨率粗略时间(relative high resolution coarse time)在给定来自单调时钟(monotonic clock)的moment coarseTimeglobal object global 时,其为duration from globalrelevant settings objecttime origincoarseTime

当前高分辨率时间(current high resolution time)在给定global object current global 时,必须返回relative high resolution time 对应unsafe shared current timecurrent global 的结果。

粗化的共享当前时间(coarsened shared current time)在给定可选布尔值 crossOriginIsolatedCapability(默认 false)时,必须返回调用coarsen time,参数为unsafe shared current timecrossOriginIsolatedCapability 的结果。

不安全的共享当前时间(unsafe shared current time)必须返回unsafe current time(不安全当前时间)对应的monotonic clock

5. DOMHighResTimeStamp 类型定义

DOMHighResTimeStamp 类型用于以毫秒为单位存储duration。根据使用场景,它可能表示moment,即此duration在某个基准moment之后的时间,例如time originUnix epoch

typedef double DOMHighResTimeStamp;

DOMHighResTimeStamp 应该表示毫秒级时间,精度足以进行测量,同时防止计时攻击——有关更多考量,请参见§ 9.1 时钟分辨率

DOMHighResTimeStamp 是一个double,因此只能表示相对于纪元的时间——即从Unix epochmoment的毫秒数——以有限的分辨率表示。对于 2023 年的时间点,这一分辨率约为 0.2 微秒。

6. EpochTimeStamp 类型定义

typedef unsigned long long EpochTimeStamp;

EpochTimeStamp 表示从Unix epochwall clock上某个moment的整数毫秒数,且不计闰秒。使用此类型的规范必须说明这些毫秒数的解释方式。

7. Performance 接口

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

7.1. now() 方法

now() 方法必须返回current high resolution time 的毫秒数,该时间由thisrelevant global objectduration)决定。

对同一time originnow() 调用返回的时间值必须使用相同的monotonic clock。如果两个时间值拥有相同的time origin,则任意两个按时间顺序记录的返回值之间的差值绝不能为负。

7.2. timeOrigin 属性

timeOrigin 属性必须返回duration 的毫秒数,此 get time origin timestamprelevant global objectthis)所返回的结果。

获取 Performance.timeOrigin 时返回的时间值必须使用与monotonic clock 相同的时钟,而该时钟在所有time origin之间共享,其参照点是[ECMA-262]time 的定义——参见§ 9 安全考虑

7.3. toJSON() 方法

当调用 toJSON() 时,执行[WEBIDL] 中的default toJSON steps

8. WindowOrWorkerGlobalScope mixin 的扩展

8.1. performance 属性

performance 属性位于接口 mixin WindowOrWorkerGlobalScope 上,允许从global object 访问性能相关的属性和方法。

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

9. 安全考虑

本节是非规范性的。

9.1. 时钟分辨率

对准确计时信息的访问——无论用于测量还是调度——是许多应用的常见需求。例如,页面上的动画、音频以及其他活动的同步需要高分辨率计时,以提供良好的用户体验。同样,测量能够帮助开发者跟踪关键代码的性能、检测回退等。

然而,相同的准确计时信息有时也可能被攻击者利用,用于推测和推断本来无法看到或访问的数据。例如,缓存攻击、统计指纹识别以及微架构攻击都是隐私和安全的威胁,恶意站点可能利用各种浏览器或应用发起的高分辨率计时数据来区分用户子集、识别特定用户或泄露同进程的其他用户数据——参见[CACHE-ATTACKS][SPECTRE] 以获取更多背景信息。

本规范定义的 API 提供亚毫秒级时间分辨率,比之前的EpochTimeStamp 所提供的毫秒分辨率更为精确。然而,即使没有此新 API,攻击者仍可通过重复执行和统计分析获得高分辨率估计。

为了确保新 API 不会显著提升此类攻击的精度或速度,DOMHighResTimeStamp 类型的最小分辨率应足够不精确,以防止攻击。

在必要时,用户代理应在coarsen time 的处理模型中为 time resolution 设置更高的分辨率值,以应对因架构或软件限制导致的隐私与安全问题,或其他考量。

为缓解此类攻击,用户代理可以部署其认为必要的任何技术。这些技术的部署可能因浏览器架构、用户设备、内容及其读取跨源数据的能力或其他实际因素而异。

这些技术可能包括:

彻底消除此类计时侧信道攻击在实践中基本不可能:要么所有操作必须在不随任何机密信息的取值而变化的时间内完成,要么应用必须与所有时间相关原语(时钟、计时器、计数器等)隔离。由于对浏览器和应用开发者带来的复杂性以及对性能和响应性的负面影响,这两种方案都不切实际。

时钟分辨率是一个未解决且仍在演进的研究领域,尚无业界共识或适用于所有浏览器的明确推荐。有关讨论请参见 Issue 79

9.2. 时钟漂移

本规范同样定义了一个 API,提供零时原点的亚毫秒级时间分辨率,这需要向应用公开monotonic clock,且该时钟必须在所有浏览器上下文之间共享。单调时钟不需要绑定到物理时间,但建议依据[ECMA-262]time 的定义进行设定,以避免向用户泄露新的指纹熵——例如,应用已经可以轻易获取此时间,而公开新的逻辑时钟则会提供额外信息。

然而,即使使用上述机制,monotonic clock 仍可能提供附加的时钟漂移分辨率。如今,应用可以在同一上下文的多个点上记录时间(通过 Date.now()now())并观察它们之间的漂移——例如,由于自动或用户手动的时钟校正。借助 timeOrigin 属性,攻击者还可以将time origin(由monotonic clock 报告)的值与当前的时间估计(即 performance.timeOriginDate.now() - performance.now() 的差值)进行比较,并可能在更长时间跨度内观察到这两种时钟之间的漂移。

实际情况中,同一应用在多次导航之间也能观察到相同的时钟漂移:应用可以在每个上下文中记录逻辑时间,并使用客户端或服务器端的时间同步机制来推断用户时钟的变化。同样,底层机制如 TCP 时间戳也可能向服务器泄露相同的高分辨率信息,而无需多次访问。因此,此 API 提供的信息不应泄露任何显著或先前不可得的用户熵。

10. 隐私考虑

本节是非规范性的。

当前对Documenttime origin 的定义会暴露跨源重定向在请求到达文档源之前所消耗的总时间。这会泄露跨源信息,但尚未确定如何在不严重破坏性能度量的前提下进行缓解。

有关讨论请参见 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]

本规范中的示例均以“例如”一词引入,或者通过 class="example" 与规范性文本隔开,如下所示

这是一个说明性示例。

说明性注释以“Note”一词开头,并使用 class="note" 与规范性文本隔开,如下所示

注意,这是一个说明性注释。

一致性算法

作为算法一部分的祈使语气要求(例如“去除任何前导空格字符”或“返回 false 并中止这些步骤”)应根据引入算法时使用的关键词(“必须”、“应该”、“可以”等)进行解释。

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

索引

本规范定义的术语

通过引用定义的术语

引用

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[HTML]
Anne van Kesteren; et al. HTML 标准. Living Standard. URL: https://html.whatwg.cn/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. RFC 中用于指示要求级别的关键词. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. Living Standard. 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 年 3 月 12 日。CRD。URL: https://w3org.cn/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-objects 必须有更好的规范。它类似于familiar with,但包括 Worker