1+ #!/usr/bin/env python3
2+ """
3+ Date Utilities for Huntarr
4+ Handles date parsing and validation across all apps
5+ """
6+
7+ import datetime
8+ from typing import Optional
9+ from src .primary .utils .logger import get_logger
10+
11+ # Get logger for the utility
12+ date_logger = get_logger (__name__ )
13+
14+
15+ def parse_date (date_str : Optional [str ]) -> Optional [datetime .datetime ]:
16+ """
17+ Parse a date string into a datetime object.
18+
19+ Args:
20+ date_str: Date string in various formats (ISO, etc.)
21+
22+ Returns:
23+ datetime object if parsing successful, None otherwise
24+ """
25+ if not date_str :
26+ return None
27+
28+ # Handle empty or whitespace-only strings
29+ if not isinstance (date_str , str ) or not date_str .strip ():
30+ return None
31+
32+ date_str = date_str .strip ()
33+
34+ # Common date formats to try
35+ date_formats = [
36+ "%Y-%m-%dT%H:%M:%S.%fZ" , # ISO with microseconds and Z
37+ "%Y-%m-%dT%H:%M:%SZ" , # ISO with Z
38+ "%Y-%m-%dT%H:%M:%S" , # ISO without Z
39+ "%Y-%m-%d" , # Simple date
40+ "%Y-%m-%dT%H:%M:%S.%f" , # ISO with microseconds no Z
41+ ]
42+
43+ for date_format in date_formats :
44+ try :
45+ parsed_date = datetime .datetime .strptime (date_str , date_format )
46+ # If the date has no timezone info and ends with Z, it's UTC
47+ if date_str .endswith ('Z' ) and parsed_date .tzinfo is None :
48+ parsed_date = parsed_date .replace (tzinfo = datetime .timezone .utc )
49+ return parsed_date
50+ except ValueError :
51+ continue
52+
53+ # If all formats fail, log a debug message
54+ date_logger .debug (f"Failed to parse date string: '{ date_str } '" )
55+ return None
56+
57+
58+ def is_future_date (date_obj : Optional [datetime .datetime ]) -> bool :
59+ """
60+ Check if a datetime object represents a future date.
61+
62+ Args:
63+ date_obj: datetime object to check
64+
65+ Returns:
66+ True if date is in the future, False otherwise
67+ """
68+ if not date_obj :
69+ return False
70+
71+ # Get current time in UTC for comparison
72+ now = datetime .datetime .now (datetime .timezone .utc )
73+
74+ # If the date object doesn't have timezone info, assume it's UTC
75+ if date_obj .tzinfo is None :
76+ date_obj = date_obj .replace (tzinfo = datetime .timezone .utc )
77+
78+ return date_obj > now
79+
80+
81+ def is_valid_date (date_str : Optional [str ]) -> bool :
82+ """
83+ Check if a date string can be parsed into a valid date.
84+
85+ Args:
86+ date_str: Date string to validate
87+
88+ Returns:
89+ True if date string is valid, False otherwise
90+ """
91+ return parse_date (date_str ) is not None
0 commit comments