UUID v1 Generator
Generate time-based UUID v1 with embedded timestamp
…
Formatieren
Was ist UUID v1?
UUID v1 ist die originale UUID-Version, standardisiert in RFC 4122 (2005). Sie generiert eindeutige Bezeichner durch Kombination eines hochpräzisen Zeitstempels mit der MAC-Adresse des generierenden Hosts sowie einer kurzen Taktsequenz für die Auflösung unterhalb des Zeitstempels.
Da der Zeitstempel eingebettet ist, sind UUID v1-Werte vom selben Host über die Zeit monoton steigend — was sie natürlich geordnet macht. Dies wurde für verteilte Systeme entwickelt, in denen jeder Knoten UUIDs unabhängig ohne Koordination generieren konnte.
Heute wird UUID v1 weitgehend durch UUID v7 (sortierbar, kein MAC-Leck) und UUID v4 (vollständig zufällig, privat) verdrängt. Es wird weiterhin in Systemen wie Apache Cassandra und Legacy-Distributed-Datenbanken verwendet.
Aufbau einer UUID v1
Ein UUID v1-String wie 550e8400-e29b-11d4-a716-446655440000 kodiert sechs verschiedene Felder:
| Feld | Größe | Beschreibung |
|---|---|---|
| time_low | 32 bits | 32-Bit-Low-Feld des 60-Bit-Gregorianischen Zeitstempels (100-Nanosekunden-Intervalle seit dem 15. Okt. 1582) |
| time_mid | 16 bits | Mittleres 16-Bit-Feld des 60-Bit-Zeitstempels |
| time_hi_and_version | 16 bits | Obere 12 Bits des 60-Bit-Zeitstempels plus die 4-Bit-Versionsnummer (immer <code>1</code>) |
| clock_seq_hi_res | 8 bits | 6-Bit-Taktsequenz-High-Feld kombiniert mit dem 2-Bit-RFC 4122-Variantenmarker |
| clock_seq_low | 8 bits | Untere 8 Bits der Taktsequenz |
| node | 48 bits | 48-Bit-Knotenbezeichner — typischerweise die MAC-Adresse der generierenden Netzwerkschnittstelle, oder ein zufälliger 48-Bit-Wert, wenn keine MAC verfügbar ist |
Das Taktsequenzfeld (clock_seq_hi_res + clock_seq_low) ist ein 14-Bit-Zähler. Er wird erhöht, wenn die Systemuhr rückwärts läuft (z. B. NTP-Anpassung) oder wenn das System neu startet, ohne den letzten bekannten Zeitstempel beizubehalten. Dies verhindert duplizierte UUIDs, wenn die Uhr nicht monoton steigend läuft.
Den UUID v1-Zeitstempel dekodieren
Der 60-Bit-Zeitstempel ist über drei Felder in der UUID verteilt. Um die Generierungszeit zu rekonstruieren:
time_low(Bytes 0–3),time_mid(Bytes 4–5) undtime_hi(Bytes 6–7, minus das Versions-Nibble) extrahieren- Zusammensetzen:
(time_hi << 48) | (time_mid << 32) | time_low - Das Ergebnis ist eine 60-Bit-Zählung von
100-Nanosekunden-Intervallenseit dem 15. Oktober 1582 (der Gregorianische Kalender-Epoche) - Den Gregorianisch-zu-Unix-Offset subtrahieren: 122.192.928.000.000.000 (100-ns-Intervalle zwischen dem 15. Okt. 1582 und dem 1. Jan. 1970)
- Durch
10.000dividieren, um 100-Nanosekunden-Intervalle in Millisekunden umzurechnen - Das Ergebnis als
Unix-Millisekunden-Zeitstempelverwenden, um ein Date-Objekt zu erstellen - Als
ISO 8601für menschenlesbare Ausgabe formatieren
Die Zeitstempel-Präzision beträgt 100 Nanosekunden — weit feiner als UUID v7's Millisekunden-Präzision. In der Praxis legen die meisten Betriebssysteme jedoch keine Sub-Millisekunden-Uhrenauflösung offen, sodass die unteren Bits oft null oder synthetisiert sind.
Datenschutzbedenken
Der bedeutendste Nachteil von UUID v1 ist, dass es die MAC-Adresse des generierenden Hosts im Knotenfeld einbettet. Das bedeutet, dass jede UUID v1 einen permanenten, global eindeutigen Fingerabdruck der Maschine trägt, die sie generiert hat.
Ein Angreifer, der eine UUID v1 erhält, kann bestimmen: (1) die ungefähre Zeit der ID-Generierung, (2) die MAC-Adresse des generierenden Hosts, und (3) durch Analyse mehrerer UUIDs, die Rate, mit der IDs generiert werden.
Aus diesem Grund sollte UUID v1 niemals als öffentlich zugänglicher Bezeichner verwendet werden (z. B. in URLs oder API-Antworten), es sei denn, Sie sind damit einverstanden, diese Informationen preiszugeben. RFC 4122 selbst weist darauf hin, dass ein System stattdessen einen zufälligen 48-Bit-Wert anstelle der MAC-Adresse verwenden kann, aber viele Implementierungen tun dies nicht.
Wann UUID v1 noch angemessen ist
UUID v1 vs UUID v7
UUID v7 ist der moderne Nachfolger von UUID v1 für zeitgeordnete Bezeichner. Hier ist ein direkter Vergleich:
| Aspekt | UUID v1 | UUID v7 |
|---|---|---|
| Epoche / Zeitbasis | Gregorianische Epoche (15. Okt. 1582) | Unix-Epoche (1. Jan. 1970) |
| Präzision | 100 Nanosekunden | 1 Millisekunde |
| Knotenbezeichner | MAC-Adresse (gibt Host-Identität preis) | Zufällig (privat) |
| Datenschutz | Gibt MAC-Adresse und Generierungszeitstempel preis | Keine Host-Informationen eingebettet |
| DB-Index-Performance | Gut — sequentiell pro Host | Hervorragend — k-sortierbar über alle Generatoren |
| Standard | RFC 4122 (2005) | RFC 9562 (2024) |
Für neue Projekte ist UUID v7 der empfohlene Ersatz für UUID v1. Es bietet ähnliche Zeitordnungsgarantien ohne die Datenschutzimplikationen der Einbettung der Host-MAC-Adresse.
Code-Beispiele
UUID v1-Generierung ist nicht nativ in Browsern oder Node.js verfügbar. Verwenden Sie das uuid-npm-Paket:
// 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))
}