Skip to content

Instantly share code, notes, and snippets.

@eevmanu
Created May 23, 2025 04:11
Show Gist options
  • Save eevmanu/7f1a2df61ecfc1e4e2b0d002abdedb99 to your computer and use it in GitHub Desktop.
Save eevmanu/7f1a2df61ecfc1e4e2b0d002abdedb99 to your computer and use it in GitHub Desktop.
re-implement the uuid v7 uuid7 uuidv7 from uuidgen (part of https://github.com/util-linux/util-linux ) as version 2.41 using source code as context into bash shell script using gemini-2.5-pro-preview-05-06 in one shot
#!/bin/bash
# Function to generate a UUID v7 string
# Implements the logic similar to libuuid's uuid_generate_time_v7
uuid_generate_time_v7() {
local ms
local ts_hex
local rand_hex
local byte6_val byte8_val
local ver_char rand_a_part1 rand_a_part2
local var_char rand_b_part1 rand_b_part2 rand_b_suffix
# 1. Get 48-bit timestamp in milliseconds (unix_ts_ms)
# GNU date specific for %N (nanoseconds)
if date +%s%N >/dev/null 2>&1; then
ms=$(( $(date +%s%N) / 1000000 ))
else
# Fallback for systems without %N (e.g., macOS default date)
# This will have lower precision for the millisecond part
# but is better than nothing.
local s
s=$(date +%s)
# Try to get microseconds if possible (e.g. gdate on macOS)
local us="000" # default to 000 milliseconds
if command -v gdate >/dev/null; then # GNU date on macOS
us=$(gdate +%N | cut -c1-3) # use first 3 digits of N for milli
elif [[ "$(uname)" == "Linux" ]]; then
# This case should ideally not be hit if the first date +%s%N failed
# but as a safeguard.
us=$(date +%N | cut -c1-3)
fi
ms=$(( s * 1000 + 10#$us )) # 10# to ensure base 10 for us
fi
# Format as 12 hex characters (6 bytes / 48 bits)
# Ensure it's exactly 12 characters, padding with leading zeros if necessary.
ts_hex=$(printf "%012x" "$ms")
# 2. Get 10 random bytes (for rand_a and rand_b parts)
# openssl is a good source of random hex. 10 bytes = 20 hex characters.
if command -v openssl >/dev/null; then
rand_hex=$(openssl rand -hex 10)
else
# Fallback if openssl is not available (less ideal, might be slower/less random quality)
# This generates 10 bytes, then converts to hex.
rand_hex=$(head -c 10 /dev/urandom | od -An -tx1 | tr -d ' \n' | sed 's/ //g')
# Ensure rand_hex is 20 characters long, pad if necessary (shouldn't be with od)
while ((${#rand_hex} < 20)); do rand_hex="0$rand_hex"; done
fi
# 3. Apply version (7) and variant (RFC 4122)
# Byte 6: (rand_hex characters 0-1)
# version = 7 (0111)
# uuid[6] = (rand_byte_6 & 0x0F) | 0x70
byte6_val=$(( (0x${rand_hex:0:2} & 0x0F) | 0x70 ))
ver_char=$(printf "%x" "$(( (byte6_val & 0xF0) >> 4 ))") # Should be '7'
rand_a_part1=$(printf "%x" "$((byte6_val & 0x0F))") # Lower nibble of modified byte 6
# Byte 7: (rand_hex characters 2-3)
# This is the rest of rand_a
rand_a_part2="${rand_hex:2:2}"
# Byte 8: (rand_hex characters 4-5)
# variant = 2 (10xx)
# uuid[8] = (rand_byte_8 & 0x3F) | 0x80
byte8_val=$(( (0x${rand_hex:4:2} & 0x3F) | 0x80 ))
var_char=$(printf "%x" "$(( (byte8_val & 0xC0) >> 6 ))") # Should be 2 (binary 10)
# resulting hex will be 8,9,a,b
rand_b_part1=$(printf "%x" "$((byte8_val & 0x3F))") # Lower 6 bits of modified byte 8
# Byte 9: (rand_hex characters 6-7)
# This is more of rand_b
rand_b_part2="${rand_hex:6:2}"
# Bytes 10-15: (rand_hex characters 8-19)
# The rest of rand_b
rand_b_suffix="${rand_hex:8:12}"
# 4. Assemble the UUID string
# xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
# M is version, N is variant
# Timestamp part 1 (8 hex chars)
# Timestamp part 2 (4 hex chars)
# Version (1 hex char from ver_char) + rand_a_part1 (1 hex char) + rand_a_part2 (2 hex chars)
# Variant (1 hex char from first nibble of byte8_val) + rand_b_part1 (1 hex char from second nibble of byte8_val) + rand_b_part2 (2 hex chars)
# Suffix (12 hex chars)
printf "%s-%s-%s%s%s-%s%s%s-%s\n" \
"${ts_hex:0:8}" \
"${ts_hex:8:4}" \
"$ver_char" \
"$rand_a_part1" \
"$rand_a_part2" \
"$(printf "%x" "$((byte8_val >> 4 & 0xF))")" \
"$(printf "%x" "$((byte8_val & 0xF))")" \
"$rand_b_part2" \
"$rand_b_suffix"
}
# Example usage:
# uuid_generate_time_v7
# for i in {1..5}; do uuid_generate_time_v7; done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment