from datetime import datetime, timedelta from typing import Union, Optional def timeago(dt: Union[datetime, str], now: Optional[datetime] = None) -> str: """ Convert a datetime to a human-readable relative time string. Args: dt: The datetime to convert (can be datetime object or ISO format string) now: Optional reference time (defaults to current time) Returns: str: Human-readable relative time string (e.g., "2 hours ago", "3 days ago") """ if isinstance(dt, str): dt = datetime.fromisoformat(dt.replace('Z', '+00:00')) if now is None: now = datetime.utcnow() diff = now - dt # Less than a minute if diff < timedelta(minutes=1): return "just now" # Less than an hour if diff < timedelta(hours=1): minutes = int(diff.total_seconds() / 60) return f"{minutes} minute{'s' if minutes != 1 else ''} ago" # Less than a day if diff < timedelta(days=1): hours = int(diff.total_seconds() / 3600) return f"{hours} hour{'s' if hours != 1 else ''} ago" # Less than a week if diff < timedelta(days=7): days = diff.days return f"{days} day{'s' if days != 1 else ''} ago" # Less than a month if diff < timedelta(days=30): weeks = int(diff.days / 7) return f"{weeks} week{'s' if weeks != 1 else ''} ago" # Less than a year if diff < timedelta(days=365): months = int(diff.days / 30) return f"{months} month{'s' if months != 1 else ''} ago" # More than a year years = int(diff.days / 365) return f"{years} year{'s' if years != 1 else ''} ago" def format_datetime(dt: Union[datetime, str], format: str = "%Y-%m-%d %H:%M:%S") -> str: """ Format a datetime object or ISO string to a specified format. Args: dt: The datetime to format (can be datetime object or ISO format string) format: The format string to use (defaults to "YYYY-MM-DD HH:MM:SS") Returns: str: Formatted datetime string """ if isinstance(dt, str): dt = datetime.fromisoformat(dt.replace('Z', '+00:00')) return dt.strftime(format) def parse_datetime(dt_str: str) -> datetime: """ Parse a datetime string in various formats to a datetime object. Args: dt_str: The datetime string to parse Returns: datetime: Parsed datetime object Raises: ValueError: If the string cannot be parsed """ # Try ISO format first try: return datetime.fromisoformat(dt_str.replace('Z', '+00:00')) except ValueError: pass # Try common formats formats = [ "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%d", "%d/%m/%Y %H:%M:%S", "%d/%m/%Y %H:%M", "%d/%m/%Y", "%m/%d/%Y %H:%M:%S", "%m/%d/%Y %H:%M", "%m/%d/%Y" ] for fmt in formats: try: return datetime.strptime(dt_str, fmt) except ValueError: continue raise ValueError(f"Could not parse datetime string: {dt_str}")