foundry_mcp/utils/
timestamp.rs1use anyhow::Result;
4use chrono::{DateTime, Datelike, NaiveDate, Timelike, Utc};
5
6pub fn iso_timestamp() -> String {
8 Utc::now().to_rfc3339()
9}
10
11pub fn spec_timestamp() -> String {
13 let now = Utc::now();
14 format!(
15 "{:04}{:02}{:02}_{:02}{:02}{:02}",
16 now.year(),
17 now.month(),
18 now.day(),
19 now.hour(),
20 now.minute(),
21 now.second()
22 )
23}
24
25pub fn human_timestamp() -> String {
27 Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string()
28}
29
30pub fn validate_timestamp_format(timestamp: &str) -> bool {
32 if timestamp.len() != 15 {
33 return false;
34 }
35
36 let parts: Vec<&str> = timestamp.split('_').collect();
38 if parts.len() != 2 || parts[0].len() != 8 || parts[1].len() != 6 {
39 return false;
40 }
41
42 parts[0].chars().all(|c| c.is_ascii_digit()) && parts[1].chars().all(|c| c.is_ascii_digit())
44}
45
46pub fn parse_spec_timestamp(spec_name: &str) -> Option<String> {
48 if let Some(underscore_pos) = spec_name.find('_')
49 && underscore_pos == 8
50 && spec_name.len() > 15
51 {
52 let timestamp_part = &spec_name[0..15];
53 if validate_timestamp_format(timestamp_part) {
54 return Some(timestamp_part.to_string());
55 }
56 }
57 None
58}
59
60pub fn spec_timestamp_to_iso(spec_timestamp: &str) -> Result<String> {
62 if !validate_timestamp_format(spec_timestamp) {
63 return Err(anyhow::anyhow!(
64 "Invalid timestamp format: {}",
65 spec_timestamp
66 ));
67 }
68
69 let date_part = &spec_timestamp[0..8];
70 let time_part = &spec_timestamp[9..15];
71
72 let year: i32 = date_part[0..4].parse()?;
73 let month: u32 = date_part[4..6].parse()?;
74 let day: u32 = date_part[6..8].parse()?;
75 let hour: u32 = time_part[0..2].parse()?;
76 let minute: u32 = time_part[2..4].parse()?;
77 let second: u32 = time_part[4..6].parse()?;
78
79 let naive_date = NaiveDate::from_ymd_opt(year, month, day)
80 .ok_or_else(|| anyhow::anyhow!("Invalid date values in timestamp"))?;
81 let naive_datetime = naive_date
82 .and_hms_opt(hour, minute, second)
83 .ok_or_else(|| anyhow::anyhow!("Invalid time values in timestamp"))?;
84
85 let datetime = DateTime::<Utc>::from_naive_utc_and_offset(naive_datetime, Utc);
86 Ok(datetime.to_rfc3339())
87}
88
89pub fn iso_to_spec_timestamp(iso_timestamp: &str) -> Result<String> {
91 let datetime = DateTime::parse_from_rfc3339(iso_timestamp)?;
92 let utc_datetime = datetime.with_timezone(&Utc);
93
94 Ok(format!(
95 "{:04}{:02}{:02}_{:02}{:02}{:02}",
96 utc_datetime.year(),
97 utc_datetime.month(),
98 utc_datetime.day(),
99 utc_datetime.hour(),
100 utc_datetime.minute(),
101 utc_datetime.second()
102 ))
103}
104
105pub fn format_timestamp_for_display(timestamp: &str) -> String {
107 if validate_timestamp_format(timestamp) {
108 format!(
109 "{}-{}-{} {}:{}:{}",
110 ×tamp[0..4],
111 ×tamp[4..6],
112 ×tamp[6..8],
113 ×tamp[9..11],
114 ×tamp[11..13],
115 ×tamp[13..15]
116 )
117 } else {
118 timestamp.to_string()
119 }
120}
121
122pub fn extract_feature_name(spec_name: &str) -> Option<String> {
124 if let Some(timestamp) = parse_spec_timestamp(spec_name)
125 && spec_name.len() > timestamp.len() + 1
126 {
127 return Some(spec_name[timestamp.len() + 1..].to_string());
128 }
129 None
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_validate_timestamp_format() {
138 assert!(validate_timestamp_format("20240824_120000"));
139 assert!(!validate_timestamp_format("2024824_120000")); assert!(!validate_timestamp_format("20240824_12000")); assert!(!validate_timestamp_format("20240824120000")); assert!(!validate_timestamp_format("2024082a_120000")); }
144
145 #[test]
146 fn test_parse_spec_timestamp() {
147 assert_eq!(
148 parse_spec_timestamp("20240824_120000_user_auth"),
149 Some("20240824_120000".to_string())
150 );
151 assert_eq!(parse_spec_timestamp("invalid_name"), None);
152 assert_eq!(parse_spec_timestamp("20240824_user_auth"), None); }
154
155 #[test]
156 fn test_spec_timestamp_to_iso() {
157 let result = spec_timestamp_to_iso("20240824_120000").unwrap();
158 assert!(result.starts_with("2024-08-24T12:00:00"));
159 }
160
161 #[test]
162 fn test_extract_feature_name() {
163 assert_eq!(
164 extract_feature_name("20240824_120000_user_auth"),
165 Some("user_auth".to_string())
166 );
167 assert_eq!(
168 extract_feature_name("20240824_120000_multi_part_feature"),
169 Some("multi_part_feature".to_string())
170 );
171 assert_eq!(extract_feature_name("invalid_name"), None);
172 }
173
174 #[test]
175 fn test_format_timestamp_for_display() {
176 assert_eq!(
177 format_timestamp_for_display("20240824_120000"),
178 "2024-08-24 12:00:00"
179 );
180 assert_eq!(format_timestamp_for_display("invalid"), "invalid");
181 }
182}