SQLAlchemy在SQLite中丢失时区信息
我遇到了一个问题,使用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中处理时区”。这篇文章主要讲了以下几点:
- 在内部处理时间时,始终使用不带时区的时间对象,并把它们当作UTC时间。
- 与用户交互时,要把时间转换为当地时间和从当地时间转换回来。
8
Python的strptime
方法不支持%z
这个格式,所以从数据库里的字符串中提取时区就没办法了。我想这就是为什么时区没有被存储的原因。
你有两个选择。你已经提到一个——把时区存储在另一个列里。
不过,通常推荐的解决方案是不同的。与其在Python的datetime
中或数据库里存储时区,不如在接收到用户输入的datetime
时,立刻把它们转换成UTC
时间,然后在内部只处理UTC
时间。等到需要把结果展示给用户时,再转换成合适的时区。
这样做可以把实际的时间和那些你不能保证始终相同的东西分开。想想看,如果你的程序用户从奥克兰搬到日本,或者新西兰政府改变了他们的时区,那该怎么办呢?