将 MSSQL datetime2(7) 值检索为 Python 日期时间,微秒四舍五入而不是截断

Retrieve MSSQL datetime2(7) value as Python datetime with microseconds rounded instead of truncated

如果我在 T-SQL 中执行 CAST0397 通过适当的舍入舍入为 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)