Skip to main content

ps_uuid/methods/
system_time_to_ticks.rs

1use std::time::SystemTime;
2
3use crate::{Gregorian, UuidConstructionError, UUID};
4
5impl UUID {
6    /// Converts a `SystemTime` into an RFC 4122 timestamp (ticks).
7    ///
8    /// # Arguments
9    ///
10    /// * `time` - The `SystemTime` to convert.
11    ///
12    /// # Returns
13    ///
14    /// The number of 100-nanosecond intervals between the Gregorian epoch
15    /// (1582-10-15) and the provided `time`.
16    ///
17    /// # Errors
18    ///
19    /// - [`UuidConstructionError::TimestampBeforeEpoch`] if `time` is before
20    ///   the Gregorian epoch.
21    /// - [`UuidConstructionError::TimestampOverflow`] if `time` is so far in
22    ///   the future that the tick count exceeds \( 2^{60} - 1 \).
23    pub fn system_time_to_ticks(time: SystemTime) -> Result<u64, UuidConstructionError> {
24        // Calculate the duration since the Gregorian epoch.
25        // The `let-else` block provides a concise way to handle the error case.
26        let Ok(duration_since_epoch) = time.duration_since(Gregorian::epoch()) else {
27            return Err(UuidConstructionError::TimestampBeforeEpoch);
28        };
29
30        // Convert the duration to ticks. The `?` operator will handle a
31        // potential `DurationToTicksError` and convert it into a
32        // `UuidConstructionError` via the `From` trait implementation.
33        let ticks = Self::duration_to_ticks(duration_since_epoch)?;
34
35        Ok(ticks)
36    }
37}
38
39#[allow(clippy::cast_possible_truncation)]
40#[cfg(test)]
41mod tests {
42    use std::time::Duration;
43
44    use crate::DurationToTicksError;
45
46    use super::*;
47
48    // --- Tests for system_time_to_ticks ---
49
50    #[test]
51    fn time_now_is_valid() {
52        // A basic sanity check with the current time.
53        let now = SystemTime::now();
54        let result = UUID::system_time_to_ticks(now);
55        assert!(result.is_ok());
56    }
57
58    #[test]
59    fn time_before_gregorian_epoch_fails() {
60        // A time one second before the epoch should fail.
61        let before_epoch = Gregorian::epoch() - Duration::from_secs(1);
62        assert_eq!(
63            UUID::system_time_to_ticks(before_epoch),
64            Err(UuidConstructionError::TimestampBeforeEpoch)
65        );
66    }
67
68    #[test]
69    fn time_at_gregorian_epoch_is_zero_ticks() {
70        // The epoch itself should result in zero ticks.
71        assert_eq!(UUID::system_time_to_ticks(Gregorian::epoch()), Ok(0));
72    }
73
74    #[test]
75    fn time_far_in_future_causes_overflow() {
76        // A time that would generate 2^60 ticks, causing an overflow.
77        // The nanosecond calculation must use u128 to avoid overflowing here.
78        let overflow_nanos = (1u128 << 60) * 100;
79        let overflow_duration = Duration::new(
80            (overflow_nanos / 1_000_000_000) as u64,
81            (overflow_nanos % 1_000_000_000) as u32,
82        );
83        let future_time = Gregorian::epoch() + overflow_duration;
84
85        assert_eq!(
86            UUID::system_time_to_ticks(future_time),
87            Err(UuidConstructionError::TimestampOverflow)
88        );
89    }
90
91    // --- Tests for duration_to_ticks (from previous example) ---
92
93    #[test]
94    fn zero_duration_yields_zero_ticks() {
95        let duration = Duration::from_nanos(0);
96        assert_eq!(UUID::duration_to_ticks(duration), Ok(0));
97    }
98
99    #[test]
100    fn maximum_allowed_duration() {
101        let max_ticks = (1u128 << 60) - 1;
102        let nanos = max_ticks * 100;
103        let duration = Duration::new(
104            (nanos / 1_000_000_000) as u64,
105            (nanos % 1_000_000_000) as u32,
106        );
107        assert_eq!(UUID::duration_to_ticks(duration), Ok(max_ticks as u64));
108    }
109
110    #[test]
111    fn duration_that_overflows() {
112        let overflow_ticks = 1u128 << 60;
113        let nanos = overflow_ticks * 100;
114        let duration = Duration::new(
115            (nanos / 1_000_000_000) as u64,
116            (nanos % 1_000_000_000) as u32,
117        );
118        assert_eq!(
119            UUID::duration_to_ticks(duration),
120            Err(DurationToTicksError::TimestampOverflow)
121        );
122    }
123}