PostgreSQL日期范围查询在本地工作,但不适用于生产环境

时间:2019-10-01 08:24:42

标签: python postgresql datetime sqlalchemy

我正在开发的应用程序除其他外还具有一些订单管理功能,并且发现了一个我认为与时区有关的问题。

背景信息

后端是用Python编写的,我们正在使用Flask和SQLAlchemy。该数据库是PostgreSQL。我们(用户和开发人员)都处于同一时区(CET / CEST)。我认为该应用程序托管在AWS上,服务器在爱尔兰。

问题的一般说明

当我检索订单并尝试按特定的日期范围进行过滤时,结果中将包含第二天的第一个小时(日期范围之外)的订单。

分步

  1. 前端将请求的日期范围发送到Python后端。
  2. Flask蓝图接收请求,提取查询字符串中的参数,并在OrderService中调用函数get_orders。
  3. get_order创建一个SQLAlchemy Query对象并根据给定的参数进行过滤。
# Very simplified version of actual code
def get_orders(self, site_id, params):
    created_after = params.get('created_after')
    created_before = params.get('created_before')

    query = self.sess.query(Order) \
        .filter(Order.site_id == site_id)

    if created_after:
        dt = datetime.strptime(created_after, '%Y%m%d-%H%M%S-%f')
        query = query.filter(Order.created_at > self._nor_to_utc_tz(dt))

    if created_before:
        dt = datetime.strptime(created_before, '%Y%m%d-%H%M%S-%f')
        query = query.filter(Order.created_at < self._nor_to_utz_tz(dt))

    ...
    return result


def _nor_to_utc_tz(dt):
    # Incoming datetime is always the same timezone
    dt = dt.replace(tzinfo=tz.gettz('Europe/Oslo'))

    # All datetimes is stored as UTC on server
    dt = dt.astimezone(tz.gettz('UTC'))
    return dt

  1. 查询结果将转换为字典并返回Flask蓝图。
  2. 蓝图将dict作为JSON返回到前端。

SQLAlchemy模型模板

class Order(Base, ModelTemplate):
    __tablename__ = 'orders'
    ...
    created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
    ...

结果

在本地测试时,一切正常。我已经在一天中的所有时间下达了测试订单,并且在正确的日期范围内对它们进行了过滤。

该问题发生在生产环境中。对于本应仅限于前一天创建的订单的查询,结果中将包括在当地时间午夜之后立即创建的订单。

示例

  • 有人在10月2日00:55(UTC + 2)买了东西。
  • 订单已创建并保存为时间戳2019-10-01 22:55(UTC + 0)。
  • 后来,有人想查看10月2日的所有订单,并发送一个请求,要求所有在2019-10-02 00:00:00之后和2019-10-02 23:59:59之前创建的订单。
  • 后端会将这些时间转换为UTC + 0,以便应返回在2019-10-01 22:00:00和2019-10-02 21:59:59之间创建的所有订单,但不返回上述订单。

理论

我的理论基于没有其他理由,我认为没有其他适合该模式的理论,那就是postgres在过滤结果之前将日期范围转换为服务器本地时间。我相信(或推测)服务器的本地时间是UTC + 1(自爱尔兰以来),这与我得到的结果相符。

我尝试过的事情

  • 从挪威时间转换为UTC时区后,从datetime中删除时区信息,以防欺骗postgres认为dt应该转换为本地时间。
  • 在SQLAlchemy模型的DateTime对象中添加timezone = True,使其成为带有postgres中时区的时间戳,然后将挪威时区添加到用于过滤查询的datetime中(而不是将其转换为UTC + 0,希望postgres能够做到)。
  • 沮丧。

0 个答案:

没有答案
相关问题