.. change::
- :tags: usecase, schema, mssql
+ :tags: usecase, mssql
:tickets: 6306
The :paramref:`_types.DateTime.timezone` parameter when set to ``True``
--- /dev/null
+.. change::
+ :tags: bug, mssql, regression
+ :tickets: 6366
+
+ Fixed regression caused by :ticket:`6306` which added support for
+ ``DateTime(timezone=True)``, where the previous behavior of the pyodbc
+ driver of implicitly dropping the tzinfo from a timezone-aware date when
+ INSERTing into a timezone-naive DATETIME column were lost, leading to a SQL
+ Server error when inserting timezone-aware datetime objects into
+ timezone-native database columns.
class _ODBCDateTime(sqltypes.DateTime):
"""Add bind processors to handle datetimeoffset behaviors"""
+ has_tz = False
+
def bind_processor(self, dialect):
def process(value):
if value is None:
elif isinstance(value, util.string_types):
# if a string was passed directly, allow it through
return value
- elif isinstance(value, datetime.datetime) and value.tzinfo is None:
+ elif not value.tzinfo or (not self.timezone and not self.has_tz):
# for DateTime(timezone=False)
return value
else:
return process
+class _ODBCDATETIMEOFFSET(_ODBCDateTime):
+ has_tz = True
+
+
class _VARBINARY_pyodbc(_ms_binary_pyodbc, VARBINARY):
pass
BINARY: _BINARY_pyodbc,
# support DateTime(timezone=True)
sqltypes.DateTime: _ODBCDateTime,
- DATETIMEOFFSET: _ODBCDateTime,
+ DATETIMEOFFSET: _ODBCDATETIMEOFFSET,
# SQL Server dialect has a VARBINARY that is just to support
# "deprecate_large_types" w/ VARBINARY(max), but also we must
# handle the usual SQL standard VARBINARY
(d1, d2, t1, d2.time(), d3),
)
+ @testing.combinations(
+ (
+ datetime.datetime(
+ 2007,
+ 10,
+ 30,
+ 11,
+ 2,
+ 32,
+ tzinfo=util.timezone(datetime.timedelta(hours=-5)),
+ ),
+ ),
+ (datetime.datetime(2007, 10, 30, 11, 2, 32)),
+ argnames="date",
+ )
+ def test_tz_present_or_non_in_dates(self, date_fixture, connection, date):
+ t, (d1, t1, d2, d3) = date_fixture
+ connection.execute(
+ t.insert(),
+ dict(
+ adatetime=date,
+ adatetimewithtimezone=date,
+ ),
+ )
+
+ row = connection.execute(
+ select(t.c.adatetime, t.c.adatetimewithtimezone)
+ ).first()
+
+ if not date.tzinfo:
+ eq_(row, (date, date.replace(tzinfo=util.timezone.utc)))
+ else:
+ eq_(row, (date.replace(tzinfo=None), date))
+
@testing.metadata_fixture()
def datetimeoffset_fixture(self, metadata):
t = Table(