use chrono::{NaiveDate, NaiveTime};
pub fn parse_iso_timestamp(s: &str) -> Option<i64> {
if s.len() < 10 {
return None;
}
let date = NaiveDate::parse_from_str(&s[0..10], "%Y-%m-%d").ok()?;
if s.len() == 10 {
return Some(date.and_hms_opt(0, 0, 0)?.and_utc().timestamp_millis());
}
let sep = s.as_bytes().get(10).copied()?;
if sep != b'T' && sep != b' ' {
return None;
}
let rest = &s[11..];
let mut zone_start = None;
for (i, c) in rest.char_indices() {
if i < 5 {
continue;
}
if c == 'Z' || c == '+' || c == '-' {
zone_start = Some(i);
break;
}
}
let (time_str, zone_str) = match zone_start {
Some(i) => (&rest[..i], Some(&rest[i..])),
None => (rest, None),
};
let time = if time_str.len() == 5 {
NaiveTime::parse_from_str(time_str, "%H:%M").ok()?
} else if time_str.contains('.') {
NaiveTime::parse_from_str(time_str, "%H:%M:%S%.f").ok()?
} else {
NaiveTime::parse_from_str(time_str, "%H:%M:%S").ok()?
};
let naive = date.and_time(time);
match zone_str {
None | Some("Z") => Some(naive.and_utc().timestamp_millis()),
Some(zone) => {
let sign: i64 = if zone.starts_with('-') { -1 } else { 1 };
let digits: String = zone.chars().filter(|c| c.is_ascii_digit()).collect();
if digits.len() < 4 {
return None;
}
let hh: i64 = digits[0..2].parse().ok()?;
let mm: i64 = digits[2..4].parse().ok()?;
let offset_secs = sign * (hh * 3600 + mm * 60);
Some(naive.and_utc().timestamp_millis() - offset_secs * 1000)
}
}
}