"""Date utility functions for todo management.""" from datetime import datetime, timezone, timedelta from typing import Tuple, Optional def get_timezone_aware_now() -> datetime: """Get current time in UTC timezone.""" return datetime.now(timezone.utc) def get_date_range_today() -> Tuple[datetime, datetime]: """Get start and end of today in UTC.""" now = get_timezone_aware_now() start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0) end_of_day = now.replace(hour=23, minute=59, second=59, microsecond=999999) return start_of_day, end_of_day def get_date_range_this_week() -> Tuple[datetime, datetime]: """Get start and end of current week (Monday to Sunday) in UTC.""" now = get_timezone_aware_now() start_of_week = now - timedelta(days=now.weekday()) start_of_week = start_of_week.replace(hour=0, minute=0, second=0, microsecond=0) end_of_week = start_of_week + timedelta( days=6, hours=23, minutes=59, seconds=59, microseconds=999999 ) return start_of_week, end_of_week def get_date_range_next_week() -> Tuple[datetime, datetime]: """Get start and end of next week (Monday to Sunday) in UTC.""" now = get_timezone_aware_now() days_until_next_monday = 7 - now.weekday() start_of_next_week = now + timedelta(days=days_until_next_monday) start_of_next_week = start_of_next_week.replace( hour=0, minute=0, second=0, microsecond=0 ) end_of_next_week = start_of_next_week + timedelta( days=6, hours=23, minutes=59, seconds=59, microseconds=999999 ) return start_of_next_week, end_of_next_week def get_date_range_next_days(days: int) -> Tuple[datetime, datetime]: """Get start and end of next N days in UTC.""" now = get_timezone_aware_now() start_time = now end_time = now + timedelta(days=days) return start_time, end_time def get_overdue_cutoff() -> datetime: """Get cutoff datetime for overdue todos (current time).""" return get_timezone_aware_now() def get_due_soon_cutoff(days: int = 7) -> datetime: """Get cutoff datetime for due soon todos (next N days).""" return get_timezone_aware_now() + timedelta(days=days) def is_overdue(due_date: Optional[datetime], completed: bool = False) -> bool: """Check if a todo is overdue.""" if due_date is None or completed: return False return get_timezone_aware_now() > due_date def is_due_today(due_date: Optional[datetime]) -> bool: """Check if a todo is due today.""" if due_date is None: return False today_start, today_end = get_date_range_today() return today_start <= due_date <= today_end def is_due_soon( due_date: Optional[datetime], days: int = 7, completed: bool = False ) -> bool: """Check if a todo is due within the next N days.""" if due_date is None or completed: return False cutoff = get_due_soon_cutoff(days) now = get_timezone_aware_now() return now <= due_date <= cutoff