
Most modern unique IDs are sortable. Almost none are readable.
UUID v7, ULID, KSUID — they all encode a timestamp so that IDs sort chronologically. That's genuinely useful. But here's the thing nobody talks about: you still can't tell when an ID was created just by looking at it.
019f6315-16e6-74b2-b49b-2d3b66ee06baThat's a UUID v7. It is time-sortable. It is also completely opaque to a human. To know it was made on a Wednesday in late July 2026, you need to paste it into a decoder.
So I built Kalid to answer a simple question: what if the ID told you the date itself?
The root problem
Sortable IDs solve the "in what order?" problem but ignore the "when, at a glance?" problem. In practice we constantly read IDs in logs, URLs, database rows, and support tickets. Every time, we drop them into a tool to recover information that was there all along — just hidden behind random-looking bytes.
The fix isn't more randomness. It's to stop hiding the calendar.
The idea
A Kalid is a 16-character string with a deliberately simple shape:
{ms_hex:012}{month}{iso_week:02}{weekday}Four parts, in order:
Segment | Length | Meaning |
|---|---|---|
| 12 | Unix epoch milliseconds, lowercase hex |
| 1 |
|
| 2 | ISO 8601 week number, zero-padded |
| 1 |
|
Take the UUID v7 from earlier. The same 48-bit millisecond timestamp, rendered as a Kalid, looks like this:
UUID v7 : 019f6315-16e6-74b2-b49b-2d3b66ee06ba
Kalid : 019f631516e6g29oRead it left to right:
- 019f631516e6 - the timestamp (2026-07-15T00:02:34Z)
- g - July
- 29 - ISO week 29
- o - WednesdayNo decoder. You know the date from the string alone.
Why it stays sortable
The first 12 characters are the timestamp in big-endian lowercase hex. Big-endian means the most significant digit comes first, so lexicographic order equals chronological order. That holds within a millisecond, within a day, across months, and across the December→January year boundary. The calendar suffix is computed purely from the timestamp, so it can never break the sort.
In other words, Kalid is K-sortable by construction, not by accident.
How it compares
Kalid shares the exact same 48-bit millisecond timestamp as UUID v7, so interop is a one-line conversion (to_uuid_v7 / from_uuid_v7). On top of that it's fast:
- ~1.3× faster than ULID
- ~19× faster than UUID v7
- ~25× faster than nanoid
Every component operation stays under 30 ns; the async path (tokio/smol) adds only the executor overhead.
- Hardware: Apple M2 Pro (10 cores), 16 GB RAM, macOS 26.5.2, Rust 1.97.0.
- Tool: criterion.rs, 100 samples per benchmark.
kalid::generate_kalidis the 1.0× baseline.
Kalid API operations
Operation | Time (mean) | × of |
|---|---|---|
| 0.36 ns | 0.007× |
| 0.57 ns | 0.012× |
| 15.3 ns | 0.32× |
| 22.6 ns | 0.47× |
| 29.8 ns | 0.62× |
| 48.2 ns | 1.0× |
vs other 16-char ID generators
Generator | Time (mean) | vs Kalid |
|---|---|---|
| 48.2 ns | 1.0× (baseline) |
| 61.4 ns | 1.27× slower |
| 896 ns | 18.6× slower |
| 1,211 ns | 25.1× slower |
Async (requires tokio feature)
Operation | Time (mean) |
|---|---|
| 4.99 µs |
| 5.12 µs |
Kalid generates a 16-character ID 1.3× faster than ULID, ~19× faster than UUID v7, and ~25× faster than nanoid. Every Kalid component operation stays under 30 ns; the async path adds a ~5 µs runtime overhead from the tokio executor (spawn_blocking).When to reach for it
Kalid is a good fit when you want human-readable, sortable IDs in a single service — order IDs, user IDs, event keys — with an optional prefix (order_019f…, user_019f…).
It is not a distributed-ID generator: with only 3 random bits, collisions are possible within the same millisecond. It's also not cryptographically secure, and it's always 16 characters plus prefix. For those needs, reach for Snowflake-style or hashed IDs instead.
Try it
[dependencies]
kalid = "0.1.0"use kalid::Kalid;
let k = Kalid::new();
println!("{}", k.as_string()); // e.g. 019f631516e6g29oKalid is zero unsafe, minimal-dependency, and MIT/Apache-2.0 dual licensed.
The code and benchmarks are on GitHub riipandi/kalid.