Created
December 16, 2025 21:38
-
-
Save mumbleskates/99cf49800a8112cbbf776456fc9a579e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env rust-script | |
| //! ```cargo | |
| //! [dependencies] | |
| //! chrono = "0.4" | |
| //! clap = {version = "4", features = ["derive"]} | |
| //! eyre = "0.6" | |
| //! regex = "1" | |
| //! uuid = "1" | |
| //! ``` | |
| use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeDelta, Utc}; | |
| use clap::Parser; | |
| use eyre::{OptionExt, Result}; | |
| use regex::{Captures, Regex}; | |
| use std::io::{read_to_string, stdin}; | |
| use uuid::Uuid; | |
| const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S%.9f %Z"; | |
| const GREGORIAN_EPOCH: DateTime<Utc> = NaiveDateTime::new( | |
| NaiveDate::from_ymd_opt(1582, 10, 15).unwrap(), | |
| NaiveTime::MIN, | |
| ) | |
| .and_utc(); | |
| struct UuidInfo { | |
| version: usize, | |
| timestamp: String, | |
| } | |
| fn uuid_info(uuid: Uuid) -> Result<UuidInfo> { | |
| let version = uuid.get_version_num(); | |
| let timestamp = match version { | |
| 1 | 6 => { | |
| let timestamp = uuid.get_timestamp().expect("v1 & v6 uuids have timestamps"); | |
| let (ticks, _) = timestamp.to_gregorian(); | |
| let seconds = ticks / 10_000_000; | |
| let ticks = ticks % 10_000_000; | |
| (GREGORIAN_EPOCH | |
| + TimeDelta::new(seconds as i64, (ticks * 100) as u32) | |
| .expect("can't make chrono timedelta")) | |
| .format(FORMAT) | |
| .to_string() | |
| } | |
| 7 => { | |
| let timestamp = uuid.get_timestamp().expect("v7 uuids have timestamps"); | |
| let (seconds, nanos) = timestamp.to_unix(); | |
| DateTime::from_timestamp(seconds as i64, nanos) | |
| .ok_or_eyre("can't make chrono datetime")? | |
| .format(FORMAT) | |
| .to_string() | |
| } | |
| 2 => "underspecified timestamp".to_owned(), | |
| 3 | 4 | 5 => "no timestamp".to_owned(), | |
| 8 => "vendor private use".to_owned(), | |
| _ => "unknown uuid version".to_owned(), | |
| }; | |
| Ok(UuidInfo { version, timestamp }) | |
| } | |
| fn main() -> Result<()> { | |
| #[derive(Parser)] | |
| /// Extract timestamps from UUIDs in the input (args or stdin) | |
| struct Cmd { | |
| /// UUIDs to parse | |
| uuids: Vec<String>, | |
| /// Annotate stdin instead of just listing UUIDs that are found | |
| #[arg(long)] | |
| annotate: bool, | |
| } | |
| let args = Cmd::parse(); | |
| if args.annotate { | |
| let input = read_to_string(stdin())?; | |
| let re = Regex::new(r"[0-9A-Fa-f][0-9A-Fa-f_-]{31,}").unwrap(); | |
| let annotated = re.replace_all(&input, |caps: &Captures| { | |
| let text = caps.get_match().as_str(); | |
| let Ok(uuid) = Uuid::parse_str(text) else { | |
| return text.to_owned(); | |
| }; | |
| let info = uuid_info(uuid).unwrap(); | |
| format!( | |
| "{text} (UUIDv{version}: {timestamp}) ", | |
| version = info.version, | |
| timestamp = info.timestamp, | |
| ) | |
| }); | |
| print!("{annotated}"); | |
| } else { | |
| let input: &[String] = if args.uuids.is_empty() { | |
| &[read_to_string(stdin())?] | |
| } else { | |
| &args.uuids | |
| }; | |
| for arg in input | |
| .iter() | |
| .flat_map(|s| s.split(|ch| !"0123456789ABCDEFabcdef_-".contains(ch))) | |
| { | |
| let Some(uuid) = Uuid::parse_str(arg).ok() else { | |
| continue; | |
| }; | |
| let info = uuid_info(uuid)?; | |
| println!( | |
| "{uuid} (UUIDv{version}): {time}", | |
| version = info.version, | |
| time = info.timestamp, | |
| ); | |
| } | |
| } | |
| Ok(()) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment