use crate::{UuidConstructionError, UUID};
use std::time::SystemTime;
impl UUID {
pub fn new_v6(
time: SystemTime,
clock_seq: u16,
node_id: [u8; 6],
) -> Result<Self, UuidConstructionError> {
let ticks = Self::system_time_to_ticks(time)?;
let time_high: u32 = ((ticks >> 28) & 0xFFFF_FFFF) as u32; let time_mid: u16 = ((ticks >> 12) & 0xFFFF) as u16; let time_low: u16 = (ticks & 0x0FFF) as u16;
Ok(Self::from_parts_v6(
time_high, time_mid, time_low, clock_seq, node_id,
))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
use super::*;
use crate::{Gregorian, Variant};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
fn manual(time: SystemTime, node: [u8; 6]) -> UUID {
let dur = time
.duration_since(Gregorian::epoch())
.expect("test timestamp should be after Gregorian epoch");
let ticks = dur.as_secs() * 10_000_000 + u64::from(dur.subsec_nanos() / 100);
let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32;
let time_mid = ((ticks >> 12) & 0xFFFF) as u16;
let time_low = (ticks & 0x0FFF) as u16;
let cs = 0x2A3Bu16;
UUID::from_parts_v6(time_high, time_mid, time_low, cs, node)
}
#[test]
fn builds_same_bytes_as_manual_version() {
let t = UNIX_EPOCH + Duration::from_secs(1_700_000_000); let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let auto =
UUID::new_v6(t, rand::random(), mac).expect("new_v6 should succeed for valid inputs");
let bytes = auto.as_bytes();
assert_eq!(auto.get_version(), Some(6));
assert_eq!(auto.get_variant(), Variant::OSF);
let manual = manual(t, mac);
assert_eq!(&bytes[0..8], &manual.as_bytes()[0..8]);
assert_eq!(&bytes[10..16], &mac);
}
#[test]
fn timestamp_before_gregorian_is_rejected() {
let ancient = UNIX_EPOCH - Duration::from_secs(17_834_668_800);
let err = UUID::new_v6(ancient, rand::random(), [0; 6])
.expect_err("new_v6 should reject timestamps before Gregorian epoch");
assert_eq!(err, UuidConstructionError::TimestampBeforeEpoch);
}
#[test]
fn timestamp_overflow_is_rejected() {
const MAX_TICKS: u64 = 0x0FFF_FFFF_FFFF_FFFF; let too_far = UNIX_EPOCH + Duration::from_nanos(MAX_TICKS + 1) * 100;
let err = UUID::new_v6(too_far, rand::random(), [0; 6])
.expect_err("new_v6 should reject timestamps that overflow");
assert_eq!(err, UuidConstructionError::TimestampOverflow);
}
#[test]
fn variant_and_version_bits_are_correct() {
let uuid = UUID::new_v6(SystemTime::now(), rand::random(), [1, 2, 3, 4, 5, 6])
.expect("new_v6 should succeed for valid inputs");
let b = uuid.as_bytes();
assert_eq!(b[8] >> 6, 0b10);
assert_eq!(b[6] >> 4, 0b0110);
}
#[test]
fn new_v6_rejects_time_before_1582_10_15() {
let before_gregorian = Gregorian::epoch() - Duration::from_secs(1);
let err = UUID::new_v6(before_gregorian, rand::random(), [0; 6])
.expect_err("new_v6 should reject timestamps before 1582-10-15");
assert_eq!(err, UuidConstructionError::TimestampBeforeEpoch);
}
#[test]
fn new_v6_rejects_time_after_5236_03_31() {
const MAX_TICKS: u64 = 0x0FFF_FFFF_FFFF_FFFF;
let overflow = UNIX_EPOCH + Duration::from_nanos(MAX_TICKS + 1) * 100;
let err = UUID::new_v6(overflow, rand::random(), [0; 6])
.expect_err("new_v6 should reject timestamps after 5236-03-31");
assert_eq!(err, UuidConstructionError::TimestampOverflow);
}
}