Generador UUID v1
Genera UUID v1 basados en tiempo con marca temporal integrada
…
Formatear
¿Qué es UUID v1?
UUID v1 es la versión original de UUID, estandarizada en RFC 4122 (2005). Genera identificadores únicos combinando una marca de tiempo de alta precisión con la dirección MAC del host generador, más una secuencia de reloj corta para manejar la resolución por debajo de la marca de tiempo.
Debido a que la marca de tiempo está incorporada, los valores UUID v1 del mismo host son monótonamente crecientes con el tiempo, lo que los hace ordenados de forma natural. Esto fue diseñado para sistemas distribuidos donde cada nodo podía generar UUIDs de forma independiente sin coordinación.
Hoy UUID v1 es ampliamente reemplazado por UUID v7 (ordenable, sin filtrado de MAC) y UUID v4 (completamente aleatorio, privado). Permanece en uso en sistemas como Apache Cassandra y bases de datos distribuidas heredadas.
Anatomía de un UUID v1
Una cadena UUID v1 como 550e8400-e29b-11d4-a716-446655440000 codifica seis campos distintos:
| Campo | Tamaño | Descripción |
|---|---|---|
| time_low | 32 bits | Campo de 32 bits de bajo orden de la marca de tiempo gregoriana de 60 bits (intervalos de 100 nanosegundos desde el 15 de oct. de 1582) |
| time_mid | 16 bits | Campo de 16 bits del medio de la marca de tiempo de 60 bits |
| time_hi_and_version | 16 bits | 12 bits superiores de la marca de tiempo de 60 bits más el número de versión de 4 bits (siempre <code>1</code>) |
| clock_seq_hi_res | 8 bits | Campo de 6 bits de alto orden de la secuencia de reloj combinado con el marcador de variante RFC 4122 de 2 bits |
| clock_seq_low | 8 bits | 8 bits inferiores de la secuencia de reloj |
| node | 48 bits | Identificador de nodo de 48 bits — típicamente la dirección MAC de la interfaz de red generadora, o un valor aleatorio de 48 bits si no hay MAC disponible |
El campo de secuencia de reloj (clock_seq_hi_res + clock_seq_low) es un contador de 14 bits. Se incrementa cada vez que el reloj del sistema retrocede (p. ej. ajuste de NTP) o cuando el sistema se reinicia sin persistir la última marca de tiempo conocida. Esto previene la generación de UUIDs duplicados si el reloj no avanza de forma monótona.
Decodificando la Marca de Tiempo de UUID v1
La marca de tiempo de 60 bits está distribuida en tres campos del UUID. Para reconstruir el tiempo de generación:
- Extraiga
time_low(bytes 0–3),time_mid(bytes 4–5) ytime_hi(bytes 6–7, menos el nibble de versión) - Reensamblar:
(time_hi << 48) | (time_mid << 32) | time_low - El resultado es un conteo de 60 bits de
intervalos de 100 nanosegundosdesde el 15 de octubre de 1582 (la época del calendario gregoriano) - Reste el desplazamiento gregoriano-a-Unix: 122.192.928.000.000.000 (intervalos de 100 ns entre el 15 oct. 1582 y el 1 ene. 1970)
- Divida entre
10.000para convertir intervalos de 100 nanosegundos a milisegundos - Use el resultado como una
marca de tiempo Unix en milisegundospara construir un objeto Date - Formatee como
ISO 8601para salida legible por humanos
La precisión de la marca de tiempo es de 100 nanosegundos — mucho más fina que la precisión de milisegundos de UUID v7. Sin embargo, en la práctica la mayoría de los sistemas operativos no exponen resolución de reloj por debajo del milisegundo, por lo que los bits inferiores suelen ser cero o sintéticos.
Problemas de Privacidad
El inconveniente más significativo de UUID v1 es que incorpora la dirección MAC del host generador en el campo de nodo. Esto significa que cada UUID v1 lleva una huella digital permanente y globalmente única de la máquina que lo generó.
Un adversario que obtiene un UUID v1 puede determinar: (1) el tiempo aproximado en que se generó el ID, (2) la dirección MAC del host generador, y (3) analizando múltiples UUIDs, la tasa a la que se están generando IDs.
Por esta razón, UUID v1 nunca debería usarse como identificador público (p. ej. en URLs o respuestas de API) a menos que esté cómodo divulgando esta información. El propio RFC 4122 señala que un sistema puede usar un valor aleatorio de 48 bits en lugar de la dirección MAC, pero muchas implementaciones no lo hacen.
Cuándo UUID v1 Sigue Siendo Apropiado
UUID v1 vs UUID v7
UUID v7 es el sucesor moderno de UUID v1 para identificadores ordenados por tiempo. Aquí hay una comparación directa:
| Aspecto | UUID v1 | UUID v7 |
|---|---|---|
| Época / Base de Tiempo | Época gregoriana (15 oct. 1582) | Época Unix (1 ene. 1970) |
| Precisión | 100 nanosegundos | 1 milisegundo |
| Identificador de Nodo | Dirección MAC (filtra identidad del host) | Aleatorio (privado) |
| Privacidad | Filtra dirección MAC y marca de tiempo de generación | Sin información del host incorporada |
| Rendimiento de índice BD | Bueno — secuencial por host | Excelente — k-ordenable entre todos los generadores |
| Estándar | RFC 4122 (2005) | RFC 9562 (2024) |
Para proyectos nuevos, UUID v7 es el reemplazo recomendado para UUID v1. Proporciona garantías similares de ordenación temporal sin las implicaciones de privacidad de incorporar la dirección MAC del host.
Ejemplos de Código
La generación de UUID v1 no está disponible de forma nativa en navegadores o Node.js. Use el paquete 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))
}