Skip to main content

How to Decode a Snowflake ID: Pulling Time, Worker, and Sequence Out of 64 Bits

A practical walkthrough of the Snowflake ID bit layout, how to extract the creation timestamp from a Twitter or Discord ID, and why these IDs sort by time.

Published By Li Lei
#snowflake-id #distributed-systems #discord #timestamps

How to Decode a Snowflake ID: Pulling Time, Worker, and Sequence Out of 64 Bits

The first time someone handed me a Discord user ID and asked "when was this account made?", I assumed I'd need the API, a bot token, and a coffee. I didn't. The answer was already inside the number. A Snowflake ID isn't a random opaque key — it's a tightly packed 64-bit record where the high bits hold a millisecond timestamp. Shift those bits down, add the platform's epoch back, and you have the exact moment that ID was minted. No network call required.

This post breaks down what's actually inside a Snowflake, how to extract the creation time by hand, why the same number reads differently on Twitter and Discord, and why these IDs sort themselves in roughly chronological order.

What a 64-bit Snowflake actually packs

A Snowflake is one 64-bit integer, and every bit has a job. In the classic Twitter layout it splits like this, from the highest bit down:

  • 1 bit — unused sign bit, always 0 (so the number stays positive)
  • 41 bits — millisecond timestamp, counted from a custom epoch
  • 5 bits — datacenter id
  • 5 bits — worker id (those two together form a 10-bit machine region)
  • 12 bits — a per-millisecond sequence counter

Add that up: 1 + 41 + 5 + 5 + 12 = 64. The timestamp sits at the top, which is the single most important design decision in the whole scheme. Because the most significant non-sign bits are the time, a larger number almost always means a later creation moment. The machine and sequence bits live in the low end where they barely move the magnitude.

The 41-bit timestamp gives you about 69 years of millisecond resolution before it overflows — counted not from 1970 but from whenever the platform decided to start. The 12-bit sequence lets a single worker mint up to 4,096 IDs in the same millisecond without collision; the 10-bit machine field allows 1,024 distinct nodes. That's how a distributed system generates unique, ordered keys with no central coordinator handing out numbers.

Why the timestamp lives in the high bits

The reason the time goes up top is sortability, and it falls straight out of how binary magnitude works. Consider two IDs minted one second apart on the same worker. The timestamp portion differs by 1,000 (one thousand milliseconds), and that difference lives in bits 22 through 62 — bits with enormous positional weight. The machine and sequence fields below can only ever contribute a small fixed range. So the later ID is reliably the larger integer.

That property is gold for databases. Sort a table by its Snowflake primary key and you get rows in roughly creation order for free, no separate created_at index needed. The b-tree that indexes the key is already chronological, so recent rows cluster together and inserts append to the right edge instead of scattering. This is the same idea behind UUIDv7 and ULID — put the time first, and ordering comes along as a bonus.

I say "roughly" deliberately. Two IDs from different workers in the same millisecond can land out of order relative to each other, because their machine ids differ in bits that outrank the sequence. So Snowflake ordering is precise to the millisecond and approximate within it. That distinction matters when you're chasing a bug.

Extracting the creation time by shifting bits out

Here's the mechanical recipe to pull the timestamp out of a Snowflake by hand:

  1. Take the 64-bit value as an integer.
  2. Shift right by 22 bits (5 + 5 + 12 = 22 low bits dropped). What remains is the raw timestamp.
  3. Add the platform epoch (in milliseconds) to that raw timestamp.
  4. The sum is a normal Unix millisecond timestamp — feed it to any date formatter.

To recover the machine id and sequence, mask instead of shift: the sequence is the bottom 12 bits (value & 0xFFF), and the machine region is the next 10 bits up.

One hard rule: do this with BigInt, not parseInt or Number. A 64-bit Snowflake can reach about 1.8 × 10¹⁹, and JavaScript's safe-integer ceiling is 2⁵³ (around 9 × 10¹⁵). Parse a Snowflake as a plain Number and the low digits get silently rounded — your timestamp might survive, but the machine id and sequence come out garbage. BigInt keeps every bit exact through the shifts and masks. The Snowflake ID Decoder does all of this on BigInt and only narrows the final ~13-digit millisecond value, which is safely inside Number range, before formatting it as a date.

A worked example, decoded end to end

Take the ID 1541815603606036480 under the Twitter epoch.

Shift it right by 22 bits to drop the machine and sequence fields. That leaves the raw 41-bit timestamp. Now add Twitter's epoch, 1288834974657 ms (which is 2010-11-04). The resulting Unix millisecond value formats to:

2022-06-28 16:07:40 UTC — machine 378, sequence 0.

Now try a Discord ID: 175928847299117063 with Discord's epoch of 1420070400000 ms (2015-01-01):

2016-04-30 11:18:25 UTC — machine 32, sequence 7.

That second result is exactly the kind of thing a moderator needs. Copy a suspicious account's Discord ID (turn on Developer Mode, right-click, Copy ID), decode it, and the UTC time is the literal account creation moment. A snowflake that reads "three minutes ago" is a fresh alt; one from 2016 is almost certainly a real, aged account.

The epoch trap: same number, two different times

This is the mistake I see most. The same 64-bit number decodes to two completely different wall-clock times depending on which epoch you apply, because the embedded timestamp counts from the platform's own start point, not from 1970.

Twitter counts from 1288834974657 ms (November 2010). Discord counts from 1420070400000 ms (January 2015). Decode a Discord ID with the Twitter epoch and the date lands years off — it parses without error, which is exactly why the bug is sneaky. There's no crash to tip you off; the number just lies quietly.

So always match the epoch preset to where the ID came from: Twitter for tweet and account IDs, Discord for anything off discord.com. And if you're reverse-engineering an unfamiliar platform, try both presets first — if the decoded date lands far in the future or before the company existed, you've got the wrong epoch or non-standard bit widths and need to adjust the custom settings until the time looks sane. If you'd rather work directly with raw Unix milliseconds, the Unix timestamp converter handles the date math once you've stripped the epoch back out.

Snowflakes versus UUIDs in one line

A UUIDv4 is 128 random bits with no embedded time — great for fully decentralized minting with zero coordination, but it sorts like noise. A Snowflake is half the size, carries a real timestamp up front, and sorts by creation time, which keeps your indexes happy. Pick UUIDs when you need uncoordinated random ids; pick Snowflakes when you want compact, ordered, 64-bit keys. UUIDv7 and ULID are the modern compromise: random tail, time-sortable head.

Decoding a Snowflake is really just reading a record someone packed into a single integer. Once you know the bit layout, the creation time is never more than a right-shift and an epoch addition away.


Made by Toolora · Updated 2026-06-13