ULID Decoder Guide: How to Decode a ULID and Read Its Timestamp
Decode a ULID by hand or with a ULID decoder: the 26-char Crockford Base32 layout, extracting the creation time from the first 10 chars, and why ULIDs sort by time.
How to Decode a ULID and Read Its Timestamp
The first time someone hands you a ULID like 01ARYZ6S41TSV4RRFFQ69G5FAV, it looks like the same wall of random characters as a UUID. It is not. Half of that string is a clock reading. A ULID quietly carries the exact millisecond it was created, and once you know where that part lives, you can pull the creation time straight out of the ID without touching a database or a created_at column. This post walks through the layout, decodes a real one by hand, and explains why these IDs happen to sort themselves into chronological order.
What a ULID Actually Is
A ULID is a 26-character string written in Crockford Base32, and it splits into two fixed pieces, most-significant first. The first 10 characters encode a 48-bit millisecond timestamp. The last 16 characters are 80 bits of pure randomness. That is the whole structure — 48 bits of time plus 80 bits of entropy makes 128 bits total, the same payload size as a UUID, just packed into a shorter, dash-free string.
That fixed 10 + 16 split is the key fact that makes everything else work. Because the split never moves, decoding is unambiguous: you always read the timestamp from the front and the randomness from the back. There is no header, no version flag, no separator to parse. You slice at character 10 and you are done.
Crockford Base32 stores 5 bits per character. Ten characters therefore hold 50 bits, but only the high 48 carry the time — the leading character contributes just 3 usable bits, which is why a valid ULID never starts with a symbol above 7. The trailing 16 characters hold the full 80 bits of randomness. If you ever see a ULID whose first character is 8 or higher, it is malformed: the timestamp would overflow its 48-bit field.
The Crockford Base32 Alphabet
The alphabet matters because it is not ordinary Base32. Crockford Base32 deliberately drops the four characters humans most often confuse when reading IDs aloud or copying them by hand: I, L, O, and U. With I and L gone there is no mixing them up with 1; with O gone there is no confusing it with 0; and U is dropped so the encoding can't accidentally spell unfortunate words. The 32 valid symbols are the digits 0 through 9 followed by the letters A through Z minus those four.
A correct decoder treats this as a hard rule. Paste in a string containing I, L, O, or U and a real ULID decoder rejects it as invalid rather than guessing at what you meant. That validation is not pedantry — a regex that allows the full A-Z alphabet will happily accept a malformed ID and silently produce a nonsense timestamp.
Decoding a ULID by Hand
Let's decode 01ARYZ6S41TSV4RRFFQ69G5FAV end to end.
First, split it at the 10-character mark:
- Timestamp part:
01ARYZ6S41 - Random part:
TSV4RRFFQ69G5FAV
Now read the timestamp part as a single big-endian Crockford Base32 number. Each character maps to a value 0–31 (0=0, 1=1, … A=10, R=24, and so on), and you accumulate them positionally just like reading a decimal number, except the base is 32. Run that arithmetic across all ten characters and you get 1469918176385.
That number is milliseconds since the Unix epoch. Convert it to a date and it reads:
1469918176385 ms
= 2016-07-30T22:36:16.385Z (ISO 8601, UTC)
= Sat, 30 Jul 2016 22:36:16.385 UTC
The random tail TSV4RRFFQ69G5FAV carries no time at all — it is the 80 bits that make collisions effectively impossible, and there is nothing meaningful to "decode" out of it. So the full result of decoding this ULID is: created on 30 July 2016 at 22:36:16.385 UTC, with a random component of TSV4RRFFQ69G5FAV.
If you would rather not do base-32 arithmetic in your head, the ULID Decoder does exactly this: it extracts the 48-bit time, renders it as a Unix-ms number, an ISO 8601 string, a readable UTC time and your local time, and shows the random tail on its own. It also validates the alphabet and the length, so a mistyped ID is caught instead of decoded into a wrong date.
Why ULIDs Sort by Time
Here is the property that makes ULIDs genuinely useful in a database. Because the timestamp lives in the leading characters and is stored most-significant first, a plain lexicographic sort of ULIDs — comparing them as ordinary strings, byte by byte from the left — is also a sort by creation time. Two ULIDs minted a second apart will always compare in the order they were created, because the differing characters appear in the timestamp region near the front.
This is the part I lean on most in real work. I once had a results page where rows were coming back in a suspicious order, and ORDER BY id was the only sort in the query. Rather than dig through the application stack, I decoded the two suspect ULIDs and compared their millisecond timestamps directly. They were correct — the IDs really were in creation order, and the bug was further up. Being able to settle "which of these two was created first" from the IDs alone, with no clock skew and no extra column to trust, turned an hour of guessing into a two-minute check.
Contrast this with a UUIDv4, which is fully random. There is no time inside it, no order to recover, and nothing to decode. Both are 128-bit identifiers, but only the ULID lets you answer when and in what order. If you are choosing between the two, or generating fresh IDs, the ULID Generator produces monotonic ULIDs that preserve this sort guarantee even within the same millisecond.
What Decoding Does and Doesn't Reveal
One caution worth keeping in mind: the timestamp half of a ULID is fully readable by anyone who holds the ID. If a ULID appears in a public URL, you have effectively published the exact millisecond that record was created, and a run of adjacent ULIDs can hint at how fast your system is minting records. The 80 random bits stay unguessable, so the ID itself remains unpredictable — but treat the embedded time as public whenever the ID is public.
That single fact, that the time is baked into the front of the ID, is the whole reason ULIDs are worth decoding. It is also the reason they sort the way they do. Master the 10 + 16 split, remember the four missing letters in the alphabet, and you can read any ULID's creation time on sight.
Made by Toolora · Updated 2026-06-13