Django日期比较在服务器上不同

时间:2015-08-13 15:59:23

标签: python django postgresql

我刚刚意识到我们的生产设置中有一些非常奇怪的日期比较行为,我不明白。

列表模型

title (CharField)
from_date (DateTimeField)
to_date (DateTimeField)

查询集

now = arrow.now()
Listing.objects.filter(to_date__gte=now.datetime)

设置

  • Python 2.7
  • Django 1.7
  • SQLite / PostgreSQL
  • Ubuntu的

问题

如果列表对象的to_date设置为比现在提前一小时,在开发服务器和生产服务器上的shell中,查询集将返回正确的结果(不包括相关列表) 。但是,在生产服务器上,查询继续包括列表(未启用缓存)。我已经确定,如果我将列表的日期设置为前一天,则会将其正确排除,使其看起来只有几天进行比较(我甚至将时间设置为00:00以排除时区问题,但这样仍然包括列表 - 如上所述,只改变工作日)。请问为什么会这样,我该如何解决?

其他信息

  1. 我使用了" date"确认服务器时区的命令与settings.py
  2. 中指定的时区一致
  3. 我已确认settings.py中的USE_TZ = True并且TIME_ZONE已正确设置。
  4. 我已登录服务器上的PostgreSQL终端并运行now命令 - 返回与系统相同的时区和settings.py中指定的时区
  5. 我已经确认postgresql中的to_date字段是"带时区字段的时间戳"
  6. 传递给db:WHERE ( "directory_listing"."to_date" >= 2015-08-22 14:01:56.663072)
  7. 的实际sql的摘录

2 个答案:

答案 0 :(得分:1)

我认为您看到的差异的关键在于您使用两个不同的数据库引擎,一个在您的开发环境中,另一个在您的生产环境中,并且它们对日期和时间的处理方式截然不同。

SQLite does not have a specific date/time data type。日期和时间可以以文本格式,浮点数量或整数存储,并且各种函数用于将输入数据转换为这些形式之一以用于存储或比较目的。此外,它没有任何特定的“带时区的日期时间”数据类型,甚至没有完全支持任意时区之间的时区转换。

Postgresql has comprehensive support表示有和没有时区的日期,时间,间隔和时间戳。它还具有专门定义的运算符,用于将它们相互比较。

我怀疑当使用SQLite作为数据库后端时,Django正在使用文本形式。如果是这种情况,那么给出一个像这样的WHERE子句:

WHERE (to_date >= '2015-08-22 14:01:56.663072')

它将使用纯文本比较 - 这似乎工作正常,因为您可以通过这种方式比较两个ISO8601字符串(只要您不关心时区)。

在您的生产环境中,您使用的是postgresql,其中包含“带时区的时间戳”类型。这意味着存储在数据库中的数据与附加的时区信息一起存储。

在这种情况下,有几个时区在起作用:

  • Django / python环境的时区
  • 运行客户端(Django)软件的计算机上的系统时区
  • 数据库的时区(通过timezone配置参数配置,您可以在psql提示符下使用命令SHOW timezone;进行检查。)
  • 在数据库中发送的字符串中嵌入了什么时区信息。

根据您的评论,您至少检查了其中一些设置。

一种可能性是,当记录插入数据库时​​,to_date字段包含一个时区,它与postgresql数据库设置使用的字段不同。例如,考虑一下:

ispdb_t2=> select '2015-08-22 00:00:00-10:00'::timestamp with time zone >= '2015-08-22 14:01:56.663072';
 ?column?
----------
 t
(1 row)

看起来它认为2015-08-22 00:00:00是> = 2015-08-22 14:01:56.663072。这里实际发生的是左边的时间戳有时区-10,而右边的时间戳没有附加任何时区,所以它在数据库会话的时区中被解释(在我的情况下是'澳大利亚/维多利亚',目前UTC + 10小时)。

要验证这是否是您的情况,我建议采取以下步骤:

  1. 验证'timezone'postgresql配置参数的值。请注意,它可以按会话设置 - 因此您还应确认Django框架是否在连接后发送SET timezone命令

  2. 检查用于在数据库中插入值的SQL。确认时间值是否包含嵌入的时区。

  3. 确认在查询中发送的文字值,例如在WHERE子句中。根据您提供的信息确认它们是否包含嵌入的时区(看起来不像它们)。

  4. 获取服务器正在执行的SQL的完全文本,包括任何占位符值。尝试使用psql命令行工具直接在数据库中运行相同的查询。执行此操作时,请确保将timezone设置为与Django正在执行的值相同的值(如果有)。然后,您可以尝试更改各种值,直到获得有效的查询,然后从那里向后工作以使Django代码正常工作。

  5. 提示:您可以通过将配置参数log_statement设置为all来获取postgresql来记录所有语句。

    您可能不希望在实际的生产系统上进行此类测试。我建议您更改开发系统的配置,以便它使用Postgresql作为后端,然后重现您遇到的问题。

    这让我得出了最后的建议:尽可能在开发系统中使用与在生产中使用相同的数据库系统。即使允许应用程序开发人员隐藏数据库详细信息的框架,不同SQL数据库的运行方式通常也存在很大差异。

答案 1 :(得分:0)

arrow.now()。datetime添加了一些tzdata。我的想法是你在不同的环境中设置了不同的时区。

>>> datetime.datetime.now()
datetime.datetime(2015, 8, 13, 11, 31, 39, 443054)
>>> arrow.now().datetime
datetime.datetime(2015, 8, 13, 11, 31, 39, 445013, tzinfo=tzlocal())

使用以下命令在Ubuntu中重新配置您的时区:

dpkg-reconfigure tzdata

还要检查您的TIME_ZONE设置。