UUID v1 Generator

Generate time-based UUID v1 with embedded timestamp

Форматировать

Количество:
Note:UUID v1 кодирует MAC-адрес хоста и высокоточную временную метку, раскрывая информацию о машине-генераторе. Используйте только тогда, когда это приемлемо.

Что такое 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_low32 bits60-битная временная метка: 100-наносекундные интервалы с 15 октября 1582 (Gregorian-эпоха UUID). Поле разделено на три части (low, mid, hi).
time_mid16 bitsНомер версии: <code>0001</code> (v1). Занимает 4 бита в поле time_hi_and_version.
time_hi_and_version16 bitsСчётчик тактов: 14-битный счётчик, увеличивающийся при изменении временной метки назад.
clock_seq_hi_res8 bitsМаркер варианта: 2 бита, установленные в <code>10</code>, идентифицирующие UUID как RFC 4122.
clock_seq_low8 bitsУзел: 48-битный MAC-адрес. Если недоступен, используется случайное значение с установленным bitом multicast.
node48 bitsКомбинация временной метки и узла гарантирует уникальность без центральной координации.

Поле последовательности часов (clock_seq_hi_res + clock_seq_low) — это 14-битный счётчик. Он увеличивается каждый раз, когда системные часы идут назад (например, корректировка NTP) или когда система перезапускается без сохранения последнего известного timestamp. Это предотвращает генерацию дублирующихся UUID, если часы не являются монотонно возрастающими.

Как кодируется временная метка

60-битная временная метка UUID v1 — не стандартная Unix-временная метка:

  1. Начальная точка — Gregorian-эпоха: 15 октября 1582 г. 00:00:00.00 UTC.
  2. Подсчитывается количество 100-наносекундных интервалов с этой даты.
  3. Это даёт 60-битное целое число.
  4. Целое разделяется: 32 младших бита → time_low, следующие 16 → time_mid, верхние 12 → time_hi.
  5. Четыре бита версии вставляются в time_hi_and_version.
  6. Для извлечения: объедините time_hi, time_mid и time_low в 60-битное целое; вычтите смещение Gregorian-эпохи (122192928000000000 × 100нс) для получения Unix-времени.
  7. Разрешение 100нс позволяет одному генератору производить до 10 миллионов UUID в секунду.

UUID v7 использует Unix-временные метки в миллисекундах — значительно проще для декодирования.

Соображения конфиденциальности

UUID v1 содержит MAC-адрес хоста в 48-битном поле узла, что создаёт проблемы конфиденциальности:

MAC-адрес постоянен и уникален. UUID v1 однозначно идентифицирует хост-машину. Два UUID v1 с одной машины имеют одинаковое поле узла.

Многие реализации заменяют реальный MAC случайным 48-битным значением с установленным bitом multicast для обозначения случайности.

Сценарии использования

Устаревшие системы
Многие корпоративные системы используют UUID v1. Совместимость — основная причина продолжения использования.
Отладка и диагностика
Временная метка позволяет извлечь точное время создания записи.
Упорядоченная генерация на одной машине
На одной машине UUID v1 сортируются по временной метке, но не лексикографически в строковом представлении.
Протоколы Cassandra и некоторые NoSQL
Apache Cassandra использует UUID v1 (<code>timeuuid</code>) для упорядоченных столбцов и первичных ключей.
Корреляция событий с отметкой времени
Когда нужен ID, позволяющий восстановить время события. Для новых систем предпочтительнее UUID v7.
Тестирование и симуляция
Генерация UUID в известном диапазоне времени для имитации хронологии.

UUID v1 против UUID v7

Сравнение UUID v1 с UUID v7 для нового проекта:

АспектUUID v1UUID 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>:

JavaScript (browser)
// 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.789Z
Python
import 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"
Go
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))
}

Часто задаваемые вопросы

Безопасно ли использовать UUID v1 в новых проектах?
Технически безопасно, но не рекомендуется. UUID v7 решает те же проблемы с лучшими свойствами. Для новых проектов выбирайте v7.
Что такое тактовый последовательный счётчик?
14-битное поле для предотвращения дубликатов: если системные часы идут назад, счётчик увеличивается.
Все ли UUID v1 содержат MAC-адрес?
Нет. Если MAC недоступен или реализация выбирает конфиденциальность, используется случайное 48-битное значение с установленным bitом multicast.
Почему строковое представление UUID v1 не сортируется по времени?
Потому что поля временной метки переставлены согласно RFC 4122. UUID v6 исправляет это.
Как извлечь временную метку из UUID v1?
Возьмите символы позиций 0-7 (time_low), 9-12 (time_mid) и 14-17 (time_hi). Объедините как <code>time_hi + time_mid + time_low</code>. Вычтите смещение Gregorian-эпохи. Инструмент UUID Decoder делает это автоматически.