Variant 0: time-sortable
View this section in the spec ↗Like UUIDv7: a millisecond timestamp, then random bits. Sorts by creation time within a name, as u128, UUID form, or TNID string.
Layout
1111.1111.1111.1111.1111.2222.2222.2222-
2222.2222.2222.2222-
3333.2222.2222.2222-
4455.2226.6666.6666-
6666.6666.6666.6666.6666.6666.6666.6666.6666.6666.6666.6666 0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| name | unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| tv| ms | random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| # | Field | Bits | MSB = 0 | Value |
|---|---|---|---|---|
| 1 | Name | 20 | 0–19 | Name encoding |
| 2 | Milliseconds since the Unix epoch | 43 = 28 + 12 + 3 | 20–47, 52–63, 68–70 | unix_ms mod 2^43 |
| 3 | UUID version | 4 | 48–51 | 0x8 |
| 4 | UUID variant | 2 | 64–65 | MUST be 0b10 |
| 5 | TNID variant | 2 | 66–67 | MUST be 0b00 |
| 6 | Random | 57 | 71–127 |
As a 100-bit payload, split 28 + 12 + 60 across the payload runs:
payload = (unix_ms mod 2^43) << 57 | random_57Worked example
user.Br2flcNDfF6LYICnT = d6157337-0ebc-8686-83ab-4075a34cdcde:
name 11010110000101010111
timestamp, upper 0011001101110000111010111100
UUID version 1000
timestamp, middle 011010000110
UUID variant 10
TNID variant 00
timestamp, lower 001
random 110101011010000000111010110100011010011001101110011011110| Field | Value |
|---|---|
| Name | 0xd6157 = user |
| TNID variant | 00 = variant 0 |
| Timestamp | 1767502656561 = 2026-01-04 04:57:36.561 UTC |
| Random | 0x1ab4075a34cdcde |
Goals and non-goals
- Goals: millisecond time-sortable; reasonable for 99% of use cases (low chance of collision, low stakes if one happens).
- Non-goals: astronomically low collision odds; extraordinary numbers of TNIDs in very short periods; fields such as the timestamp being useful after creation.
Caveats
Sorting is per name
The name precedes the timestamp, so TNIDs sort by time only within one name. Order within a millisecond is undefined; the random bits decide.
Collisions
Only TNIDs with the same name and millisecond can collide. 1 million in one millisecond: about 0.00035%, by the birthday bound with N = 2^57:
P(collision) ≈ 1 - e^(-n² / 2N)
= 1 - e^(-(10^6)² / (2 × 2^57))
≈ 0.00035%Hex sortability
UUID hex is case-insensitive, so one value sorts differently in different cases:
As strings: "A" (ASCII 65) < "a" (ASCII 97) → "0xA1" < "0xa1"
As values: 0xA1 = 0xa1 = 161Sorting hex by time needs consistent case, as for any hex data. Case-free alternative: sort the TNID string or the 128-bit value (as Postgres's uuid type does).
Future TNIDs
43 bits of milliseconds overflow around the year 2248. Implementations SHOULD wrap (keep the low 43 bits) rather than error; see the wrap test vector.