diff --git a/headroom/proxy/outcome.py b/headroom/proxy/outcome.py index e0b304d4c..5a23acc04 100644 --- a/headroom/proxy/outcome.py +++ b/headroom/proxy/outcome.py @@ -27,7 +27,7 @@ from __future__ import annotations import logging from dataclasses import dataclass, field -from datetime import datetime +from datetime import datetime, timezone from typing import Any from headroom.proxy.tool_schema_savings_policy import ( @@ -526,7 +526,10 @@ async def emit_request_outcome(handler: Any, outcome: RequestOutcome) -> None: request_logger.log( RequestLog( request_id=outcome.request_id, - timestamp=datetime.now().isoformat(), + # Request logs are consumed by browsers in arbitrary time zones. + # Include the UTC offset so relative-age calculations represent + # the same instant regardless of where the proxy runs. + timestamp=datetime.now(timezone.utc).isoformat(), provider=outcome.provider, model=outcome.model, input_tokens_original=outcome.original_tokens, diff --git a/tests/test_request_outcome.py b/tests/test_request_outcome.py index 09936a094..d2295a5ea 100644 --- a/tests/test_request_outcome.py +++ b/tests/test_request_outcome.py @@ -15,6 +15,7 @@ import asyncio import contextlib import logging from dataclasses import FrozenInstanceError +from datetime import datetime, timezone from typing import Any from unittest.mock import AsyncMock, MagicMock @@ -325,6 +326,22 @@ async def test_funnel_logs_request_with_derived_cache_hit() -> None: assert log_entry.cache_hit is True +@pytest.mark.asyncio +async def test_funnel_logs_request_timestamp_with_utc_offset() -> None: + """Recent-request timestamps must identify an absolute instant. + + A naive ISO timestamp is interpreted in the browser's local timezone, + which makes the dashboard show negative ages when the proxy and browser + use different timezone settings. + """ + h = _FunnelHarness() + await h._record_request_outcome(_outcome()) + + timestamp = datetime.fromisoformat(h.logger.logs[0].timestamp) + assert timestamp.tzinfo is not None + assert timestamp.utcoffset() == timezone.utc.utcoffset(timestamp) + + @pytest.mark.asyncio async def test_funnel_skips_request_log_when_logger_absent() -> None: """Same pattern as cost_tracker — optional surface."""