@ -601,6 +601,35 @@ pub fn parse_date_string(s: &str) -> Option<DateTime<Utc>> {
return Some ( naive . and_utc ( ) ) ;
}
// Try European/French formats: "25/03/2026", "25-03-2026"
for fmt in & [ "%d/%m/%Y" , "%d-%m-%Y" ] {
if let Ok ( naive ) = NaiveDate ::parse_from_str ( s , fmt ) {
return naive . and_hms_opt ( 0 , 0 , 0 ) . map ( | ndt | ndt . and_utc ( ) ) ;
}
}
// Try US format: "03/25/2026", "March 25, 2026"
for fmt in & [ "%m/%d/%Y" , "%B %d, %Y" , "%b %d, %Y" ] {
if let Ok ( naive ) = NaiveDate ::parse_from_str ( s , fmt ) {
return naive . and_hms_opt ( 0 , 0 , 0 ) . map ( | ndt | ndt . and_utc ( ) ) ;
}
}
// Try French format: "25 mars 2026", "25 mar 2026"
// chrono doesn't parse French month names natively, so we translate first
let lowered = s . to_lowercase ( ) ;
let english = lowered
. replace ( "janvier" , "January" ) . replace ( "février" , "February" ) . replace ( "fevrier" , "February" )
. replace ( "mars" , "March" ) . replace ( "avril" , "April" ) . replace ( "mai" , "May" )
. replace ( "juin" , "June" ) . replace ( "juillet" , "July" ) . replace ( "août" , "August" ) . replace ( "aout" , "August" )
. replace ( "septembre" , "September" ) . replace ( "octobre" , "October" )
. replace ( "novembre" , "November" ) . replace ( "décembre" , "December" ) . replace ( "decembre" , "December" ) ;
for fmt in & [ "%d %B %Y" , "%d %b %Y" ] {
if let Ok ( naive ) = NaiveDate ::parse_from_str ( & english , fmt ) {
return naive . and_hms_opt ( 0 , 0 , 0 ) . map ( | ndt | ndt . and_utc ( ) ) ;
}
}
None
}
@ -1130,6 +1159,41 @@ mod tests {
assert! ( parse_date_string ( "" ) . is_none ( ) ) ;
}
#[ test ]
fn test_parse_european_date_slash ( ) {
let dt = parse_date_string ( "25/03/2026" ) ;
assert! ( dt . is_some ( ) ) ;
assert_eq! ( dt . unwrap ( ) . format ( "%Y-%m-%d" ) . to_string ( ) , "2026-03-25" ) ;
}
#[ test ]
fn test_parse_european_date_dash ( ) {
let dt = parse_date_string ( "25-03-2026" ) ;
assert! ( dt . is_some ( ) ) ;
assert_eq! ( dt . unwrap ( ) . format ( "%Y-%m-%d" ) . to_string ( ) , "2026-03-25" ) ;
}
#[ test ]
fn test_parse_us_date_long_month ( ) {
let dt = parse_date_string ( "March 25, 2026" ) ;
assert! ( dt . is_some ( ) ) ;
assert_eq! ( dt . unwrap ( ) . format ( "%Y-%m-%d" ) . to_string ( ) , "2026-03-25" ) ;
}
#[ test ]
fn test_parse_french_date ( ) {
let dt = parse_date_string ( "25 mars 2026" ) ;
assert! ( dt . is_some ( ) ) ;
assert_eq! ( dt . unwrap ( ) . format ( "%Y-%m-%d" ) . to_string ( ) , "2026-03-25" ) ;
}
#[ test ]
fn test_parse_french_date_accent ( ) {
let dt = parse_date_string ( "15 février 2026" ) ;
assert! ( dt . is_some ( ) ) ;
assert_eq! ( dt . unwrap ( ) . format ( "%Y-%m-%d" ) . to_string ( ) , "2026-02-15" ) ;
}
// ── Scheme Validation ───────────────────────────────────────────
#[ test ]