Python 'US/Pacific' 时区的 Django 时区转换时间不正确

Python Django Time Zone Conversion Incorrect Time for 'US/Pacific' Time Zone

虽然我几乎阅读了所有 post 与时区转换相关的内容,但我仍然遇到一些问题,我转换的时间不正确

settings.py

TIME_ZONE = 'UTC'
USE_TZ = True

views.py

utc = datetime.utcnow()
instance_time_zone = pytz.timezone(instance.timezone) # 'US/Pacific'
start_date = instance_time_zone.localize(datetime.utcnow(), is_dst=None)

template.html

utc: Oct. 2, 2015, 5:32 p.m. #correct time
start_date: Oct. 3, 2015, 1:32 a.m. #incorrect time

由于某种原因,转换后的时间有误,比太平洋时间早 15 小时,比 UTC 时间早 8 小时。

timezone.localize() 应该用于 naive datetime 对象(没有自己时区的对象)。时区附加到 datetime,就好像日期和时间对于那个时区 是正确的 。因此,在您的情况下,您 'localised' UTC 就好像是没有 DST 的当地时间,将它朝错误的方向移动了 8 小时。

但是您使用了 UTC 时间戳,因此您需要将 UTC 时区附加到它,然后将时间戳移动到所需时区:

utc = pytz.utc.localize(datetime.utcnow())
instance_time_zone = pytz.timezone(instance.timezone) # 'US/Pacific'
start_date = utc.astimezone(instance_time_zone)

请注意,utc 值现在是具有时区的 datetime 对象,因此您可以使用 datetime.astimezone() method 从中生成所需目标时区的值。

演示:

>>> from datetime import datetime
>>> utc = pytz.utc.localize(datetime.utcnow())
>>> utc
datetime.datetime(2015, 10, 2, 17, 58, 10, 168575, tzinfo=<UTC>)
>>> instance_time_zone = pytz.timezone('US/Pacific')
>>> utc.astimezone(instance_time_zone)
datetime.datetime(2015, 10, 2, 10, 58, 10, 168575, tzinfo=<DstTzInfo 'US/Pacific' PDT-1 day, 17:00:00 DST>)

现在生成的 datetime 与 UTC 相差 5 小时。

但是,如果您要将这些值输出到 Django 模板中,请注意 Django 也会转换时区。见 Django timezone documentation, specifically the section on using aware datetime objects in templates:

When you enable time zone support, Django converts aware datetime objects to the current time zone when they’re rendered in templates. This behaves very much like format localization.

并且来自 current time zone section

You should set the current time zone to the end user’s actual time zone with activate(). Otherwise, the default time zone is used.

然后将 datetime 对象移动到哪个时区都没有关系;它将使用当前时区来显示值。您通常希望在 UTC 时区中使用感知 datetime 对象,然后使用 activate() 切换显示所有内容的时区。

所以在 Django 中,只需在所有地方使用 timezone.now(),让模板系统担心将其转换为给定的时区。

要在 django 中获取当前时间,请使用 timezone.now():

from django.utils import timezone

start_date = timezone.now()

如果 instance.timezone 指的是与 timezone.get_current_timezone() (default is TIME_ZONE) 相同的时区,那么这就是您所需要的(timezone.now() returns 一个 UTC 中的感知日期时间对象(如果 USE_TZ=True) 在渲染期间转换为当前时区)。

否则,您可以调用timezone.activate(instance.timezone)设置当前时区。

如果需要(不需要),您可以明确转换时区:

import pytz
from django.utils import timezone

now = timezone.localtime(timezone.now(), pytz.timezone(instance.timezone))

在 django 代码之外,您可以通过显式传递 tzinfo 来获取给定时区的当前时间:

from datetime import datetime
import pytz

start_date = datetime.now(pytz.timezone('America/Los_Angeles'))

要转换表示给定 pytz 时区中时间的现有原始日期时间对象:

start_date = instance_time_zone.localize(datetime_in_instance_time_zone,
                                         is_dst=None)

此代码在实例时区中引发异常 ambiguous/non-existing 次(例如在 DST 转换期间)。如果在某些情况下 return 不精确的结果而不是异常是可以的,那么不要传递 is_dst=None:

tz = instance_time_zone
start_date = tz.normalize(tz.localize(datetime_in_instance_time_zone))

有关 is_dst 的更多详细信息,请参阅