SQLAlchemy在SQLite中丢失时区信息

8 投票
2 回答
4736 浏览
提问于 2025-04-16 23:10

我遇到了一个问题,使用SQLAlchemy(版本0.6.4和0.6.8)时,当我把一个datetime对象保存到SQLite数据库中时,它会丢失时区信息。这个列是用SQLAlchemy的DateTime类定义的。(我知道SQLAlchemy会把这个对象转换成字符串再转换回来,这可能是问题的一部分)。

为了更清楚地说明问题,我有一个示例代码:

import datetime, pytz, sqlalchemy
from sqlalchemy import create_engine, Column, Integer, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()

Session = sessionmaker(bind=engine)
session = Session()

class Example(Base):
    __tablename__ = "example"
    id = Column(Integer, primary_key=True)
    date = Column(DateTime(timezone=True))

    def __init__(self, date):
        self.date = date

Base.metadata.create_all(engine)

aucklandtz = pytz.timezone('Pacific/Auckland')
exdatetime = datetime.datetime(2011,8,8,2,23)

print aucklandtz, exdatetime

# inject TZ to exdatetime:
injdatetime = aucklandtz.localize(exdatetime)

print injdatetime

newrecord = Example(injdatetime)
print newrecord.date
session.add(newrecord)
session.commit()
print newrecord.date

从控制台的输出来看,主要的问题是它把记录插入SQLite时,值变成了'2011-08-08 02:23:00.000000'

有没有简单的方法来解决这个问题?还是说我需要把时区信息放到一个单独的列里,或者把所有数据都存成时间戳,然后在不同格式之间转换?

2 个回答

1

Armin Ronacher写了一篇很棒的博客,标题是“‘Eppur si muove!’* – 在Python中处理时区”。这篇文章主要讲了以下几点:

  1. 在内部处理时间时,始终使用不带时区的时间对象,并把它们当作UTC时间。
  2. 与用户交互时,要把时间转换为当地时间和从当地时间转换回来。
8

Python的strptime方法不支持%z这个格式,所以从数据库里的字符串中提取时区就没办法了。我想这就是为什么时区没有被存储的原因。

你有两个选择。你已经提到一个——把时区存储在另一个列里。

不过,通常推荐的解决方案是不同的。与其在Python的datetime中或数据库里存储时区,不如在接收到用户输入的datetime时,立刻把它们转换成UTC时间,然后在内部只处理UTC时间。等到需要把结果展示给用户时,再转换成合适的时区。

这样做可以把实际的时间和那些你不能保证始终相同的东西分开。想想看,如果你的程序用户从奥克兰搬到日本,或者新西兰政府改变了他们的时区,那该怎么办呢?

撰写回答