
Added due_date field to TodoBase, TodoCreate, TodoUpdate schemas with proper validation and timezone handling. Included computed fields is_overdue and days_until_due for enhanced todo management capabilities.
78 lines
2.9 KiB
Python
78 lines
2.9 KiB
Python
"""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 |