Quick Answer: Decode UUIDv7 in One Command
To extract the embedded timestamp from a UUIDv7, isolate the first 48 bits (the first 12 hexadecimal characters, skipping the first hyphen) and convert that big-endian value into a Unix epoch millisecond timestamp.
Linux (Bash + GNU Coreutils)
UUID="018f43a8-b647-7000-811c-6d9b4b9b00e9"
# 1. Strip the hyphen and extract the first 12 hex characters (48 bits)
HEX_MS="${UUID:0:8}${UUID:9:4}"
# 2. Convert hex to decimal milliseconds
MS_DEC=$((16#$HEX_MS))
# 3. Format using GNU date via seconds and remaining milliseconds
SEC=$((MS_DEC / 1000))
MILLI=$((MS_DEC % 1000))
date -d "@$SEC" -u +"%Y-%m-%d %H:%M:%S.${MILLI} UTC"
Windows (PowerShell CLI)
$uuid = "018f43a8-b647-7000-811c-6d9b4b9b00e9"
# Extract 48-bit hex prefix, parse to Int64, and map to DateTimeOffset
$hexMs = ($uuid.Substring(0, 8) + $uuid.Substring(9, 4))
$ms = [System.Convert]::ToInt64($hexMs, 16)
[System.DateTimeOffset]::FromUnixTimeMilliseconds($ms).UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss.fff 'UTC'")
---
128-Bit Memory Layout of UUIDv7 (RFC 9562)
The IETF RFC 9562 defines the bit allocation for UUID version 7. Unlike legacy UUIDv4 identifiers that rely entirely on pseudo-random entropy, UUIDv7 provides time-ordered locality by dedicating its most significant bits to an absolute Unix timestamp.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | rand_a |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field Breakdown
* **`unix_ts_ms` (Bits 0β47 / Bytes 0β5)**: A 48-bit unsigned integer representing the number of milliseconds elapsed since the Unix epoch (`1970-01-01T00:00:00Z`). It is encoded in **big-endian (network byte order)**.
* **`ver` (Bits 48β51 / Nibble 12)**: The 4-bit version identifier, set to `0111` binary (`0x7`).
* **`rand_a` (Bits 52β63 / Nibbles 13β15)**: 12 bits of pseudo-random data or sub-millisecond precision extensions.
* **`var` (Bits 64β65)**: The 2-bit variant field defined by RFC 4122/9562, fixed to `10` binary.
* **`rand_b` (Bits 66β127)**: The remaining 62 bits of entropy to guarantee uniqueness across distributed nodes.
Because the first 48 bits map sequentially from left to right, decoding does not require parsing complex record structures. You only need to extract the first 6 bytes.
---
Native Linux Parsing via Bash and GNU Coreutils
Modern Linux environments provide the tools required to parse 48-bit integers without installing external utilities like Node.js, Python, or Ruby.
018f43a8-b647-7000-811c-6d9b4b9b00e9
^^^^^^^^ ^^^^
8 chars 4 chars -> Total 12 hex chars (48 bits / 6 bytes)
In Bash, extract the high-order bits using parameter expansion:
UUID="018f43a8-b647-7000-811c-6d9b4b9b00e9"
HEX="${UUID:0:8}${UUID:9:4}"
Bash natively supports arbitrary-base integer arithmetic using the $((base#value)) syntax. Convert the 48-bit hexadecimal value to base-10:
Because date -d expects seconds rather than milliseconds, divide the base value by 1000 and append the remainder:
#!/usr/bin/env bash
set -euo pipefail
decode_uuidv7() {
local uuid="$1"
local clean_uuid="${uuid//-/}"
local hex_ts="${clean_uuid:0:12}"
# Base-16 arithmetic expansion
local epoch_ms=$((16#$hex_ts))
local epoch_sec=$((epoch_ms / 1000))
local millis=$((epoch_ms % 1000))
# Format directly to ISO-8601 UTC
printf "UUID: %s\n" "$uuid"
printf "Epoch Milliseconds: %d\n" "$epoch_ms"
printf "Timestamp UTC: %s.%03dZ\n" \
"$(date -u -d "@$epoch_sec" +'%Y-%m-%dT%H:%M:%S')" \
"$millis"
}
decode_uuidv7 "018f43a8-b647-7000-811c-6d9b4b9b00e9"
---
Native Windows Parsing via PowerShell and the .NET Runtime
Windows PowerShell and PowerShell Core have direct access to the Base Class Library (BCL), allowing native, high-performance byte manipulation without third-party modules.
The Endianness Problem with `System.Guid`
A common pitfall on Windows is passing the UUID directly to `[System.Guid]::Parse()`.
The internal storage of Microsoft's Guid structure reflects legacy Mixed-Endian (Little-Endian for Data1, Data2, and Data3 fields) layouts for compatibility with COM/RPC interfaces.
# CAUTION: [System.Guid]::ToByteArray() swaps bytes in Data1, Data2, Data3
$guid = [System.Guid]::Parse("018f43a8-b647-7000-811c-6d9b4b9b00e9")
$bytes = $guid.ToByteArray()
# $bytes[0..3] will be reversed relative to the RFC 9562 big-endian text representation!
To avoid endianness corruption when processing raw byte buffers, parse the string directly or handle the explicit big-endian byte array via System.Convert and [Microsoft's System.BitConverter](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter).
Direct Bitwise and BCL Implementation
The most direct, allocation-light method in PowerShell uses [System.Convert] combined with [Microsoft's System.DateTimeOffset](https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset):
function Get-UUIDv7Timestamp {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidatePattern('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-7[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$')]
[string]$Guid
)
process {
# Extract the 12 hex characters containing the 48-bit timestamp
$hexTimestamp = $Guid.Substring(0, 8) + $Guid.Substring(9, 4)
# Parse base-16 directly into a 64-bit signed integer
$epochMilliseconds = [System.Convert]::ToInt64($hexTimestamp, 16)
# Instantiate UTC DateTimeOffset directly from epoch ms
$dto = [System.DateTimeOffset]::FromUnixTimeMilliseconds($epochMilliseconds)
[PSCustomObject]@{
UUID = $Guid
EpochMilliseconds = $epochMilliseconds
TimestampUtc = $dto.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
}
}
}
Get-UUIDv7Timestamp -Guid "018f43a8-b647-7000-811c-6d9b4b9b00e9"
Parsing Raw 16-Byte Buffers with `System.BitConverter`
If your PowerShell pipeline ingests raw 16-byte binary files (e.g., database dumps, stream sockets), use bitwise shifting with `System.BitConverter` while accounting for system architecture endianness:
# Example: 16-byte raw UUIDv7 binary array
[byte[]]$rawUuid = @(
0x01, 0x8F, 0x43, 0xA8, 0xB6, 0x47, 0x70, 0x00,
0x81, 0x1C, 0x6D, 0x9B, 0x4B, 0x9B, 0x00, 0xE9
)
# Extract first 6 bytes and pad to 8 bytes for Int64 conversion
[byte[]]$paddedBytes = @(0, 0) + $rawUuid[0..5]
# System.BitConverter expects Little-Endian on modern x64 hardware
if ([System.BitConverter]::IsLittleEndian) {
[System.Array]::Reverse($paddedBytes)
}
$epochMs = [System.BitConverter]::ToInt64($paddedBytes, 0)
$date = [System.DateTimeOffset]::FromUnixTimeMilliseconds($epochMs).UtcDateTime
Write-Output "Extracted UTC Date: $($date.ToString('o'))"
---
Binary Memory Representation and Bitwise Extraction
Understanding the binary mechanics helps diagnose corrupted IDs or incorrect offset alignment.
UUIDv7: 018f43a8-b647-7000-811c-6d9b4b9b00e9
1. Hex to Binary Breakdown (First 64 Bits)
| Byte Index | Hex Value | Binary Representation | Field Assignment |
|---|
| **Byte 0** | `01` | `00000001` | `unix_ts_ms [47:40]` |
| **Byte 1** | `8f` | `10001111` | `unix_ts_ms [39:32]` |
| **Byte 2** | `43` | `01000011` | `unix_ts_ms [31:24]` |
| **Byte 3** | `a8` | `10101000` | `unix_ts_ms [23:16]` |
| **Byte 4** | `b6` | `10110110` | `unix_ts_ms [15:8]` |
| **Byte 5** | `47` | `01000111` | `unix_ts_ms [7:0]` |
| **Byte 6** | `70` | `0111 0000` | `ver (0111)` \ | `rand_a (0000)` |
| **Byte 7** | `00` | `00000000` | `rand_a [7:0]` |
2. Hex Value Reconstruction
Combine the first 6 bytes into a 48-bit hex string:
$$\text{0x018F43A8B647} = 1,714,727,466,567 \text{ ms}$$
3. Unix Millisecond Conversion
Divide by 1000 to extract seconds and milliseconds:
$$\text{Seconds} = \lfloor 1714727466567 / 1000 \rfloor = 1714727466 \implies \text{2024-05-03 09:11:06 UTC}$$
$$\text{Milliseconds} = 1714727466567 \pmod{1000} = 567 \text{ ms}$$
Resulting Timestamp: 2024-05-03T09:11:06.567Z
---
Critical Edge Cases: Monotonicity and Distributed Clocks
While UUIDv7 provides natural time-ordering capabilities, production systems introduce constraints defined by hardware limitations and distributed network conditions.
Node A (NTP +15ms) ---> [ 018f43a8-b647... (ms: 567) ]
Node B (NTP -20ms) ---> [ 018f43a8-b630... (ms: 544) ] <-- Generated *after* Node A
1. Millisecond Precision Limits
According to **RFC 9562 Section 5.7**, the core `unix_ts_ms` field is limited strictly to millisecond precision.
- Sub-millisecond accuracy cannot be guaranteed from the first 48 bits alone.
- While implementations *may* pack fractional sub-millisecond bins into the 12-bit
rand_a field, this behavior is optional and varies across language libraries. - Do not rely on the
unix_ts_ms field for microsecond-accurate event sequencing or distributed race-condition arbitration.
2. NTP Desynchronization and Clock Drift
Because the primary sort key is generated by the local machine's system clock:
* Sorting UUIDv7 values across independent servers depends entirely on the accuracy of the local **Network Time Protocol (NTP)** daemons.
* If Node A's clock leads Node B's clock by 50ms, a UUIDv7 generated on Node A will sort *after* a UUIDv7 generated on Node B, even if Node B generated its record later in absolute physical time.
* Database systems relying on UUIDv7 for B-Tree indexing preserve index locality, but cross-node ingestion may experience slight out-of-order writes during clock drift.
---
Inspecting data structures shouldn't require pulling heavy runtimes into your toolchain. A simple 48-bit bitmask and timestamp extraction can be handled directly by standard operating system tools.
Bloated Approach:
[npm install uuid-tool] -> [12 packages] -> [Node Runtime ~40MB] -> Parse 6 Bytes
Minimalist Approach:
[Bash Parameter Expansion] -> [$((16#HEX))] -> [GNU date] -> Parse 6 Bytes
Overhead Comparison
| Pipeline Component | Runtime Dependency | Memory Footprint | External Packages Required |
|---|
| **Node.js Script** | Node.js V8 Engine (`node`) | ~35 MB β 50 MB | `uuid` or custom parsing module |
| **Web-Based Decoders** | Browser Engine (Chromium/Gecko) | ~150 MB β 500 MB | JavaScript tracking + remote network roundtrip |
| **Native Bash / GNU** | GNU Coreutils (`date`, `bash`) | < 2 MB | **Zero** (Built into OS) |
| **PowerShell / .NET** | .NET CLR Engine (`pwsh`) | Embedded within OS | **Zero** (Native BCL) |
Relying on built-in utilities like Bash arithmetic and the .NET Base Class Library minimizes supply chain exposure, ensures scripts run reliably in locked-down production servers, and eliminates runtime overhead for basic systems tasks.