| //! The implementation for Version 7 UUIDs. |
| //! |
| //! Note that you need to enable the `v7` Cargo feature |
| //! in order to use this module. |
| |
| use crate::{std::convert::TryInto, rng, timestamp::Timestamp, Builder, Uuid}; |
| |
| impl Uuid { |
| /// Create a new version 7 UUID using the current time value and random bytes. |
| /// |
| /// This method is a convenient alternative to [`Uuid::new_v7`] that uses the current system time |
| /// as the source timestamp. |
| #[cfg(feature = "std")] |
| pub fn now_v7() -> Self { |
| Self::new_v7(Timestamp::now(crate::NoContext)) |
| } |
| |
| /// Create a new version 7 UUID using a time value and random bytes. |
| /// |
| /// When the `std` feature is enabled, you can also use [`Uuid::now_v7`]. |
| /// |
| /// Note that usage of this method requires the `v7` feature of this crate |
| /// to be enabled. |
| /// |
| /// Also see [`Uuid::now_v7`] for a convenient way to generate version 7 |
| /// UUIDs using the current system time. |
| /// |
| /// # Examples |
| /// |
| /// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit |
| /// random number. When supplied as such, the data will be |
| /// |
| /// ```rust |
| /// # use uuid::{Uuid, Timestamp, NoContext}; |
| /// let ts = Timestamp::from_unix(NoContext, 1497624119, 1234); |
| /// |
| /// let uuid = Uuid::new_v7(ts); |
| /// |
| /// assert!( |
| /// uuid.hyphenated().to_string().starts_with("015cb15a-86d8-7") |
| /// ); |
| /// ``` |
| /// |
| /// # References |
| /// |
| /// * [Version 7 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.2) |
| pub fn new_v7(ts: Timestamp) -> Self { |
| let (secs, nanos) = ts.to_unix(); |
| let millis = (secs * 1000).saturating_add(nanos as u64 / 1_000_000); |
| |
| Builder::from_unix_timestamp_millis(millis, &rng::bytes()[..10].try_into().unwrap()) |
| .into_uuid() |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::{std::string::ToString, NoContext, Variant, Version}; |
| #[cfg(target_arch = "wasm32")] |
| use wasm_bindgen_test::*; |
| |
| #[test] |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] |
| fn test_new() { |
| let ts: u64 = 1645557742000; |
| |
| let seconds = ts / 1000; |
| let nanos = ((ts % 1000) * 1_000_000) as u32; |
| |
| let uuid = Uuid::new_v7(Timestamp::from_unix(NoContext, seconds, nanos)); |
| let uustr = uuid.hyphenated().to_string(); |
| |
| assert_eq!(uuid.get_version(), Some(Version::SortRand)); |
| assert_eq!(uuid.get_variant(), Variant::RFC4122); |
| assert!(uuid.hyphenated().to_string().starts_with("017f22e2-79b0-7")); |
| |
| // Ensure parsing the same UUID produces the same timestamp |
| let parsed = Uuid::parse_str(uustr.as_str()).unwrap(); |
| |
| assert_eq!(uuid, parsed); |
| } |
| |
| #[test] |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] |
| #[cfg(feature = "std")] |
| fn test_now() { |
| let uuid = Uuid::now_v7(); |
| |
| assert_eq!(uuid.get_version(), Some(Version::SortRand)); |
| assert_eq!(uuid.get_variant(), Variant::RFC4122); |
| } |
| |
| #[test] |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] |
| fn test_sorting() { |
| let time1: u64 = 1_496_854_535; |
| let time_fraction1: u32 = 812_000_000; |
| |
| let time2 = time1 + 4000; |
| let time_fraction2 = time_fraction1; |
| |
| let uuid1 = Uuid::new_v7(Timestamp::from_unix(NoContext, time1, time_fraction1)); |
| let uuid2 = Uuid::new_v7(Timestamp::from_unix(NoContext, time2, time_fraction2)); |
| |
| assert!(uuid1.as_bytes() < uuid2.as_bytes()); |
| assert!(uuid1.to_string() < uuid2.to_string()); |
| } |
| |
| #[test] |
| #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] |
| fn test_new_timestamp_roundtrip() { |
| let time: u64 = 1_496_854_535; |
| let time_fraction: u32 = 812_000_000; |
| |
| let ts = Timestamp::from_unix(NoContext, time, time_fraction); |
| |
| let uuid = Uuid::new_v7(ts); |
| |
| let decoded_ts = uuid.get_timestamp().unwrap(); |
| |
| assert_eq!(ts.to_unix(), decoded_ts.to_unix()); |
| } |
| } |