33 种编程语言的 UUIDv7 实现
UUIDv7 与广泛使用的 UUIDv4 等同类产品一样,都是 128 位唯一标识符。但与 v4 不同的是,UUIDv7 可进行时间排序,精度为 1 毫秒。通过结合时间戳和随机部分,UUIDv7 成为数据库(包括分布式数据库)中记录标识符的绝佳选择。
让我们简要探讨一下 UUIDv7 的结构,然后再看看 33 种语言中的零依赖实现(根据 Stack Overflow 调查排名)。
这些实现可能不是最快的,也不是最成语化的,但它们简洁易懂。许多代码示例都是交互式的(但结果是缓存的,所以你不会经常看到不同的 UUID)。
可以想象,我并不精通所有这些语言,所以如果你发现了错误,请提交拉取请求。我们也欢迎其他语言的用户提交拉取请求!
JavaScript • Python • SQL • Shell • Java • C# • C++ • C • PHP • PowerShell • Go • Rust • Kotlin • Ruby • Lua • Dart • Swift • R • Pascal • Perl • Elixir • Clojure • Julia • Erlang • Zig • Crystal • Nim • Gleam • Tcl • V • Emacs Lisp • Vimscript • Nushell
结构
UUIDv7 结构以字符串表示时是这样的:
0190163d-8694-739b-aea5-966c26f8ad91
└─timestamp─┘ │└─┤ │└───rand_b─────┘
ver │var
rand_a
128 位数值由几个部分组成:
timestamp
(48 bits) 是以毫秒为单位的 Unix 时间戳。ver
(4 bits) 是 UUID 版本 (7
)。rand_a
(12 bits) 是随机生成的。var
* (2 bits) 等于10
rand_b
(62 bits) 是随机生成的。
* 在字符串表示法中,每个符号编码 4 位十六进制数,因此示例中的 a 是 1010
,其中前两位是固定变量(10
),后两位是随机数。 因此得到的十六进制数可以是 8
(1000
)、9
(1001
)、a
(1010
) 或 b
(1011
)。
详见 RFC 9652。
JavaScript
使用 crypto.getRandomValues()
初始化随机数组,使用 Date.now()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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);
TypeScript 版本完全相同,唯一不同的是函数签名:
function uuidv7(): Uint8Array {
// ...
}
Python
使用 os.urandom()
初始化随机数组,使用 time.time()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 strftime()
(SQLite) 或 now()
(PostgreSQL) 获取当前时间戳部分,使用 random()
获取随机部分,将所有内容连接成 UUID
字符串。 SQLite(由 Fabio Lima 提供):
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
用 /dev/urandom
初始化随机字节字符串,用日期获取当前时间戳,用时间戳和字节字符串填充数组,设置版本和变量。
#!/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
使用 SecureRandom.nextBytes()
初始化随机数组,使用 System.currentTimeMillis()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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#
使用 RandomNumberGenerator.GetBytes()
初始化随机数组,使用 DateTimeOffset.UtcNow
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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++
使用 random_device
初始化随机数组,使用 system_clock.time_since_epoch()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
#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
使用 getentropy()
初始化随机数组,使用 timespec_get()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
#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
使用 random_bytes()
初始化随机字符串,使用 microtime()
获取当前时间戳,从时间戳中填充字符,设置版本和变量。
<?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
使用 Random.GetBytes()
初始化随机数组,使用 DateTimeOffset.UtcNow
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 rand.Read()
初始化随机数组,使用 time.Now()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 getrandom::getrandom()
初始化随机数组,使用 SystemTime::now()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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),
}
}
Kotlin
使用 SecureRandom.nextBytes()
初始化随机数组,使用 Instant.now()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 SecureRandom.random_bytes()
初始化随机数组,使用 Time.now
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 math.random()
初始化随机表,使用 os.time()
获取当前时间戳,根据时间戳填充列表,设置版本和变量。
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
使用 Random.nextInt()
初始化随机列表,使用 DateTime.now()
获取当前时间戳,根据时间戳填充列表,设置版本和变量。
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
使用 v7
函数扩展现有的 UUID
类型。 使用 UInt8.random()
初始化随机元组,使用 Date().timeIntervalSince1970
获取当前时间戳,根据时间戳填充元组,设置版本和变量。
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
使用 sample()
初始化随机向量,使用 Sys.time()
获取当前时间戳,根据时间戳填充向量,设置版本和变量。
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 = ''))
Pascal
使用 Random()
初始化随机数组,使用 DateTimeToUnix()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
// 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
使用 rand()
初始化随机数组,使用 Time::HiRes::time()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
#!/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
使用 crypto.strong_rand_bytes()
初始化随机列表,使用 os.system_time()
获取当前时间戳,根据时间戳填充列表,设置版本和变量。
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
使用 SecureRandom.nextBytes()
初始化随机数组,使用 System.currentTimeMillis()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
(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
使用 rand()
初始化随机数组,使用 time()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 crypto:strong_rand_bytes()
生成随机字节,使用 os:system_time()
获取当前时间戳,设置版本和变体,然后将所有内容组合在一起。
-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]]).
Zig
使用 std.crypto.random.bytes()
初始化随机数组,使用 std.time.milliTimestamp()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 rand.random_bytes
初始化随机片段,使用 Time.utc
获取当前时间戳,根据时间戳填充片段,设置版本和变量。
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
使用 random.rand()
初始化随机序列,使用 times.epochTime()
获取当前时间戳,根据时间戳填充序列,设置版本和变量。
import std/[times, strutils, sequtils, sysrand]
proc uuidv7(): seq[byte] =
# random bytes
result = urandom(16)
# 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()
Gleam
使用 crypto.strong_rand_bytes()
初始化一个随机比特数组,使用 os.system_time()
获取当前时间戳,然后将时间戳、设置版本和变量写入数组。
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
用 rand()
初始化一个随机数组,用时钟毫秒获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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
使用 rand.bytes()
初始化随机数组,使用 time.now()
获取当前时间戳,根据时间戳填充数组,设置版本和变量。
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('')
}
Emacs Lisp
用 random
初始化随机数组,用 current-time
获取当前时间戳,用时间戳填充数组,设置版本和变量。
(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
用 rand
初始化随机数组,用 localtime
获取当前时间戳,用时间戳填充数组,设置版本和变量。
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
用随机 int
生成随机字节,用日期 now
获取当前时间戳,设置版本和变量,然后将所有内容组合在一起。
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
最后的想法
UUID 规范的上一版本(RFC 4122)发布于 2005 年。 按 K 排序、按时间排序的 UUID 是我们急需的标准更新,希望它能在未来几年内为我们提供良好的服务。
你也许感兴趣的:
- 【程序员搞笑图片】数据类型简明指导
- 【外评】Rust,你错了
- 【外评】为什么人们对 Go 1.23 的迭代器设计感到愤怒?
- 华为自研编程语言“仓颉”来了!鸿蒙应用开发新语言,性能优于 Java、Go、Swift
- 【外评】JavaScript 变得很好
- 【外评】华为发布自己的编程语言 “仓颉”
- VBScript 废弃:时间表和后续步骤
- 【外评】BASIC 编程语言 60 岁了
- 【外评】为什么 ALGOL 是一种重要的编程语言?
- 【程序员搞笑图片】如果的话
你对本文的反应是: