"""
Sorting string dates in Python using the standard library.
This program demonstrates:
- Converting string dates into datetime objects
- Safely handling parsing errors
- Sorting using built-in functions
- Modular design with clear responsibilities
- Multiple test cases, including edge cases
- Notes on performance, memory, and pitfalls
"""
from datetime import datetime
from typing import List, Tuple
# ------------------------------------------------------------
# Core parsing function
# ------------------------------------------------------------
def parse_date(date_str: str, fmt: str) -> Tuple[datetime, str]:
"""
Convert a date string into a datetime object using the given format.
Returns:
(parsed_datetime, original_string)
The original string is returned so that the caller can preserve
the original representation if needed.
Error handling:
- If parsing fails, we raise ValueError with a clear message.
- This allows the caller to decide how to handle invalid input.
Security note:
- Using datetime.strptime is safe for user input; it does not
execute arbitrary code and only interprets structured date formats.
Performance:
- O(1) per parse; overall O(n) for n dates.
"""
try:
return datetime.strptime(date_str, fmt), date_str
except ValueError as exc:
raise ValueError(f"Invalid date '{date_str}' for format '{fmt}'") from exc
# ------------------------------------------------------------
# Sorting function
# ------------------------------------------------------------
def sort_dates(date_strings: List[str], fmt: str) -> List[str]:
"""
Sort a list of date strings according to the given format.
Steps:
1. Parse each date string into a datetime object.
2. Sort the parsed results by the datetime value.
3. Return the original strings in sorted order.
Complexity:
- Parsing: O(n)
- Sorting: O(n log n)
- Total: O(n log n)
Pitfalls:
- Mixed formats in the input will cause parsing errors.
- Leading/trailing whitespace should be stripped by the caller if needed.
- Timezone-aware strings require different parsing logic.
Memory:
- Temporary list of parsed dates is proportional to input size.
"""
parsed = []
for ds in date_strings:
parsed.append(parse_date(ds, fmt))
# Sort by the datetime object (index 0 of tuple)
parsed.sort(key=lambda item: item[0])
# Return the original strings in sorted order
return [orig for _, orig in parsed]
# ------------------------------------------------------------
# Test harness
# ------------------------------------------------------------
def run_test_case(name: str, dates: List[str], fmt: str) -> None:
"""
Run a single test case and print results.
Includes:
- Clear labeling
- Error handling
- Edge case demonstration
"""
print(f"\nTest case: {name}")
print("Input:", dates)
try:
result = sort_dates(dates, fmt)
print("Sorted:", result)
except ValueError as err:
print("Error:", err)
# ------------------------------------------------------------
# Main entry point
# ------------------------------------------------------------
def main() -> None:
"""
Demonstrates multiple test cases, including:
- Normal dates
- Already sorted dates
- Reverse-sorted dates
- Duplicate dates
- Invalid dates (error handling)
- Edge cases (empty list)
"""
# Common format for tests
fmt = "%Y-%m-%d"
# Normal case
run_test_case(
name="Normal case",
dates=["2024-01-05", "2023-12-31", "2024-01-01"],
fmt=fmt,
)
# Already sorted
run_test_case(
name="Already sorted",
dates=["2023-01-01", "2023-02-01", "2023-03-01"],
fmt=fmt,
)
# Reverse sorted
run_test_case(
name="Reverse sorted",
dates=["2024-12-31", "2024-06-01", "2024-01-01"],
fmt=fmt,
)
# Duplicate dates
run_test_case(
name="Duplicates",
dates=["2024-01-01", "2024-01-01", "2023-12-31"],
fmt=fmt,
)
# Invalid date
run_test_case(
name="Invalid date",
dates=["2024-01-01", "not-a-date", "2024-02-01"],
fmt=fmt,
)
# Empty list
run_test_case(
name="Empty list",
dates=[],
fmt=fmt,
)
if __name__ == "__main__":
main()
"""
run:
Test case: Normal case
Input: ['2024-01-05', '2023-12-31', '2024-01-01']
Sorted: ['2023-12-31', '2024-01-01', '2024-01-05']
Test case: Already sorted
Input: ['2023-01-01', '2023-02-01', '2023-03-01']
ERROR!
Sorted: ['2023-01-01', '2023-02-01', '2023-03-01']
Test case: Reverse sorted
Input: ['2024-12-31', '2024-06-01', '2024-01-01']
Sorted: ['2024-01-01', '2024-06-01', '2024-12-31']
Test case: Duplicates
Input: ['2024-01-01', '2024-01-01', '2023-12-31']
Sorted: ['2023-12-31', '2024-01-01', '2024-01-01']
Test case: Invalid date
Input: ['2024-01-01', 'not-a-date', '2024-02-01']
Error: Invalid date 'not-a-date' for format '%Y-%m-%d'
Test case: Empty list
Input: []
Sorted: []
"""