2021 年 4 月更新:
这仍然是pandas的一个已知问题.to_sql()
原始答案(警告:包括版本 1.4 中已弃用的过时 SQLAlchemy 1.3 使用模式)
datetimeoffsetSQLAlchemy中的处理有一些最近(如“今天早上”)的改进。它们将包含在下一个版本(可能是 1.3.13)中,但同时尝试从 1.3.x 分支的最新源安装...
pip install --upgrade git+https://github.com/sqlalchemy/sqlalchemy@rel_1_3
...看看这是否更适合您。
编辑:
经过进一步调查,问题似乎出在to_sql. 如果 DataFrame 包含单行,则时区偏移量将丢失:
import datetime
from pprint import pprint
import sqlalchemy as sa
# ...
engine = sa.create_engine(connection_uri, fast_executemany=True)
# test environment
table_name = 'DateTimeOffset_Test'
engine.execute(sa.text(f"DROP TABLE IF EXISTS [{table_name}]"))
engine.execute(sa.text(f"CREATE TABLE [{table_name}] (id int primary key, dto datetimeoffset)"))
# test data
my_tz = datetime.timezone(datetime.timedelta(hours=-7))
dto_value = datetime.datetime(2020, 1, 1, 0, 0, 0, tzinfo=my_tz)
print(dto_value) # 2020-01-01 00:00:00-07:00
# ^
num_rows = 1
row_data = [(x, dto_value) for x in range(num_rows)]
df = pd.DataFrame(row_data, columns=['id', 'dto'])
print(df)
# id dto
# 0 0 2020-01-01 00:00:00-07:00
# ^
df.to_sql(table_name, engine, if_exists='append', index=False)
result = engine.execute(sa.text(f"SELECT id, CAST(dto as varchar(50)) AS foo FROM [{table_name}]")).fetchall()
pprint(result)
# [(0, '2020-01-01 00:00:00.0000000 +00:00')]
# ^ -- wrong
但是,如果 DataFrame 包含多于一行,则正确上传 datetimeoffset 值:
# ...
num_rows = 2
row_data = [(x, dto_value) for x in range(num_rows)]
df = pd.DataFrame(row_data, columns=['id', 'dto'])
print(df)
# id dto
# 0 0 2020-01-01 00:00:00-07:00
# 1 1 2020-01-01 00:00:00-07:00
# ^
df.to_sql(table_name, engine, if_exists='append', index=False)
result = engine.execute(sa.text(f"SELECT id, CAST(dto as varchar(50)) AS foo FROM [{table_name}]")).fetchall()
pprint(result)
# [(0, '2020-01-01 00:00:00.0000000 -07:00'),
# (1, '2020-01-01 00:00:00.0000000 -07:00')]
# ^ -- correct
如果你真的对此有强烈的感觉,你可能想提出一个关于它的熊猫问题。