from datetime import datetime, timedelta, timezone import re from typing import Optional from dateutil.relativedelta import relativedelta import pytz from src.config import TIMEZONE def parse_relative_expression(date_str: str) -> Optional[datetime]: """ Parse relative date expressions using T (days), M (months), and Y (years) Returns tuple of (datetime, type_description) or None if not a relative date """ pattern = r"^([HTMY])([+-]\d+)?$" match = re.match(pattern, date_str) if not match: return None unit, offset = match.groups() offset = int(offset) if offset else 0 # Use UTC timezone for consistency today = datetime.now(timezone.tzname("Asia/Jakarta")) if unit == "H": # For hours, keep minutes and seconds result_time = today + timedelta(hours=offset) return result_time elif unit == "T": return today + timedelta(days=offset) elif unit == "M": return today + relativedelta(months=offset) elif unit == "Y": return today + relativedelta(years=offset) def parse_date_string(date_str: str) -> Optional[datetime]: """ Parse date strings in various formats including relative expressions Returns tuple of (datetime, type) """ # Try parsing as relative expression first relative_result = parse_relative_expression(date_str) if relative_result: return relative_result # Try different date formats date_formats = [ ("%Y-%m-%d", "iso"), # 2024-11-08 ("%Y/%m/%d", "slash"), # 2024/11/08 ("%d-%m-%Y", "european"), # 08-11-2024 ("%d/%m/%Y", "european_slash"), # 08/11/2024 ("%Y.%m.%d", "dot"), # 2024.11.08 ("%d.%m.%Y", "european_dot"), # 08.11.2024 ] for fmt, type_name in date_formats: try: # Parse the date and set it to start of day in UTC dt = datetime.strptime(date_str, fmt) dt = dt.replace( hour=0, minute=0, second=0, microsecond=0, tzinfo=timezone.tzname("Asia/Jakarta") ) return dt except ValueError: continue raise ValueError( "Invalid date format. Supported formats:\n" "Relative formats:\n" "- T (days): T, T-n, T+n\n" "- M (months): M, M-1, M+2\n" "- Y (years): Y, Y-1, Y+1\n" "Regular formats:\n" "- YYYY-MM-DD\n" "- YYYY/MM/DD\n" "- DD-MM-YYYY\n" "- DD/MM/YYYY\n" "- YYYY.MM.DD\n" "- DD.MM.YYYY" ) def time_now(): return datetime.now(pytz.timezone(TIMEZONE))