如何使用 Python 将本地化时间戳转换为 UTC?

How to convert a localized timestamp to UTC using Python?

我需要将任意时区的 "localized" 时间戳转换为 unix 时间戳(UTC)。

>>> import pytz

# This represents 2019-09-11T16:14:00 (US/Central) or 2019-09-11T21:14:00 UTC!
>>> local_timestamp = 1568218440 

>>> tz = pytz.timezone("US/Central")

>>> my_unix_timestamp = unix_timestamp(local_timestamp, tz)

>>> print(my_unix_timestamp)
1568236440 # 2019-09-11T21:14:00

我知道这个问题之前已经被问过很多次了,但是我对从任意时区进行时间戳的初始转换感到困惑,因为在构造初始 datetime 对象(如我的回答所述)。

pytz documentation 中所述,标准 Python datetime.replace() 方法未正确考虑夏令时。您应该使用 pytz 的 timezone.localize() 方法。

另一个问题是,在根据输入时间戳创建 datetime 对象时,您必须显式定义 tz 信息。否则,假定您的时间戳是本地(系统)时间。正如 datetime documentation 中所述,出于这个原因,您应该避免使用 datetime.utcfromtimestamp()

所以:

from datetime import datetime, timezone

def unix_timestamp(local_timestamp, local_timezone):
    """turn the input timestamp into a UTC `datetime` object, even though
    the timestamp is not in UTC time, we must do this to construct a datetime
    object with the proper date/time values"""
    dt_fake_utc = datetime.fromtimestamp(local_timestamp, tz=timezone.utc)

    """remove the (incorrect) timezone info that we supplied """
    dt_naive = dt_fake_utc.replace(tzinfo=None)

    """localize the datetime object to our `timezone`. You cannot use
    datetime.replace() here, because it does not account for daylight savings
    time"""
    dt_local = local_timezone.localize(dt_naive)

    """Convert our datetime object back to a timestamp"""
    return int(dt_local.timestamp())

A timestamp (POSIX) 永远不应 本地化;它应该总是指 UTC。否则,这可能会因含糊不清而导致严重的头痛。只有 datetime 对象应该被本地化(tz 感知),即包含时区信息 (tzinfo != None)。示例:

from datetime import datetime, timezone
import dateutil

datetime.fromisoformat('2019-09-11T21:14:00').replace(tzinfo=timezone.utc).timestamp()
# 1568236440.0
datetime.fromisoformat('2019-09-11T16:14:00').replace(tzinfo=dateutil.tz.gettz("US/Central")).timestamp()
# 1568236440.0

旁注:pytz 使用与 datetime 模块(Python 标准库)不同的时区模型。这就是为什么您不能安全地使用日期时间对象的 replace 方法来设置时区的原因。如果你想让你的生活更轻松一点,看看dateutil。您可以在这里安全地使用 replace

还要小心天真的日期时间对象。 Python 默认将它们视为 本地时间 ,参见例如this answer.