"""
Convert multiple human-friendly date formats into a normalized YYYY-MM-DD string.
Architecture notes:
- Uses Python's standard library: datetime.strptime and datetime.fromisoformat.
- Modular design: a dedicated function handles parsing and normalization.
- Attempts several known formats; if all fail, falls back to a broader parser.
- Complexity: O(n) per date, where n = number of formats attempted.
- Memory: minimal; only small objects created.
- Security: avoids unsafe parsing; relies on trusted standard library.
- Pitfalls: ambiguous formats (e.g., 7/5/2026 could be MM/DD or DD/MM). Here we assume MM/DD.
- Error handling: invalid or impossible dates return None.
- Tests in main cover valid, invalid, edge cases, and mixed formats.
"""
from datetime import datetime
def normalize_date(raw: str) -> str | None:
"""
Attempt to convert a date string into YYYY-MM-DD.
Returns None if unsupported or invalid.
The function tries explicit formats first, then a fallback parser.
"""
# Clean quotes and whitespace; common when input comes from external sources.
clean = raw.strip().strip('"').strip("'")
# List of formats to try. Order matters: specific formats first.
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
"%d %b %Y", # 6 Jan 2025
"%b %d, %Y", # Jan 7, 2025
"%d %B %Y", # 8 January 2025
"%m/%d/%Y", # 7/5/2026 (assumed MM/DD)
]
# Try each explicit format.
for fmt in formats:
try:
dt = datetime.strptime(clean, fmt)
return dt.strftime("%Y-%m-%d")
except ValueError:
# Continue trying other formats.
pass
# Fallback: try Python's flexible ISO parser.
try:
dt = datetime.fromisoformat(clean)
return dt.strftime("%Y-%m-%d")
except ValueError:
# Invalid or unsupported format.
return None
def main() -> None:
"""
Test harness with multiple test cases, including edge cases.
Prints results for each input.
"""
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:
result = normalize_date(t)
print(f"Input: {t} => Output: {result or 'INVALID'}")
if __name__ == "__main__":
main()
"""
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
"""