Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ dependencies = [
"pytz>=2023.3",
"rich>=13.7.0",
"tomli>=1.2.0; python_version < '3.11'",
"tzdata; sys_platform == 'win32'"
"tzdata; sys_platform == 'win32'",
"tzlocal>=5.0"
]

[project.optional-dependencies]
Expand Down
20 changes: 20 additions & 0 deletions src/claude_monitor/utils/time_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ def get_timezone_location(
return None


try:
from tzlocal import get_localzone_name

HAS_TZLOCAL = True
except ImportError:
HAS_TZLOCAL = False

def get_localzone_name():
"""Stub function when tzlocal is not available."""
return None


logger: logging.Logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -331,6 +343,14 @@ def get_timezone() -> str:
pass

elif system == "Windows":
# Try tzlocal first for proper IANA timezone name
if HAS_TZLOCAL:
with contextlib.suppress(Exception):
tz_name = get_localzone_name()
if tz_name:
return tz_name

# Fallback to tzutil if tzlocal is not available
with contextlib.suppress(Exception):
tzutil_result: subprocess.CompletedProcess[str] = subprocess.run(
["tzutil", "/g"], capture_output=True, text=True, check=True
Expand Down
24 changes: 21 additions & 3 deletions src/tests/test_time_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,17 +359,35 @@ def test_get_timezone_linux_timedatectl(
result = SystemTimeDetector.get_timezone()
assert result == "Europe/London"

`@patch`("os.environ.get", return_value=None)
`@patch`("platform.system")
`@patch`("claude_monitor.utils.time_utils.HAS_TZLOCAL", True)
`@patch`("claude_monitor.utils.time_utils.get_localzone_name")
def test_get_timezone_windows(
self, mock_get_localzone_name: Mock, mock_system: Mock, mock_env: Mock
) -> None:
"""Test Windows timezone detection with tzlocal."""
mock_system.return_value = "Windows"
mock_get_localzone_name.return_value = "America/New_York"

# Should return the IANA timezone name from tzlocal
result = SystemTimeDetector.get_timezone()
assert result == "America/New_York"

@patch("platform.system")
@patch("claude_monitor.utils.time_utils.HAS_TZLOCAL", False)
@patch("subprocess.run")
def test_get_timezone_windows(self, mock_run: Mock, mock_system: Mock) -> None:
"""Test Windows timezone detection."""
def test_get_timezone_windows_fallback(
self, mock_run: Mock, mock_system: Mock
) -> None:
"""Test Windows timezone detection fallback to tzutil when tzlocal is not available."""
mock_system.return_value = "Windows"

mock_result = Mock()
mock_result.stdout = "Eastern Standard Time"
mock_run.return_value = mock_result

# Should return the Windows timezone name
# Should fallback to Windows timezone name from tzutil
result = SystemTimeDetector.get_timezone()
assert result == "Eastern Standard Time"

Expand Down