使用本地时区在 Python 中获取正确的时区偏移量
Posted
技术标签:
【中文标题】使用本地时区在 Python 中获取正确的时区偏移量【英文标题】:Getting the correct timezone offset in Python using local timezone 【发布时间】:2013-07-17 23:23:37 【问题描述】:好的,首先让我说我的时区是 CET/CEST。它从 CEST 变为 CET(从 DST,即 GMT+2 回到正常,即 GMT+1,因此)的确切时刻始终是 10 月的最后一个星期日凌晨 3 点。 2010 年是 10 月 31 日凌晨 3 点。
现在请注意以下几点:
>>> import datetime
>>> import pytz.reference
>>> local_tnz = pytz.reference.LocalTimezone()
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30))
datetime.timedelta(0, 3600)
如上所述,这是错误的。
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 30, 2, 12, 30))
datetime.timedelta(0, 7200)
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30))
datetime.timedelta(0, 7200)
现在突然正确了:/
我知道对此已经有几个问题,但给出的解决方案始终是“使用本地化”,但我的问题是 LocalTimezone 不提供该方法。
事实上,我有几个以毫秒为单位的时间戳,我需要本地时区的 utcoffset(不仅是我的,还有任何使用该程序的人)。其中之一是我的时区中的 1288483950000 或 Sun Oct 31 2010 02:12:30 GMT+0200 (CEST)。
目前我执行以下操作来获取日期时间对象:
datetime.datetime.fromtimestamp(int(int(millis)/1E3))
这可以在几分钟内获得 utcoffset:
-int(local_tnz.utcoffset(date).total_seconds()/60)
不幸的是,这在很多情况下都是错误的:(。
有什么想法吗?
注意:我使用的是 python3.2.4,在这种情况下并不重要。
编辑:
感谢@JamesHolderness,找到了解决方案:
def datetimeFromMillis(millis):
return pytz.utc.localize(datetime.datetime.utcfromtimestamp(int(int(millis)/1E3)))
def getTimezoneOffset(date):
return -int(date.astimezone(local_tz).utcoffset().total_seconds()/60)
local_tz 等于 tzlocal 模块中的 tzlocal.get_localzone()。
【问题讨论】:
使用 Python 2.7.5 和 pytz 2013b,我得到了不同且一致的结果:datetime.timedelta(0, 3600)
。分别为datetime.timedelta(0, 7200)
和datetime.timedelta(0, 3600)
。在运行测试之前,我将时区设置为 CET。
尝试在 cli 上手动遍历 utcoffset 和 _isdst 函数中的行,代码见此处:bazaar.launchpad.net/~stub/pytz/devel/view/head:/src/pytz/… 看看你是否能看到任何错误。
相关:Getting computer's UTC offset in Python
【参考方案1】:
根据Wikipedia,与夏令时的转换发生在世界标准时间 01:00。
在 00:12 UTC 您仍处于中欧夏令时(即 UTC+02:00),因此当地时间为 02:12。
在 01:12 UTC,您又回到标准的欧洲中部时间(即 UTC+01:00),因此当地时间又是 02:12。
从夏令时改回标准时间时,当地时间会从 02:59 变回 02:00,并且小时会重复。因此,当询问 02:12(当地时间)的 UTC 偏移量时,答案可能是 +01:00 或 +02:00 - 这取决于您所谈论的 02:12 版本。
在进一步调查 pytz 库时,我认为您的问题可能是您不应该使用 pytz.reference 实现,它可能无法很好地处理这些歧义。引用源代码中的cmets:
参考 Python 文档中的 tzinfo 实现。 用于测试,因为它们仅在几年内是正确的 1987 年到 2006 年。请勿将这些用于实际代码。
在 pytz 中处理模棱两可的时间
你应该做的是为适当的时区构造一个 timezone 对象:
import pytz
cet = pytz.timezone('CET')
然后您可以使用 utcoffset 方法来计算该时区中日期/时间的 UTC 偏移量。
dt = datetime.datetime(2010, 10, 31, 2, 12, 30)
offset = cet.utcoffset(dt)
请注意,上面的示例将抛出 AmbiguousTimeError 异常,因为它无法判断您指的是 02:12:30 的两个版本中的哪一个。幸运的是,pytz 会让你通过设置 is_dst 参数来指定你想要 dst 版本还是标准版本。例如:
offset = cet.utcoffset(dt, is_dst = True)
请注意,在所有对 utcoffset 的调用中设置此参数并没有什么害处,即使时间不会有歧义。根据文档,它仅在 DST 过渡模糊期使用以解决这种模糊性。
如何处理时间戳
至于处理时间戳,最好尽可能长时间地将它们存储为 UTC 值,否则最终可能会丢弃有价值的信息。所以首先使用 datetime.utcfromtimestamp 方法转换为 UTC 日期时间。
dt = datetime.datetime.utcfromtimestamp(1288483950)
然后使用 pytz 将时间本地化为 UTC,因此时区附加到 datetime 对象。
dt = pytz.utc.localize(dt)
最后,您可以将该 UTC 日期时间转换为您的本地时区,并获取时区偏移量,如下所示:
offset = dt.astimezone(cet).utcoffset()
请注意,这组计算将为 1288483950 和 1288487550 生成正确的偏移量,即使这两个时间戳都由 CET 时区中的 02:12:30 表示。
确定当地时区
如果您需要使用计算机的本地时区而不是固定时区,则不能直接从 pytz 执行此操作。您也不能只使用 time.tzname 中的时区名称构造一个 pytz.timezone 对象,因为名称不会总是被 pytz 识别。
解决方案是使用tzlocal module - 它的唯一目的是在 pytz 中提供这个缺失的功能。你可以这样使用它:
import tzlocal
local_tz = tzlocal.get_localzone()
get_localzone() 函数返回一个 pytz.timezone 对象,因此您应该能够在我使用过 的所有地方使用该值上面示例中的 cet 变量。
【讨论】:
我用的是最新版本,结果也是同一天01:12 CE(S)T的结果错误,所以与日期不明确无关。此外,在这些过渡期间假设 DST 是通常的惯例,javascript 就是这样做的。 我很好奇是什么让你认为关于这些过渡期有任何约定。在我的计算机上,javascript 实现因浏览器而异。尝试在浏览器控制台中输入new Date(2010, 9, 31, 2, 12, 30, 0).getTime()
。在 Chrome 上,我得到 1288487550000(即 01:12 UTC)。在 Firefox 上,我得到 1288483950000(即 00:12 UTC)。
尝试使用新日期(1288483950000),在 Firefox 和 Chrome 上为我提供“Sun Oct 31 2010 02:12:30 GMT+0200”。
我开始明白这个问题了,使用时间戳时没有歧义,因此可以正确确定 new Date(1288483950000) 的时区和确切日期。所以我需要一种从python中的时间戳计算timezoneoffset的方法......
@JorenVanSeveren 恐怕你不能依赖 time.tzname 在所有情况下工作。我已经用获取本地时区的解决方案更新了我的答案。【参考方案2】:
给定一个以毫秒为单位的时间戳,您可以仅使用 stdlib 获取本地时区的 utc 偏移量:
#!/usr/bin/env python
from datetime import datetime
millis = 1288483950000
ts = millis * 1e-3
# local time == (utc time + utc offset)
utc_offset = datetime.fromtimestamp(ts) - datetime.utcfromtimestamp(ts)
如果我们忽略闰秒前后的时间,那么就没有歧义或不存在时间。
如果操作系统维护历史时区数据库,它支持 DST 和由于其他原因更改 utc 偏移量,例如,它应该在 Ubuntu 上适用于任何过去/现在的日期,但可能会在 Windows 上因使用不同 utc 偏移量的过去日期而中断。
下面是使用 tzlocal
的相同模块,它应该可以在 *nix 和 Win32 系统上运行:
#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # pip install tzlocal
millis = 1288483950000
ts = millis * 1e-3
local_dt = datetime.fromtimestamp(ts, get_localzone())
utc_offset = local_dt.utcoffset()
见How to convert a python utc datetime to a local datetime using only python standard library?
以分钟为单位获取 utc 偏移量(Python 3.2+):
from datetime import timedelta
minutes = utc_offset / timedelta(minutes=1)
不要使用pytz.reference.LocalTimezone()
、it is only for tests。
【讨论】:
【参考方案3】:import pytz, datetime
tz = timezone('CET')
tz.utcoffset(datetime.datetime.now()).total_seconds()
7200.0
【讨论】:
以上是关于使用本地时区在 Python 中获取正确的时区偏移量的主要内容,如果未能解决你的问题,请参考以下文章