--- /dev/null
+.. change::
+ :tags: usecase, schema, mssql
+ :tickets: 6306
+
+ The :paramref:`_types.DateTime.timezone` parameter when set to ``True``
+ will now make use of the ``DATETIMEOFFSET`` column type with SQL Server
+ when used to emit DDL, rather than ``DATETIME`` where the flag was silently
+ ignored.
def visit_TINYINT(self, type_, **kw):
return "TINYINT"
- def visit_DATETIMEOFFSET(self, type_, **kw):
- if type_.precision is not None:
- return "DATETIMEOFFSET(%s)" % type_.precision
- else:
- return "DATETIMEOFFSET"
-
def visit_TIME(self, type_, **kw):
precision = getattr(type_, "precision", None)
if precision is not None:
def visit_ROWVERSION(self, type_, **kw):
return "ROWVERSION"
+ def visit_datetime(self, type_, **kw):
+ if type_.timezone:
+ return self.visit_DATETIMEOFFSET(type_, **kw)
+ else:
+ return self.visit_DATETIME(type_, **kw)
+
+ def visit_DATETIMEOFFSET(self, type_, **kw):
+ precision = getattr(type_, "precision", None)
+ if precision is not None:
+ return "DATETIMEOFFSET(%s)" % type_.precision
+ else:
+ return "DATETIMEOFFSET"
+
def visit_DATETIME2(self, type_, **kw):
precision = getattr(type_, "precision", None)
if precision is not None:
return process
-class _ODBCDateTimeOffset(DATETIMEOFFSET):
+class _ODBCDateTime(sqltypes.DateTime):
+ """Add bind processors to handle datetimeoffset behaviors"""
+
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:
+ # for DateTime(timezone=False)
+ return value
else:
+ # for DATETIMEOFFSET or DateTime(timezone=True)
+ #
# Convert to string format required by T-SQL
dto_string = value.strftime("%Y-%m-%d %H:%M:%S.%f %z")
# offset needs a colon, e.g., -0700 -> -07:00
sqltypes.Numeric: _MSNumeric_pyodbc,
sqltypes.Float: _MSFloat_pyodbc,
BINARY: _BINARY_pyodbc,
- DATETIMEOFFSET: _ODBCDateTimeOffset,
+ # support DateTime(timezone=True)
+ sqltypes.DateTime: _ODBCDateTime,
+ DATETIMEOFFSET: _ODBCDateTime,
# 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
(mssql.MSDateTime2, [1], {}, "DATETIME2(1)", None),
(mssql.MSTime, [0], {}, "TIME(0)", None),
(mssql.MSDateTimeOffset, [0], {}, "DATETIMEOFFSET(0)", None),
+ (types.DateTime, [], {"timezone": True}, "DATETIMEOFFSET", None),
+ (types.DateTime, [], {"timezone": False}, "DATETIME", None),
]
metadata = MetaData()
Column("atime2", Time),
Column("adatetime", DateTime),
Column("adatetimeoffset", DATETIMEOFFSET),
+ Column("adatetimewithtimezone", DateTime(timezone=True)),
)
d1 = datetime.date(2007, 10, 30)
t1 = datetime.time(11, 2, 32)
d2 = datetime.datetime(2007, 10, 30, 11, 2, 32)
- return t, (d1, t1, d2)
+ d3 = datetime.datetime(
+ 2007,
+ 10,
+ 30,
+ 11,
+ 2,
+ 32,
+ 123456,
+ util.timezone(datetime.timedelta(hours=-5)),
+ )
+ return t, (d1, t1, d2, d3)
def test_date_roundtrips(self, date_fixture, connection):
- t, (d1, t1, d2) = date_fixture
+ t, (d1, t1, d2, d3) = date_fixture
connection.execute(
- t.insert(), dict(adate=d1, adatetime=d2, atime1=t1, atime2=d2)
+ t.insert(),
+ dict(
+ adate=d1,
+ adatetime=d2,
+ atime1=t1,
+ atime2=d2,
+ adatetimewithtimezone=d3,
+ ),
)
row = connection.execute(t.select()).first()
eq_(
- (row.adate, row.adatetime, row.atime1, row.atime2),
- (d1, d2, t1, d2.time()),
+ (
+ row.adate,
+ row.adatetime,
+ row.atime1,
+ row.atime2,
+ row.adatetimewithtimezone,
+ ),
+ (d1, d2, t1, d2.time(), d3),
)
@testing.metadata_fixture()