UUIDv7 in 33 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 33 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!

JavaScriptPythonSQLShellJavaC#C++CPHPPowerShellGoRustKotlinRubyLuaDartSwiftRPascalPerlElixirClojureJuliaErlangZigCrystalNimGleamTclVEmacs LispVimscriptNushell

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;

SQL Server (by Onur Keskin):

create procedure GenerateUUIDv7
as
begin
    -- Declare variables to hold different parts of the UUID
    declare @timestamp_hex    char(8)
    declare @milliseconds_hex char(4)
    declare @version_rand_a   char(4)
    declare @variant_rand_b   char(4)
    declare @rand_b           char(12)

    -- Calculate the current time in milliseconds since the Unix epoch
    declare @currentTime bigint = datediff_big(millisecond, '1970-01-01', getutcdate())

    -- Convert the timestamp to hexadecimal, divided into two parts
    set @timestamp_hex = right('00000000' + convert(varchar(8), convert(varbinary(4), @currentTime / 65536), 2), 8)
    set @milliseconds_hex = right('0000' + convert(varchar(4), convert(varbinary(2), @currentTime % 65536), 2), 4)

    -- Generate a random value for the version/rand_a part, combine it with the version identifier (0x7000), and convert to hexadecimal
    declare @rand_a int = cast(floor(rand(checksum(newid())) * 4096) as int) -- 0x0FFF = 4095
    set @version_rand_a = right('0000' + convert(varchar(4), convert(varbinary(2), 0x7000 + @rand_a), 2), 4)

    -- Generate a random value for the variant/rand_b part, combine it with the variant identifier (0x8000), and convert to hexadecimal
    declare @rand_b_part int = cast(floor(rand(checksum(newid())) * 16384) as int) -- 0x3FFF = 16383
    set @variant_rand_b = right('0000' + convert(varchar(4), convert(varbinary(2), 0x8000 + @rand_b_part), 2), 4)

    -- Generate a large random value for the rand_b part and convert to hexadecimal
    declare @rand_b_bigint bigint = floor(rand(checksum(newid())) * power(cast(2 as bigint), 48))
    set @rand_b = right('000000000000' + convert(varchar(12), convert(varbinary(6), @rand_b_bigint / 65536), 2), 12)

    -- Combine all parts to form the final UUID v7-like value
    declare @UUIDv7 char(36)
    set @UUIDv7 = @timestamp_hex + '-' + @milliseconds_hex + '-' + @version_rand_a + '-' + @variant_rand_b + '-' + @rand_b

    -- Return the generated UUID v7-like value
    select @UUIDv7 as UUIDv7
end

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() {
    # current timestamp in ms. POSIX date does not support %3N.
    timestamp=$(date +%s)000
    timestamp_hi=$(( timestamp >> 16 ))
    timestamp_lo=$(( timestamp & 0xFFFF ))

    # 16 random bits (12 will be used)
    rand_a=0x$(LC_ALL=C tr -dc '0-9a-f' < /dev/urandom|head -c4)
    # ver is 0x7
    ver_rand_a=$(( 0x7000 | ( 0xFFF & rand_a ) ))

    # 16 random bits (14 will be used)
    rand_b_hi=0x$(LC_ALL=C tr -dc '0-9a-f' < /dev/urandom|head -c4)
    # var is 0b10
    var_rand_hi=$(( 0x8000 | ( 0x3FFF & rand_b_hi ) ))
    # remaining 48 bits of rand b
    rand_b_lo=$(LC_ALL=C tr -dc '0-9a-f' < /dev/urandom|head -c12)

    printf "%08x-%04x-%04x-%4x-%s" "$timestamp_hi" "$timestamp_lo" "$ver_rand_a" "$var_rand_hi" "$rand_b_lo"
}

echo $(uuidv7)

by Brian Ewins

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
        ByteBuffer timestamp = ByteBuffer.allocate(Long.BYTES);
        timestamp.putLong(System.currentTimeMillis());

        // timestamp
        System.arraycopy(timestamp.array(), 2, value, 0, 6);

        // 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);
    }
}

by 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 <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <random>

std::array<uint8_t, 16> uuidv7() {
    // random bytes
    std::random_device rd;
    std::array<uint8_t, 16> random_bytes;
    std::generate(random_bytes.begin(), random_bytes.end(), std::ref(rd));
    std::array<uint8_t, 16> 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<std::chrono::milliseconds>(
        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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>

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.

<?php
function uuidv7() {
    // random bytes
    $value = random_bytes(16);

    // current timestamp in ms
    $timestamp = intval(microtime(true) * 1000);

    // timestamp
    $value[0] = chr(($timestamp >> 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);

PowerShell

Initialize a random array with Random.GetBytes(), get the current timestamp with DateTimeOffset.UtcNow, fill the array from the timestamp, set version and variant.

function New-Uuidv7
{
  # random bytes
  $value = [byte[]]::new(16)
  [System.Random]::new().NextBytes($value)

  # current timestamp
  $timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
  [System.BitConverter]::GetBytes($timestamp)[5..0].Copyto($value, 0)

  # version and variant
  $value[6] = ($value[6] -band 0x0F) -bor 0x70
  $value[8] = ($value[8] -band 0x0F) -bor 0x80

  $value
}

(New-Uuidv7 | ForEach-Object ToString x2) -join ''

by lost

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"
	"math/big"
	"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 := big.NewInt(time.Now().UnixMilli())

	// timestamp
	timestamp.FillBytes(value[0:6])

	// version and variant
	value[6] = (value[6] & 0x0F) | 0x70
	value[8] = (value[8] & 0x3F) | 0x80

	return value, nil
}

func main() {
	uuidVal, _ := uuidv7()
	fmt.Printf("%x\n", uuidVal)
}

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<dyn Error>> {
    // 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),
    }
}

by Rodolphe Bréard

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<String>) {
        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(0),
            UInt8(0),
            UInt8(0),
            UInt8(0),
            UInt8(0),
            UInt8(0),
            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)

by Prathamesh Kowarkar, Sam Dean

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() {
  # Initialise vector with current timestamp & random numbers
  value = numeric(16)
  value[1:6] <- as.numeric(Sys.time()) * 1000
  value[7:16] <- sample(0:255, 10L, replace = TRUE)

  # timestamp
  value[1:6] <- value[1:6] %/% 2^c(40, 32, 24, 16, 8, 1) %% 256L

  # version and variant
  value[7] <- bitwOr(bitwAnd(value[7], 0x0F), 0x70)
  value[9] <- bitwOr(bitwAnd(value[9], 0x3F), 0x80)
  as.raw(value)
}

uuid_val <- uuidv7()
cat(paste(sprintf('%02x', as.integer(uuid_val)), collapse = ''))

by Colin Gillespie

Pascal

Initialize a random array with Random(), get the current timestamp with DateTimeToUnix(), fill the array from the timestamp, set version and variant.

// Use as a regular unit from Delphi, or run as a console app from FreePascal
unit uuidv7;

interface

uses
  SysUtils, DateUtils;

function GenerateUUIDv7: TGUID;

implementation

function GenerateUUIDv7: TGUID;
var
  timestamp: Int64;
  randomBytes: array[0..9] of Byte;
  uuid: TGUID;
  i: Integer;
begin
  FillChar(uuid, SizeOf(uuid), 0);
  {$IFDEF FPC}
  timestamp := DateTimeToUnix(Now) * 1000; // seconds accuracy
  {$ELSE}
  timestamp := DateTimeToMilliseconds(Now) - Int64(UnixDateDelta + DateDelta) * MSecsPerDay; // millisecond accuracy
  {$ENDIF}

  // Generate 10 random bytes
  for i := 0 to 9 do
    randomBytes[i] := Random($FF);

  // Populate the TGUID fields
  uuid.D1 := (timestamp shr 16) and $FFFFFFFF;       // Top 32 bits of the 48-bit timestamp
  uuid.D2 := ((timestamp shr 4) and $0FFF) or $7000; // Next 12 bits of the timestamp and version 7
  uuid.D3 := ((timestamp and $0000000F) shl 12) or   // the last 4 bits of timestamp
              (randomBytes[0] and $F0);              // the top 4 bits of randomBytes[0]
  uuid.D4[0] := (randomBytes[0] and $0F) or $80;     // Set the variant to 10xx
  Move(randomBytes[1], uuid.D4[1], 7);               // Remaining 7 bytes

  Result := uuid;
end;

// Optionally remove this to make a regular unit for FPC too
{$IFDEF FPC}
var i: Integer;
begin
  Randomize;
  for i := 0 to 30 do
    writeln(GUIDToString(GenerateUUIDv7).ToLower);
  readln;
{$ELSE}
initialization
  Randomize;
{$ENDIF}
end.

by Jim McKeeth

Perl

Initialize a random array with rand(), get the current timestamp with Time::HiRes::time(), fill the array from the timestamp, set version and variant.

#!/usr/bin/env perl

use v5.16;
use Time::HiRes;

sub uuidv7 {
	my $type = shift() || "";

	# 16 random bytes (4 * 4)
	my $uuid = "";
	for (my $i = 0; $i < 4; $i++) {
		$uuid .= pack('I', int(rand(2 ** 32)));
	}

	# current timestamp in ms
	my $timestamp = int(Time::HiRes::time() * 1000);

	# timestamp
	substr($uuid, 0, 1, chr(($timestamp >> 40) & 0xFF));
	substr($uuid, 1, 1, chr(($timestamp >> 32) & 0xFF));
	substr($uuid, 2, 1, chr(($timestamp >> 24) & 0xFF));
	substr($uuid, 3, 1, chr(($timestamp >> 16) & 0xFF));
	substr($uuid, 4, 1, chr(($timestamp >>  8) & 0xFF));
	substr($uuid, 5, 1, chr($timestamp         & 0xFF));

	# version and variant
	substr($uuid, 6, 1, chr((ord(substr($uuid, 6, 1)) & 0x0F) | 0x70));
	substr($uuid, 8, 1, chr((ord(substr($uuid, 8, 1)) & 0x3F) | 0x80));

	return $uuid;
}

my $uuid_val = uuidv7('hex');
printf(unpack("H*", $uuid_val));

by Scott Baker

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()

Clojure

Initialize a random array with SecureRandom.nextBytes(), get the current timestamp with System.currentTimeMillis(), fill the array from the timestamp, set version and variant.

(ns uuidv7
  (:require [clojure.string :as str])
  (:import (java.security SecureRandom)))

(defn gen-uuid-v7 []
  (let [rand-array (byte-array 10)]
    (.nextBytes (SecureRandom.) rand-array)
    (concat
      ;; timestamp
      (map byte (.toByteArray (biginteger (System/currentTimeMillis))))
      ;; version
      [(bit-or (bit-and (first rand-array) 0x0F) 0x70)]
      [(nth rand-array 1)]
      ;; variant
      [(bit-or (bit-and (nth rand-array 2) 0x3F) 0x80)]
      (drop 3 rand-array))))

(defn uuid-to-string [uuid-bytes]
  (apply str (map #(format "%02x" %) uuid-bytes)))

(def uuid-bytes (gen-uuid-v7))
(println (uuid-to-string uuid-bytes))

by Saidone

Julia

Initialize a random array with rand(), get the current timestamp with time(), fill the array from the timestamp, set version and variant.

function uuidv7()
  # random bytes
  value = rand(UInt8, 16)

  # current timestamp
  timestamp = trunc(UInt64, time() * 1000)
  digits!(UInt8[0, 0, 0, 0, 0, 0, 0, 0], hton(timestamp), base=256) |> x -> copyto!(value, 1, x, 3, 6)

  # version and variant
  value[7] = value[7] & 0x0F | 0x70
  value[9] = value[9] & 0x3F | 0x80

  value
end

uuidv7() |> bytes2hex |> println

by lost

Erlang

Generate random bytes with crypto:strong_rand_bytes(), get the current timestamp with os:system_time(), set version and variant, combine everything together.

-module(uuidv7).

-export([generate/0, main/1]).

-spec generate() -> binary().
generate() ->
    <<RandA:12, RandB:62, _:6>> = crypto:strong_rand_bytes(10),
    UnixTsMs = os:system_time(millisecond),
    Ver = 2#0111,
    Var = 2#10,
    <<UnixTsMs:48, Ver:4, RandA:12, Var:2, RandB:62>>.

main(_) ->
    UUIDv7 = generate(),
    %% note: if you use an erlang release newer than OTP23,
    %%       there is binary:encode_hex/1,2
    io:format("~s~n", [[io_lib:format("~2.16.0b",[X]) || <<X:8>> <= UUIDv7]]).

by Stefan Grundmann

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[6..]);

    // current timestamp in ms
    const timestamp: u48 = @intCast(std.time.milliTimestamp());

    // timestamp
    std.mem.writeInt(u48, value[0..6], timestamp, .big);

    // 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();
    std.debug.print("{s}\n", .{std.fmt.bytesToHex(uuid_val, .upper)});
}

by Frank Denis

Crystal

Initialize a random slice with rand.random_bytes, get the current timestamp with Time.utc, fill the slice from the timestamp, set version and variant.

require "uuid"

class Uuidv7
  @@rand = Random.new

  @uuid : UUID

  forward_missing_to @uuid

  def initialize
    # random bytes
    value = @@rand.random_bytes(16)

    # current timestamp in ms
    timestamp = Time.utc.to_unix_ms

    # timestamp
    timestamp_bytes = StaticArray(UInt8, 8).new(0).to_slice
    IO::ByteFormat::BigEndian.encode(timestamp, timestamp_bytes)
    timestamp_bytes[2..].copy_to(value)

    # version and variant
    value[6] = (value[6] & 0x0F) | 0x70
    value[8] = (value[8] & 0x0F) | 0x80

    @uuid = UUID.new(value)
  end
end

puts Uuidv7.new.hexstring

by lost

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

Gleam

Initialize a random bit array with crypto.strong_rand_bytes(), get the current timestamp with os.system_time(), write the timestamp, set version and variant into the array.

import gleam/int
import gleam/io

@external(erlang, "crypto", "strong_rand_bytes")
pub fn strong_random_bytes(a: Int) -> BitArray

@external(erlang, "os", "system_time")
pub fn system_time(time_unit: Int) -> Int

pub fn uuiv7() -> BitArray {
  let assert <<a:size(12), b:size(62), _:size(6)>> = strong_random_bytes(10)

  let timestamp = system_time(1000)
  let version = 7
  let var = 10

  <<timestamp:48, version:4, a:12, var:2, b:62>>
}

pub fn to_string(ints: BitArray) -> String {
  to_base16(ints, 0, "")
}

fn to_base16(ints: BitArray, position: Int, acc: String) -> String {
  case position {
    8 | 13 | 18 | 23 -> to_base16(ints, position + 1, acc)
    _ ->
      case ints {
        <<i:size(4), rest:bits>> -> {
          to_base16(rest, position + 1, acc <> int.to_base16(i))
        }
        _ -> acc
      }
  }
}

pub fn main() {
  let uuid = uuiv7()
  io.debug(to_string(uuid))
}

by Pavel

Tcl

Initialize a random array with rand(), get the current timestamp with clock milliseconds, fill the array from the timestamp, set version and variant.

package require Tcl 8.6

namespace eval uuidv7 {
    namespace export uuidv7
}

proc ::uuidv7::generate { } {
    # random bytes
    set randomBytes {}
    for {set i 0} {$i < 16} {incr i} {
        lappend randomBytes [expr {int(rand() * 256)}]
    }

    # current timestamp in ms
    set timestamp_ms [expr {[clock milliseconds]}]

    # timestamp
    set timestamp_bytes {}
    for {set i 5} {$i >= 0} {incr i -1} {
        lappend timestamp_bytes [expr {($timestamp_ms >> ($i * 8)) & 0xFF}]
    }

    # version and variant
    set bytes [lreplace $randomBytes 0 5 {*}$timestamp_bytes]
    lset bytes 6 [expr {([lindex $bytes 6] & 0x0F) | 0x70}]
    lset bytes 8 [expr {([lindex $bytes 8] & 0x3F) | 0x80}]

    return [binary format c* $bytes]
}

proc ::uuidv7::tostring { uuid } {
    binary scan $uuid H* s
    return [string tolower $s]
}

puts [uuidv7::tostring [uuidv7::generate]]

by Ștefan Alecu

V

Initialize a random array with rand.bytes(), get the current timestamp with time.now(), fill the array from the timestamp, set version and variant.

import rand
import time

fn uuidv7() ![]u8 {
	mut value := rand.bytes(16)!

	// current timestamp in ms
	timestamp := u64(time.now().unix_milli())

	// timestamp
	value[0] = u8((timestamp >> 40) & 0xFF)
	value[1] = u8((timestamp >> 32) & 0xFF)
	value[2] = u8((timestamp >> 24) & 0xFF)
	value[3] = u8((timestamp >> 16) & 0xFF)
	value[4] = u8((timestamp >> 8) & 0xFF)
	value[5] = u8(timestamp & 0xFF)

	// version and variant
	value[6] = (value[6] & 0x0F) | 0x70
	value[8] = (value[8] & 0x3F) | 0x80

	return value
}

fn main() {
	uuid_val := uuidv7()!

	for _, val in uuid_val {
		print('${val:02x}')
	}
	println('')
}

by Laurent Cheylus

Emacs Lisp

Initialize a random array with random, get the current timestamp with current-time, fill the array from the timestamp, set version and variant.

(require 'cl-lib)

(defun uuidv7 ()
  "Generates an array representing the bytes of an UUIDv7 label."
  (let* ((timestamp (car (time-convert (current-time) 1000)))
         (timestamp-bytes
          (cl-loop for i from 5 downto 0
                   collect (logand (ash timestamp (* i -8)) #xFF)))
         (uuid (make-vector 16 0)))

    (cl-loop for i below 16 do
	     (aset uuid i
		   (if (< i 6)
		       (nth i timestamp-bytes)
		   (random 256))))

    (aset uuid 6 (logior (logand (elt uuid 6) #x0F) #x70))
    (aset uuid 8 (logior (logand (elt uuid 8) #x3F) #x80))

    uuid))

(defun bytes-to-hexstring (bytes)
  "Converts a vector of bytes into a hexadecimal string."
  (cl-loop for byte across bytes
           concat (format "%02x" byte)))

(let ((uuid-bytes (uuidv7)))
  (message "%s" (bytes-to-hexstring uuid-bytes)))

by Ștefan Alecu

Vimscript

Initialize a random array with rand, get the current timestamp with localtime, fill the array from the timestamp, set version and variant.

function! s:uuidv7() abort
	let timestamp = localtime() * 1000
	let uuid = []

	for i in range(0, 15)
		if i < 6
			call add(uuid, and(timestamp >> (40 - 8*i), 255))
		else
			call add(uuid, rand() % 256)
		endif
	endfor

	let uuid[6] = or(and(uuid[6], 15), 112)
	let uuid[8] = or(and(uuid[8], 63), 128)

	return uuid
endfunction

function! s:bytes_to_hexstring(bytes) abort
	let hexstring = ''
	for byte in a:bytes
		let hex = printf("%02x", byte)
		let hexstring .= hex
	endfor
	return hexstring
endfunction

let uuid_bytes = s:uuidv7()
let hex_uuid = s:bytes_to_hexstring(uuid_bytes)
echo hex_uuid

by Ștefan Alecu

Nushell

Generate random bytes with random int, get the current timestamp with date now, set version and variant, combine everything together.

def "random uuid v7" [] {
  # timestamp in ms
  let timestamp_ms = (date now | into int) // 1_000_000
  let timestamp = $timestamp_ms | into binary | bytes at 0..=5 | bytes reverse

  # random bytes
  let rand =  1..=10 | each { random int 0..=255 | into binary | bytes at 0..=0  } | bytes collect

  # version and variant
  let version = $rand | bytes at 0..=0 | bits and 0x[0F] | bits or 0x[70]
  let variant = $rand | bytes at 2..=2 | bits and 0x[3F] | bits or 0x[80]

  [ $timestamp $version ($rand | bytes at 1..=1) $variant ($rand | bytes at 3..) ] | bytes collect
}

random uuid v7 | encode hex

by lost

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.