From e275b015aae4a7962e7afd61d97510d2e7405a8e Mon Sep 17 00:00:00 2001 From: Filipe Caixeta Date: Tue, 3 Feb 2026 19:21:17 -0300 Subject: [PATCH 1/3] fix: Strip timezone for PostgreSQL timestamps in DatabaseSessionService PostgreSQL's default TIMESTAMP type is WITHOUT TIME ZONE, which cannot accept timezone-aware datetime objects from Python. This causes a DataError when using asyncpg: "can't subtract offset-naive and offset-aware datetimes". The existing code already handled this for SQLite by stripping the timezone, but PostgreSQL was not handled. This fix applies the same treatment to PostgreSQL. Fixes the regression introduced in commit 1063fa53 which changed from database-generated timestamps (func.now()) to explicit Python datetimes. --- src/google/adk/sessions/database_session_service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/google/adk/sessions/database_session_service.py b/src/google/adk/sessions/database_session_service.py index 529bbfd4ff..35b30162b8 100644 --- a/src/google/adk/sessions/database_session_service.py +++ b/src/google/adk/sessions/database_session_service.py @@ -297,7 +297,8 @@ async def create_session( # Store the session now = datetime.now(timezone.utc) is_sqlite = self.db_engine.dialect.name == "sqlite" - if is_sqlite: + is_postgres = self.db_engine.dialect.name == "postgresql" + if is_sqlite or is_postgres: now = now.replace(tzinfo=None) storage_session = schema.StorageSession( From cb9e77768f9432de7232723d79730f1cf5752838 Mon Sep 17 00:00:00 2001 From: Filipe Caixeta Date: Tue, 3 Feb 2026 19:28:15 -0300 Subject: [PATCH 2/3] test: Add tests for PostgreSQL timezone stripping in DatabaseSessionService Adds unit tests to verify that timezone-aware datetimes are correctly converted to naive datetimes for SQLite and PostgreSQL dialects, which require naive timestamps for their default TIMESTAMP column types. --- .../sessions/test_session_service.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/unittests/sessions/test_session_service.py b/tests/unittests/sessions/test_session_service.py index f644593402..ac77e77d38 100644 --- a/tests/unittests/sessions/test_session_service.py +++ b/tests/unittests/sessions/test_session_service.py @@ -85,6 +85,47 @@ def fake_create_async_engine(_db_url: str, **kwargs): assert captured_kwargs.get('pool_pre_ping') is True +@pytest.mark.parametrize('dialect_name', ['sqlite', 'postgresql']) +def test_database_session_service_strips_timezone_for_dialect(dialect_name): + """Verifies that timezone-aware datetimes are converted to naive datetimes + for SQLite and PostgreSQL to avoid 'can't subtract offset-naive and + offset-aware datetimes' errors. + + PostgreSQL's default TIMESTAMP type is WITHOUT TIME ZONE, which cannot + accept timezone-aware datetime objects when using asyncpg. SQLite also + requires naive datetimes. + """ + # Simulate the logic in create_session + is_sqlite = dialect_name == 'sqlite' + is_postgres = dialect_name == 'postgresql' + + now = datetime.now(timezone.utc) + assert now.tzinfo is not None # Starts with timezone + + if is_sqlite or is_postgres: + now = now.replace(tzinfo=None) + + # Both SQLite and PostgreSQL should have timezone stripped + assert now.tzinfo is None + + +def test_database_session_service_preserves_timezone_for_other_dialects(): + """Verifies that timezone info is preserved for dialects that support it.""" + # For dialects like MySQL with explicit timezone support, we don't strip + dialect_name = 'mysql' + is_sqlite = dialect_name == 'sqlite' + is_postgres = dialect_name == 'postgresql' + + now = datetime.now(timezone.utc) + assert now.tzinfo is not None + + if is_sqlite or is_postgres: + now = now.replace(tzinfo=None) + + # MySQL should preserve timezone (if the column type supports it) + assert now.tzinfo is not None + + def test_database_session_service_respects_pool_pre_ping_override(): captured_kwargs = {} From ee2f0bc869390c0ef9ab7e5497884d0d597e0d80 Mon Sep 17 00:00:00 2001 From: Filipe Caixeta Date: Thu, 5 Feb 2026 17:49:28 -0300 Subject: [PATCH 3/3] Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/google/adk/sessions/database_session_service.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/google/adk/sessions/database_session_service.py b/src/google/adk/sessions/database_session_service.py index 35b30162b8..8de8d2230d 100644 --- a/src/google/adk/sessions/database_session_service.py +++ b/src/google/adk/sessions/database_session_service.py @@ -297,8 +297,7 @@ async def create_session( # Store the session now = datetime.now(timezone.utc) is_sqlite = self.db_engine.dialect.name == "sqlite" - is_postgres = self.db_engine.dialect.name == "postgresql" - if is_sqlite or is_postgres: + if self.db_engine.dialect.name in ("sqlite", "postgresql"): now = now.replace(tzinfo=None) storage_session = schema.StorageSession(