一個規範的 ULID 由 26 個 Crockford Base32 字元組成,其中前 10 個字元編碼 Unix 毫秒數,最後 16 個字元編碼 80 位元的隨機數,因此任何持有該字串的人都能讀取其中嵌入的毫秒時間戳,無需網路請求或伺服器端依賴。這使得「解碼 ULID 時間戳的 API 替代方案」這個問題變得實用:只需要從既有識別碼擷取建立時間的開發人員,可以跳過 HTTP 來回,在瀏覽器中直接執行轉換。ULID Generator 工具將兩個方向整合在同一個地方——從加密隨機數產生單調遞增的 ULID 批次,以及將任何規範的 26 字元字串解碼回其毫秒數與 ISO UTC 瞬間。由於產生、解碼、格式化與複製皆完全在瀏覽器中執行,不會上傳任何識別碼或時間戳,即使在離線機器上,頁面依然可用。本機路徑並非執行階段中正式函式庫的替代品,但卻是以無需啟動端點的方式滿足單次解碼需求最乾淨的做法。

decode ulid timestamp api alternative
無需 API 呼叫即可解碼 ULID 時間戳

瀏覽器工具何時能取代解碼端點

大多數公開的 ULID 函式庫會公開一個 decodeTime 函式,或一個小型 REST 端點,回傳嵌入的 Unix 毫秒數與 ISO 瞬間。對於已在 Node、Deno 或 Bun 中執行的正式程式碼,從 JavaScript 參考實作等套件呼叫 decodeTime,或閱讀規範 ULID 規格中的規則,才是正確的路徑。僅限瀏覽器的路徑在三種情況下有意義:支援工程師從記錄檔讀取客戶提供的 ULID、開發人員對已產生的 ID 進行離線審查,或審查人員稽核那些絕不應離開機器的 ID。在這三種情況下,目標都相同——將 26 個字元轉為可讀的時間戳,而無需發送 HTTP 請求或付出遠端查詢的延遲成本。

ULID Generator 在單一頁面中處理兩個方向:Generate 從加密隨機數建立單調遞增的批次,Decode 則從任何規範字串讀取時間戳與 ISO UTC 瞬間。由於每一步皆在本機執行,頁面永遠不會收到你的識別碼副本,這在你審查連結到私密資料的記錄時,是一項有意義的改善。該頁面也維持其初次渲染的確定性——Date.now 僅在 Generate 點擊處理程序內讀取,而非在伺服器渲染期間——因此重新整理分頁並不會將時鐘值洩漏到靜態版面中。

26 字元字串的結構解析

規範 ULID 固定為 26 個字元,取自 Crockford Base32 字集,該字集排除 I、L、O 與 U,以減少視覺上的混淆。分割正好是 10 加 16:前 10 個字元編碼 48 位元的 Unix 毫秒時間,最後 16 個字元編碼 80 位元的隨機數。字集由規格固定,任何在字集外的字元即使長度正確,字串也會被視為無效。

位置字元數欄位範圍 / 大小備註
1–1010Unix 毫秒數0 至 281,474,976,710,65548 位元無符號;因此最大規範字串以 7 開頭
11–2616加密隨機數每個 ID 80 位元,在批次內遞增溢位時拒絕而非環繞
字集Crockford Base3232 個符號,省略 I、L、O、U解碼不區分大小寫,但輸出為規範大寫

48 位元時間欄位正是讓識別碼能以 ASCII 字典序排序的原因——對於相同長度的字串,左側較大的整數也對應較大的字元序列。此特性正是多數團隊在有序主鍵、記錄索引與事件時間關聯上選擇 ULID 而非 UUID v4 的原因。同樣的特性,也使得解碼作業能僅憑字串本身回傳有意義的時間戳,而無需任何帶外詮釋資料。

在本機解碼 ULID 時間戳

透過 Decode 面板處理既有識別碼,是規範的讀取工作流程。該工具在接受 Crockford 正規化後,可處理任何 26 字元的規範字串,並同時回傳原始毫秒數與等價的 ISO UTC 瞬間。

  1. 開啟 ULID Generator 頁面,將模式切換至 Decode。
  2. 將規範的 26 字元 ULID 貼入輸入欄位;字集會在大小寫正規化後進行檢查。
  3. 讀取毫秒值,以及以 ISO 8601 格式呈現的對應 UTC 時間戳。
  4. 若你需要將該值作為 SQL 友善的 Unix 秒數,或希望與另一個時間戳進行比較,請將毫秒數交給 Unix 時間戳 SQL 指南,以取得對應方言中的精確轉換規則。

值得事先了解拒絕的情況。以 8 或以上開頭的 26 字元值包含超過 128 位元,因此會被拒絕,因為規格僅保留總共 128 位元。含有 Crockford Base32 以外字母的字串同樣會失敗——例如小寫 u 並不屬於該字集,也不會被正規化為 v。工具保持嚴格的驗證,以確保讀回的內容永遠與編碼內容一致。顯示的 ISO 時間以 UTC 呈現,而同一毫秒數的本地日曆檢視,在你的時區中可能落在不同的日期。

在本機產生單調遞增批次

產生的合約比解碼更為嚴格。每次點擊會擷取一個 Date.now 值,將其編碼進 10 字元的時間欄位,向瀏覽器加密隨機來源索取 80 位元,然後對同一批次中每個後續 ID 的隨機區域進行加一遞增——包括跨越整個 16 字元隨機後綴的進位傳遞。

  1. 開啟 ULID Generator,並將模式保留在 Generate。
  2. 選擇介於 1 至 100 之間的數量。超出此範圍的值將不被接受。
  3. 點擊 Generate 一次。整個批次共用擷取到的毫秒數,每個 ID 在 ASCII 順序上嚴格大於前一個。
  4. 複製完整的 26 字元識別碼,並將其儲存於保留大小寫的欄位中。為該欄位加上唯一性約束,讓資料庫而非工具成為唯一性的最終裁決者。

若批次內的 80 位元隨機欄位即將溢位,則產生作業會失敗而非環繞——這是有意為之的選擇,以確保回傳的字串嚴格遞增。該工具同樣拒絕與其他分頁、裝置、服務或先前產生的值進行協調,因此請勿依賴它來解決分散式系統間的唯一性問題。請將點擊視為本機熵加上擷取毫秒數的來源,僅此而已。

重要的限制與儲存規則

該工具在規格的 128 位元總預算上劃下明確界線。因此 48 位元時間戳可表示 0 至 281,474,976,710,655 毫秒,而最大規範字串以 7 開頭。任何需要 8 或更高前導值的內容,都包含規格外的位元模式,在 Generate 與 Decode 路徑上都會被拒絕。字典序排序僅在規範的 26 字元表示下,於一般的位元組或 ASCII 相容的定序下成立——大小寫摺疊、地區設定定序、截斷、填補,或儲存於過小的欄位中,皆會破壞預期的順序,因此在將 ULID 提升為主鍵之前,務必測試確切的資料庫定序。

可見的時間戳與隨機後綴,無法在缺乏存取控制時保護記錄不被列舉或揭露。ULID 並非密碼、API 金鑰、持有者權杖、授權規則或加密金鑰,而左側時間欄位刻意公開近似的建立時間。當 ULID 連結到個人或機密記錄時,應避免將其寫入記錄檔;任何需要保密的內容,請使用平台專屬的憑證系統。較短的 ULID 欄位或不區分大小寫的定序,將在索引填滿時悄悄地破壞順序。

瀏覽器工具與託管解碼端點的比較

考量本機瀏覽器工具託管解碼端點
網路來回無——在同分頁中執行每次解碼皆需 HTTP 請求
識別碼是否離開機器不會上傳任何識別碼或時間戳視提供者政策而定
離線可用性頁面載入後即可使用需要連線
驗證嚴格度強制執行 128 位元上限與 Crockford 字集視實作而異
批次吞吐量Generate 每次點擊最多產生 100 個 ID受主機速率限制
跨系統協調無法與其他分頁或服務協調可在呼叫者之間共用狀態

本機工作流程在隱私與離線使用方面勝出,且由於 Date.now 僅在 Generate 點擊處理程序內讀取,而非在伺服器渲染期間,因此保持確定性。託管端點在許多服務需要協調唯一性時仍然勝出,因此對於正式後端,請在執行階段中保留真正的 ULID 函式庫,並在需要快速、離線讀取時使用瀏覽器工具。八個外部固件鎖定了邊界值——零、Base32 邊緣、一秒、參考 decodeTime 範例,以及最大時間戳——因此你在此讀到的常數,與規格強制執行的常數一致。

出貨前的實用檢查清單

三項規則可消除人們從 UUID 轉換至 ULID 時遇到的大部分摩擦。首先,保留完整的大寫字串,並將其儲存於保留大小寫的 26 字元欄位中——絕不截斷為較少字元,也絕不因為認為前導字元可預測而將其移除。其次,強制執行資料庫唯一性約束,並以原子方式處理衝突;隨機性會降低碰撞機率,但永遠無法取代唯一性保證,而檢查後插入的競爭條件永遠不應作為最終控制手段。第三,將解碼後的時間視為詮釋資料而非已驗證的真相——它對於有序索引、記錄關聯與粗略的建立時間估計很有用,但它無法證明特定行為者在該瞬間建立了該記錄。

當問題是「解碼 ULID 時間戳的 API 替代方案」時,誠實的回答是:替代方案並非不同的規格——而是同樣的 Crockford Base32 編碼,只是改為在你的瀏覽器中讀取,而非透過 HTTP。這個單一的轉變,足以在不為技術堆疊新增外部依賴的情況下,涵蓋支援審查、離線稽核與單次查詢。對於高流量的程式碼路徑,請回到符合你執行階段的函式庫;對於偶爾的解碼,請維持在本機,並讓識別碼留在你的機器上。

如需更深入的了解,請參閱 在 JavaScript 中將 Unix 時間戳轉換為日期