use chrono::{NaiveDate};
/*
Architecture notes:
- Uses chrono’s NaiveDate for strict date parsing without auto-correction.
- Modular design: normalize_date orchestrates parsing; try_format handles each layout.
- Strict validation: NaiveDate::parse_from_str rejects impossible dates (e.g., Feb 30).
- Complexity: O(n) per date, where n = number of formats attempted.
- Memory: minimal; only small structs created.
- Security: avoids unsafe parsing; relies on trusted standard library + chrono.
- Pitfalls: ambiguous formats (e.g., 7/5/2026). Here we treat it as MM/DD/YYYY.
- Error handling: invalid or unsupported formats return None.
- Tests in main cover valid, invalid, and edge cases.
*/
// Try parsing with a specific format; return None if invalid.
fn try_format(input: &str, fmt: &str) -> Option<NaiveDate> {
NaiveDate::parse_from_str(input, fmt).ok()
}
// Normalize a date string to YYYY-MM-DD or return None.
fn normalize_date(raw: &str) -> Option<String> {
// Clean quotes and whitespace.
let clean = raw.trim().trim_matches('"').trim_matches('\'');
// Supported formats (chrono uses %m/%d/%Y style patterns).
let formats = [
"%m/%d/%Y", // 05/01/2024
"%m-%d-%Y", // 12-31-2023
"%Y.%m.%d", // 2024.02.09
"%B %d, %Y", // January 5, 2024
"%Y/%m/%d", // 2024/09/17
"%Y-%m-%d", // 2024-09-17
"%d %b %Y", // 25 Dec 2025, 6 Jan 2025
"%b %d, %Y", // Jan 7, 2025
"%d %B %Y", // 8 January 2025
"%m/%d/%Y", // 7/5/2026 (non-zero-padded also works)
];
// Try each explicit format.
for fmt in formats {
if let Some(date) = try_format(clean, fmt) {
return Some(date.format("%Y-%m-%d").to_string());
}
}
// No fallback natural-language parsing in Rust std; chrono does not guess formats.
None
}
fn main() {
let tests = [
"05/01/2024",
"12-31-2023",
"2024.02.09",
"January 5, 2024",
"2024/09/17",
"2024-09-17",
"25 Dec 2025",
"6 Jan 2025",
"Jan 7, 2025",
"8 January 2025",
"7/5/2026",
"BadFormat",
"Feb 30, 2024",
"April 31, 2024",
];
for t in tests {
match normalize_date(t) {
Some(result) => println!("Input: {:<15} => Output: {}", t, result),
None => println!("Input: {:<15} => Output: INVALID", t),
}
}
}
/*
run:
Input: 05/01/2024 => Output: 2024-05-01
Input: 12-31-2023 => Output: 2023-12-31
Input: 2024.02.09 => Output: 2024-02-09
Input: January 5, 2024 => Output: 2024-01-05
Input: 2024/09/17 => Output: 2024-09-17
Input: 2024-09-17 => Output: 2024-09-17
Input: 25 Dec 2025 => Output: 2025-12-25
Input: 6 Jan 2025 => Output: 2025-01-06
Input: Jan 7, 2025 => Output: 2025-01-07
Input: 8 January 2025 => Output: 2025-01-08
Input: 7/5/2026 => Output: 2026-07-05
Input: BadFormat => Output: INVALID
Input: Feb 30, 2024 => Output: INVALID
Input: April 31, 2024 => Output: INVALID
*/