Skip to content

Commit 97129df

Browse files
committed
Merge pull request pymssql#398 from pymssql/pr331-date-time-datetime2
Add suport for date, time and datetime2
2 parents 267f725 + 0c68217 commit 97129df

5 files changed

Lines changed: 84 additions & 19 deletions

File tree

ChangeLog

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,15 @@ General
1414
Features
1515
--------
1616

17-
TBA
17+
- Support for new in SQL Server 2008 ``DATE``, ``TIME`` and ``DATETIME2`` data
18+
types (GH-156). The following conditions need to be additionally met so
19+
values of these column types can be returned from the database as their
20+
native corresponding Python data types instead of as strings:
21+
22+
* Underlying FreeTDS must be 0.95 or newer.
23+
* TDS protocol version in use must be 7.3 or newer.
24+
25+
Thanks Ed Avis for the implementation. (GH-331)
1826

1927
Bug fixes
2028
---------

ChangeLog_highlights.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@ General
1212
Features
1313
--------
1414

15-
TBA
15+
- Support for new in SQL Server 2008 ``DATE``, ``TIME`` and ``DATETIME2`` data
16+
types (GH-156). The following conditions need to be additionally met so
17+
values of these column types can be returned from the database as their
18+
native corresponding Python data types instead of as strings:
19+
20+
* Underlying FreeTDS must be 0.95 or newer.
21+
* TDS protocol version in use must be 7.3 or newer.
22+
23+
Thanks Ed Avis for the implementation. (GH-331)
1624

1725
Bug fixes
1826
---------

docs/faq.rst

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -264,22 +264,17 @@ and ``TIME`` columns as simple strings. For example::
264264

265265
Yep, so the problem here is that ``DATETIME`` has been supported by `FreeTDS
266266
<http://www.freetds.org/>`_ for a long time, but ``DATE`` and ``TIME`` are
267-
newer types in SQL Server and Microsoft never added support for them to db-lib
268-
and FreeTDS never added support for them either.
267+
newer types in SQL Server, Microsoft never added support for them to db-lib
268+
and FreeTDS added support for them in version 0.95.
269269

270-
There was some discussion of adding it to FreeTDS, but I think that stalled.
271-
See this thread:
270+
If you need support for these data types (i.e. they get returned from the
271+
database as their native corresponding Python data types instead of as strings)
272+
as well as for the ``DATETIME2`` one, then make sure the following conditions
273+
are met:
272274

273-
http://lists.ibiblio.org/pipermail/freetds/2013q2/thread.html#28348
274-
275-
So we would need to get FreeTDS to support it and then the user would have to
276-
make sure to use a very recent FreeTDS (unless pymssql links in said version of
277-
FreeTDS).
278-
279-
Links:
280-
281-
* https://github.com/pymssql/pymssql/issues/156
282-
* `Discussion of adding support for DATE and TIME to FreeTDS <http://lists.ibiblio.org/pipermail/freetds/2013q2/thread.html#28348>`_
275+
* You are connecting to SQL Server 2008 or newer.
276+
* You are using FreeTDS 0.95 or newer.
277+
* You are using TDS protocol version 7.3 or newer.
283278

284279
Shared object "libsybdb.so.3" not found
285280
=======================================

src/_mssql.pyx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ SQLVARBINARY = SYBVARBINARY
132132
SQLVARCHAR = SYBVARCHAR
133133
SQLUUID = 36
134134

135+
SQLDATE = 40
136+
SQLTIME = 41
137+
SQLDATETIME2 = 42
138+
135139
#######################
136140
## Exception classes ##
137141
#######################
@@ -800,13 +804,25 @@ cdef class MSSQLConnection:
800804
ctx.prec = precision if precision > 0 else 1
801805
return decimal.Decimal(_remove_locale(buf, converted_length).decode(self._charset))
802806

803-
elif dbtype == SQLDATETIM4:
807+
elif dbtype in (SQLDATETIM4, SQLDATETIME2):
804808
dbconvert(self.dbproc, dbtype, data, -1, SQLDATETIME,
805809
<BYTE *>&dt, -1)
806810
dbdatecrack(self.dbproc, &di, <DBDATETIME *><BYTE *>&dt)
807811
return datetime.datetime(di.year, di.month, di.day,
808812
di.hour, di.minute, di.second, di.millisecond * 1000)
809813

814+
elif dbtype == SQLDATE:
815+
dbconvert(self.dbproc, dbtype, data, -1, SQLDATETIME,
816+
<BYTE *>&dt, -1)
817+
dbdatecrack(self.dbproc, &di, <DBDATETIME *><BYTE *>&dt)
818+
return datetime.date(di.year, di.month, di.day)
819+
820+
elif dbtype == SQLTIME:
821+
dbconvert(self.dbproc, dbtype, data, -1, SQLDATETIME,
822+
<BYTE *>&dt, -1)
823+
dbdatecrack(self.dbproc, &di, <DBDATETIME *><BYTE *>&dt)
824+
return datetime.time(di.hour, di.minute, di.second, di.millisecond * 1000)
825+
810826
elif dbtype == SQLDATETIME:
811827
dbdatecrack(self.dbproc, &di, <DBDATETIME *>data)
812828
return datetime.datetime(di.year, di.month, di.day,

tests/test_types.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import binascii
3+
from datetime import time
4+
from datetime import date
35
from datetime import datetime
46
import decimal
57
from decimal import Decimal as D
@@ -9,7 +11,9 @@
911
import unittest
1012
import uuid
1113

12-
from .helpers import skip_test
14+
from .helpers import get_sql_server_version
15+
16+
import pytest
1317

1418

1519
def get_bytes_buffer():
@@ -34,6 +38,9 @@ def typeeq(v1, v2):
3438
float_no float,
3539
money_no money,
3640
stamp_datetime datetime,
41+
stamp_date date,
42+
stamp_time time,
43+
stamp_datetime2 datetime2,
3744
data_bit bit,
3845
comment_vch varchar(50),
3946
comment_nvch nvarchar(50),
@@ -44,7 +51,7 @@ def typeeq(v1, v2):
4451
decimal_no decimal(38,2),
4552
decimal_no2 decimal(38,10),
4653
numeric_no numeric(38,8),
47-
stamp_time timestamp,
54+
stamp_timestamp timestamp,
4855
uuid uniqueidentifier
4956
)
5057
"""
@@ -156,6 +163,37 @@ def test_datetime_params_as_dict(self):
156163
typeeq(testval, colval)
157164
eq_(testval, colval)
158165

166+
def test_date(self):
167+
if get_sql_server_version(self.conn) < 2008:
168+
pytest.skip("DATE field type isn't supported by SQL Server versions prior to 2008.")
169+
if self.conn.tds_version < 7.3:
170+
pytest.skip("DATE field type isn't supported by TDS protocol older than 7.3.")
171+
testval = date(2013, 1, 2)
172+
colval = self.insert_and_select('stamp_date', testval, 's')
173+
typeeq(testval, colval)
174+
eq_(testval, colval)
175+
176+
def test_time(self):
177+
if get_sql_server_version(self.conn) < 2008:
178+
pytest.skip("TIME field type is supported by SQL Server versions prior to 2008.")
179+
if self.conn.tds_version < 7.3:
180+
pytest.skip("TIME field type isn't supported by TDS protocol older than 7.3.")
181+
testval = datetime(2013, 1, 2, 3, 4, 5, 3000)
182+
colval = self.insert_and_select('stamp_time', testval, 's')
183+
testval_no_date = testval.time()
184+
typeeq(testval_no_date, colval)
185+
eq_(testval_no_date, colval)
186+
187+
def test_datetime2(self):
188+
if get_sql_server_version(self.conn) < 2008:
189+
pytest.skip("DATETIME2 field type isn't supported by SQL Server versions prior to 2008.")
190+
if self.conn.tds_version < 7.3:
191+
pytest.skip("DATETIME2 field type isn't supported by TDS protocol older than 7.3.")
192+
testval = datetime(2013, 1, 2, 3, 4, 5, 3000)
193+
colval = self.insert_and_select('stamp_datetime2', testval, 's')
194+
typeeq(testval, colval)
195+
eq_(testval, colval)
196+
159197
def test_decimal(self):
160198
# test rounding down
161199
origval = D('1.2345')

0 commit comments

Comments
 (0)