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时区的时间,但默认情况下:
- 如果数据库返回的
datetime对象不带时区信息,Pydantic会将其视为UTC时间,转换后输出UTC格式字符串 - 即使数据库返回带时区的
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
相关产品推荐
相关产品推荐

