Coverage for src / local_deep_research / news / core / utils.py: 100%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:55 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:55 +0000
1"""
2Core utilities for the news system.
3"""
5import os
6import uuid
7from datetime import datetime, timezone
8from zoneinfo import ZoneInfo
10from loguru import logger
13def get_local_date_string(settings_manager=None) -> str:
14 """
15 Get the current date as a string in the user's configured timezone.
17 This is used for replacing the YYYY-MM-DD placeholder in news subscriptions.
18 The timezone can be configured via:
19 1. The 'app.timezone' setting in the database
20 2. The TZ environment variable
21 3. Falls back to UTC if neither is set
23 Args:
24 settings_manager: Optional settings manager to read timezone setting from.
25 If not provided, will check TZ env var or default to UTC.
27 Returns:
28 str: Current date in ISO format (YYYY-MM-DD) in the configured timezone
29 """
30 tz_name = None
32 # Try to get timezone from settings
33 if settings_manager is not None:
34 try:
35 tz_name = settings_manager.get_setting("app.timezone")
36 except Exception as e:
37 logger.debug(f"Could not read timezone from settings: {e}")
39 # Fall back to TZ environment variable
40 if not tz_name:
41 tz_name = os.environ.get("TZ")
43 # Default to UTC if nothing is configured
44 if not tz_name:
45 tz_name = "UTC"
47 try:
48 tz = ZoneInfo(tz_name)
49 local_date = datetime.now(tz).date()
50 logger.debug(f"Using timezone {tz_name}, local date is {local_date}")
51 return local_date.isoformat()
52 except Exception:
53 logger.warning(f"Invalid timezone '{tz_name}', falling back to UTC")
54 return datetime.now(timezone.utc).date().isoformat()
57def generate_card_id() -> str:
58 """
59 Generate a unique ID for a news card using UUID.
61 Returns:
62 str: A unique UUID string
63 """
64 return str(uuid.uuid4())
67def generate_subscription_id() -> str:
68 """
69 Generate a unique ID for a subscription.
71 Returns:
72 str: A unique UUID string
73 """
74 return str(uuid.uuid4())
77def utc_now() -> datetime:
78 """
79 Get current UTC time with timezone awareness.
81 Returns:
82 datetime: Current UTC time
83 """
84 return datetime.now(timezone.utc)
87def hours_ago(dt: datetime) -> float:
88 """
89 Calculate how many hours ago a datetime was.
91 Args:
92 dt: The datetime to compare
94 Returns:
95 float: Number of hours ago (negative if in future)
96 """
97 if dt.tzinfo is None:
98 dt = dt.replace(tzinfo=timezone.utc)
100 delta = utc_now() - dt
101 return delta.total_seconds() / 3600