You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI+SQLAlchemy中服务器响应时间与PostgreSQL数据库时间不一致问题

解决FastAPI返回时间与PostgreSQL数据库不一致的问题

问题场景

你编写了从PostgreSQL获取记录的函数、FastAPI端点和Pydantic模型,但服务器返回的时间和数据库存储的不一致:

  • 数据库记录:2023-01-15 01:49:06.535 +0300
  • 服务器响应:2023-01-14T22:49:06.535436+00:00

相关代码如下:

数据库查询函数

async def all_message(self, chat_id: int) -> List[MessageAll]:
    query = message.select().where(message.c.chat_id == chat_id).order_by(desc(message.c.date_send))
    return await self.database.fetch_all(query)

FastAPI端点

@mes_router.get('/{chat_id}')
async def get_message_for_chat(
        chat_id: int,
        message: MessageRepository = Depends(get_message_repository),
):
    return await message.all_message(chat_id=chat_id)

Pydantic模型(修正原代码类型错误)

class MessageAll(BaseModel):
    id: int
    message: str
    date_send: datetime  # 原代码中`d`为类型错误,修正为datetime
    chat_id: int
    sender_id: int

问题原因

数据库存储的是带+0300时区的时间,但默认情况下:

  1. 如果数据库返回的datetime对象不带时区信息,Pydantic会将其视为UTC时间,转换后输出UTC格式字符串
  2. 即使数据库返回带时区的datetime,Pydantic默认也会将其转换为UTC的ISO格式输出

解决方案

方案1:Pydantic模型自定义时间序列化逻辑

通过字段序列化器,将时间转换为目标时区(+0300)并格式化:

from datetime import datetime
from pydantic import BaseModel, field_serializer
from zoneinfo import ZoneInfo  # Python 3.9+,低版本可替换为pytz

class MessageAll(BaseModel):
    id: int
    message: str
    date_send: datetime
    chat_id: int
    sender_id: int

    @field_serializer('date_send')
    def serialize_date(self, value: datetime) -> str:
        # 定义目标时区(以+0300对应的Europe/Moscow为例)
        target_tz = ZoneInfo('Europe/Moscow')
        
        # 处理不带时区的时间对象
        if value.tzinfo is None:
            value = value.replace(tzinfo=target_tz)
        # 转换带时区的时间到目标时区
        else:
            value = value.astimezone(target_tz)
        
        # 格式化为和数据库一致的字符串格式
        return value.strftime('%Y-%m-%d %H:%M:%S.%f %z')

方案2:配置数据库连接时区

在PostgreSQL连接URL中添加时区参数,让SQLAlchemy直接返回带目标时区的datetime对象:

# 替换为你的数据库信息
DATABASE_URL = "postgresql://user:password@localhost/your_db?timezone=Europe/Moscow"

之后在Pydantic模型中配置保留时区的ISO格式输出:

from pydantic import BaseModel, ConfigDict

class MessageAll(BaseModel):
    id: int
    message: str
    date_send: datetime
    chat_id: int
    sender_id: int

    # 输出带时区的ISO格式时间
    model_config = ConfigDict(json_encoders={datetime: lambda dt: dt.isoformat()})

此时返回的时间会是2023-01-15T01:49:06.535+03:00,与数据库时间一致。

方案3:全局配置FastAPI时间序列化

如果需要所有接口统一处理时区,可在FastAPI实例初始化时配置全局JSON编码器:

from fastapi import FastAPI
from datetime import datetime
from zoneinfo import ZoneInfo

app = FastAPI()

def custom_datetime_encoder(dt: datetime) -> str:
    target_tz = ZoneInfo('Europe/Moscow')
    if dt.tzinfo is None:
        dt = dt.replace(tzinfo=target_tz)
    else:
        dt = dt.astimezone(target_tz)
    return dt.strftime('%Y-%m-%d %H:%M:%S.%f %z')

# 替换默认JSON编码器
app.json_encoder = lambda obj: custom_datetime_encoder(obj) if isinstance(obj, datetime) else obj

内容的提问来源于stack exchange,提问作者Maksim Smolovsky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 03:12:18