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