Python 中的夏令时

问题描述 投票:0回答:4

我正在编写一个程序,该程序涉及很多时区和跨越时区的问题。我最常处理的两件事是从“现在”创建一个日期时间对象,然后本地化一个简单的日期时间对象。

要从现在开始在太平洋时区创建一个日期时间对象,我目前正在这样做(python 2.7.2+)

from datetime import datetime
import pytz
la = pytz.timezone("America/Los_Angeles")
now = datetime.now(la)

关于夏令时,这是否正确?如果没有,我想我应该这样做:

now2 = la.localize(datetime.now())

我的问题是为什么?谁能告诉我第一个错误而第二个正确的情况?

对于我的第二个问题,假设我从一些用户输入中得到了 2012 年 9 月 1 日上午 8:00 在加利福尼亚州洛杉矶的天真的日期和时间。使日期时间像这样的正确方法是:

la.localize(datetime(2012, 9, 1, 8, 0))

如果没有,我应该如何构建这些日期时间?

python datetime dst pytz
4个回答
41
投票

来自 pytz 文档

处理时间的首选方法是始终使用 UTC,仅在生成可供人类读取的输出时转换为本地时间。

所以理想情况下您应该使用

utcnow
而不是
now

假设由于某种原因您的双手被束缚并且需要使用当地时间,如果您在夏令时过渡窗口期间执行此操作,则在尝试本地化当前时间时仍然可能会遇到问题。相同的

datetime
可能会发生两次,一次在白天,另一次在标准时间,并且
localize
方法不知道如何解决冲突,除非您使用
is_dst
参数明确告知它。

因此要获取当前的 UTC 时间:

utc = pytz.timezone('UTC')
now = utc.localize(datetime.datetime.utcnow())

并将其转换为您当地时间(但仅当您必须时):

la = pytz.timezone('America/Los_Angeles')
local_time = now.astimezone(la)

编辑:正如 @J.F. 的评论中指出的那样。 Sebastian,您使用

datetime.now(tz)
的第一个示例适用于所有情况。正如我上面概述的那样,您的第二个示例在秋季过渡期间失败了。我仍然主张使用 UTC 而不是本地时间来处理除显示之外的所有事情。


10
投票

第一个解决方案对于 DST 是正确的,第二个解决方案不好。

我举个例子。在欧洲,运行此代码时:

from datetime import datetime
import pytz # $ pip install pytz

la = pytz.timezone("America/Los_Angeles")
fmt = '%Y-%m-%d %H:%M:%S %Z%z'
now = datetime.now(la)
now2 = la.localize(datetime.now())
now3 = datetime.now()
print(now.strftime(fmt))
print(now2.strftime(fmt))
print(now3.strftime(fmt))

我得到以下信息:

2012-08-30 12:34:06 PDT-0700
2012-08-30 21:34:06 PDT-0700
2012-08-30 21:34:06 

datetime.now(la)
使用洛杉矶的当前时间以及洛杉矶的时区信息创建日期时间。

la.localize(datetime.now())
将时区信息添加到原始日期时间,但不进行时区转换;它只是假设时间已经在这个时区。

datetime.now()
使用当地时间创建一个简单的日期时间(没有时区信息)。

只要您在洛杉矶,您就不会看到差异,但如果您的代码在其他地方运行,它可能不会执行您想要的操作。

除此之外,如果您需要认真处理时区,最好将所有时间都设置为 UTC,这样可以避免 DST 带来的很多麻烦。


7
投票

编辑:

现在,我建议使用

zoneinfo

from zoneinfo import ZoneInfo
from datetime import datetime, timedelta

dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
正如原始答案中提到的,

pytz
在几种情况下并不可靠。

原答案

这有效:

# naive datetime
d = datetime.datetime(2016, 11, 5, 16, 43, 45) 
utc = pytz.UTC # UTC timezone
pst = pytz.timezone('America/Los_Angeles') # LA timezone

# Convert to UTC timezone aware datetime
d = utc.localize(d) 
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>)
    
# show as in LA time zone (not converting here)
d.astimezone(pst) 
>>> datetime.datetime(2016, 11, 5, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>)
# we get Pacific Daylight Time: PDT

# add 1 day to UTC date
d = d + datetime.timedelta(days=1) 
>>> datetime.datetime(2016, 11, 6, 16, 43, 45, tzinfo=<UTC>)
    
d.astimezone(pst) # now cast to LA time zone 
>>> datetime.datetime(2016, 11, 6, 8, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PST-1 day, 16:00:00 STD>) 
# Daylight saving is applied -> we get Pacific Standard Time PST

这不起作用:

# naive datetime
d = datetime.datetime(2016, 11, 5, 16, 43, 45) 
utc = pytz.UTC # UTC timezone
pst = pytz.timezone('America/Los_Angeles') # LA timezone

# convert to UTC timezone aware datetime
d = utc.localize(d) 
>>> datetime.datetime(2016, 11, 5, 16, 43, 45, tzinfo=<UTC>)

# convert to 'America/Los_Angeles' timezone: DON'T DO THIS
d = d.astimezone(pst) 
>>> datetime.datetime(2016, 11, 5, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# we are in Pacific Daylight Time PDT

# add 1 day to LA local date: DON'T DO THAT
d = d + datetime.timedelta(days=1)
>>> datetime.datetime(2016, 11, 6, 9, 43, 45, 
tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>) 
# Daylight Saving is NOT respected, we are still in PDT time, not PST

结论:

datetime.timedelta()
考虑夏令时。

始终在 UTC 时区中添加/减去您的时间。 转换为当地时间仅用于输出/显示。


1
投票

pytz 网站 说:

不幸的是使用标准日期时间的 tzinfo 参数 对于许多时区,构造函数“不适用于 pytz”。

所以你不应该使用

datetime.now(la)
。我不知道具体细节,但有些时区的运行规则比我们习惯的更奇特,而 python 的日期时间代码无法处理它们。通过使用 pytz 的代码,应该正确处理它们,因为这是 pytz 的目的。由于夏令时的跳跃时间,它也可能会出现两次出现的问题。

至于第二个问题,这正是文档所显示的,所以你应该很好。

© www.soinside.com 2019 - 2024. All rights reserved.