Skip to content

Instantly share code, notes, and snippets.

@mumbleskates
Created December 16, 2025 21:38
Show Gist options
  • Select an option

  • Save mumbleskates/99cf49800a8112cbbf776456fc9a579e to your computer and use it in GitHub Desktop.

Select an option

Save mumbleskates/99cf49800a8112cbbf776456fc9a579e to your computer and use it in GitHub Desktop.
#!/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