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-2d3b66ee06ba

That'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

ms_hex

12

Unix epoch milliseconds, lowercase hex

month

1

a=Jan … l=Dec

week

2

ISO 8601 week number, zero-padded

day

1

m=Mon … s=Sun

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   : 019f631516e6g29o

Read it left to right:

- 019f631516e6 - the timestamp (2026-07-15T00:02:34Z)
- g            - July
- 29           - ISO week 29
- o            - Wednesday

No 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_kalid is the 1.0× baseline.

Kalid API operations

Operation

Time (mean)

× of generate_kalid

kalid::from_epoch_ms

0.36 ns

0.007×

kalid::from_uuid_v7

0.57 ns

0.012×

kalid::parse

15.3 ns

0.32×

kalid::as_string

22.6 ns

0.47×

kalid::to_uuid_v7

29.8 ns

0.62×

kalid::generate_kalid (base)

48.2 ns

1.0×

vs other 16-char ID generators

Generator

Time (mean)

vs Kalid

kalid::generate_kalid

48.2 ns

1.0× (baseline)

ulid::Ulid::r#gen().to_string()

61.4 ns

1.27× slower

uuid::Uuid::now_v7

896 ns

18.6× slower

nanoid::nanoid!(16)

1,211 ns

25.1× slower

Async (requires tokio feature)

Operation

Time (mean)

kalid::Kalid::new_async

4.99 µs

kalid::generate_kalid_async

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. 019f631516e6g29o

Kalid is zero unsafe, minimal-dependency, and MIT/Apache-2.0 dual licensed.

The code and benchmarks are on GitHub riipandi/kalid.