奇怪的时区行为Django

时间:2017-03-23 23:44:11

标签: python django postgresql django-orm

这里有些古怪我无法理解:

鉴于以下设置:

>>> from django.conf import settings
>>> settings.TIME_ZONE
'Europe/London'
>>> settings.USE_TZ
False

鉴于以下模型:

HALF_YEAR = timedelta(days=30*6)

class ProductManager(models.Manager):
    def get_queryset(self):
        from_date = datetime.now() - HALF_YEAR
        return super(ProductManager, self).get_queryset().filter(start_date_time__gt=from_date)

class Product(models.Model):
    product_number = models.CharField(max_length=45)
    start_date_time = models.DateTimeField()
    cover_renewal_date = models.DateField()
    objects = ProductManager()

这给了我们数据库表:

shopapp=>\d shop_product

        Column         |           Type           |                        Modifiers
-----------------------+--------------------------+----------------------------------------------------------
 id                    | integer                  | not null default nextval('shop_product_id_seq'::regclass)
 product_number        | character varying(45)    | not null
 start_date_time       | timestamp with time zone | not null


shopapp=> show timezone;
 TimeZone
----------
 UTC
(1 row)

使用以下数据:

        shopapp=> select product_number, start_date_time 
from shop_product 
where product_number in ('PN63145707', 'PN57284554', 'PN57291674', 'PN66177827');

 product_number | start_date_time
---------------+------------------------
 PN57284554     | 2013-04-05 00:00:00+00
 PN57284554     | 2014-04-05 00:00:00+00
 PN57284554     | 2015-04-05 00:00:00+00
 PN57284554     | 2016-04-05 00:00:00+00
 PN57284554     | 2017-04-05 00:00:00+00
 PN57291674     | 2013-04-04 00:00:00+00
 PN57291674     | 2014-04-04 00:00:00+00
 PN57291674     | 2015-04-04 00:00:00+00
 PN57291674     | 2016-04-04 00:00:00+00
 PN57291674     | 2017-04-04 00:00:00+00
 PN63145707     | 2015-03-25 00:00:00+00
 PN63145707     | 2016-03-25 00:00:00+00
 PN63145707     | 2017-03-25 00:00:00+00
 PN66177827     | 2017-03-25 00:00:00+00
(14 rows)

但运行此代码:

now = datetime.now().date()
start_time = now - timedelta(days=1)
end_time = now + timedelta(days=14)
res = Product.objects.filter(start_date_time__range=(start_time, end_time), product_number__in=['PN63145707', 'PN57284554', 'PN57291674', 'PN66177827'])
for item in res:
    print(item.product_number, str(item.start_date_time))

给我结果

(u'PN63145707', '2017-03-25 00:00:00')
(u'PN57284554', '2017-04-05 01:00:00')
(u'PN57291674', '2017-04-04 01:00:00')
(u'PN66177827', '2017-03-25 00:00:00')

看起来任何超出BST(3月26日)的start_date_time都显示时间为凌晨1点。如果USE_TZ设置为False,为什么会这样?

感谢。

版本: Django的1.10.4, postgresql psql(9.4.10,server 9.6.1)

修改: 当我在测试服务器上使用相同的设置运行相同的代码时,结果不一样:

In [20]: settings.TIME_ZONE
Out[20]: 'Europe/London'

In [21]: settings.USE_TZ
Out[21]: False

(u'PN63145707', '2017-03-25 00:00:00')
(u'PN66177827', '2017-03-25 00:00:00')
(u'PN57291674', '2017-04-04 00:00:00')
(u'PN57284554', '2017-04-05 00:00:00')

为什么这些记录没有像生产服务器上的值那样调整到欧洲/伦敦时区?

2 个答案:

答案 0 :(得分:1)

您所看到的行为完全如documentation中所述。它是Django坚持使用postgresql timestamptz而不是timestamp的副产品。

在内部,Postgresql timestamptz以UTC格式存储日期时间。查询时,它会返回转换为请求者时区的时间戳。

documentation注意到,当USE_TZ设置为false时,数据库连接时区将设置为TIMEZONE(在您的情况下为欧洲/伦敦)。这将导致 Postgresql将存储的UTC时间戳转换为伦敦时间,然后再将它们提供给Django。

答案 1 :(得分:0)

including返回当前的本地日期和时间,在您的情况下,它会返回生产服务器的日期和时间。您应该将其转换为您的特定时区datetime.now(),或者您可以使用符合Europe/London配置的django.utils.timezone.now

settings.py

请注意django.utils.timezone.now根据from django.utils import timezone now = timezone.now().date()

的值表现不同