ulid/
time.rs

1use crate::{bitmask, Ulid};
2use std::time::{Duration, SystemTime};
3
4impl Ulid {
5    /// Creates a new Ulid with the current time (UTC)
6    ///
7    /// Using this function to generate Ulids will not guarantee monotonic sort order.
8    /// See [ulid::Generator] for a monotonic sort order.
9    /// # Example
10    /// ```rust
11    /// use ulid::Ulid;
12    ///
13    /// let my_ulid = Ulid::new();
14    /// ```
15    pub fn new() -> Ulid {
16        Ulid::from_datetime(crate::time_utils::now())
17    }
18
19    /// Creates a new Ulid using data from the given random number generator
20    ///
21    /// # Example
22    /// ```rust
23    /// use rand::prelude::*;
24    /// use ulid::Ulid;
25    ///
26    /// let mut rng = StdRng::from_os_rng();
27    /// let ulid = Ulid::with_source(&mut rng);
28    /// ```
29    pub fn with_source<R: rand::Rng>(source: &mut R) -> Ulid {
30        Ulid::from_datetime_with_source(crate::time_utils::now(), source)
31    }
32
33    /// Creates a new Ulid with the given datetime
34    ///
35    /// This can be useful when migrating data to use Ulid identifiers.
36    ///
37    /// This will take the maximum of the `[SystemTime]` argument and `[SystemTime::UNIX_EPOCH]`
38    /// as earlier times are not valid for a Ulid timestamp
39    ///
40    /// # Example
41    /// ```rust
42    /// use std::time::{SystemTime, Duration};
43    /// use ulid::Ulid;
44    ///
45    /// let ulid = Ulid::from_datetime(SystemTime::now());
46    /// ```
47    pub fn from_datetime(datetime: SystemTime) -> Ulid {
48        Ulid::from_datetime_with_source(datetime, &mut rand::rng())
49    }
50
51    /// Creates a new Ulid with the given datetime and random number generator
52    ///
53    /// This will take the maximum of the `[SystemTime]` argument and `[SystemTime::UNIX_EPOCH]`
54    /// as earlier times are not valid for a Ulid timestamp
55    ///
56    /// # Example
57    /// ```rust
58    /// use std::time::{SystemTime, Duration};
59    /// use rand::prelude::*;
60    /// use ulid::Ulid;
61    ///
62    /// let mut rng = StdRng::from_os_rng();
63    /// let ulid = Ulid::from_datetime_with_source(SystemTime::now(), &mut rng);
64    /// ```
65    pub fn from_datetime_with_source<R>(datetime: SystemTime, source: &mut R) -> Ulid
66    where
67        R: rand::Rng + ?Sized,
68    {
69        let timestamp = datetime
70            .duration_since(SystemTime::UNIX_EPOCH)
71            .unwrap_or(Duration::ZERO)
72            .as_millis();
73        let timebits = (timestamp & bitmask!(Self::TIME_BITS)) as u64;
74
75        let msb = timebits << 16 | u64::from(source.random::<u16>());
76        let lsb = source.random::<u64>();
77        Ulid::from((msb, lsb))
78    }
79
80    /// Gets the datetime of when this Ulid was created accurate to 1ms
81    ///
82    /// # Example
83    /// ```rust
84    /// use std::time::{SystemTime, Duration};
85    /// use ulid::Ulid;
86    ///
87    /// let dt = SystemTime::now();
88    /// let ulid = Ulid::from_datetime(dt);
89    ///
90    /// assert!(
91    ///     dt + Duration::from_millis(1) >= ulid.datetime()
92    ///     && dt - Duration::from_millis(1) <= ulid.datetime()
93    /// );
94    /// ```
95    pub fn datetime(&self) -> SystemTime {
96        let stamp = self.timestamp_ms();
97        SystemTime::UNIX_EPOCH + Duration::from_millis(stamp)
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_dynamic() {
107        let ulid = Ulid::new();
108        let encoded = ulid.to_string();
109        let ulid2 = Ulid::from_string(&encoded).expect("failed to deserialize");
110
111        println!("{}", encoded);
112        println!("{:?}", ulid);
113        println!("{:?}", ulid2);
114        assert_eq!(ulid, ulid2);
115    }
116
117    #[test]
118    fn test_source() {
119        use rand::rngs::mock::StepRng;
120        let mut source = StepRng::new(123, 0);
121
122        let u1 = Ulid::with_source(&mut source);
123        let dt = SystemTime::now() + Duration::from_millis(1);
124        let u2 = Ulid::from_datetime_with_source(dt, &mut source);
125        let u3 = Ulid::from_datetime_with_source(dt, &mut source);
126
127        assert!(u1 < u2);
128        assert_eq!(u2, u3);
129    }
130
131    #[test]
132    fn test_order() {
133        let dt = SystemTime::now();
134        let ulid1 = Ulid::from_datetime(dt);
135        let ulid2 = Ulid::from_datetime(dt + Duration::from_millis(1));
136        assert!(ulid1 < ulid2);
137    }
138
139    #[test]
140    fn test_datetime() {
141        let dt = SystemTime::now();
142        let ulid = Ulid::from_datetime(dt);
143
144        println!("{:?}, {:?}", dt, ulid.datetime());
145        assert!(ulid.datetime() <= dt);
146        assert!(ulid.datetime() + Duration::from_millis(1) >= dt);
147    }
148
149    #[test]
150    fn test_timestamp() {
151        let dt = SystemTime::now();
152        let ulid = Ulid::from_datetime(dt);
153        let ts = dt
154            .duration_since(SystemTime::UNIX_EPOCH)
155            .unwrap()
156            .as_millis();
157
158        assert_eq!(u128::from(ulid.timestamp_ms()), ts);
159    }
160
161    #[test]
162    fn default_is_nil() {
163        assert_eq!(Ulid::default(), Ulid::nil());
164    }
165
166    #[test]
167    fn nil_is_at_unix_epoch() {
168        assert_eq!(Ulid::nil().datetime(), SystemTime::UNIX_EPOCH);
169    }
170
171    #[test]
172    fn truncates_at_unix_epoch() {
173        if let Some(before_epoch) = SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)) {
174            assert!(before_epoch < SystemTime::UNIX_EPOCH);
175            assert_eq!(
176                Ulid::from_datetime(before_epoch).datetime(),
177                SystemTime::UNIX_EPOCH
178            );
179        } else {
180            // Prior dates are not representable (e.g. wasm32-wasi)
181        }
182    }
183}