""" Date utilities for the owned clip analysis backfill job. This module provides utilities for date range generation and date-based processing logic for the backfill job. """ from datetime import datetime, timedelta from typing import List, Generator def generate_date_range(start_date: str, end_date: str, date_format: str = "%Y-%m-%d", reverse: bool = False) -> Generator[str, None, None]: """ Generate a sequence of dates from start_date to end_date (inclusive). Args: start_date: Start date in YYYY-MM-DD format end_date: End date in YYYY-MM-DD format (inclusive) date_format: Date format for parsing and output reverse: If True, generate dates from end_date to start_date Yields: Date strings in the specified format Raises: ValueError: If date format is invalid or start_date > end_date """ try: start_dt = datetime.strptime(start_date, date_format) end_dt = datetime.strptime(end_date, date_format) if start_dt > end_dt: raise ValueError(f"start_date ({start_date}) must be <= end_date ({end_date})") if reverse: # Generate dates from end_date backward to start_date current_dt = end_dt while current_dt >= start_dt: yield current_dt.strftime(date_format) current_dt -= timedelta(days=1) else: # Generate dates from start_date forward to end_date (original behavior) current_dt = start_dt while current_dt <= end_dt: yield current_dt.strftime(date_format) current_dt += timedelta(days=1) except ValueError as e: if "time data" in str(e): raise ValueError(f"Invalid date format. Expected {date_format}. Error: {e}") raise def get_date_list(start_date: str, end_date: str, date_format: str = "%Y-%m-%d") -> List[str]: """ Get a list of all dates from start_date to end_date (inclusive). Args: start_date: Start date in YYYY-MM-DD format end_date: End date in YYYY-MM-DD format (inclusive) date_format: Date format for parsing and output Returns: List of date strings in the specified format Raises: ValueError: If date format is invalid or start_date > end_date """ return list(generate_date_range(start_date, end_date, date_format)) def get_next_date(date_str: str, date_format: str = "%Y-%m-%d") -> str: """ Get the next date after the given date. Args: date_str: Date string in specified format date_format: Date format for parsing and output Returns: Next date string in the specified format Raises: ValueError: If date format is invalid """ try: date_dt = datetime.strptime(date_str, date_format) next_dt = date_dt + timedelta(days=1) return next_dt.strftime(date_format) except ValueError as e: raise ValueError(f"Invalid date format. Expected {date_format}. Error: {e}") def get_date_range_info(start_date: str, end_date: str, date_format: str = "%Y-%m-%d") -> dict: """ Get information about a date range. Args: start_date: Start date in specified format end_date: End date in specified format date_format: Date format for parsing Returns: Dictionary with range information Raises: ValueError: If date format is invalid or start_date > end_date """ try: start_dt = datetime.strptime(start_date, date_format) end_dt = datetime.strptime(end_date, date_format) if start_dt > end_dt: raise ValueError(f"start_date ({start_date}) must be <= end_date ({end_date})") total_days = (end_dt - start_dt).days + 1 # +1 to include end_date return { "start_date": start_date, "end_date": end_date, "total_days": total_days, "date_range_description": f"{start_date} to {end_date} ({total_days} days)" } except ValueError as e: if "time data" in str(e): raise ValueError(f"Invalid date format. Expected {date_format}. Error: {e}") raise