UUID v1 Generator
Generate time-based UUID v1 with embedded timestamp
…
Formatera
Vad är UUID v1?
UUID v1 är den ursprungliga UUID-versionen, standardiserad i RFC 4122 (2005). Den genererar unika identifierare genom att kombinera en högprecisionstidsstämpel med MAC-adressen för den genererande värden, plus en kort klocksekvens för att hantera sub-tidsstämpelupplösning.
Eftersom tidsstämpeln är inbäddad ökar UUID v1-värden från samma värd monotont med tiden — vilket gör dem naturligt ordnade. Detta var utformat för distribuerade system där varje nod kunde generera UUID:n oberoende utan koordination.
Idag är UUID v1 till stor del ersatt av UUID v7 (sorterbar, ingen MAC-läcka) och UUID v4 (helt slumpmässigt, privat). Det används fortfarande i system som Apache Cassandra och äldre distribuerade databaser.
Anatomy of a UUID v1
En UUID v1-sträng som 550e8400-e29b-11d4-a716-446655440000 kodar sex distinkta fält:
| Fält | Storlek | Beskrivning |
|---|---|---|
| time_low | 32 bits | 32-bitars lågt fält av den 60-bitars gregorianska tidsstämpeln (100-nanosekundintervall sedan 15 okt 1582) |
| time_mid | 16 bits | Mellersta 16-bitars fält av den 60-bitars tidsstämpeln |
| time_hi_and_version | 16 bits | Topp 12 bitar av den 60-bitars tidsstämpeln plus det 4-bitars versionsnumret (alltid <code>1</code>) |
| clock_seq_hi_res | 8 bits | 6-bitars klocksekvensfält kombinerat med den 2-bitars RFC 4122-variantmarkören |
| clock_seq_low | 8 bits | Lägre 8 bitar av klocksekvensen |
| node | 48 bits | 48-bitars nodidentifierare — vanligtvis MAC-adressen för det genererande nätverksgränssnittet, eller ett slumpmässigt 48-bitarsvärde om ingen MAC är tillgänglig |
Klocksekvens-fältet (clock_seq_hi_res + clock_seq_low) är en 14-bitars räknare. Den ökas när systemklockan går bakåt (t.ex. NTP-justering) eller när systemet startar om utan att bevara den senast kända tidsstämpeln. Det förhindrar att duplicerade UUID:n genereras om klockan inte är monotont stigande.
Avkoda UUID v1-tidsstämpeln
Den 60-bitars tidsstämpeln är spridd över tre fält i UUID. För att rekonstruera genereringstiden:
- Extrahera
time_low(byte 0–3),time_mid(byte 4–5) ochtime_hi(byte 6–7, minus versionsnibble) - Sätt ihop:
(time_hi << 48) | (time_mid << 32) | time_low - Resultatet är ett 60-bitars antal
100-nanosekundintervallsedan den 15 oktober 1582 (den gregorianska kalenderepoken) - Subtrahera Gregoriansk-till-Unix-offset: 122 192 928 000 000 000 (100-ns intervall mellan 15 okt 1582 och 1 jan 1970)
- Dividera med
10 000för att konvertera 100-nanosekundintervall till millisekunder - Använd resultatet som en
Unix-millisekund-tidsstämpelför att skapa ett Date-objekt - Formatera som
ISO 8601för mänskligt läsbar utdata
Tidsstämpelprecisionen är 100 nanosekunder — mycket finare än UUID v7:s millisekundprecision. Men i praktiken exponerar de flesta operativsystem inte sub-millisekund klockupplösning, så de lägre bitarna är ofta noll eller syntetiserade.
Integritetsproblem
Den mest betydande nackdelen med UUID v1 är att det bäddar in MAC-adressen för den genererande värden i nodfältet. Det innebär att varje UUID v1 bär ett permanent, globalt unikt fingeravtryck av maskinen som genererade det.
En angripare som erhåller ett UUID v1 kan bestämma: (1) den ungefärliga tid som ID:t genererades, (2) MAC-adressen för den genererande värden, och (3) genom att analysera flera UUID:n, hastigheten med vilken ID:n genereras.
Av denna anledning bör UUID v1 aldrig användas som en publikt exponerad identifierare (t.ex. i URL:er eller API-svar) om du inte är bekväm med att avslöja denna information. RFC 4122 noterar själv att ett system kan använda ett slumpmässigt 48-bitarsvärde istället för MAC-adressen, men många implementationer gör det inte.
När UUID v1 fortfarande är lämpligt
UUID v1 vs UUID v7
UUID v7 är den moderna efterträdaren till UUID v1 för tidsordnade identifierare. Här är en direkt jämförelse:
| Aspekt | UUID v1 | UUID v7 |
|---|---|---|
| Epok / Tidsbas | Gregoriansk epok (15 okt 1582) | Unix-epok (1 jan 1970) |
| Precision | 100 nanosekunder | 1 millisekund |
| Nodidentifierare | MAC-adress (läcker värdidentitet) | Slumpmässig (privat) |
| Integritet | Läcker MAC-adress och genereringstidsstämpel | Ingen värdinformation inbäddad |
| DB-indexprestanda | Bra — sekventiellt per värd | Utmärkt — k-sorterbar över alla generatorer |
| Standard | RFC 4122 (2005) | RFC 9562 (2024) |
För nya projekt är UUID v7 den rekommenderade ersättningen för UUID v1. Det ger liknande tidsordningsgarantier utan integritetsimplikationerna av att bädda in värdens MAC-adress.
Kodexempel
UUID v1-generering är inte tillgänglig inbyggt i webbläsare eller Node.js. Använd npm-paketet uuid:
// Generate a UUID v1 using the Web Crypto API
function generateUuidV1() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
const gregorianOffset = 122192928000000000n
const t = ms * 10000n + gregorianOffset
const tLow = Number(t & 0xFFFFFFFFn)
const tMid = Number((t >> 32n) & 0xFFFFn)
const tHiVer = Number((t >> 48n) & 0x0FFFn) | 0x1000 // version 1
const clockSeq = (buf[8] & 0x3F) | 0x80 // variant 10xxxxxx
const clockSeqLow = buf[9]
const hex = (n, pad) => n.toString(16).padStart(pad, '0')
const node = [...buf.slice(10)].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex(tLow,8)}-${hex(tMid,4)}-${hex(tHiVer,4)}-${hex(clockSeq,2)}${hex(clockSeqLow,2)}-${node}`
}
// Extract the embedded timestamp from a UUID v1
function extractTimestamp(uuid) {
const parts = uuid.split('-')
const tHex = parts[2].slice(1) + parts[1] + parts[0]
const t = BigInt('0x' + tHex)
const ms = (t - 122192928000000000n) / 10000n
return new Date(Number(ms))
}
const id = generateUuidV1()
console.log(id) // e.g. "1eb5e8b0-6b4d-11ee-9c45-a1f2b3c4d5e6"
console.log(extractTimestamp(id)) // e.g. 2023-10-15T12:34:56.789Zimport uuid from datetime import datetime, timezone # Generate UUID v1 (uses MAC address by default) uid = uuid.uuid1() print(uid) # Extract embedded timestamp # uuid.time is 100-ns intervals since Oct 15, 1582 GREGORIAN_OFFSET = 122192928000000000 # 100-ns intervals ts_100ns = uid.time ts_ms = (ts_100ns - GREGORIAN_OFFSET) // 10000 dt = datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc) print(dt.isoformat()) # e.g. "2023-10-15T12:34:56.789000+00:00"
package main
import (
"fmt"
"time"
"github.com/google/uuid" // go get github.com/google/uuid
)
func main() {
id, _ := uuid.NewUUID() // UUID v1
fmt.Println(id)
// Extract timestamp from UUID v1
// uuid.Time is 100-ns ticks since Oct 15, 1582
t := id.Time()
sec := int64(t)/1e7 - 12219292800 // convert to Unix seconds
nsec := (int64(t) % 1e7) * 100
ts := time.Unix(sec, nsec).UTC()
fmt.Println(ts.Format(time.RFC3339Nano))
}