From 1a7fe665c82cf373648767319aa224ff1ea9da1b Mon Sep 17 00:00:00 2001 From: Anubhav Banerjee Date: Sun, 26 Apr 2026 18:02:37 +0530 Subject: [PATCH] fix(issue#152): match issued_at default to naive column schema The issued_at column is defined as a timezone-naive DateTime, but its default value (datetime.now(timezone.utc)) was returning a timezone-aware object. While some database drivers silently fix this mismatch for us behind the scenes, stricter ones will immediately crash with a TypeError during inserts. This commit fixes the root problem by explicitly removing the timezone info (.replace(tzinfo=None)) before the value hits the database. This ensures the data perfectly matches the expected column schema, preventing crashes across different environments. A regression test has also been added. --- sqlalchemy_history/transaction.py | 4 +++- tests/relationships/test_many_to_many_relations.py | 2 +- .../reported_bugs/test_bug_27_datetime_insertion_issue.py | 2 +- tests/schema/test_update_end_transaction_id.py | 2 +- tests/test_hybrid_property.py | 4 +++- tests/test_transaction.py | 8 ++++++++ tests/utils/test_parent_table.py | 2 +- tests/utils/test_version_table.py | 2 +- 8 files changed, 19 insertions(+), 7 deletions(-) diff --git a/sqlalchemy_history/transaction.py b/sqlalchemy_history/transaction.py index c534ef0..71bc444 100644 --- a/sqlalchemy_history/transaction.py +++ b/sqlalchemy_history/transaction.py @@ -16,7 +16,9 @@ def compile_big_integer(element, compiler, **kw): class TransactionBase: - issued_at = sa.Column(sa.DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc)) + issued_at = sa.Column( + sa.DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) + ) @property def entity_names(self): diff --git a/tests/relationships/test_many_to_many_relations.py b/tests/relationships/test_many_to_many_relations.py index 3f3730b..36f3366 100644 --- a/tests/relationships/test_many_to_many_relations.py +++ b/tests/relationships/test_many_to_many_relations.py @@ -34,7 +34,7 @@ class Article(self.Model): sa.DateTime, nullable=False, server_default=sa.func.current_timestamp(), - default=lambda: datetime.datetime.now(datetime.timezone.utc), + default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None), ), ) diff --git a/tests/reported_bugs/test_bug_27_datetime_insertion_issue.py b/tests/reported_bugs/test_bug_27_datetime_insertion_issue.py index 0712f95..d70435a 100644 --- a/tests/reported_bugs/test_bug_27_datetime_insertion_issue.py +++ b/tests/reported_bugs/test_bug_27_datetime_insertion_issue.py @@ -19,7 +19,7 @@ def create_models(self): sa.DateTime, nullable=False, server_default=sa.func.current_timestamp(), - default=lambda: datetime.datetime.now(datetime.timezone.utc), + default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None), ), ) diff --git a/tests/schema/test_update_end_transaction_id.py b/tests/schema/test_update_end_transaction_id.py index 46b5d19..e4ccabb 100644 --- a/tests/schema/test_update_end_transaction_id.py +++ b/tests/schema/test_update_end_transaction_id.py @@ -24,7 +24,7 @@ def create_models(self): sa.DateTime, nullable=False, server_default=sa.func.current_timestamp(), - default=lambda: datetime.datetime.now(datetime.timezone.utc), + default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None), ), ) diff --git a/tests/test_hybrid_property.py b/tests/test_hybrid_property.py index 6f00f79..0635576 100644 --- a/tests/test_hybrid_property.py +++ b/tests/test_hybrid_property.py @@ -18,7 +18,9 @@ class Article(self.Model): name = sa.Column(sa.Unicode(255), nullable=False) content = sa.Column(sa.UnicodeText) description = sa.Column(sa.UnicodeText) - publish = sa.Column(sa.DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc)) + publish = sa.Column( + sa.DateTime, default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) + ) author_id = sa.Column(sa.Integer, sa.ForeignKey("article_author.id"), nullable=False) diff --git a/tests/test_transaction.py b/tests/test_transaction.py index 624e69f..d2122bf 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -1,3 +1,4 @@ +import datetime import os import time @@ -51,6 +52,13 @@ def test_transaction_issued_at(self): self.session.commit() assert self.article.versions[0].transaction.issued_at != self.article.versions[1].transaction.issued_at + def test_transaction_issued_at_is_naive(self): + uow = versioning_manager.unit_of_work(self.session) + tx = uow.create_transaction(self.session) + + assert isinstance(tx.issued_at, datetime.datetime) + assert tx.issued_at.tzinfo is None + # Check that the tests pass without TransactionChangesPlugin class TestTransactionWithoutChangesPlugin(TestTransaction): diff --git a/tests/utils/test_parent_table.py b/tests/utils/test_parent_table.py index c1a75d0..26dc1ac 100644 --- a/tests/utils/test_parent_table.py +++ b/tests/utils/test_parent_table.py @@ -21,7 +21,7 @@ def create_models(self): sa.DateTime, nullable=False, server_default=sa.func.current_timestamp(), - default=lambda: datetime.datetime.now(datetime.timezone.utc), + default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None), ), ) diff --git a/tests/utils/test_version_table.py b/tests/utils/test_version_table.py index 55ead06..8ab2f2d 100644 --- a/tests/utils/test_version_table.py +++ b/tests/utils/test_version_table.py @@ -22,7 +22,7 @@ def create_models(self): sa.DateTime, nullable=False, server_default=sa.func.current_timestamp(), - default=lambda: datetime.datetime.now(datetime.timezone.utc), + default=lambda: datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None), ), )