UUID v1 Generator
Generate time-based UUID v1 with embedded timestamp
…
Форматувати
Що таке UUID v1?
UUID v1 — версія UUID на основі часу, визначена в <strong>RFC 4122</strong>. Вона поєднує високоточну часову мітку, MAC-адресу та лічильник тактів для генерації унікальних ідентифікаторів.
UUID v1 розроблені для розподілених систем, де кілька машин мають незалежно генерувати UUID без координації. MAC-адреса гарантувала унікальність між машинами.
Сьогодні UUID v1 значною мірою застарів. UUID v7 вирішує ту саму проблему без витоку ідентифікатора хоста. RFC 9562 офіційно застаріває v1 на користь v6 і v7.
Анатомія UUID v1
UUID v1 упаковує кілька полів у 128 бітів:
| Поле | Розмір | Опис |
|---|---|---|
| time_low | 32 bits | 60-бітна часова мітка: 100-наносекундні інтервали з 15 жовтня 1582 (Gregorian-епоха UUID). Поле розділене на три частини (low, mid, hi). |
| time_mid | 16 bits | Номер версії: <code>0001</code> (v1). Займає 4 біти в полі time_hi_and_version. |
| time_hi_and_version | 16 bits | Лічильник тактів: 14-бітний лічильник, що зростає при зміні часової мітки назад. |
| clock_seq_hi_res | 8 bits | Маркер варіанта: 2 біти, встановлені в <code>10</code>, що ідентифікують UUID як RFC 4122. |
| clock_seq_low | 8 bits | Вузол: 48-бітна MAC-адреса. Якщо недоступна, використовується випадкове значення з встановленим бітом multicast. |
| node | 48 bits | Комбінація часової мітки і вузла гарантує унікальність без центральної координації. |
Поле послідовності годинника (clock_seq_hi_res + clock_seq_low) — це 14-бітний лічильник. Він збільшується щоразу, коли системний годинник рухається назад (наприклад, корекція NTP) або коли система перезавантажується без збереження останнього відомого timestamp. Це запобігає генерації дублікатів UUID, якщо годинник не є монотонно зростаючим.
Як кодується часова мітка
60-бітна часова мітка UUID v1 — не стандартна Unix-часова мітка:
- Початкова точка — Gregorian-епоха: 15 жовтня 1582 р. 00:00:00.00 UTC.
- Підраховується кількість 100-наносекундних інтервалів з цієї дати.
- Це дає 60-бітне ціле число.
- Ціле розділяється: 32 молодших біти → time_low, наступні 16 → time_mid, верхні 12 → time_hi.
- Чотири біти версії вставляються в time_hi_and_version.
- Для вилучення: об'єднайте time_hi, time_mid і time_low у 60-бітне ціле; відніміть зміщення Gregorian-епохи (122192928000000000 × 100нс) для отримання Unix-часу.
- Роздільна здатність 100нс дозволяє одному генератору виробляти до 10 мільйонів UUID на секунду.
UUID v7 використовує Unix-часові мітки в мілісекундах — значно простіше для декодування.
Міркування конфіденційності
UUID v1 містить MAC-адресу хоста в 48-бітному полі вузла, що створює проблеми конфіденційності:
MAC-адреса є постійною та унікальною. UUID v1 однозначно ідентифікує хост-машину. Два UUID v1 з однієї машини мають однакове поле вузла.
Багато реалізацій замінюють реальний MAC випадковим 48-бітним значенням із встановленим бітом multicast.
Сценарії використання
UUID v1 проти UUID v7
Порівняння UUID v1 і UUID v7 для нового проекту:
| Аспект | UUID v1 | UUID v7 |
|---|---|---|
| Епоха часової мітки | 15 жовтня 1582 (Gregorian) | 1 січня 1970 (Unix) |
| Точність часової мітки | 100 наносекунд | Мілісекунди |
| Лексикографічне сортування | Ні (поля переставлені) | Так |
| Конфіденційність | Розкриває MAC-адресу | Повністю випадковий вузол |
| Стандарт | RFC 4122 (2005), застарілий | RFC 9562 (2024) |
| Продуктивність індексів | Погана (несортовані вставки) | Відмінна (монотонні вставки) |
Для нових проектів використовуйте UUID v7 — суворе поліпшення порівняно з v1.
Приклади коду
Node.js не включає генератор UUID v1 до стандартної бібліотеки. Використовуйте пакет <code>uuid</code>:
// 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))
}