工程趣聞 2026 年 9 月 22 日

2026-09-22 — FILETIME 到底能撐到哪一年才不出包?

primary=https://devblogs.microsoft.com/oldnewthing/20260921-00/?p=112711/

FILETIME 到底能撐到哪一年才不出包?

Raymond Chen・The Old New Thing・2026-09-21

FILETIME 型別的合法上界是0x7FFFFFFF`FFFFFFFF,一旦超過這個值,CreateWaitableTimer 之類的函式就會把最高位當成負號,把它解讀成「負的相對時長」而不是絕對時間點,甚至有程式會把它當成早於西元 1601 年 1 月 1 日的時間。Raymond Chen 在《Old New Thing》這篇文章裡,拆解了 FILETIME 邊界值背後的來龍去脈。

背景

這篇文章的起點,是一位工程師想定義一個哨兵用FILETIME,讓它拿去跟任何其他 FILETIME 比較時,透過 CompareFileTime 永遠顯示自己比較晚。最直覺的作法,是挑 FILETIME 型別能表示的最大位元組合來當這個哨兵值。但 Raymond Chen 指出,這個直覺一路會撞上好幾層陷阱,牽扯到 SetFileTimeCreateWaitableTimerSYSTEMTIME,甚至 CLR 的 System.DateTime 與特定行事曆型別的年份上限。換句話說,一個看似單純的「挑最大值」決定,同時牽動了作業系統 API、CLR 執行環境,以及行事曆資料表三個不同層級的邊界。

規格細節

FILETIME 本質上是從西元 1601 年 1 月 1 日起算的無號 tick 數,照定義推算,理論最大值應該是 0xFFFFFFFF`FFFFFFFF。但這個數值早被保留做特殊用途:SetFileTime 把它當成「不要更新這個 handle 的時間」的訊號,並非真正的時間戳。工程師常見的誤解,就是把「型別能表示的最大位元組合」誤當成「可以安全使用的最大時間值」,這兩者其實差了關鍵的一個位元。這也是為什麼只看型別定義就假設某個極端數值可以直接拿來用,是相當危險的做法。

真正安全的上限,是最高位保持 0 的 0x7FFFFFFF`FFFFFFFF,再往上至少會踩到兩個問題。CreateWaitableTimer 會把最高位當成正負號解讀;FileTimeToSystemTime 則直接拒絕轉換超過這個值的 FILETIME

影響範圍

下表整理了 FILETIME 上界,以及與其他 Windows、CLR 時間型別上限的對照關係,換算時可以直接查表。

數值 / 型別意義
0xFFFFFFFF`FFFFFFFF64 位元無號整數最大值,被 SetFileTime 保留為特殊訊號,非合法時間戳
0x7FFFFFFF`FFFFFFFFFILETIME 建議的安全上界,最高位為 0
西元 30828 年FileTimeToSystemTime 能把安全上界轉成的最大 SYSTEMTIME 日期
西元 30827 年底SystemTimeToFileTime 支援轉換的最後日期,比前者少一年
西元 9999 年CLR 的 System.DateTime 上限
西元 2101 年初ChineseLunisolarCalendar 因對照表長度而定的上限

FileTimeToSystemTime 願意把 0x7FFFFFFF`FFFFFFFF 轉成西元 30828 年的 SYSTEMTIME,但反過來呼叫 SystemTimeToFileTime 卻會失敗,因為它只支援到西元 30827 年年底為止。就算硬是拿 30827 年最後一毫秒去轉,實際數值還會因為閏秒資料庫累積了多少筆閏秒而有所不同。

這代表任何自行往返轉換 FILETIMESYSTEMTIME 的程式碼,都不能假設「能轉出去就一定轉得回來」。對負責 Windows 時間戳序列化、跨語言時間轉換的工程師來說,這篇文章點出真正該檢查的邊界:不要拿 0xFFFFFFFF`FFFFFFFF 或任何逼近上限的數值當哨兵值,一旦這種值不小心流出你的程式碼,時區調整、「一天後」這類加法運算、或轉成 C# 的 System.DateTime 都可能溢位。

回到文章開頭那個哨兵值的問題,如果只是需要一個保證比任何值都晚、拿去跟 CompareFileTime 比較用的數字,0xFFFFFFFF`FFFFFFFF 的確是 CompareFileTime 支援的最大值,但前提是你絕不能讓這個值逃出你自己的程式碼。Raymond Chen 建議改用型別系統本身的手段,例如 C++ 的 std::optional<FILETIME> 或 C# 的 Nullable<DateTime> 來表示「沒有時間值」,而不是把極端數字塞進 FILETIME 欄位本身。這樣每個消費端都能依照自己語言的慣例去處理空值,不必共享同一組容易被誤用的魔術數字。換句話說,讓特殊數字留在 FILETIME 型別內部本身,才是問題的根源,而不是選錯了哪一個具體數值。

原始來源:The Old New Thing - What's the highest legal FILETIME? Is it safe to use?


End of article
0
Would love your thoughts, please comment.x
()
x