python/django 日期时间本地化
Datetime localization with python/django
我正在尝试解析 RSS 提要。提要中的条目具有日期元素,例如:
<dc:date>2016-09-21T16:00:00+02:00</dc:date>
使用 feedparser,我尝试做:
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
但问题是我在数据库中存储的时间似乎有误。在这种特殊情况下,日期时间存储为:
2016-09-21 13:00:00
...当我期望 14:00 - 正确的 UTC 时间。
我假设问题出在我们的 django 设置中,我们有:
TIME_ZONE = 'Europe/Berlin'
因为当我切换到:
TIME_ZONE = 'UTC'
...数据时间存储为正确的 UTC 时间:
2016-09-21 14:00:00
有什么方法可以保持 django 设置不变,但又能正确解析和存储此日期时间,而不受 django 时区设置的影响?
编辑:
也许这样更清楚...
print entry.published_parsed
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
print published_time
localized_time = pytz.timezone(settings.TIME_ZONE).localize(published_time, is_dst=None)
print localized_time
time.struct_time(tm_year=2016, tm_mon=9, tm_mday=21, tm_hour=14, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=265, tm_isdst=0)
2016-09-21 15:00:00
2016-09-21 15:00:00+02:00
您是否尝试过使用 datetime.utcfromtimestamp()
而不是 datetime.fromtimestamp()
?
作为辅助解决方案,您可以获得未解析的数据(我相信它可以作为 entry.published
获得?),只需使用 python-dateutil 解析字符串,然后将其转换为 pytz.utc
这样的时区。
>>> import pytz
>>> from dateutil import parser
>>> dt = parser.parse('2016-09-21T16:00:00+02:00')
>>> dt
datetime.datetime(2016, 9, 21, 16, 0, tzinfo=tzoffset(None, 7200))
>>> dt.astimezone(pytz.utc)
datetime.datetime(2016, 9, 21, 14, 0, tzinfo=<UTC>)
使用
published_time = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
Feedparser 可以解析多种日期格式,您可以找到它们 here。
如您在 feedparser/feedparser/datetimes/__init__.py
中所见,Feedparser _parse_date
的内置函数执行以下操作:
Parses a variety of date formats into a 9-tuple in GMT
这意味着在 parsed_entry.published_parsed
你有一个 GMT 时区的 time.struct_time
对象。
当您使用
将其转换为 datetime
对象时
published_time = datetime.fromtimestamp(mktime(parsed_entry.published_parsed))
问题是mktime
假设传递的元组是当地时间,其实不是,是GMT/UTC!除此之外,您在转换结束时没有正确本地化 datetime
对象。
您需要用以下内容替换该转换,记住 Feedparser returns 格林威治标准时间 struct_time
,并将其本地化为您喜欢的时区(为简单起见采用 UTC)。
- 你使用
calendar.timegm
,它给出了作为参数传递的纪元和日期之间的秒数,假设传递的对象在UTC/GMT中(我们从Feedparser知道它是)
- 您使用
utcfromtimestamp
获得一个天真的 datetime
对象(我们知道它表示 UTC 中的日期时间,但 Python 目前还没有)
- 使用
pytz.utc.localize
您可以在 UTC 中正确定位 datetime
对象。
示例:
import calendar
from datetime import datetime
import pytz
localized_dt = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
只要一致,用fromtimestamp
还是utcfromtimestamp
都无所谓。如果您使用 fromtimestamp
,您需要告诉 Python 您创建的 datetime
对象具有本地时区。假设你在 Europe/Berlin,这也可以:
pytz.timezone('Europe/Berlin').localize(datetime.fromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
如果 parsed_entry.published_parsed
也在本地时区,则必须使用 mktime
代替 calendar.timegm
。
作为替代方案,您可以自己解析从 Feedparser parsed_entry['published']
获得的数据字符串
from dateutil import parser
localized_dt = parser.parse(parsed_entry['published'])
您可以检查以下 returns True
:
parser.parse(parsed_entry['published']) == pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
Django TIME_ZONE
设置实际上并不重要,因为它仅用于可视化目的或自动转换原始日期时间。
When USE_TZ is True, this is the default time zone that Django will use to display datetimes in templates and to interpret datetimes entered in forms.
重要的是始终使用正确本地化的日期时间,无论使用哪个时区。只要它们不是原始格式,它们就会被 Django 正确处理。
feedparser 的 entry.published_parsed
始终是一个 utc 时间元组,无论输入时间字符串是什么。要获得时区感知 datetime
对象:
from datetime import datetime
utc_time = datetime(*entry.published_parsed[:6], tzinfo=utc)
其中 utc
是一个 tzinfo 对象,例如 datetime.timezone.utc
, pytz.utc
, or just your custom tzinfo (for older python versions)。
您不应将 utc 时间传递给需要当地时间的 mktime()
。同样的错误:.
确保 USE_TZ=True
以便 django 在任何地方都使用 aware datetime 对象。给定一个时区感知的日期时间对象,django 应该将它正确地保存到 db,无论你的 TIME_ZONE
or timezone.get_current_timezone()
are 是什么。
我正在尝试解析 RSS 提要。提要中的条目具有日期元素,例如:
<dc:date>2016-09-21T16:00:00+02:00</dc:date>
使用 feedparser,我尝试做:
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
但问题是我在数据库中存储的时间似乎有误。在这种特殊情况下,日期时间存储为:
2016-09-21 13:00:00
...当我期望 14:00 - 正确的 UTC 时间。
我假设问题出在我们的 django 设置中,我们有:
TIME_ZONE = 'Europe/Berlin'
因为当我切换到:
TIME_ZONE = 'UTC'
...数据时间存储为正确的 UTC 时间:
2016-09-21 14:00:00
有什么方法可以保持 django 设置不变,但又能正确解析和存储此日期时间,而不受 django 时区设置的影响?
编辑: 也许这样更清楚...
print entry.published_parsed
published_time = datetime.fromtimestamp(mktime(entry.published_parsed))
print published_time
localized_time = pytz.timezone(settings.TIME_ZONE).localize(published_time, is_dst=None)
print localized_time
time.struct_time(tm_year=2016, tm_mon=9, tm_mday=21, tm_hour=14, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=265, tm_isdst=0)
2016-09-21 15:00:00
2016-09-21 15:00:00+02:00
您是否尝试过使用 datetime.utcfromtimestamp()
而不是 datetime.fromtimestamp()
?
作为辅助解决方案,您可以获得未解析的数据(我相信它可以作为 entry.published
获得?),只需使用 python-dateutil 解析字符串,然后将其转换为 pytz.utc
这样的时区。
>>> import pytz
>>> from dateutil import parser
>>> dt = parser.parse('2016-09-21T16:00:00+02:00')
>>> dt
datetime.datetime(2016, 9, 21, 16, 0, tzinfo=tzoffset(None, 7200))
>>> dt.astimezone(pytz.utc)
datetime.datetime(2016, 9, 21, 14, 0, tzinfo=<UTC>)
使用
published_time = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
Feedparser 可以解析多种日期格式,您可以找到它们 here。
如您在 feedparser/feedparser/datetimes/__init__.py
中所见,Feedparser _parse_date
的内置函数执行以下操作:
Parses a variety of date formats into a 9-tuple in GMT
这意味着在 parsed_entry.published_parsed
你有一个 GMT 时区的 time.struct_time
对象。
当您使用
将其转换为datetime
对象时
published_time = datetime.fromtimestamp(mktime(parsed_entry.published_parsed))
问题是mktime
假设传递的元组是当地时间,其实不是,是GMT/UTC!除此之外,您在转换结束时没有正确本地化 datetime
对象。
您需要用以下内容替换该转换,记住 Feedparser returns 格林威治标准时间 struct_time
,并将其本地化为您喜欢的时区(为简单起见采用 UTC)。
- 你使用
calendar.timegm
,它给出了作为参数传递的纪元和日期之间的秒数,假设传递的对象在UTC/GMT中(我们从Feedparser知道它是) - 您使用
utcfromtimestamp
获得一个天真的datetime
对象(我们知道它表示 UTC 中的日期时间,但 Python 目前还没有) - 使用
pytz.utc.localize
您可以在 UTC 中正确定位datetime
对象。
示例:
import calendar
from datetime import datetime
import pytz
localized_dt = pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
只要一致,用fromtimestamp
还是utcfromtimestamp
都无所谓。如果您使用 fromtimestamp
,您需要告诉 Python 您创建的 datetime
对象具有本地时区。假设你在 Europe/Berlin,这也可以:
pytz.timezone('Europe/Berlin').localize(datetime.fromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
如果 parsed_entry.published_parsed
也在本地时区,则必须使用 mktime
代替 calendar.timegm
。
作为替代方案,您可以自己解析从 Feedparser parsed_entry['published']
from dateutil import parser
localized_dt = parser.parse(parsed_entry['published'])
您可以检查以下 returns True
:
parser.parse(parsed_entry['published']) == pytz.utc.localize(datetime.utcfromtimestamp(calendar.timegm(parsed_entry.published_parsed)))
Django TIME_ZONE
设置实际上并不重要,因为它仅用于可视化目的或自动转换原始日期时间。
When USE_TZ is True, this is the default time zone that Django will use to display datetimes in templates and to interpret datetimes entered in forms.
重要的是始终使用正确本地化的日期时间,无论使用哪个时区。只要它们不是原始格式,它们就会被 Django 正确处理。
feedparser 的 entry.published_parsed
始终是一个 utc 时间元组,无论输入时间字符串是什么。要获得时区感知 datetime
对象:
from datetime import datetime
utc_time = datetime(*entry.published_parsed[:6], tzinfo=utc)
其中 utc
是一个 tzinfo 对象,例如 datetime.timezone.utc
, pytz.utc
, or just your custom tzinfo (for older python versions)。
您不应将 utc 时间传递给需要当地时间的 mktime()
。同样的错误:
确保 USE_TZ=True
以便 django 在任何地方都使用 aware datetime 对象。给定一个时区感知的日期时间对象,django 应该将它正确地保存到 db,无论你的 TIME_ZONE
or timezone.get_current_timezone()
are 是什么。