UUIDv7 for Elixir.
Used for generating version 7 UUIDs using submicrosecond precision. Normally the default precision is 1 millisecond, this causes issues when generating UUIDs in bulk because we can generate multiple UUIDs in the same millisecond. This module allows you to generate UUIDs with submicrosecond precision.
UUIDv7.generate() "018e90d8-06e8-7f9f-bfd7-6730ba98a51b"
UUIDv7.bingenerate() <<1, 142, 144, 216, 6, 232, 127, 159, 191, 215, 103, 48, 186, 152, 165, 27>>
Summary
Functions
Generates a version 7 UUID in the binary format.
Generates a random version 7 UUID in binary format for a specific DateTime.
Bulk-generates n raw UUIDv7 binaries.
Decode a string representation of a UUID to the raw binary version.
Callback implementation for Ecto.Type.embed_as/1.
Encode a raw UUID to the string representation.
Callback implementation for Ecto.Type.equal?/2.
Extract the millisecond timestamp from the UUID.
Generates a version 7 UUID using submilliseconds for increased clock precision.
Generates a random version 7 UUID for a specific DateTime.
String-encoded counterpart of bingenerate_many/1.
returns the current time in milliseconds and scaled nanoseconds
scales the remaining nanoseconds to fit in 12 bits
Extracts a Date (UTC) from a UUIDv7.
Extracts a DateTime (UTC, millisecond precision) from a UUIDv7.
Types
Functions
Generates a version 7 UUID in the binary format.
Example
iex> [UUIDv7.bingenerate(), UUIDv7.bingenerate()] iex> |> Enum.map(& :binary.part(&1, 0, 5)) iex> |> Enum.reduce(fn second, first -> second == first end) true
@spec bingenerate_from_datetime(DateTime.t()) :: raw()
Generates a random version 7 UUID in binary format for a specific DateTime.
Binary version of generate_from_datetime/1.
Example
iex> {:ok, dt, _} = DateTime.from_iso8601("2024-01-01T00:00:00.000Z")
iex> uuid = UUIDv7.bingenerate_from_datetime(dt)
iex> is_binary(uuid) and byte_size(uuid) == 16
true
@spec bingenerate_many(pos_integer()) :: [raw()]
Bulk-generates n raw UUIDv7 binaries.
Each millisecond can hold up to 4096 strictly-ordered UUIDs (the
capacity of the 12-bit sub-millisecond field). For n <= 4096 the
whole batch shares a single millisecond. For larger batches, full
chunks of 4096 are generated at consecutive milliseconds and the
remainder (if any) lands in the next millisecond — so the batch keeps
monotonically-increasing timestamps without ever colliding inside a
single ms.
Within each millisecond the 12-bit sub-millisecond field is
partitioned into n equal slots (step = div(4096, n)); each UUID's
value is step * i + jitter, where the jitter is a crypto-strong
offset within [0, step). This guarantees strict in-batch ordering
while keeping positions inside each slot randomized.
Both rand_b (62 bits) and the jitter (12 bits) are sliced from the
same :crypto.strong_rand_bytes/1 call — 10 bytes per UUID, no extra
PRNG overhead.
Note: when n > 4096, this function burns div(n - 1, 4096) future
milliseconds. Subsequent bingenerate/0 calls landing in those same
milliseconds still won't collide (62 random bits per UUID), but
conceptually the batch reaches ahead of the wall clock.
Examples
iex> uuids = UUIDv7.bingenerate_many(4)
iex> length(uuids)
4
iex> uuids == Enum.sort(uuids)
true
iex> uuids = UUIDv7.bingenerate_many(10_000)
iex> length(uuids)
10_000
iex> uuids == Enum.sort(uuids)
true
Decode a string representation of a UUID to the raw binary version.
Example
iex> UUIDv7.decode("018e90d8-06e8-7f9f-bfd7-6730ba98a51b")
<<1, 142, 144, 216, 6, 232, 127, 159, 191, 215, 103, 48, 186, 152, 165, 27>>
Callback implementation for Ecto.Type.embed_as/1.
Encode a raw UUID to the string representation.
Example
iex> UUIDv7.encode(<<1, 142, 144, 216, 6, 232, 127, 159, 191, 215, 103, 48, 186, 152, 165, 27>>)
"018e90d8-06e8-7f9f-bfd7-6730ba98a51b"
Callback implementation for Ecto.Type.equal?/2.
Extract the millisecond timestamp from the UUID.
Example
iex> UUIDv7.extract_timestamp("018ecb40-c457-73e6-a400-000398daddd9")
1712807003223
@spec generate() :: t()
Generates a version 7 UUID using submilliseconds for increased clock precision.
Example
iex> [UUIDv7.generate(), UUIDv7.generate()] iex> |> Enum.map(& String.byte_slice(&1, 0, 11)) iex> |> Enum.reduce(fn second, first -> second == first end) true
@spec generate_from_datetime(DateTime.t()) :: t()
Generates a random version 7 UUID for a specific DateTime.
Similar to generate/0, but uses the provided DateTime for the timestamp
instead of the current time. The sub-millisecond precision and random bits
are still randomly generated.
Useful for:
- Generating backdated UUIDs for testing
- Creating UUIDs for historical data
- Partitioning data by time ranges
Example
iex> {:ok, dt, _} = DateTime.from_iso8601("2024-01-01T00:00:00.000Z")
iex> uuid = UUIDv7.generate_from_datetime(dt)
iex> UUIDv7.extract_timestamp(uuid) == DateTime.to_unix(dt, :millisecond)
true
@spec generate_many(pos_integer()) :: [t()]
String-encoded counterpart of bingenerate_many/1.
Example
iex> uuids = UUIDv7.generate_many(8)
iex> length(uuids)
8
iex> uuids == Enum.sort(uuids)
true
returns the current time in milliseconds and scaled nanoseconds
@spec min_uuid(Date.t() | DateTime.t()) :: t()
Generates the minimum UUID v7 for a given Date or DateTime.
Creates a UUID with the timestamp from the given moment and all random
bits set to zero — the smallest possible UUID for that millisecond.
For a Date, midnight UTC of that day is used.
Used for half-closed time ranges in database partitioning:
- Range
[T1, T2):WHERE uuid >= min_uuid(T1) AND uuid < min_uuid(T2) - Ranges are continuous with no gaps or overlaps
Examples
iex> {:ok, dt, _} = DateTime.from_iso8601("2024-01-01T00:00:00.000Z")
iex> uuid = UUIDv7.min_uuid(dt)
iex> String.ends_with?(uuid, "7000-8000-000000000000")
true
iex> UUIDv7.min_uuid(~D[2024-01-01])
"018cc251-f400-7000-8000-000000000000"
scales the remaining nanoseconds to fit in 12 bits
Example
iex> UUIDv7.scale_nanoseconds(0)
0
iex> UUIDv7.scale_nanoseconds(500000)
2048
iex> UUIDv7.scale_nanoseconds(999999)
0xFFF
Extracts a Date (UTC) from a UUIDv7.
Accepts the hex string form, the raw 16-byte binary, or a %UUID{}
struct.
Example
iex> UUIDv7.to_date("018ecb40-c457-73e6-a400-000398daddd9")
~D[2024-04-11]
@spec to_datetime(t() | raw() | UUID.t()) :: DateTime.t()
Extracts a DateTime (UTC, millisecond precision) from a UUIDv7.
Accepts the hex string form, the raw 16-byte binary, or a %UUID{}
struct.
Example
iex> UUIDv7.to_datetime("018ecb40-c457-73e6-a400-000398daddd9")
~U[2024-04-11 03:43:23.223Z]