Skip to content

Commit 6f1efe7

Browse files
anxkhncopybara-github
authored andcommitted
fix: migrate v0 event timestamps as local time, not UTC
Merge #6396 PiperOrigin-RevId: 951160752
1 parent 79cc1f2 commit 6f1efe7

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

src/google/adk/sessions/migration/migrate_from_sqlalchemy_pickle.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,12 @@ def _safe_json_load(val: Any) -> dict[str, Any] | None:
220220
author=row.get("author", "agent"),
221221
branch=row.get("branch"),
222222
actions=actions,
223-
timestamp=timestamp.replace(tzinfo=timezone.utc).timestamp(),
223+
# v0 wrote this column as a naive datetime in local time (via
224+
# datetime.fromtimestamp) and read it back the same way, so interpret a
225+
# naive value as local time here too. Forcing UTC would shift every
226+
# migrated timestamp by the host's UTC offset. datetime.timestamp()
227+
# treats naive datetimes as local and honors tzinfo when present.
228+
timestamp=timestamp.timestamp(),
224229
long_running_tool_ids=long_running_tool_ids,
225230
partial=row.get("partial"),
226231
turn_complete=row.get("turn_complete"),

tests/unittests/sessions/migration/test_migration.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from datetime import timezone
2020
import os
2121
import pickle
22+
import time
2223

2324
from fastapi.openapi.models import HTTPBearer
2425
from google.adk.auth.auth_tool import AuthConfig
@@ -367,6 +368,38 @@ def test_migrate_from_sqlalchemy_pickle_ignores_non_object_json_fields():
367368
assert event.content is None
368369

369370

371+
def test_migrate_from_sqlalchemy_pickle_reads_naive_timestamp_as_local(
372+
monkeypatch,
373+
):
374+
"""Naive v0 event timestamps must migrate as local time, not UTC.
375+
376+
The v0 schema stored the event ``timestamp`` column as a naive datetime in
377+
local time (``StorageEvent.from_event`` uses ``datetime.fromtimestamp`` and
378+
``to_event`` reads it back with naive ``.timestamp()``). Forcing UTC on that
379+
naive value shifted every migrated timestamp by the host's UTC offset. Pin a
380+
fixed non-UTC zone so the round trip is exact regardless of the host.
381+
"""
382+
monkeypatch.setenv("TZ", "Asia/Kolkata")
383+
time.tzset()
384+
try:
385+
original_epoch = 1000000.0
386+
# Exactly what v0.StorageEvent.from_event persisted: naive local time.
387+
naive_local_timestamp = datetime.fromtimestamp(original_epoch)
388+
assert naive_local_timestamp.tzinfo is None
389+
390+
event = mfsp._row_to_event({
391+
"id": "event-naive-timestamp",
392+
"invocation_id": "invoke1",
393+
"author": "user",
394+
"actions": EventActions(),
395+
"timestamp": naive_local_timestamp,
396+
})
397+
398+
assert event.timestamp == original_epoch
399+
finally:
400+
time.tzset()
401+
402+
370403
def test_migrate_from_sqlalchemy_pickle_blocks_unsafe_actions_pickle(
371404
tmp_path, monkeypatch
372405
):

0 commit comments

Comments
 (0)