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 src/google/adk/sessions/database_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
41 changes: 41 additions & 0 deletions tests/unittests/sessions/test_session_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}

Expand Down