ULID の生成とデコード | 時系列ソート可能な ID を一画面で
Crockford Base32 形式の 26 文字 ULID をワンクリックで発行、または貼り付けて発行時刻を逆算するツール。先頭 10 文字に 48bit のミリ秒タイムスタンプ、末尾 16 文字に 80bit のランダム値が格納され、デコード結果欄にミリ秒値・ISO 8601 UTC 日時・ランダム部の Hex・文字列の妥当性を一覧表示。
💡 このツールについて
ULID は 26 文字の無機質な文字列に見えますが、前半は実は読み取れるタイムスタンプです。問題は、そのタイムスタンプが Hex でも 10 進でもなく Crockford Base32 で書かれている点。01ARZ3NDEKTSV4RRFFQ69G5FAV を眺めても、いつ作られたのかは分かりません。手作業でのデコードは、先頭 10 文字を 1 つずつ 5bit 値へ写し、32 進で累積し、結果を日時へ変換する作業になります。
このツールはその往復を双方向で行います。貼り付けると文字数を検証し、Crockford アルファベット(誤読防止のため I・L・O・U を除外)以外の文字を弾き、タイムスタンプ部とランダム部を分離して、発行時刻を ISO 8601 文字列で描画。セグメント表示では時刻側とランダム側を別色で塗り分けるため、構造が一目で把握できます。ライブラリの 1 行呼び出しでは得られない可視化です。
🧐 よくある質問
UUID v4 と何が違うのか UUID v4 は 122bit の純粋な乱数で、固有の順序を持ちません。ULID は先頭に 48bit のミリ秒タイムスタンプを置くため、異なる時刻に発行した 2 つの ULID は単純な文字列比較で時系列順に並びます。UUID v7 が後年同様のタイムスタンプ接頭を導入しましたが、ULID は 2016 年の仕様策定時から備えています。
なぜ辞書順が時系列順と一致するのか 最上位ビットが先頭に来るためです。タイムスタンプが先頭 10 文字を占めるので、ULID 文字列をアルファベット順に並べると古いものが自然に先頭へ来ます。そのためデータベースの主キーに向き、順序通りの挿入で B-tree インデックスが密に保たれます。
Crockford Base32 とは何か I・L・O・U を除いた 32 進アルファベットです。I・L は 1 との、O は 0 との混同を避け、U は意図しない不適切語の生成を避ける目的で除外されています。残るのは 0-9 と大文字 22 字の計 32 記号で、各記号が 5bit を表します。
ULID が衝突する可能性は
同一ミリ秒内では 80bit のランダム部に依存し、その組み合わせは約 1.2 × 10^24 通り。現実的な衝突は天文学的に起こりにくい数字です。本ツールの乱数はブラウザの crypto.getRandomValues を用いており、脆弱な疑似乱数ではありません。
大文字・小文字を区別するか 区別しません。Crockford のデコードは大文字小文字を問わないため、入力は検証前に大文字へ正規化されます。小文字の ULID を貼り付けても正しくデコードされます。
📚 豆知識:26 文字目の理由
ULID は UUID v4 がソート済みデータベースに不向きという問題への対応として、2016 年に Alizain Feerasta が公開した形式です。知っておくと面白い意図的な仕様がいくつかあります。タイムスタンプは 48bit で、西暦 10889 年までの日付を表現できてからオーバーフローするため、実務で困ることはありません。エンコードは値をちょうど 26 字の Crockford 文字に整えます。48bit のタイムスタンプと 80bit のランダムで計 128bit、UUID と同じ幅になるのは偶然ではなく、同じ 128bit の格納列にそのまま収めるための設計。128bit を 5 で割れば 25 字になりそうなところを 26 字に着地するのは、先頭文字が 5bit のうち 3bit しか使わないため。有効な ULID の先頭文字が 7 を超えないのも同じ理由。