""" Time Formatting Utilities This module provides utilities for formatting time durations and timestamps into human-readable formats for the frontend. """ import re from datetime import datetime, timezone from typing import Optional, Union def format_uptime_for_frontend(uptime_value: Optional[str]) -> str: """ Format uptime value for frontend display in "Xd Xh Xm" format. Args: uptime_value: Raw uptime value (timestamp, epoch, duration string, etc.) Returns: Formatted uptime string like "2d 5h 30m" or "0d 0h" if invalid """ if not uptime_value: return "0d 0h" try: # Try to parse as timestamp (ISO format) if _is_timestamp(uptime_value): return _format_timestamp_uptime(uptime_value) # Try to parse as epoch timestamp if _is_epoch(uptime_value): return _format_epoch_uptime(uptime_value) # Try to parse as duration string (e.g., "2h 30m", "5d 2h 15m") if _is_duration_string(uptime_value): return _format_duration_string(uptime_value) # Try to parse as numeric seconds if _is_numeric_seconds(uptime_value): return _format_seconds_uptime(float(uptime_value)) # If none of the above, return as-is or default return uptime_value if len(uptime_value) < 50 else "0d 0h" except Exception: return "0d 0h" def _is_timestamp(value: str) -> bool: """Check if value is an ISO timestamp.""" try: datetime.fromisoformat(value.replace("Z", "+00:00")) return True except (ValueError, AttributeError): return False def _is_epoch(value: str) -> bool: """Check if value is an epoch timestamp.""" try: float(value) return len(value) >= 10 and float(value) > 1000000000 # Reasonable epoch range except (ValueError, TypeError): return False def _is_duration_string(value: str) -> bool: """Check if value is a duration string like '2h 30m' or '5d 2h 15m'.""" # Look for patterns like "2h 30m", "5d 2h 15m", "1d 2h 3m 4s" pattern = r"^\d+[dhms]\s*(\d+[dhms]\s*)*$" return bool(re.match(pattern, value.strip())) def _is_numeric_seconds(value: str) -> bool: """Check if value is numeric seconds.""" try: float(value) return True except (ValueError, TypeError): return False def _format_timestamp_uptime(timestamp: str) -> str: """Format timestamp uptime (time since timestamp).""" try: # Parse timestamp dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00")) if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) # Calculate time difference now = datetime.now(timezone.utc) diff = now - dt return _format_timedelta(diff) except Exception: return "0d 0h" def _format_epoch_uptime(epoch_str: str) -> str: """Format epoch timestamp uptime.""" try: epoch = float(epoch_str) dt = datetime.fromtimestamp(epoch, tz=timezone.utc) now = datetime.now(timezone.utc) diff = now - dt return _format_timedelta(diff) except Exception: return "0d 0h" def _format_duration_string(duration: str) -> str: """Format duration string to standardized format.""" try: # Parse duration string like "2h 30m" or "5d 2h 15m" total_seconds = _parse_duration_string(duration) return _format_seconds_uptime(total_seconds) except Exception: return "0d 0h" def _format_seconds_uptime(seconds: float) -> str: """Format seconds to "Xd Xh Xm" format.""" return _format_timedelta_from_seconds(seconds) def _parse_duration_string(duration: str) -> float: """Parse duration string to total seconds.""" total_seconds = 0 # Extract days days_match = re.search(r"(\d+)d", duration) if days_match: total_seconds += int(days_match.group(1)) * 86400 # Extract hours hours_match = re.search(r"(\d+)h", duration) if hours_match: total_seconds += int(hours_match.group(1)) * 3600 # Extract minutes minutes_match = re.search(r"(\d+)m", duration) if minutes_match: total_seconds += int(minutes_match.group(1)) * 60 # Extract seconds seconds_match = re.search(r"(\d+)s", duration) if seconds_match: total_seconds += int(seconds_match.group(1)) return total_seconds def _format_timedelta(td) -> str: """Format timedelta to "Xd Xh Xm" format.""" total_seconds = int(td.total_seconds()) return _format_timedelta_from_seconds(total_seconds) def _format_timedelta_from_seconds(total_seconds: Union[int, float]) -> str: """Format total seconds to "Xd Xh Xm" format.""" if total_seconds < 0: return "0d 0h" # Convert to int to avoid decimal places total_seconds = int(total_seconds) days = total_seconds // 86400 hours = (total_seconds % 86400) // 3600 minutes = (total_seconds % 3600) // 60 # Only show days if > 0 if days > 0: return f"{days}d {hours}h {minutes}m" elif hours > 0: return f"{hours}h {minutes}m" else: return f"{minutes}m" def format_response_time(seconds: Optional[float]) -> str: """ Format response time for display. Args: seconds: Response time in seconds Returns: Formatted response time string """ if seconds is None: return "N/A" if seconds < 1: return f"{seconds * 1000:.0f}ms" else: return f"{seconds:.2f}s"