#[must_use]
pub fn normalize_feed_date(raw: &str) -> String {
let trimmed = raw.trim();
if let Ok(dt) = chrono::DateTime::parse_from_rfc2822(trimmed) {
return dt.format("%Y-%m-%d").to_string();
}
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(trimmed) {
return dt.format("%Y-%m-%d").to_string();
}
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(trimmed, "%Y-%m-%dT%H:%M:%S") {
return dt.format("%Y-%m-%d").to_string();
}
if chrono::NaiveDate::parse_from_str(trimmed, "%Y-%m-%d").is_ok() {
return trimmed.to_string();
}
if let Some(date) = parse_partial_rfc2822(trimmed) {
return date;
}
let mut t = trimmed.to_string();
if let Some(pos) = t.rfind(" +") {
t.truncate(pos);
t = t.trim_end().to_string();
}
if t.len() >= 9 {
let n = t.len();
let time_part = &t[n - 8..n];
let looks_time = time_part.chars().enumerate().all(|(i, c)| match i {
2 | 5 => c == ':',
_ => c.is_ascii_digit(),
});
if looks_time && t.as_bytes()[n - 9] == b' ' {
t.truncate(n - 9);
}
}
t.trim_end().to_string()
}
fn parse_partial_rfc2822(s: &str) -> Option<String> {
let without_weekday = s.split_once(',').map_or(s, |(_, rest)| rest).trim();
for fmt in ["%d %b %Y %H:%M:%S", "%d %b %Y"] {
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(without_weekday, fmt) {
return Some(dt.format("%Y-%m-%d").to_string());
}
if let Ok(d) = chrono::NaiveDate::parse_from_str(without_weekday, fmt) {
return Some(d.format("%Y-%m-%d").to_string());
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_formats() {
assert_eq!(
normalize_feed_date("Thu, 21 Aug 2025 12:34:56 +0000"),
"2025-08-21"
);
assert_eq!(normalize_feed_date("2025-12-07T14:00:00Z"), "2025-12-07");
assert_eq!(normalize_feed_date("2025-12-07T14:00:00"), "2025-12-07");
assert_eq!(normalize_feed_date("2025-12-07"), "2025-12-07");
assert_eq!(normalize_feed_date("Thu, 21 Aug 2025"), "2025-08-21");
assert_eq!(
normalize_feed_date("Thu, 21 Aug 2025 12:34:56"),
"2025-08-21"
);
}
#[test]
fn fallback_stripping() {
assert_eq!(normalize_feed_date("someday 12:34:56 +0100"), "someday");
assert_eq!(normalize_feed_date("not a date"), "not a date");
assert_eq!(normalize_feed_date(""), "");
}
}