Coverage for src / local_deep_research / news / core / utils.py: 23%

35 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-01-11 00:51 +0000

1""" 

2Core utilities for the news system. 

3""" 

4 

5import os 

6import uuid 

7from datetime import datetime, timezone 

8from zoneinfo import ZoneInfo 

9 

10from loguru import logger 

11 

12 

13def get_local_date_string(settings_manager=None) -> str: 

14 """ 

15 Get the current date as a string in the user's configured timezone. 

16 

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 

22 

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. 

26 

27 Returns: 

28 str: Current date in ISO format (YYYY-MM-DD) in the configured timezone 

29 """ 

30 tz_name = None 

31 

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}") 

38 

39 # Fall back to TZ environment variable 

40 if not tz_name: 

41 tz_name = os.environ.get("TZ") 

42 

43 # Default to UTC if nothing is configured 

44 if not tz_name: 

45 tz_name = "UTC" 

46 

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 as e: 

53 logger.warning( 

54 f"Invalid timezone '{tz_name}', falling back to UTC: {e}" 

55 ) 

56 return datetime.now(timezone.utc).date().isoformat() 

57 

58 

59def generate_card_id() -> str: 

60 """ 

61 Generate a unique ID for a news card using UUID. 

62 

63 Returns: 

64 str: A unique UUID string 

65 """ 

66 return str(uuid.uuid4()) 

67 

68 

69def generate_subscription_id() -> str: 

70 """ 

71 Generate a unique ID for a subscription. 

72 

73 Returns: 

74 str: A unique UUID string 

75 """ 

76 return str(uuid.uuid4()) 

77 

78 

79def utc_now() -> datetime: 

80 """ 

81 Get current UTC time with timezone awareness. 

82 

83 Returns: 

84 datetime: Current UTC time 

85 """ 

86 return datetime.now(timezone.utc) 

87 

88 

89def hours_ago(dt: datetime) -> float: 

90 """ 

91 Calculate how many hours ago a datetime was. 

92 

93 Args: 

94 dt: The datetime to compare 

95 

96 Returns: 

97 float: Number of hours ago (negative if in future) 

98 """ 

99 if dt.tzinfo is None: 

100 dt = dt.replace(tzinfo=timezone.utc) 

101 

102 delta = utc_now() - dt 

103 return delta.total_seconds() / 3600