ps_uuid/methods/
new_v1.rs1use crate::{UuidConstructionError, UUID};
2use std::time::SystemTime;
3
4impl UUID {
5 pub fn new_v1(
12 time: SystemTime,
13 clock_seq: u16,
14 node_id: [u8; 6],
15 ) -> Result<Self, UuidConstructionError> {
16 let ticks = Self::system_time_to_ticks(time)?;
20
21 let time_low: u32 = (ticks & 0xFFFF_FFFF) as u32;
25 let time_mid: u16 = ((ticks >> 32) & 0xFFFF) as u16;
26 let time_hi: u16 = ((ticks >> 48) & 0x0FFF) as u16; Ok(Self::from_parts_v1(
33 time_low, time_mid, time_hi, clock_seq, node_id,
34 ))
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 #![allow(clippy::expect_used)]
41 use std::time::{Duration, UNIX_EPOCH};
42
43 use super::*;
44 use crate::{Gregorian, Variant, UUID};
45
46 fn manual(time: SystemTime, node: [u8; 6]) -> UUID {
48 let dur = time
49 .duration_since(Gregorian::epoch())
50 .expect("test timestamp should be after Gregorian epoch");
51 let ticks = dur.as_secs() * 10_000_000 + u64::from(dur.subsec_nanos() / 100);
52
53 let time_low = (ticks & 0xFFFF_FFFF) as u32;
54 let time_mid = ((ticks >> 32) & 0xFFFF) as u16;
55 let time_hi = ((ticks >> 48) & 0x0FFF) as u16;
56 let cs = 0x2A3Bu16; UUID::from_parts_v1(time_low, time_mid, time_hi, cs, node)
59 }
60
61 #[test]
62 fn builds_same_bytes_as_manual_version() {
63 let t = UNIX_EPOCH + Duration::from_secs(1_700_000_000); let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
65
66 let auto =
67 UUID::new_v1(t, rand::random(), mac).expect("new_v1 should succeed for valid inputs");
68 let bytes = auto.as_bytes();
69
70 assert_eq!(auto.get_version(), Some(1));
72 assert_eq!(auto.get_variant(), Variant::OSF);
73
74 let manual = manual(t, mac);
76 assert_eq!(&bytes[0..8], &manual.as_bytes()[0..8]);
78 assert_eq!(&bytes[10..16], &mac);
80 }
81
82 #[test]
83 fn timestamp_before_gregorian_is_rejected() {
84 let ancient = UNIX_EPOCH - Duration::from_secs(17_834_668_800);
86 let err = UUID::new_v1(ancient, rand::random(), [0; 6])
87 .expect_err("new_v1 should reject timestamps before Gregorian epoch");
88 assert_eq!(err, UuidConstructionError::TimestampBeforeEpoch);
89 }
90
91 #[test]
92 fn timestamp_overflow_is_rejected() {
93 let too_far = UNIX_EPOCH
95 + Duration::from_secs(
96 u64::try_from(1u128 << 60).expect("2^60 should fit into u64") / 10_000_000
97 + 12_219_292_800
98 + 10,
99 );
100
101 let err = UUID::new_v1(too_far, rand::random(), [0; 6])
102 .expect_err("new_v1 should reject timestamps that overflow");
103 assert_eq!(err, UuidConstructionError::TimestampOverflow);
104 }
105
106 #[test]
107 fn variant_and_version_bits_are_correct() {
108 let uuid = UUID::new_v1(SystemTime::now(), rand::random(), [1, 2, 3, 4, 5, 6])
109 .expect("new_v1 should succeed for valid inputs");
110 let b = uuid.as_bytes();
111
112 assert_eq!(b[8] >> 6, 0b10);
114 assert_eq!(b[6] >> 4, 0b0001);
116 }
117
118 #[test]
119 fn new_v1_rejects_time_before_1582_10_15() {
120 let before_gregorian = Gregorian::epoch() - Duration::from_secs(1);
122
123 eprintln!("{before_gregorian:?}");
124
125 let err = UUID::new_v1(before_gregorian, rand::random(), [0; 6])
126 .expect_err("timestamp prior to Gregorian epoch must fail");
127
128 assert!(
129 matches!(err, UuidConstructionError::TimestampBeforeEpoch),
130 "wrong error variant: got {err:?}"
131 );
132 }
133
134 #[test]
135 fn new_v1_rejects_time_after_5236_03_31() {
136 const MAX_TICKS: u64 = 0x0FFF_FFFF_FFFF_FFFF;
138 let overflow = SystemTime::UNIX_EPOCH + Duration::from_nanos(MAX_TICKS + 1) * 100;
139
140 let err = UUID::new_v1(overflow, rand::random(), [0; 6])
141 .expect_err("timestamp overflow must fail");
142
143 assert!(
144 matches!(err, UuidConstructionError::TimestampOverflow),
145 "wrong error variant: got {err:?}"
146 );
147 }
148}