SQLAlchemy的DateTime对象只能是朴素的吗?

29 投票
3 回答
13681 浏览
提问于 2025-04-15 20:55

我正在使用SQLAlchemy,但还没决定用哪个数据库,所以我想尽量不依赖特定的数据库。我想知道怎么才能把带时区的日期时间对象存储到数据库里,而不和某个特定的数据库绑定在一起。目前,我在存储之前确保时间是UTC格式,然后在显示时再转换成本地时间,但这样感觉不太优雅,也不太稳定。有没有一种不依赖数据库的方法,可以从SQLAlchemy中获取带时区的日期时间,而不是从数据库中得到没有时区的日期时间对象呢?

3 个回答

1

SQLAlchemy的文档里有一个解决方案,可以帮助你处理这个问题:

import datetime

class TZDateTime(TypeDecorator):
    impl = DateTime

    def process_bind_param(self, value, dialect):
        if value is not None:
            if not value.tzinfo:
                raise TypeError("tzinfo is required")
            value = value.astimezone(datetime.timezone.utc).replace(
                tzinfo=None
            )
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = value.replace(tzinfo=datetime.timezone.utc)
        return value
2

我想在我的代码中使用UTC时间来处理日期和时间,这样可以确保所有内部的时间都是一致的。唯一的问题是,当我从数据库中读取数据时,尽管我把日期时间存储得很准确,但取出来的时候却是没有时区信息的。这让我很困扰。为了解决这个问题,我做了以下处理:

import pytz

dt = mydb.query.filter_by(name='test').first().last_update.replace(tzinfo=pytz.utc)
  • dt是一个变量,用来存储从数据库中获取的最后更新时间,格式是日期时间。
  • mydb是我数据库表的名字。
  • name是表中的一个列名。
  • last_update是一个存储日期时间格式的列。

解决这个问题的关键在于使用replace(tzinfo=pytz.utc)这段代码。

46

DateTime列中,有一个timezone参数,这样就可以存储带有时区信息的datetime对象,没什么问题。不过,我发现用一个简单的类型装饰器自动把存储的datetime转换成UTC时间非常方便:

from sqlalchemy import types
from datetime import datetime, timezone

class UTCDateTime(types.TypeDecorator):

    impl = types.DateTime

    def process_bind_param(self, value, engine):
        if value is None:
            return
        if value.utcoffset() is None:
            raise ValueError(
                'Got naive datetime while timezone-aware is expected'
            )
        return value.astimezone(timezone.utc)

    def process_result_value(self, value, engine):
        if value is not None:
            return value.replace(tzinfo=timezone.utc)

需要注意的是,如果你不小心使用了没有时区信息的datetime(也就是所谓的“天真”datetime),它会抛出一个ValueError错误。

撰写回答