diff --git a/keycloak_mcp/server.py b/keycloak_mcp/server.py index eb32462..6e971b9 100644 --- a/keycloak_mcp/server.py +++ b/keycloak_mcp/server.py @@ -10,7 +10,7 @@ import sys from collections import Counter from collections.abc import Callable -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from mcp.server.fastmcp import FastMCP @@ -22,8 +22,8 @@ def _format_ts(epoch_ms: int | str) -> str: """Convert epoch milliseconds to local datetime string.""" try: ts = int(epoch_ms) / 1000 - return datetime.fromtimestamp(ts).astimezone().strftime("%Y-%m-%d %H:%M:%S") - except (ValueError, TypeError, OSError): + return datetime.fromtimestamp(ts, tz=timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M:%S") + except (ValueError, TypeError): return str(epoch_ms) @@ -445,13 +445,13 @@ def get_login_stats_by_hour(date_from: str = "", date_to: str = "") -> str: failure_by_hour: Counter[int] = Counter() for e in success: try: - hour = datetime.fromtimestamp(int(e["time"]) / 1000).astimezone().hour + hour = datetime.fromtimestamp(int(e["time"]) / 1000, tz=timezone.utc).astimezone().hour success_by_hour[hour] += 1 except (KeyError, ValueError, TypeError): pass for e in failure: try: - hour = datetime.fromtimestamp(int(e["time"]) / 1000).astimezone().hour + hour = datetime.fromtimestamp(int(e["time"]) / 1000, tz=timezone.utc).astimezone().hour failure_by_hour[hour] += 1 except (KeyError, ValueError, TypeError): pass diff --git a/tests/test_server.py b/tests/test_server.py index e72c102..4bd8b6e 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1,11 +1,8 @@ """Tests for MCP server tools.""" -import sys from datetime import datetime, timedelta from unittest.mock import patch -import pytest - from keycloak_mcp import server SAMPLE_USER = { @@ -27,10 +24,6 @@ def test_valid_epoch_ms(self): def test_invalid_value(self): assert server._format_ts("invalid") == "invalid" - @pytest.mark.skipif( - sys.platform == "win32", - reason="Windows raises OSError on datetime.fromtimestamp(0); _format_ts falls back to str(0)", - ) def test_zero(self): result = server._format_ts(0) assert "1970" in result or "1969" in result # depends on TZ