使 SQLAlchemy 使用本地时区作为数据库生成的日期

问题描述

在我的 SQLAlchemy 模型中,数据库 (sqlite) 在写操作期间更新时间戳列:

class DBObject(declarative_base()):
    __tablename__ = 'dbobj'
    id = Column(Integer,primary_key=True,autoincrement=True)
    date = Column(DateTime(timezone=True))
    text = Column(String(64))
    created_at = Column(DateTime(timezone=True),server_default=func.now())
    updated_at = Column(DateTime(timezone=True),server_default=func.now(),onupdate=func.now())

engine = create_engine('sqlite:///:memory:')
DBObject.metadata.create_all(bind=engine)

然而,这似乎并没有考虑到时区。如何让 SQLAlchemy 将这些时间戳转换为我的本地时区? (我知道 sqlite 仅适用于 UTC)

fmt = '%Y-%m-%d %H:%M:%S'
print(f"tzone  = {time.timezone}s")  # Local timezone offset to UTC.
print(f"now    = {datetime.now().strftime(fmt)}")  # Current local time.
s = Session(bind=engine)
try:

    # Create new entry.
    s.add(DBObject(date=datetime.now(),text='created'))
    s.flush()

    # Wait a bit
    time.sleep(1)

    # Update entry.
    obj = s.query(DBObject).one()
    obj.text = 'updated'
    s.flush()

    # Read entry and print dates.
    obj = s.query(DBObject).one()
    print(f"date   = {obj.date.strftime(fmt)}")
    print(f"create = {obj.created_at.strftime(fmt)}")
    print(f"update = {obj.updated_at.strftime(fmt)}")
finally:
    s.close()

实际输出:

tzone  = -3600s
now    = 2021-01-26 18:40:15
date   = 2021-01-26 18:40:15
create = 2021-01-26 17:40:15
update = 2021-01-26 17:40:16

预期输出:

tzone  = -3600s
now    = 2021-01-26 18:40:15
date   = 2021-01-26 18:40:15
create = 2021-01-26 18:40:15
update = 2021-01-26 18:40:16

我还尝试了什么:

  • 删除timezone=True中的DateTime
  • 使用sqlalchemy.TIMESTAMP(有或没有timezone=True
  • 使用 default 而不是 server_default
  • 使用 datetime.utcnow 而不是 func.now()

请注意,这包括对非常相似的 stackoverflow 问题(例如 SQLAlchemy default DateTime)的解决方案。 这些似乎都没有任何区别。 SQLAlchemy 和 sqlite 函数似乎都只能在 UTC 中工作。我发现的唯一解决方案是完全删除他们的日期时间功能:

class DBObject(declarative_base()):
    __tablename__ = 'dbobj'
    id = Column(Integer,autoincrement=True)
    date = Column(DateTime)
    text = Column(String(64))
    created_at = Column(DateTime,default=datetime.now)
    updated_at = Column(DateTime,default=datetime.now,onupdate=datetime.now)

在这种情况下,我得到了预期的输出,但我猜所有数据库本机时间函数仍将关闭,因此无法在任何 SQL 查询中使用。

如何告诉 SQLAlchemy 和/或 sqlite 在本地时区返回日期时间对象? 有什么办法可以使用这两者并让数据库分配日期无需一直转换为 UTC 或从 UTC 转换?我好像遗漏了什么。

奖金要求:

使用 s.query(DBObject).all() 读取需要返回与使用熊猫读取相同的日期:pd.read_sql(s.query(DBObject).statement,s.bind)

版本:

  • Python:3.7.9
  • SQLAlchemy:1.3.22
  • sqlite:2.6.0

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)