我正在向 pyodbc
连接对象添加输出转换器,以处理从 SQL Server 返回的日期类型。我能够使用以下命令解压 datetime.time
结构:
tuple = struct.unpack("HHHI", dateObj)
效果很好。不过,我无法弄清楚 datetime.datetime
对象的 secret ,根据 pyodbc 文档,它是一个 TIMESTAMP_STRUCT,定义为 here :
typedef struct tagTIMESTAMP_STRUCT
{
SQLSMALLINT year;
SQLUSMALLINT month;
SQLUSMALLINT day;
SQLUSMALLINT hour;
SQLUSMALLINT minute;
SQLUSMALLINT second;
SQLUINTEGER fraction;
} TIMESTAMP_STRUCT;
数据库中该列的数据为 2018-01-11 11:50:16.000
,并且没有 add_output_convert
陷阱 pyodbc 返回:
TypeError: datetime.datetime(2018, 1, 11, 11, 50, 16) 不可 JSON 序列化
看起来 pyodbc 默默地丢弃了分数,这很好。 unpack()
格式不应该是以下之一:
tuple = struct.unpack("hHHHHHI", dateObj) # with missing fraction
tuple = struct.unpack("hHHHHH", dateObj)
?后者只是返回:
错误:解包需要长度为 12 的字符串参数
郑重声明,根据sys.getsizeof
,dateObj 为 41 字节。对格式有什么建议吗?这是 Windows 10 64 位,以及 Linux 64 位。
最佳答案
你似乎一直在追寻一些错误的线索。 SQL Server ODBC 驱动程序不会为 DATETIME 值返回 41 字节的数据,它只返回 8 字节。 (sys.getsizeof
返回值 41,因为它包含与垃圾收集相关的“开销”。)并且这 8 个字节不可能表示 TIMESTAMP_STRUCT,因此它一定是其他内容。
从基本测试开始......
import pyodbc
def datetime_as_string(raw_bytes):
return raw_bytes
cnxn = pyodbc.connect('DSN=SQLmyDb;', autocommit=True)
cnxn.add_output_converter(pyodbc.SQL_TYPE_TIMESTAMP, datetime_as_string)
crsr = cnxn.cursor()
test_value = '2018-01-11 11:50:16'
rtn = crsr.execute("SELECT CAST(? AS DATETIME)", test_value).fetchval()
print(repr(rtn))
crsr.close()
cnxn.close()
...我看到您的测试值由 'e\xa8\x00\x00\xa0\x14\xc3\x00'
表示。在 Windows 计算器中使用十六进制转换器一段时间后,我发现内容并不是很明显。凭直觉,我尝试了 test_value = '1900-01-01 00:00:00'
并返回了 '\x00\x00\x00\x00\x00\x00\x00\x00 '
所以至少我有一个起点(SQL Server DATETIME 值的“纪元”)。
test_value = '1901-01-01 00:00:00'
(纪元后 1 年)返回 'm\x01\x00\x00\x00\x00\x00\x00'
,'m'
是 0x6d
,0x016d
是 365,所以这是令人鼓舞的。
test_value = '1900-01-01 00:00:01'
(纪元后 1 秒)返回 '\x00\x00\x00\x00,\x01\x00\x00'
、','
为 0x2c
,0x012c
为 300。
test_value = '1900-01-01 00:00:02'
(纪元后 2 秒)返回 '\x00\x00\x00\x00X\x02\x00\x00 '
,'X'
为 0x58
,0x0258
为 600。
因此,SQL Server ODBC 驱动程序返回两个 4 字节有符号整数,第一个是整日距纪元的偏移量,第二个是以 1/300 秒为增量的部分日期。
因此我将输出转换器功能更改为
def datetime_as_string(raw_bytes):
tup = struct.unpack("<2l", raw_bytes)
days_since_1900 = tup[0]
partial_day = round(tup[1] / 300.0, 3)
date_time = datetime(1900, 1, 1) + timedelta(days=days_since_1900) + timedelta(seconds=partial_day)
return date_time.strftime('%Y-%m-%d %H:%M:%S.%f')[:23]
这似乎成功了。
关于python - 如何在 pyodbc 输出转换器函数中解压 SQL Server DATETIME?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48346046/