misskey_id/
meid.rs

1//! 24 characters unique identifier
2//! 
3//! 24-character identifier that is composed of a 12-character timestamp and a 12-character random string.
4//! 
5//! The first 12 characters represent the time in milliseconds since the Unix epoch (1970-01-01T00:00:00Z).
6//! 
7//! The last 12 characters are a random string.
8use std::{num::ParseIntError, time::SystemTime};
9
10use chrono::{DateTime, TimeZone, Utc};
11use lazy_static::lazy_static;
12use rand::Rng;
13use regex::Regex;
14
15const CHARS: &str = "0123456789abcdef";
16lazy_static! {
17    static ref MEID_REGEX: Regex = Regex::new(r"^[0-9a-f]{24}$").unwrap();
18}
19
20fn get_time(mut time: u64) -> String {
21    if time == 0 {
22        return CHARS[0..1].to_string();
23    }
24
25    time += 0x800000000000;
26
27    format!("{:012x}", time)
28}
29
30fn get_random() -> String {
31    let mut rng = rand::rng();
32    (0..12)
33        .map(|_| {
34            let idx = rng.random_range(0..CHARS.len());
35            CHARS.chars().nth(idx).unwrap()
36        })
37        .collect()
38}
39
40// TODO: Resultにする
41pub fn gen(time: u64) -> String {
42    format!("{}{}", get_time(time), get_random())
43}
44
45pub fn parse(id: &str) -> Result<SystemTime, ParseIntError> {
46    let timestamp = u64::from_str_radix(&id[0..12], 16).unwrap() - 0x800000000000;
47    let time = SystemTime::UNIX_EPOCH + std::time::Duration::from_millis(timestamp);
48    Ok(time)
49}
50
51pub fn parse_meid_with_format(id: &str) -> DateTime<Utc> {
52    let time = parse(id).unwrap();
53    let duration = time.duration_since(SystemTime::UNIX_EPOCH).unwrap();
54    Utc.timestamp_millis_opt(duration.as_millis() as i64)
55        .unwrap()
56}
57
58// id: 81942b1629fec5c845bb697d
59// SystemTime: 1735889660414
60// date: 2025-01-03T07:34:20.414Z
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_gen() {
68        let generated = gen(1735889660414);
69        println!("{}", generated);
70        assert_eq!(generated.len(), 24);
71        assert!(MEID_REGEX.is_match(&generated));
72    }
73
74    #[test]
75    fn test_parse() {
76        let generated = gen(1735889660414);
77        let parsed = parse(&generated).unwrap();
78        assert_eq!(
79            parsed
80                .duration_since(SystemTime::UNIX_EPOCH)
81                .unwrap()
82                .as_millis(),
83            1735889660414
84        );
85    }
86}