https://antonz.org/uuidv7/ [logo] Anton Zhiyanov projects books blog about [ ] UUIDv7 in 20 languages UUIDv7 is a 128-bit unique identifier like it's older siblings, such as the widely used UUIDv4. But unlike v4, UUIDv7 is time-sortable with 1 ms precision. By combining the timestamp and the random parts, UUIDv7 becomes an excellent choice for record identifiers in databases, including distributed ones. Let's briefly explore the UUIDv7 structure and move on to the zero-dependency implementations in 20 languages (as ranked by the Stack Overflow survey). These implementations may not be the fastest or most idiomatic, but they are concise and easy to understand. Many of the code examples are interactive (but the results are cached, so you won't see different UUIDs very often). As you can imagine, I am by no means fluent in all these languages, so if you spot a bug -- please submit a pull request. PRs for other languages are also welcome! JavaScript * Python * SQL * Shell * Java * C# * C++ * C * PHP * Go * Rust * Kotlin * Ruby * Lua * Dart * Swift * R * Elixir * Zig * Nim Structure UUIDv7 looks like this when represented as a string: 0190163d-8694-739b-aea5-966c26f8ad91 +-timestamp-+ |+-+ |+---rand_b-----+ ver |var rand_a The 128-bit value consists of several parts: * timestamp (48 bits) is a Unix timestamp in milliseconds. * ver (4 bits) is a UUID version (7). * rand_a (12 bits) is randomly generated. * var* (2 bits) is equal to 10. * rand_b (62 bits) is randomly generated. * In string representation, each symbol encodes 4 bits as a hex number, so the a in the example is 1010, where the first two bits are the fixed variant (10) and the next two are random. So the resulting hex number can be either 8 (1000), 9 (1001), a (1010) or b (1011). See RFC 9652 for details. JavaScript Initialize a random array with crypto.getRandomValues(), get the current timestamp with Date.now(), fill the array from the timestamp, set version and variant. function uuidv7() { // random bytes const value = new Uint8Array(16); crypto.getRandomValues(value); // current timestamp in ms const timestamp = BigInt(Date.now()); // timestamp value[0] = Number((timestamp >> 40n) & 0xffn); value[1] = Number((timestamp >> 32n) & 0xffn); value[2] = Number((timestamp >> 24n) & 0xffn); value[3] = Number((timestamp >> 16n) & 0xffn); value[4] = Number((timestamp >> 8n) & 0xffn); value[5] = Number(timestamp & 0xffn); // version and variant value[6] = (value[6] & 0x0f) | 0x70; value[8] = (value[8] & 0x3f) | 0x80; return value; } const uuidVal = uuidv7(); const uuidStr = Array.from(uuidVal) .map((b) => b.toString(16).padStart(2, "0")) .join(""); console.log(uuidStr); The TypeScript version is identical, the only difference is the function signature: function uuidv7(): Uint8Array { // ... } Python Initialize a random array with os.urandom(), get the current timestamp with time.time(), fill the array from the timestamp, set version and variant. import os import time def uuidv7(): # random bytes value = bytearray(os.urandom(16)) # current timestamp in ms timestamp = int(time.time() * 1000) # timestamp value[0] = (timestamp >> 40) & 0xFF value[1] = (timestamp >> 32) & 0xFF value[2] = (timestamp >> 24) & 0xFF value[3] = (timestamp >> 16) & 0xFF value[4] = (timestamp >> 8) & 0xFF value[5] = timestamp & 0xFF # version and variant value[6] = (value[6] & 0x0F) | 0x70 value[8] = (value[8] & 0x3F) | 0x80 return value if __name__ == "__main__": uuid_val = uuidv7() print(''.join(f'{byte:02x}' for byte in uuid_val)) SQL Get the current timestamp parts with strftime() (SQLite) or now() (PostgreSQL), get the random parts with random(), concatenate everything into a UUID string. SQLite (by Fabio Lima): select -- timestamp format('%08x', ((strftime('%s') * 1000) >> 16)) || '-' || format('%04x', ((strftime('%s') * 1000) + ((strftime('%f') * 1000) % 1000)) & 0xffff) || '-' || -- version / rand_a format('%04x', 0x7000 + abs(random()) % 0x0fff) || '-' || -- variant / rand_b format('%04x', 0x8000 + abs(random()) % 0x3fff) || '-' || -- rand_b format('%012x', abs(random()) >> 16) as value; PostgreSQL: select -- timestamp lpad(to_hex(((extract(epoch from now()) * 1000)::bigint >> 16)), 8, '0') || '-' || lpad(to_hex(((extract(epoch from now()) * 1000 + (date_part('milliseconds', now())::bigint % 1000))::bigint & 0xffff)), 4, '0') || '-' || -- version / rand_a lpad(to_hex((0x7000 + (random() * 0x0fff)::int)), 4, '0') || '-' || -- variant / rand_b lpad(to_hex((0x8000 + (random() * 0x3fff)::int)), 4, '0') || '-' || -- rand_b lpad(to_hex((floor(random() * (2^48))::bigint >> 16)), 12, '0') AS value; Shell Initialize a random byte string with /dev/urandom, get the current timestamp with date, fill the array from the timestamp and the byte string, set version and variant. #!/bin/sh uuidv7() { # random bytes rand_bytes=$(dd if=/dev/urandom bs=1 count=16 2>/dev/null | xxd -p) # current timestamp in ms timestamp=$(date +%s%3N) t_hex=$(printf "%012x" $timestamp) # timestamp value[0]=${t_hex:0:2} value[1]=${t_hex:2:2} value[2]=${t_hex:4:2} value[3]=${t_hex:6:2} value[4]=${t_hex:8:2} value[5]=${t_hex:10:2} # version / rand_a value[6]=$(printf "%02x" $((0x70 | (0x${rand_bytes:12:2} & 0x0F)))) value[7]=${rand_bytes:14:2} # variant / rand_b value[8]=$(printf "%02x" $((0x80 | (0x${rand_bytes:16:2} & 0x3F)))) # rand_b value[9]=${rand_bytes:18:2} value[10]=${rand_bytes:20:2} value[11]=${rand_bytes:22:2} value[12]=${rand_bytes:24:2} value[13]=${rand_bytes:26:2} value[14]=${rand_bytes:28:2} value[15]=${rand_bytes:30:2} echo "${value[@]}" } for byte in $(uuidv7); do printf "%s" "$byte" done echo Java Initialize a random array with SecureRandom.nextBytes(), get the current timestamp with System.currentTimeMillis(), fill the array from the timestamp, set version and variant. import java.nio.ByteBuffer; import java.security.SecureRandom; import java.util.UUID; public class UUIDv7 { private static final SecureRandom random = new SecureRandom(); public static UUID randomUUID() { byte[] value = randomBytes(); ByteBuffer buf = ByteBuffer.wrap(value); long high = buf.getLong(); long low = buf.getLong(); return new UUID(high, low); } public static byte[] randomBytes() { // random bytes byte[] value = new byte[16]; random.nextBytes(value); // current timestamp in ms long timestamp = System.currentTimeMillis(); // timestamp value[0] = (byte) ((timestamp >> 40) & 0xFF); value[1] = (byte) ((timestamp >> 32) & 0xFF); value[2] = (byte) ((timestamp >> 24) & 0xFF); value[3] = (byte) ((timestamp >> 16) & 0xFF); value[4] = (byte) ((timestamp >> 8) & 0xFF); value[5] = (byte) (timestamp & 0xFF); // version and variant value[6] = (byte) ((value[6] & 0x0F) | 0x70); value[8] = (byte) ((value[8] & 0x3F) | 0x80); return value; } public static void main(String[] args) { var uuid = UUIDv7.randomUUID(); System.out.println(uuid); } } Co-author: David Ankin C# Initialize a random array with RandomNumberGenerator.GetBytes(), get the current timestamp with DateTimeOffset.UtcNow, fill the array from the timestamp, set version and variant. using System; using System.Security.Cryptography; public class UUIDv7 { private static readonly RandomNumberGenerator random = RandomNumberGenerator.Create(); public static byte[] Generate() { // random bytes byte[] value = new byte[16]; random.GetBytes(value); // current timestamp in ms long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); // timestamp value[0] = (byte)((timestamp >> 40) & 0xFF); value[1] = (byte)((timestamp >> 32) & 0xFF); value[2] = (byte)((timestamp >> 24) & 0xFF); value[3] = (byte)((timestamp >> 16) & 0xFF); value[4] = (byte)((timestamp >> 8) & 0xFF); value[5] = (byte)(timestamp & 0xFF); // version and variant value[6] = (byte)((value[6] & 0x0F) | 0x70); value[8] = (byte)((value[8] & 0x3F) | 0x80); return value; } public static void Main(string[] args) { byte[] uuidVal = Generate(); foreach (byte b in uuidVal) { Console.Write("{0:x2}", b); } Console.WriteLine(); } } C++ Initialize a random array with random_device, get the current timestamp with system_clock.time_since_epoch(), fill the array from the timestamp, set version and variant. #include #include #include #include #include std::array uuidv7() { // random bytes std::random_device rd; std::array random_bytes; std::generate(random_bytes.begin(), random_bytes.end(), std::ref(rd)); std::array value; std::copy(random_bytes.begin(), random_bytes.end(), value.begin()); // current timestamp in ms auto now = std::chrono::system_clock::now(); auto millis = std::chrono::duration_cast( now.time_since_epoch() ).count(); // timestamp value[0] = (millis >> 40) & 0xFF; value[1] = (millis >> 32) & 0xFF; value[2] = (millis >> 24) & 0xFF; value[3] = (millis >> 16) & 0xFF; value[4] = (millis >> 8) & 0xFF; value[5] = millis & 0xFF; // version and variant value[6] = (value[6] & 0x0F) | 0x70; value[8] = (value[8] & 0x3F) | 0x80; return value; } int main() { auto uuid_val = uuidv7(); for (const auto& byte : uuid_val) { printf("%02x", byte); } printf("\n"); return 0; } C Initialize a random array with getentropy(), get the current timestamp with timespec_get(), fill the array from the timestamp, set version and variant. #include #include #include #include #include int uuidv7(uint8_t* value) { // random bytes int err = getentropy(value, 16); if (err != EXIT_SUCCESS) { return EXIT_FAILURE; } // current timestamp in ms struct timespec ts; int ok = timespec_get(&ts, TIME_UTC); if (ok == 0) { return EXIT_FAILURE; } uint64_t timestamp = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; // timestamp value[0] = (timestamp >> 40) & 0xFF; value[1] = (timestamp >> 32) & 0xFF; value[2] = (timestamp >> 24) & 0xFF; value[3] = (timestamp >> 16) & 0xFF; value[4] = (timestamp >> 8) & 0xFF; value[5] = timestamp & 0xFF; // version and variant value[6] = (value[6] & 0x0F) | 0x70; value[8] = (value[8] & 0x3F) | 0x80; return EXIT_SUCCESS; } int main() { uint8_t uuid_val[16]; uuidv7(uuid_val); for (size_t i = 0; i < 16; i++) { printf("%02x", uuid_val[i]); } printf("\n"); } PHP Initialize a random string with random_bytes(), get the current timestamp with microtime(), fill the characters from the timestamp, set version and variant. > 40) & 0xFF); $value[1] = chr(($timestamp >> 32) & 0xFF); $value[2] = chr(($timestamp >> 24) & 0xFF); $value[3] = chr(($timestamp >> 16) & 0xFF); $value[4] = chr(($timestamp >> 8) & 0xFF); $value[5] = chr($timestamp & 0xFF); // version and variant $value[6] = chr((ord($value[6]) & 0x0F) | 0x70); $value[8] = chr((ord($value[8]) & 0x3F) | 0x80); return $value; } $uuid_val = uuidv7(); echo bin2hex($uuid_val); Go Initialize a random array with rand.Read(), get the current timestamp with time.Now(), fill the array from the timestamp, set version and variant. package main import ( "crypto/rand" "fmt" "time" ) func uuidv7() ([16]byte, error) { // random bytes var value [16]byte _, err := rand.Read(value[:]) if err != nil { return value, err } // current timestamp in ms timestamp := uint64(time.Now().UnixNano() / int64(time.Millisecond)) // timestamp value[0] = byte((timestamp >> 40) & 0xFF) value[1] = byte((timestamp >> 32) & 0xFF) value[2] = byte((timestamp >> 24) & 0xFF) value[3] = byte((timestamp >> 16) & 0xFF) value[4] = byte((timestamp >> 8) & 0xFF) value[5] = byte(timestamp & 0xFF) // version and variant value[6] = (value[6] & 0x0F) | 0x70 value[8] = (value[8] & 0x3F) | 0x80 return value, nil } func main() { uuidVal, err := uuidv7() if err != nil { panic(err) } for _, byte := range uuidVal { fmt.Printf("%02x", byte) } fmt.Println() } Rust Initialize a random array with getrandom::getrandom(), get the current timestamp with SystemTime::now(), fill the array from the timestamp, set version and variant. use std::error::Error; use std::time::{SystemTime, UNIX_EPOCH}; fn uuidv7() -> Result<[u8; 16], Box> { // random bytes let mut value = [0u8; 16]; getrandom::getrandom(&mut value)?; // current timestamp in ms let timestamp = match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(duration) => duration.as_millis() as u64, Err(_) => return Err(Box::from("Failed to get system time")), }; // timestamp value[0] = (timestamp >> 40) as u8; value[1] = (timestamp >> 32) as u8; value[2] = (timestamp >> 24) as u8; value[3] = (timestamp >> 16) as u8; value[4] = (timestamp >> 8) as u8; value[5] = timestamp as u8; // version and variant value[6] = (value[6] & 0x0F) | 0x70; value[8] = (value[8] & 0x3F) | 0x80; Ok(value) } fn main() { match uuidv7() { Ok(uuid_val) => { for byte in &uuid_val { print!("{:02x}", byte); } println!(); } Err(e) => eprintln!("Error: {}", e), } } Co-author: Rodolphe Breard Kotlin Initialize a random array with SecureRandom.nextBytes(), get the current timestamp with Instant.now(), fill the array from the timestamp, set version and variant. import java.security.SecureRandom import java.time.Instant object UUIDv7 { private val random = SecureRandom() fun generate(): ByteArray { // random bytes val value = ByteArray(16) random.nextBytes(value) // current timestamp in ms val timestamp = Instant.now().toEpochMilli() // timestamp value[0] = ((timestamp shr 40) and 0xFF).toByte() value[1] = ((timestamp shr 32) and 0xFF).toByte() value[2] = ((timestamp shr 24) and 0xFF).toByte() value[3] = ((timestamp shr 16) and 0xFF).toByte() value[4] = ((timestamp shr 8) and 0xFF).toByte() value[5] = (timestamp and 0xFF).toByte() // version and variant value[6] = (value[6].toInt() and 0x0F or 0x70).toByte() value[8] = (value[8].toInt() and 0x3F or 0x80).toByte() return value } @JvmStatic fun main(args: Array) { val uuidVal = generate() uuidVal.forEach { b -> print("%02x".format(b)) } println() } } Ruby Initialize a random array with SecureRandom.random_bytes(), get the current timestamp with Time.now, fill the array from the timestamp, set version and variant. require 'securerandom' require 'time' def uuidv7 # random bytes value = SecureRandom.random_bytes(16).bytes # current timestamp in ms timestamp = (Time.now.to_f * 1000).to_i # timestamp value[0] = (timestamp >> 40) & 0xFF value[1] = (timestamp >> 32) & 0xFF value[2] = (timestamp >> 24) & 0xFF value[3] = (timestamp >> 16) & 0xFF value[4] = (timestamp >> 8) & 0xFF value[5] = timestamp & 0xFF # version and variant value[6] = (value[6] & 0x0F) | 0x70 value[8] = (value[8] & 0x3F) | 0x80 value end if __FILE__ == $0 uuid_val = uuidv7 puts uuid_val.pack('C*').unpack1('H*') end Lua Initialize a random table with math.random(), get the current timestamp with os.time(), fill the list from the timestamp, set version and variant. local function uuidv7() -- random bytes local value = {} for i = 1, 16 do value[i] = math.random(0, 255) end -- current timestamp in ms local timestamp = os.time() * 1000 -- timestamp value[1] = (timestamp >> 40) & 0xFF value[2] = (timestamp >> 32) & 0xFF value[3] = (timestamp >> 24) & 0xFF value[4] = (timestamp >> 16) & 0xFF value[5] = (timestamp >> 8) & 0xFF value[6] = timestamp & 0xFF -- version and variant value[7] = (value[7] & 0x0F) | 0x70 value[9] = (value[9] & 0x3F) | 0x80 return value end local uuid_val = uuidv7() for i = 1, #uuid_val do io.write(string.format('%02x', uuid_val[i])) end print() Dart Initialize a random list with Random.nextInt(), get the current timestamp with DateTime.now(), fill the list from the timestamp, set version and variant. import 'dart:math'; import 'dart:typed_data'; Uint8List uuidv7() { // random bytes final rng = Random.secure(); final value = Uint8List(16); for (int i = 0; i < 16; i++) { value[i] = rng.nextInt(256); } // current timestamp in ms final timestamp = DateTime.now().millisecondsSinceEpoch; // timestamp value[0] = (timestamp ~/ pow(2, 40)) & 0xFF; value[1] = (timestamp ~/ pow(2, 32)) & 0xFF; value[2] = (timestamp ~/ pow(2, 24)) & 0xFF; value[3] = (timestamp ~/ pow(2, 16)) & 0xFF; value[4] = (timestamp ~/ pow(2, 8)) & 0xFF; value[5] = timestamp & 0xFF; // version and variant value[6] = (value[6] & 0x0F) | 0x70; value[8] = (value[8] & 0x3F) | 0x80; return value; } void main() { final uuidVal = uuidv7(); print(uuidVal.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join()); } Swift Extend the existing UUID type with a v7 function. Initialize a random tuple with UInt8.random(), get the current timestamp with Date ().timeIntervalSince1970, fill the tuple from the timestamp, set version and variant. import Foundation extension UUID { static func v7() -> Self { // random bytes var value = ( UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255), UInt8.random(in: 0...255) ) // current timestamp in ms let timestamp: Int = .init(Date().timeIntervalSince1970 * 1000) // timestamp value.0 = .init((timestamp >> 40) & 0xFF) value.1 = .init((timestamp >> 32) & 0xFF) value.2 = .init((timestamp >> 24) & 0xFF) value.3 = .init((timestamp >> 16) & 0xFF) value.4 = .init((timestamp >> 8) & 0xFF) value.5 = .init(timestamp & 0xFF) // version and variant value.6 = (value.6 & 0x0F) | 0x70 value.8 = (value.8 & 0x3F) | 0x80 return UUID(uuid: value) } } let uuidVal: UUID = .v7() print(uuidVal) Co-author: Prathamesh Kowarkar R Initialize a random vector with sample(), get the current timestamp with Sys.time(), fill the vector from the timestamp, set version and variant. uuidv7 <- function() { # random bytes value <- as.raw(sample(0:255, 16, replace = TRUE)) # current timestamp in ms timestamp <- as.numeric(Sys.time()) * 1000 # timestamp value[1] <- as.raw((timestamp %/% 2^40) %% 256) value[2] <- as.raw((timestamp %/% 2^32) %% 256) value[3] <- as.raw((timestamp %/% 2^24) %% 256) value[4] <- as.raw((timestamp %/% 2^16) %% 256) value[5] <- as.raw((timestamp %/% 2^8) %% 256) value[6] <- as.raw(timestamp %% 256) # version and variant value[7] <- as.raw(bitwOr(bitwAnd(as.integer(value[7]), 0x0F), 0x70)) value[9] <- as.raw(bitwOr(bitwAnd(as.integer(value[9]), 0x3F), 0x80)) return(value) } uuid_val <- uuidv7() cat(paste(sprintf('%02x', as.integer(uuid_val)), collapse = '')) Elixir Initialize a random list with crypto.strong_rand_bytes(), get the current timestamp with os.system_time(), fill the list from the timestamp, set version and variant. use Bitwise defmodule UUIDv7 do def generate do # random bytes value = :crypto.strong_rand_bytes(16) |> :binary.bin_to_list() # current timestamp in ms timestamp = :os.system_time(:millisecond) # timestamp value = List.replace_at(value, 0, (timestamp >>> 40) &&& 0xFF) value = List.replace_at(value, 1, (timestamp >>> 32) &&& 0xFF) value = List.replace_at(value, 2, (timestamp >>> 24) &&& 0xFF) value = List.replace_at(value, 3, (timestamp >>> 16) &&& 0xFF) value = List.replace_at(value, 4, (timestamp >>> 8) &&& 0xFF) value = List.replace_at(value, 5, timestamp &&& 0xFF) # timestamp value = List.replace_at(value, 6, (Enum.at(value, 6) &&& 0x0F) ||| 0x70) value = List.replace_at(value, 8, (Enum.at(value, 8) &&& 0x3F) ||| 0x80) value end end uuid_val = UUIDv7.generate() Enum.map(uuid_val, &Integer.to_string(&1, 16)) |> Enum.map(&String.pad_leading(&1, 2, "0")) |> Enum.join() |> IO.puts() Zig Initialize a random array with std.crypto.random.bytes(), get the current timestamp with std.time.milliTimestamp(), fill the array from the timestamp, set version and variant. const std = @import("std"); fn uuidv7() [16]u8 { // random bytes var value: [16]u8 = undefined; std.crypto.random.bytes(&value); // current timestamp in ms const timestamp: u64 = @intCast(std.time.milliTimestamp()); // timestamp value[0] = @intCast((timestamp >> 40) & 0xFF); value[1] = @intCast((timestamp >> 32) & 0xFF); value[2] = @intCast((timestamp >> 24) & 0xFF); value[3] = @intCast((timestamp >> 16) & 0xFF); value[4] = @intCast((timestamp >> 8) & 0xFF); value[5] = @intCast(timestamp & 0xFF); // version and variant value[6] = (value[6] & 0x0F) | 0x70; value[8] = (value[8] & 0x3F) | 0x80; return value; } pub fn main() void { const uuid_val = uuidv7(); for (uuid_val) |byte| { std.debug.print("{x:0>2}", .{byte}); } std.debug.print("\n", .{}); } Nim Initialize a random sequence with random.rand(), get the current timestamp with times.epochTime(), fill the sequence from the timestamp, set version and variant. import std/[times, strutils, sequtils, random] randomize() proc uuidv7(): seq[byte] = # random bytes result = 16.newSeqWith(256.rand().byte) # current timestamp in ms let timestamp = epochTime().uint64 * 1000 # timestamp result[0] = (timestamp shr 40).byte and 0xFF result[1] = (timestamp shr 32).byte and 0xFF result[2] = (timestamp shr 24).byte and 0xFF result[3] = (timestamp shr 16).byte and 0xFF result[4] = (timestamp shr 8).byte and 0xFF result[5] = timestamp.byte and 0xFF # version and variant result[6] = (result[6] and 0x0F) or 0x70 result[8] = (result[8] and 0x3F) or 0x80 var uuidVal = uuidv7() echo uuidVal.mapIt(it.toHex(2)).join() by Sultan Al Isaiee Final thoughts The previous version of the UUID specification (RFC 4122) was published in 2005. A K-sorted, time-ordered UUID is a much-needed standard refresher that will hopefully serve us well for years to come. -- P.S. Interactive examples in this post are powered by codapi -- an open source tool I'm building. Use it to embed live code snippets into your product docs, online course or blog. Subscribe to keep up with new posts. 15 Jun, 2024 software - Modern SQLite: Delete limit See also LLMs are insecure Grep by example: Interactive guide Git by example: Interactive guide * Subscribe: Newsletter Discord Twitter LinkedIn GitHub RSS * Email: m@antonz.org