将 MSSQL datetime2(7) 值检索为 Python 日期时间,微秒四舍五入而不是截断
Retrieve MSSQL datetime2(7) value as Python datetime with microseconds rounded instead of truncated
如果我在 T-SQL 中执行 CAST
,0397
通过适当的舍入舍入为 040
,但 pyodbc 将其截断为 039
.我怎样才能像 SQL 服务器那样轻松地进行舍入?
1> select logid, timestamputc from eventlog where logid=166944;
2> go
logid timestamputc
-------------------- --------------------------------------
166944 2017-05-30 08:59:37.6650397
1> select logid from eventlog
where cast(timestamputc as datetime2(6))='2017-05-30 08:59:37.665039';
2> go
logid
--------------------
(0 rows affected)
1> select logid from eventlog
where cast(timestamputc as datetime2(6))='2017-05-30 08:59:37.665040';
2> go
logid
--------------------
166944
使用 pyodbc:
[{'logid': 166944, 'timestamputc': '2017-05-30 08:59:37.665039'}]
如您所见,当 pyodbc 检索 datetime2(7)
列作为 Python datetime
对象时,其默认行为是截断小数点后第七位。如果您希望 datetime
对象四舍五入,因为 SQL 服务器会将 return 一个 datetime2(7)
值 CAST 到 datetime2(6)
,那么您可以使用 output converter function.
例如,如果您将输出转换器函数定义为
def handle_datetime2(dt2_value):
tup = struct.unpack("<6hI", dt2_value) # e.g., (2017, 5, 30, 8, 59, 37, 0, 665039700)
return datetime(tup[0], tup[1], tup[2],
hour=tup[3], minute=tup[4], second=tup[5],
microsecond=math.floor(tup[6] / 1000.0 + 0.5))
并像这样使用它
cnxn = pyodbc.connect(conn_str, autocommit=True)
crsr = cnxn.cursor()
cnxn.add_output_converter(pyodbc.SQL_TYPE_TIMESTAMP, handle_datetime2)
dt_string = '2017-05-30 08:59:37.6650397'
dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
print(f'{dt_string}\n -> {repr(dt_value)}')
dt_string = '2017-05-30 08:59:37.6650395'
dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
print(f'{dt_string}\n -> {repr(dt_value)}')
dt_string = '2017-05-30 08:59:37.6650394'
dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
print(f'{dt_string}\n -> {repr(dt_value)}')
结果将如下所示
2017-05-30 08:59:37.6650397
-> datetime.datetime(2017, 5, 30, 8, 59, 37, 665040)
2017-05-30 08:59:37.6650395
-> datetime.datetime(2017, 5, 30, 8, 59, 37, 665040)
2017-05-30 08:59:37.6650394
-> datetime.datetime(2017, 5, 30, 8, 59, 37, 665039)
如果我在 T-SQL 中执行 CAST
,0397
通过适当的舍入舍入为 040
,但 pyodbc 将其截断为 039
.我怎样才能像 SQL 服务器那样轻松地进行舍入?
1> select logid, timestamputc from eventlog where logid=166944;
2> go
logid timestamputc
-------------------- --------------------------------------
166944 2017-05-30 08:59:37.6650397
1> select logid from eventlog
where cast(timestamputc as datetime2(6))='2017-05-30 08:59:37.665039';
2> go
logid
--------------------
(0 rows affected)
1> select logid from eventlog
where cast(timestamputc as datetime2(6))='2017-05-30 08:59:37.665040';
2> go
logid
--------------------
166944
使用 pyodbc:
[{'logid': 166944, 'timestamputc': '2017-05-30 08:59:37.665039'}]
如您所见,当 pyodbc 检索 datetime2(7)
列作为 Python datetime
对象时,其默认行为是截断小数点后第七位。如果您希望 datetime
对象四舍五入,因为 SQL 服务器会将 return 一个 datetime2(7)
值 CAST 到 datetime2(6)
,那么您可以使用 output converter function.
例如,如果您将输出转换器函数定义为
def handle_datetime2(dt2_value):
tup = struct.unpack("<6hI", dt2_value) # e.g., (2017, 5, 30, 8, 59, 37, 0, 665039700)
return datetime(tup[0], tup[1], tup[2],
hour=tup[3], minute=tup[4], second=tup[5],
microsecond=math.floor(tup[6] / 1000.0 + 0.5))
并像这样使用它
cnxn = pyodbc.connect(conn_str, autocommit=True)
crsr = cnxn.cursor()
cnxn.add_output_converter(pyodbc.SQL_TYPE_TIMESTAMP, handle_datetime2)
dt_string = '2017-05-30 08:59:37.6650397'
dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
print(f'{dt_string}\n -> {repr(dt_value)}')
dt_string = '2017-05-30 08:59:37.6650395'
dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
print(f'{dt_string}\n -> {repr(dt_value)}')
dt_string = '2017-05-30 08:59:37.6650394'
dt_value = crsr.execute(f"SELECT CAST('{dt_string}' AS DATETIME2(7))").fetchval()
print(f'{dt_string}\n -> {repr(dt_value)}')
结果将如下所示
2017-05-30 08:59:37.6650397
-> datetime.datetime(2017, 5, 30, 8, 59, 37, 665040)
2017-05-30 08:59:37.6650395
-> datetime.datetime(2017, 5, 30, 8, 59, 37, 665040)
2017-05-30 08:59:37.6650394
-> datetime.datetime(2017, 5, 30, 8, 59, 37, 665039)