diff --git a/src/google/adk/sessions/database_session_service.py b/src/google/adk/sessions/database_session_service.py index d033f1f234..740e5540d9 100644 --- a/src/google/adk/sessions/database_session_service.py +++ b/src/google/adk/sessions/database_session_service.py @@ -461,7 +461,8 @@ async def create_session( now = datetime.fromtimestamp(platform_time.get_time(), tz=timezone.utc) is_sqlite = self.db_engine.dialect.name == _SQLITE_DIALECT is_postgresql = self.db_engine.dialect.name == _POSTGRESQL_DIALECT - if is_sqlite or is_postgresql: + is_mysql = self.db_engine.dialect.name == _MYSQL_DIALECT + if is_sqlite or is_postgresql or is_mysql: now = now.replace(tzinfo=None) storage_session = schema.StorageSession( diff --git a/tests/unittests/sessions/test_session_service.py b/tests/unittests/sessions/test_session_service.py index 2d7d89f15f..6eda8d288c 100644 --- a/tests/unittests/sessions/test_session_service.py +++ b/tests/unittests/sessions/test_session_service.py @@ -102,44 +102,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']) +@pytest.mark.parametrize('dialect_name', ['sqlite', 'postgresql', 'mysql']) 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. + for SQLite, PostgreSQL, and MySQL. PostgreSQL's default TIMESTAMP type is WITHOUT TIME ZONE, which cannot accept timezone-aware datetime objects when using asyncpg. SQLite also - requires naive datetimes. + requires naive datetimes. MySQL's DATETIME type silently drops timezone + info, causing stale-writer false positives when the marker string differs + between tz-aware writes and naive reads. """ # Simulate the logic in create_session is_sqlite = dialect_name == 'sqlite' is_postgres = dialect_name == 'postgresql' + is_mysql = dialect_name == 'mysql' now = datetime.now(timezone.utc) assert now.tzinfo is not None # Starts with timezone - if is_sqlite or is_postgres: + if is_sqlite or is_postgres or is_mysql: now = now.replace(tzinfo=None) - # Both SQLite and PostgreSQL should have timezone stripped + # SQLite, PostgreSQL, and MySQL 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' + # For dialects like Spanner with explicit timezone support, we don't strip + dialect_name = 'spanner' is_sqlite = dialect_name == 'sqlite' is_postgres = dialect_name == 'postgresql' + is_mysql = dialect_name == 'mysql' now = datetime.now(timezone.utc) assert now.tzinfo is not None - if is_sqlite or is_postgres: + if is_sqlite or is_postgres or is_mysql: now = now.replace(tzinfo=None) - # MySQL should preserve timezone (if the column type supports it) + # Spanner should preserve timezone assert now.tzinfo is not None