How does uuid::Builder::from_rfc4122_timestamp enable time-based UUID generation with millisecond precision?

uuid::Builder::from_rfc4122_timestamp creates UUID version 1 identifiers by encoding a 60-bit timestamp (100-nanosecond intervals since October 15, 1582), a clock sequence, and a node ID into the UUID structure, enabling deterministic, time-ordered identifiers with millisecond-level precision while maintaining RFC 4122 compliance. The function allows manual control over timestamp, clock sequence, and node ID components that would normally be generated automatically.

Understanding UUID Version 1 Structure

use uuid::Uuid;
 
fn uuid_v1_structure() {
    // UUID v1 consists of:
    // - 60-bit timestamp (100ns intervals since Gregorian epoch)
    // - 14-bit clock sequence (prevents duplicates on clock regression)
    // - 48-bit node ID (typically MAC address)
    
    // The timestamp provides roughly millisecond-level granularity
    // 100ns = 0.1 microseconds = 0.0001 milliseconds
    
    // Total: 128 bits
    // - 60 bits: timestamp
    // - 14 bits: clock sequence  
    // - 48 bits: node ID
    // - 6 bits: version/variant markers
}

UUID v1 encodes time, clock sequence, and node into a unique identifier.

Basic from_rfc4122_timestamp Usage

use uuid::Builder;
use uuid::timestamp::{Timestamp, ClockSequence, Context};
 
fn basic_usage() {
    // Create a timestamp from the current time
    let context = Context::new(rand::random());
    let ts = Timestamp::now(context);
    
    // Build UUID from timestamp
    let uuid = Builder::from_rfc4122_timestamp(&ts)
        .into_uuid();
    
    println!("UUID v1: {}", uuid);
    println!("Version: {:?}", uuid.get_version());
    // Version: Some(V1) - Time-based
}

from_rfc4122_timestamp constructs a UUID from timestamp components.

Manual Timestamp Construction

use uuid::timestamp::{Timestamp, ClockSequence, Context};
use uuid::Builder;
 
fn manual_timestamp() {
    // Create timestamp with specific clock sequence
    let context = Context::new(42);  // Clock sequence
    let ts = Timestamp::now(context);
    
    // Build UUID from this timestamp
    let uuid = Builder::from_rfc4122_timestamp(&ts).into_uuid();
    
    // The timestamp encodes:
    // - time_low: bits 0-31 of timestamp
    // - time_mid: bits 32-47 of timestamp
    // - time_hi_and_version: bits 48-59 with version marker
}

The timestamp is constructed with a clock sequence context.

Understanding the 100-Nanosecond Timestamp

use uuid::timestamp::{Timestamp, Context};
use uuid::Builder;
 
fn timestamp_precision() {
    // RFC 4122 uses 100-nanosecond intervals since October 15, 1582
    // This is the Gregorian epoch (start of Gregorian calendar)
    
    // Precision: 100ns = 0.0001 milliseconds = 10,000 intervals per millisecond
    // This is finer than millisecond precision!
    
    // Convert from Unix timestamp (seconds since 1970)
    // to RFC 4122 timestamp (100ns intervals since 1582)
    
    // Unix epoch: January 1, 1970
    // Gregorian epoch: October 15, 1582
    // Difference: 122,192,928,000,000,000 100ns intervals
    
    const GREGORIAN_UNIX_OFFSET: u64 = 122_192_928_000_000_000;
    
    // Current time in 100ns intervals
    let now_unix_ms = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_millis() as u64;
    
    let now_100ns = (now_unix_ms * 10_000) + GREGORIAN_UNIX_OFFSET;
    // But Timestamp::now handles this conversion
}

The timestamp uses 100-nanosecond precision, finer than milliseconds.

Extracting Timestamp from UUID

use uuid::Uuid;
use uuid::timestamp::Timestamp;
use std::time::SystemTime;
 
fn extract_timestamp() {
    // Generate a v1 UUID
    let uuid = Uuid::now_v1();
    
    // Extract the timestamp
    let ts = uuid.get_timestamp().unwrap();
    
    // Get the raw counter (100ns intervals)
    let counter = ts.counter();
    println!("Timestamp counter: {}", counter);
    
    // Convert to actual time
    // The counter is 100ns intervals since Gregorian epoch
    // Subtract Gregorian-Unix offset to get Unix timestamp
    
    // For display purposes:
    let seconds = counter / 10_000_000;  // Convert 100ns intervals to seconds
    let nanos_remaining = (counter % 10_000_000) * 100;  // Nanoseconds
    
    println!("Timestamp: {} seconds, {} nanoseconds", seconds, nanos_remaining);
}

The timestamp can be extracted from an existing UUID v1.

Clock Sequence and Context

use uuid::timestamp::{Context, ClockSequence};
use uuid::Builder;
 
fn clock_sequence() {
    // Clock sequence prevents duplicates when:
    // - Clock goes backwards (NTP adjustment, manual change)
    // - System restarts with stale clock
    // - UUIDs generated on different machines might collide
    
    // Context holds the clock sequence
    let context1 = Context::new(0x1234);
    let context2 = Context::new(0x5678);
    
    // Same timestamp, different clock sequences = different UUIDs
    let ts1 = uuid::timestamp::Timestamp::now(context1);
    let ts2 = uuid::timestamp::Timestamp::now(context2);
    
    let uuid1 = Builder::from_rfc4122_timestamp(&ts1).into_uuid();
    let uuid2 = Builder::from_rfc4122_timestamp(&ts2).into_uuid();
    
    // uuid1 and uuid2 have same time but different clock_seq
    // This prevents collisions on different machines
}

The clock sequence prevents collisions across machines.

Node ID Configuration

use uuid::timestamp::{Timestamp, Context, NodeId};
use uuid::Builder;
 
fn node_id() {
    // Node ID is typically the MAC address (48 bits)
    // But can be any unique identifier
    
    // Automatic node ID (from MAC address if available)
    let context = Context::new(rand::random());
    
    // Custom node ID
    let custom_node = NodeId::from_bytes([0x01, 0x23, 0x45, 0x67, 0x89, 0xAB]);
    
    // Extract node from UUID
    let uuid = Uuid::now_v1();
    if let Some(node) = uuid.get_node_id() {
        println!("Node ID: {:?}", node);
    }
    
    // The node ID combined with timestamp and clock sequence
    // ensures global uniqueness
}

The node ID provides machine uniqueness.

Building UUID with Custom Components

use uuid::Builder;
use uuid::timestamp::{Timestamp, Context};
 
fn custom_components() {
    // Create a timestamp with specific values
    let context = Context::new(0x1234);  // Clock sequence
    
    // Build from explicit timestamp
    // Timestamp represents 100ns intervals since Gregorian epoch
    let counter: u64 = 122_192_928_000_000_000 + (1_000_000 * 10_000);  // 1M ms after Unix epoch
    
    let ts = Timestamp::from_rfc4122(context, counter);
    
    let uuid = Builder::from_rfc4122_timestamp(&ts)
        .into_uuid();
    
    println!("Custom timestamp UUID: {}", uuid);
}

Custom components enable reproducible UUID generation.

Millisecond Precision in Practice

use uuid::Uuid;
use std::time::{SystemTime, Duration};
 
fn millisecond_generation() {
    // Generate UUIDs at millisecond intervals
    let uuid1 = Uuid::now_v1();
    
    std::thread::sleep(Duration::from_millis(1));
    
    let uuid2 = Uuid::now_v1();
    
    // The timestamps will differ (at least 1ms = 10,000 100ns intervals)
    let ts1 = uuid1.get_timestamp().unwrap();
    let ts2 = uuid2.get_timestamp().unwrap();
    
    let diff = ts2.counter().saturating_sub(ts1.counter());
    println!("Counter difference: {} 100ns intervals", diff);
    println!("Approximately {} ms", diff / 10_000);
}

UUIDs generated milliseconds apart have distinguishable timestamps.

Time-Ordered UUIDs

use uuid::{Uuid, Builder};
use uuid::timestamp::Context;
 
fn time_ordering() {
    // UUID v1 IDs are naturally time-ordered
    let mut uuids: Vec<Uuid> = Vec::new();
    
    for _ in 0..5 {
        uuids.push(Uuid::now_v1());
        std::thread::sleep(std::time::Duration::from_millis(10));
    }
    
    // UUIDs are sortable by timestamp
    uuids.sort();
    // Sorted UUIDs will be in chronological order
    
    // This is useful for:
    // - Database indexing (time-ordered inserts)
    // - Event ordering
    // - Log correlation
}

UUID v1 timestamps enable natural time ordering.

Handling Clock Regression

use uuid::timestamp::{Timestamp, Context, ClockSequence};
use uuid::Builder;
 
fn clock_regression() {
    // When system clock goes backwards:
    // - NTP adjustment
    // - Manual clock change
    // - VM restore from snapshot
    
    // The clock sequence prevents duplicates
    // When regression is detected, clock_seq is incremented
    
    // Example: Simulating regression
    let context = Context::new(0x0000);
    
    // "Current" time
    let ts1 = Timestamp::from_rfc4122(context.clone(), 100_000_000);
    
    // "Earlier" time (clock went backwards)
    // The Context handles this by incrementing clock sequence
    let ts2 = Timestamp::from_rfc4122(context, 50_000_000);
    
    // Despite earlier timestamp, clock_seq changed
    // So UUIDs remain unique
}

The clock sequence handles clock regression gracefully.

Comparison with Other UUID Versions

use uuid::Uuid;
 
fn version_comparison() {
    // UUID v1: Time-based (what we're discussing)
    let v1 = Uuid::now_v1();
    // Pros: Time-ordered, sortable
    // Cons: Reveals timestamp, machine info
    
    // UUID v4: Random
    let v4 = Uuid::new_v4();
    // Pros: No timing info revealed
    // Cons: Not time-ordered, random sort
    
    // UUID v7: Unix timestamp-based (newer)
    // v7 uses milliseconds since Unix epoch directly
    // Simpler than v1's Gregorian 100ns intervals
    
    // v1 provides higher precision (100ns vs 1ms for v7)
    // But v7 is simpler and uses Unix epoch
}

UUID v1 offers higher precision than newer time-based alternatives.

Reproducible UUIDs for Testing

use uuid::Builder;
use uuid::timestamp::{Timestamp, Context, NodeId};
 
fn reproducible_uuids() {
    // For testing, create reproducible UUIDs
    let context = Context::new(0x1234);  // Fixed clock sequence
    let counter = 122_192_928_000_000_000u64 + 1_000_000 * 10_000;  // Fixed timestamp
    
    let ts = Timestamp::from_rfc4122(context, counter);
    let uuid = Builder::from_rfc4122_timestamp(&ts).into_uuid();
    
    // This UUID is deterministic - same inputs = same output
    println!("Reproducible UUID: {}", uuid);
    
    // Regenerate with same values
    let context2 = Context::new(0x1234);
    let ts2 = Timestamp::from_rfc4122(context2, counter);
    let uuid2 = Builder::from_rfc4122_timestamp(&ts2).into_uuid();
    
    assert_eq!(uuid, uuid2);  // Always same
}

Fixed inputs produce reproducible UUIDs for testing.

Security Considerations

use uuid::{Uuid, Builder};
use uuid::timestamp::Context;
 
fn security_considerations() {
    // UUID v1 reveals:
    // 1. Timestamp of generation
    // 2. Node ID (often MAC address)
    // 3. Potentially process/machine info
    
    // This can be a privacy concern:
    let uuid = Uuid::now_v1();
    let ts = uuid.get_timestamp().unwrap();
    let node = uuid.get_node_id().unwrap();
    
    // Timestamp reveals when UUID was created
    // Node ID reveals which machine created it
    
    // For security-sensitive applications:
    // - Use UUID v4 (random) for privacy
    // - Use UUID v7 for time-ordering without machine info
    // - Consider hashing the node ID
    
    // Alternative: use custom random node
    let random_node = uuid::timestamp::NodeId::from_bytes(rand::random::<[u8; 6]>());
}

UUID v1 exposes timestamp and machine information.

Database Indexing Benefits

use uuid::Uuid;
 
fn database_benefits() {
    // Time-ordered UUIDs improve database performance
    // B-tree indexes benefit from sequential inserts
    
    let mut uuids: Vec<Uuid> = Vec::new();
    
    // Generate sequential UUIDs
    for _ in 0..1000 {
        uuids.push(Uuid::now_v1());
        std::thread::sleep(std::time::Duration::from_micros(100));
    }
    
    // UUIDs are naturally ordered by time
    // Inserting into indexed column:
    // - B-tree stays balanced
    // - Less page splitting
    // - Better cache locality
    
    // Compare with UUID v4:
    // Random insertions cause more page splits
    // B-tree requires more maintenance
}

Time-ordered UUIDs improve database index performance.

Converting Between Timestamp Formats

use uuid::timestamp::{Timestamp, Context};
use uuid::Builder;
use std::time::SystemTime;
 
fn timestamp_conversion() {
    // From SystemTime to RFC 4122 timestamp
    let now = SystemTime::now();
    let duration = now.duration_since(SystemTime::UNIX_EPOCH).unwrap();
    
    // Unix timestamp (seconds since 1970) to 100ns intervals since 1582
    // Formula: unix_nanos / 100 + GREGORIAN_UNIX_OFFSET
    
    const GREGORIAN_UNIX_OFFSET_NS: u64 = 122_192_928_000_000_000;  // In 100ns units
    
    // Create timestamp manually
    let unix_ns = duration.as_nanos() as u64;
    let counter = (unix_ns / 100) + GREGORIAN_UNIX_OFFSET_NS;
    
    let context = Context::new(rand::random());
    let ts = Timestamp::from_rfc4122(context, counter);
    
    let uuid = Builder::from_rfc4122_timestamp(&ts).into_uuid();
    
    // The uuid crate handles this conversion in Timestamp::now
}

Convert between Unix timestamp and RFC 4122 format.

Practical Example: Event Sourcing

use uuid::{Uuid, Builder};
use uuid::timestamp::{Timestamp, Context};
use serde::{Serialize, Deserialize};
 
#[derive(Serialize, Deserialize)]
struct Event {
    id: Uuid,
    timestamp: u64,  // Unix timestamp in milliseconds
    data: String,
}
 
fn event_sourcing() {
    let context = Context::new(rand::random());
    
    // Create event with time-based UUID
    let event = Event {
        id: Uuid::now_v1(),
        timestamp: std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64,
        data: "User logged in".to_string(),
    };
    
    // UUID provides:
    // 1. Unique identifier
    // 2. Time ordering (for event replay)
    // 3. Millisecond precision (sufficient for most events)
    
    // Events can be sorted by UUID:
    let mut events: Vec<Event> = get_events();
    events.sort_by_key(|e| e.id);  // Chronological order
}

Time-based UUIDs enable ordered event sourcing.

Builder Pattern

use uuid::Builder;
use uuid::timestamp::Timestamp;
 
fn builder_pattern() {
    // from_rfc4122_timestamp returns a Builder
    // which can be further customized
    
    let context = uuid::timestamp::Context::new(0x1234);
    let ts = Timestamp::now(context);
    
    let uuid = Builder::from_rfc4122_timestamp(&ts)
        // Additional customization possible
        .into_uuid();  // Finalize into UUID
    
    // The Builder allows:
    // - Setting version/variant bits
    // - Customizing fields
    // - Converting to UUID
    
    // For v1, most fields are set from timestamp
}

The builder pattern enables UUID customization.

Summary Table

use uuid::Uuid;
 
fn summary() {
    // | Component        | Size    | Purpose                          |
    // |------------------|---------|----------------------------------|
    // | timestamp        | 60 bits | Time in 100ns intervals          |
    // | clock_seq        | 14 bits | Prevents duplicates on regression|
    // | node_id          | 48 bits | Machine identifier              |
    // | version/variant  | 6 bits  | UUID version markers            |
    
    // Precision: 100 nanoseconds = 0.0001 milliseconds
    // Range: ~1582 to ~5236 (centuries)
    
    // Key functions:
    // - Uuid::now_v1(): Generate with current time
    // - Builder::from_rfc4122_timestamp(): Build from components
    // - uuid.get_timestamp(): Extract timestamp
}

UUID v1 combines timestamp, clock sequence, and node ID.

Synthesis

Quick reference:

use uuid::{Uuid, Builder};
use uuid::timestamp::{Timestamp, Context};
 
// Generate UUID v1 with current time
let uuid = Uuid::now_v1();
 
// Build UUID from custom timestamp
let context = Context::new(0x1234);
let ts = Timestamp::now(context);
let uuid = Builder::from_rfc4122_timestamp(&ts).into_uuid();
 
// Extract timestamp from UUID
if let Some(ts) = uuid.get_timestamp() {
    println!("Timestamp counter: {}", ts.counter());
    println!("Clock sequence: {:?}", ts.clock_sequence());
}

When to use UUID v1:

use uuid::Uuid;
 
// Use UUID v1 when you need:
// - Time-ordered identifiers
// - Millisecond precision
// - Database-friendly sequential inserts
// - Event ordering across systems
 
// Avoid UUID v1 when:
// - Privacy is important (reveals timestamp/machine)
// - You don't want sequential IDs
// - Security-sensitive applications

Key insight: Builder::from_rfc4122_timestamp provides fine-grained control over UUID v1 generation by allowing manual specification of the timestamp, clock sequence, and node ID components. The RFC 4122 timestamp uses 100-nanosecond intervals since October 15, 1582 (the Gregorian epoch), which provides precision even finer than milliseconds—10,000 intervals per millisecond. This precision level enables generating multiple unique UUIDs within a single millisecond while maintaining time ordering. The clock sequence prevents collisions when clocks regress or systems restart, and the node ID ensures uniqueness across machines. While UUID v1's precision and ordering are valuable for database indexing and event ordering, the timestamp and node ID exposure is a privacy concern—UUID v4 (random) or v7 (Unix timestamp-based) may be preferable when privacy matters. The from_rfc4122_timestamp function enables reproducible UUID generation for testing by accepting fixed input values, while Uuid::now_v1() provides convenient generation with current timestamp and random clock sequence.