<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Clock-Drift on Tarragon</title><link>https://tarrragon.github.io/blog/tags/clock-drift/</link><description>Recent content in Clock-Drift on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/clock-drift/index.xml" rel="self" type="application/rss+xml"/><item><title>跨平台 timestamp 一致性</title><link>https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/cross-platform-timestamp/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/cross-platform-timestamp/</guid><description>&lt;p>跨平台的監控系統收到來自不同平台（JS / Flutter / Python / Go）的事件，每個平台的 timestamp 格式、精度和時鐘來源不同。Collector 需要對這些 timestamp 做排序、分組和時間範圍查詢，一致性問題會導致事件順序錯亂和分析結果偏差。&lt;/p>
&lt;h2 id="統一格式iso-8601--時區偏移">統一格式：ISO 8601 + 時區偏移&lt;/h2>
&lt;p>所有平台的 SDK 統一使用 ISO 8601 格式，包含毫秒精度和時區偏移：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">2026-06-19T14:30:00.123+08:00&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>避免使用 Unix timestamp（秒或毫秒）作為僅有的時間表示 — Unix timestamp 沒有時區資訊，如果 SDK 端和 collector 端在不同時區，需要額外的 metadata 才能正確轉換。&lt;/p>
&lt;p>避免使用「本地時間不帶時區」的格式（&lt;code>2026-06-19T14:30:00&lt;/code>）— 無法區分 UTC+8 的 14:30 和 UTC+0 的 14:30。&lt;/p>
&lt;h2 id="各平台的-timestamp-來源">各平台的 timestamp 來源&lt;/h2>
&lt;h3 id="javascript">JavaScript&lt;/h3>
&lt;p>&lt;code>Date.now()&lt;/code> 回傳毫秒精度的 Unix timestamp。&lt;code>new Date().toISOString()&lt;/code> 回傳 UTC 時間的 ISO 8601 字串。&lt;/p>
&lt;p>SDK 應該用 &lt;code>Intl.DateTimeFormat&lt;/code> 或手動計算時區偏移，產生帶本地時區的 ISO 8601 字串 — collector 端需要知道事件的本地時間，以便做使用者時區的分析。&lt;/p>
&lt;p>&lt;code>performance.now()&lt;/code> 提供微秒精度的高解析度時間，但起點是頁面載入時間，無法用來產生絕對 timestamp。用於計算 duration（兩個時間點的差值），不用於記錄事件時間。&lt;/p>
&lt;h3 id="flutter--dart">Flutter / Dart&lt;/h3>
&lt;p>&lt;code>DateTime.now()&lt;/code> 回傳本地時間的 DateTime 物件。&lt;code>DateTime.now().toUtc()&lt;/code> 轉成 UTC。&lt;code>DateTime.now().toIso8601String()&lt;/code> 產生 ISO 8601 字串，但不包含時區偏移（Dart 的 ISO 8601 格式不包含 offset）。&lt;/p>
&lt;p>SDK 需要手動附加時區偏移：&lt;code>DateTime.now().timeZoneOffset&lt;/code> 取得偏移量，手動格式化為 &lt;code>+08:00&lt;/code> 格式附加到 ISO 8601 字串後面。&lt;/p>
&lt;h3 id="python">Python&lt;/h3>
&lt;p>&lt;code>datetime.now(timezone.utc)&lt;/code> 取得 UTC 時間。&lt;code>datetime.now().astimezone()&lt;/code> 取得本地時間帶時區。&lt;code>.isoformat()&lt;/code> 產生帶時區偏移的 ISO 8601 字串。&lt;/p>
&lt;p>Python 3.2+ 的 &lt;code>datetime&lt;/code> 原生支援 timezone-aware 的 ISO 8601 輸出，是各平台中最完整的。&lt;/p>
&lt;h3 id="go">Go&lt;/h3>
&lt;p>&lt;code>time.Now()&lt;/code> 回傳帶時區的 Time 值。&lt;code>time.Now().Format(time.RFC3339Milli)&lt;/code> 產生帶毫秒和時區偏移的字串。&lt;/p>
&lt;p>Go 的 &lt;code>time.RFC3339Nano&lt;/code> 提供奈秒精度，但監控事件不需要這個精度 — 毫秒足夠。&lt;/p>
&lt;h2 id="clock-drift">Clock drift&lt;/h2>
&lt;p>不同裝置的系統時鐘可能有偏差（clock drift）。使用者手機的時鐘比 collector server 快 5 分鐘，SDK 產生的 timestamp 會比 collector 收到時間早 5 分鐘。&lt;/p>
&lt;p>Clock drift 的影響：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>排序錯亂&lt;/strong>：裝置 A（時鐘快）和裝置 B（時鐘慢）的事件混合排序時，時間順序可能和真實發生順序不一致&lt;/li>
&lt;li>&lt;strong>告警延遲計算錯誤&lt;/strong>：collector 用「事件 timestamp 到收到時間的差值」計算延遲，clock drift 讓延遲值不準確&lt;/li>
&lt;/ul>
&lt;p>處理策略：&lt;/p>
&lt;p>&lt;strong>Collector 記錄 receive_timestamp&lt;/strong>：每筆事件除了 SDK 端的 timestamp，collector 在收到時附加 &lt;code>receive_timestamp&lt;/code>。兩者的差值用於估算 clock drift 和網路延遲。&lt;/p>
&lt;p>&lt;strong>容忍而非修正&lt;/strong>：在數秒到數分鐘級的 drift 範圍內，容忍 drift 帶來的排序不精確。跨裝置的事件排序本身就不需要毫秒精度 — 分析的粒度通常是秒或分鐘。&lt;/p>
&lt;p>&lt;strong>異常值偵測&lt;/strong>：timestamp 比 receive_timestamp 早超過 1 小時，或晚超過 5 分鐘，標記為可疑的 clock drift — 可能是使用者手動調整了系統時鐘。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>JS 平台適配 → &lt;a href="https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/js-ts-platform/" data-link-title="JS/TS 平台適配" data-link-desc="CORS 限制、Service Worker 攔截、SPA 路由變換偵測 — 瀏覽器環境中 SDK 需要處理的平台特殊問題">JS/TS 平台適配&lt;/a>&lt;/li>
&lt;li>Flutter 平台適配 → &lt;a href="https://tarrragon.github.io/blog/monitoring/05-platform-adaptation/flutter-platform/" data-link-title="Flutter 平台適配" data-link-desc="Isolate 安全、Platform channel 攔截、app lifecycle 事件 — Flutter SDK 的平台特殊考量">Flutter 平台適配&lt;/a>&lt;/li>
&lt;li>Log schema 中的 timestamp 欄位 → &lt;a href="https://tarrragon.github.io/blog/monitoring/02-log-schema/event-schema-fields/" data-link-title="event.schema.json 完整欄位解說" data-link-desc="監控事件的 JSON Schema 定義 — 每個欄位的語意、必填/選填、資料型別和設計理由">模組二 event.schema.json 欄位解說&lt;/a>&lt;/li>
&lt;li>各平台的 error 攔截差異影響 test 設計 → &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/" data-link-title="模組五：測試設計判斷" data-link-desc="Mock 邊界判斷、assertion 設計、test data 代表性、flaky test 診斷">testing 模組五 測試設計判斷&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>跨平台的監控系統收到來自不同平台（JS / Flutter / Python / Go）的事件，每個平台的 timestamp 格式、精度和時鐘來源不同。Collector 需要對這些 timestamp 做排序、分組和時間範圍查詢，一致性問題會導致事件順序錯亂和分析結果偏差。</p>
<h2 id="統一格式iso-8601--時區偏移">統一格式：ISO 8601 + 時區偏移</h2>
<p>所有平台的 SDK 統一使用 ISO 8601 格式，包含毫秒精度和時區偏移：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">2026-06-19T14:30:00.123+08:00</span></span></code></pre></div><p>避免使用 Unix timestamp（秒或毫秒）作為僅有的時間表示 — Unix timestamp 沒有時區資訊，如果 SDK 端和 collector 端在不同時區，需要額外的 metadata 才能正確轉換。</p>
<p>避免使用「本地時間不帶時區」的格式（<code>2026-06-19T14:30:00</code>）— 無法區分 UTC+8 的 14:30 和 UTC+0 的 14:30。</p>
<h2 id="各平台的-timestamp-來源">各平台的 timestamp 來源</h2>
<h3 id="javascript">JavaScript</h3>
<p><code>Date.now()</code> 回傳毫秒精度的 Unix timestamp。<code>new Date().toISOString()</code> 回傳 UTC 時間的 ISO 8601 字串。</p>
<p>SDK 應該用 <code>Intl.DateTimeFormat</code> 或手動計算時區偏移，產生帶本地時區的 ISO 8601 字串 — collector 端需要知道事件的本地時間，以便做使用者時區的分析。</p>
<p><code>performance.now()</code> 提供微秒精度的高解析度時間，但起點是頁面載入時間，無法用來產生絕對 timestamp。用於計算 duration（兩個時間點的差值），不用於記錄事件時間。</p>
<h3 id="flutter--dart">Flutter / Dart</h3>
<p><code>DateTime.now()</code> 回傳本地時間的 DateTime 物件。<code>DateTime.now().toUtc()</code> 轉成 UTC。<code>DateTime.now().toIso8601String()</code> 產生 ISO 8601 字串，但不包含時區偏移（Dart 的 ISO 8601 格式不包含 offset）。</p>
<p>SDK 需要手動附加時區偏移：<code>DateTime.now().timeZoneOffset</code> 取得偏移量，手動格式化為 <code>+08:00</code> 格式附加到 ISO 8601 字串後面。</p>
<h3 id="python">Python</h3>
<p><code>datetime.now(timezone.utc)</code> 取得 UTC 時間。<code>datetime.now().astimezone()</code> 取得本地時間帶時區。<code>.isoformat()</code> 產生帶時區偏移的 ISO 8601 字串。</p>
<p>Python 3.2+ 的 <code>datetime</code> 原生支援 timezone-aware 的 ISO 8601 輸出，是各平台中最完整的。</p>
<h3 id="go">Go</h3>
<p><code>time.Now()</code> 回傳帶時區的 Time 值。<code>time.Now().Format(time.RFC3339Milli)</code> 產生帶毫秒和時區偏移的字串。</p>
<p>Go 的 <code>time.RFC3339Nano</code> 提供奈秒精度，但監控事件不需要這個精度 — 毫秒足夠。</p>
<h2 id="clock-drift">Clock drift</h2>
<p>不同裝置的系統時鐘可能有偏差（clock drift）。使用者手機的時鐘比 collector server 快 5 分鐘，SDK 產生的 timestamp 會比 collector 收到時間早 5 分鐘。</p>
<p>Clock drift 的影響：</p>
<ul>
<li><strong>排序錯亂</strong>：裝置 A（時鐘快）和裝置 B（時鐘慢）的事件混合排序時，時間順序可能和真實發生順序不一致</li>
<li><strong>告警延遲計算錯誤</strong>：collector 用「事件 timestamp 到收到時間的差值」計算延遲，clock drift 讓延遲值不準確</li>
</ul>
<p>處理策略：</p>
<p><strong>Collector 記錄 receive_timestamp</strong>：每筆事件除了 SDK 端的 timestamp，collector 在收到時附加 <code>receive_timestamp</code>。兩者的差值用於估算 clock drift 和網路延遲。</p>
<p><strong>容忍而非修正</strong>：在數秒到數分鐘級的 drift 範圍內，容忍 drift 帶來的排序不精確。跨裝置的事件排序本身就不需要毫秒精度 — 分析的粒度通常是秒或分鐘。</p>
<p><strong>異常值偵測</strong>：timestamp 比 receive_timestamp 早超過 1 小時，或晚超過 5 分鐘，標記為可疑的 clock drift — 可能是使用者手動調整了系統時鐘。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>JS 平台適配 → <a href="/blog/monitoring/05-platform-adaptation/js-ts-platform/" data-link-title="JS/TS 平台適配" data-link-desc="CORS 限制、Service Worker 攔截、SPA 路由變換偵測 — 瀏覽器環境中 SDK 需要處理的平台特殊問題">JS/TS 平台適配</a></li>
<li>Flutter 平台適配 → <a href="/blog/monitoring/05-platform-adaptation/flutter-platform/" data-link-title="Flutter 平台適配" data-link-desc="Isolate 安全、Platform channel 攔截、app lifecycle 事件 — Flutter SDK 的平台特殊考量">Flutter 平台適配</a></li>
<li>Log schema 中的 timestamp 欄位 → <a href="/blog/monitoring/02-log-schema/event-schema-fields/" data-link-title="event.schema.json 完整欄位解說" data-link-desc="監控事件的 JSON Schema 定義 — 每個欄位的語意、必填/選填、資料型別和設計理由">模組二 event.schema.json 欄位解說</a></li>
<li>各平台的 error 攔截差異影響 test 設計 → <a href="/blog/testing/05-test-design-judgment/" data-link-title="模組五：測試設計判斷" data-link-desc="Mock 邊界判斷、assertion 設計、test data 代表性、flaky test 診斷">testing 模組五 測試設計判斷</a></li>
</ul>
]]></content:encoded></item></channel></rss>