Générateur UUID v1
Génère des UUID v1 basés sur le temps avec horodatage intégré
…
Formater
Qu'est-ce qu'UUID v1 ?
UUID v1 est la version UUID originale, standardisée dans RFC 4122 (2005). Elle génère des identifiants uniques en combinant un horodatage haute précision avec l'adresse MAC de l'hôte générateur, plus une courte séquence d'horloge pour gérer la résolution sous-horodatage.
Comme l'horodatage est intégré, les valeurs UUID v1 du même hôte sont monotoniquement croissantes dans le temps — les rendant naturellement ordonnées. Cela était conçu pour les systèmes distribués où chaque noeud pouvait générer des UUID indépendamment sans coordination.
Aujourd'hui UUID v1 est largement supplanté par UUID v7 (triable, sans divulgation MAC) et UUID v4 (entièrement aléatoire, privé). Il reste utilisé dans des systèmes comme Apache Cassandra et les bases de données distribuées legacy.
Anatomie d'un UUID v1
Une chaîne UUID v1 comme 550e8400-e29b-11d4-a716-446655440000 encode six champs distincts :
| Champ | Taille | Description |
|---|---|---|
| time_low | 32 bits | Champ bas de 32 bits de l'horodatage grégorien de 60 bits (intervalles de 100 nanosecondes depuis le 15 oct. 1582) |
| time_mid | 16 bits | Champ milieu de 16 bits de l'horodatage de 60 bits |
| time_hi_and_version | 16 bits | 12 bits supérieurs de l'horodatage de 60 bits plus le numéro de version de 4 bits (toujours <code>1</code>) |
| clock_seq_hi_res | 8 bits | Champ haut de séquence d'horloge de 6 bits combiné avec le marqueur de variante RFC 4122 de 2 bits |
| clock_seq_low | 8 bits | 8 bits inférieurs de la séquence d'horloge |
| node | 48 bits | Identifiant de noeud de 48 bits — généralement l'adresse MAC de l'interface réseau génératrice, ou une valeur aléatoire de 48 bits si aucune MAC n'est disponible |
Le champ de séquence d'horloge (clock_seq_hi_res + clock_seq_low) est un compteur de 14 bits. Il est incrémenté chaque fois que l'horloge système recule (ex. ajustement NTP) ou lors du redémarrage du système sans persister le dernier horodatage connu. Cela empêche la génération de UUID en double si l'horloge n'avance pas de manière monotone.
Décodage de l'horodatage UUID v1
L'horodatage de 60 bits est dispersé dans trois champs de l'UUID. Pour reconstruire le temps de génération :
- Extraire
time_low(octets 0-3),time_mid(octets 4-5) ettime_hi(octets 6-7, moins le nibble de version) - Réassembler :
(time_hi << 48) | (time_mid << 32) | time_low - Le résultat est un compte de 60 bits d<code>intervalles de 100 nanosecondes</code> depuis le 15 octobre 1582 (lepoch du calendrier grégorien)
- Soustraire l'offset grégorien-vers-Unix : 122 192 928 000 000 000 (intervalles de 100 ns entre le 15 oct. 1582 et le 1er jan. 1970)
- Diviser par
10 000pour convertir les intervalles de 100 nanosecondes en millisecondes - Utiliser le résultat comme
horodatage Unix en millisecondespour construire un objet Date - Formater en
ISO 8601pour une sortie lisible par l'humain
La précision de l'horodatage est de 100 nanosecondes — bien plus fine que la précision milliseconde d'UUID v7. Cependant, en pratique, la plupart des systèmes d'exploitation n'exposent pas une résolution d'horloge sous-milliseconde, donc les bits inférieurs sont souvent à zéro ou synthétisés.
Problèmes de confidentialité
L'inconvénient le plus significatif d'UUID v1 est qu'il intègre l'adresse MAC de l'hôte générateur dans le champ node. Cela signifie que chaque UUID v1 porte une empreinte permanente et globalement unique de la machine qui l'a généré.
Un adversaire qui obtient un UUID v1 peut déterminer : (1) le temps approximatif où l'identifiant a été généré, (2) l'adresse MAC de l'hôte générateur, et (3) en analysant plusieurs UUID, le taux auquel les identifiants sont générés.
Pour cette raison, UUID v1 ne devrait jamais être utilisé comme identifiant public (ex. dans les URL ou les réponses d'API) sauf si vous êtes à l'aise de divulguer ces informations. RFC 4122 lui-même note qu'un système peut utiliser une valeur aléatoire de 48 bits à la place de l'adresse MAC, mais de nombreuses implémentations ne le font pas.
Quand UUID v1 est encore approprié
UUID v1 vs UUID v7
UUID v7 est le successeur moderne d'UUID v1 pour les identifiants ordonnés dans le temps. Voici une comparaison directe :
| Aspect | UUID v1 | UUID v7 |
|---|---|---|
| Epoch / Base temporelle | Epoch grégorien (15 oct. 1582) | Epoch Unix (1er jan. 1970) |
| Précision | 100 nanosecondes | 1 milliseconde |
| Identifiant de noeud | Adresse MAC (divulgue l'identité de l'hôte) | Aléatoire (privé) |
| Confidentialité | Divulgue l'adresse MAC et l'horodatage de génération | Aucune information sur l'hôte intégrée |
| Performance d'index BD | Bonne — séquentielle par hôte | Excellente — k-sortable sur tous les générateurs |
| Standard | RFC 4122 (2005) | RFC 9562 (2024) |
Pour les nouveaux projets, UUID v7 est le remplacement recommandé pour UUID v1. Il fournit des garanties similaires d'ordre temporel sans les implications de confidentialité liées à l'intégration de l'adresse MAC de l'hôte.
Exemples de code
La génération UUID v1 n'est pas disponible nativement dans les navigateurs ou Node.js. Utilisez le package npm 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))
}