除了GMT之外,Python能否对时区不敏感地处理时间戳?

时间:2016-10-30 18:06:31

标签: python python-3.x datetime timezone unix-timestamp

我尝试编写一个接受UTC时间戳(自纪元以来的秒数)的函数,并使用tz = utc为该时间戳发出时区感知日期时间。但我遇到了一个奇怪的问题。

重现的步骤:

(a)将您的系统时区设置为非GMT。 (在这种情况下,洛杉矶。在GMT + 0000之前和之后,还测试了各种俄罗斯,中国,南美和冰岛时区。)

>>> import datetime
>>> from dateutil import tz
>>> epoch_seconds = 0
>>> dt = datetime.datetime.fromtimestamp(epoch_seconds)
>>> print(dt)
1969-12-31 16:00:00
>>> dt = dt.replace(tzinfo=tz.tzlocal())
>>> print(dt)
1969-12-31 16:00:00-08:00
>>> dt = dt.astimezone(tz=tz.tzutc())
>>> print(dt)
1970-01-01 00:00:00+00:00
>>> assert '{:%Y-%m-%d %H:%M:%S %z}'.format(dt) == '1970-01-01 00:00:00 +0000'
>>>

(b)将您的时区设置为伦敦/都柏林/等同于格林威治标准时间。

>>> import datetime
>>> from dateutil import tz
>>> epoch_seconds = 0
>>> dt = datetime.datetime.fromtimestamp(epoch_seconds)
>>> print(dt)
1970-01-01 01:00:00
>>> dt = dt.replace(tzinfo=tz.tzlocal())
>>> print(dt)
1970-01-01 01:00:00+00:00
>>> dt = dt.astimezone(tz=tz.tzutc())
>>> print(dt)
1970-01-01 01:00:00+00:00
>>> assert '{:%Y-%m-%d %H:%M:%S %z}'.format(dt) == '1970-01-01 00:00:00 +0000'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
>>>

你能帮助我理解为什么这个看似在任何非GMT时区有效但在GMT没有吗?

代码的意图是:

  1. 自纪元以来使用0秒创建日期时间:dt = datetime.datetime.fromtimestamp(0)
  2. 告诉datetime当前的时区:dt = dt.replace(tzinfo=tz.tzlocal())
  3. &#34;移动&#34;它从当地时区到UTC:dt = dt.astimezone(tz=tz.tzutc())
  4. 我认为问题在于dt = datetime.datetime.fromtimestamp(0),因为即使在那时,调用print(dt)显示01:00作为时间而不是00:00。

    更新它凌晨1点,因为英国实际上是在英国标准时间&#34; (GMT + 0100)1970年1月1日。也许dateutil的tz不知道吗?它只是当前规则的抽象表示?可能?

2 个答案:

答案 0 :(得分:0)

我最终使用pytz库解决了这个问题。我不认为可以使用datetime来解决,因为datetime的时区转换并不知道英国人在整个1970年使用GMT + 0100而不仅仅是夏天。

将系统时区设置为Europe / London:

>>> import datetime
>>> from pytz import timezone
>>> epoch_seconds = 0
>>> dt = datetime.datetime.utcfromtimestamp(epoch_seconds)
>>> print(dt)
1970-01-01 00:00:00
>>> dt = timezone('UTC').localize(dt)
>>> print(dt)
1970-01-01 00:00:00+00:00
>>> assert '{:%Y-%m-%d %H:%M:%S %z}'.format(dt) == '1970-01-01 00:00:00 +0000'

将系统时区设置为America / Los_Angeles

>>> import datetime
>>> from pytz import timezone
>>> epoch_seconds = 0
>>> dt = datetime.datetime.utcfromtimestamp(epoch_seconds)
>>> print(dt)
1970-01-01 00:00:00
>>> dt = timezone('UTC').localize(dt)
>>> print(dt)
1970-01-01 00:00:00+00:00
>>> assert '{:%Y-%m-%d %H:%M:%S %z}'.format(dt) == '1970-01-01 00:00:00 +0000'
>>>

本地时间永远不必以这种方式进入 - 你可以生成一个天真的时间戳,用pytz localize告诉它是UTC。

答案 1 :(得分:0)

  

我正在尝试编写一个接受UTC时间戳(自纪元以来的秒数)的函数,并使用tz = utc

为该时间戳发出时区感知日期时间
#!/usr/bin/env python3
from datetime import datetime, timezone

aware_dt = datetime.fromtimestamp(epoch_seconds, timezone.utc)
  

我不认为使用datetime可以解决这个问题,因为datetime的时区转换并不知道英国人在整个1970年使用GMT + 0100而不仅仅是夏天。

如果python可以访问您系统上的tz数据库,那么它确实知道规则:

$ TZ=Europe/London ptpython
>>> from datetime import datetime, timezone
>>> epoch_seconds = 0
>>> aware_dt = datetime.fromtimestamp(epoch_seconds, timezone.utc) 
>>> aware_dt
datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)
>>> aware_dt.astimezone() # local time (1am)
datetime.datetime(1970, 1, 1, 1, 0, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'BST'))
>>> _.astimezone(timezone.utc) # back to utc
datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)

要提供对tz数据库的便携式访问,您可以使用pytz时区,例如,通过tzlocal模块:

>>> import tzlocal # $ pip install tzlocal
>>> datetime.fromtimestamp(epoch_seconds, tzlocal.get_localzone())  # local time (1am)
datetime.datetime(1970, 1, 1, 1, 0, tzinfo=<DstTzInfo 'Europe/London' BST+1:00:00 STD>)

btw,dateutil也提供对tz数据库的访问权限。如果它不起作用:它是错误的用法或错误。尝试更新到最新的python-dateutil版本,以查看问题是否消失。