如何将一个日期时间/时间戳从一个时区转换到另一个时区?

61

具体而言,假设我已知服务器所在的时区(系统时间视角),并输入了一个时区,那么如何计算出系统时间在这个新时区的时间(不考虑夏令时等因素)?

import datetime
current_time = datetime.datetime.now() #system time

server_timezone = "US/Eastern"
new_timezone = "US/Pacific"

current_time_in_new_timezone = ???
4个回答

80

如果你知道原始时区和要转换的新时区,那么转换过程非常简单:

  1. 创建两个 pytz.timezone 对象,一个表示当前时区,另一个表示新时区,例如 pytz.timezone("US/Pacific")。你可以在 pytz 库中找到所有官方时区列表: import pytz; pytz.all_timezones

  2. 将需要转换的日期时间/时间戳本地化到当前时区,例如:

current_timezone = pytz.timezone("US/Eastern")
localized_timestamp = current_timezone.localize(timestamp)
  1. 使用所需时区的pytz对象作为输入,在从步骤2中新定位的日期时间戳上使用.astimezone()进行转换,例如localized_timestamp.astimezone(new_timezone)

完成了!

一个完整的例子:

import datetime
import pytz

# a timestamp I'd like to convert
my_timestamp = datetime.datetime.now()

# create both timezone objects
old_timezone = pytz.timezone("US/Eastern")
new_timezone = pytz.timezone("US/Pacific")

# two-step process
localized_timestamp = old_timezone.localize(my_timestamp)
new_timezone_timestamp = localized_timestamp.astimezone(new_timezone)

# or alternatively, as an one-liner
new_timezone_timestamp = old_timezone.localize(my_timestamp).astimezone(new_timezone) 

额外提供:如果你只需要特定时区的当前时间,你可以将该时区直接传递给datetime.now()以直接获取当前时间:

datetime.datetime.now(new_timezone)

在涉及到时区转换的情况下,我强烈建议您将数据库中的所有时间戳存储为UTC时间,因为UTC没有夏令时转换。并且作为良好的实践,即使您的用户都处于同一个时区,也应该选择启用时区支持!这将有助于避免困扰许多软件的夏令时问题。

除了夏令时问题之外,在软件中处理时间通常会非常棘手。为了让您更好地了解在软件中处理时间有多困难,这是一个具有启发性的资源:http://yourcalendricalfallacyis.com

即使是看似简单的操作,如将日期时间/时间戳转换为日期,也可能变得不明显。正如这个有用的文档所指出的:

日期时间表示时间点。它是绝对的:不依赖于任何东西。相反,日期是一个日历概念。它是一个时间段,其界限取决于考虑日期的时区。正如您可以看到的那样,这两个概念根本不同。

了解这种差异是避免基于时间的错误的关键步骤。祝您好运。


(1)如果您需要当前时间,请使用datetime.now(server_timezone)而不是server_timezone.localize(datetime.now())。 (2)考虑使用is_dst参数。 (3)在server_timezone.localize()之后,您可能需要使用server_timezone.normalize()来修复不存在的本地时间。请参见我的答案中的更多细节和参考资料 - jfs
我认为在你的例子中,'timezone_localized_timestamp' 应该改为 'localized_timestamp'。或者反过来也可以。除此之外,一切都完美无误。 - Rivers Cuomo
2
由于这个问题/答案是一个顶级结果,我想通知大家pytz不再推荐使用。请参见:https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html和https://talkpython.fm/episodes/show/271/unlock-the-mysteries-of-time-pythons-datetime-that-is - Denilson Sá Maia

28

使用Python 3.9,标准库已包含您所需的所有内容:zoneinfo。不再需要使用pytz(已弃用;-> pytz deprecation shim)。

例如:

from datetime import datetime
from zoneinfo import ZoneInfo

server_timezone = "US/Eastern"
new_timezone = "US/Pacific"

current_time = datetime.now(ZoneInfo(server_timezone)) 

# current_time_in_new_timezone = ???
current_time_in_new_timezone = current_time.astimezone(ZoneInfo(new_timezone))

那就举个例子

print(current_time.isoformat(timespec='seconds'))
# 2021-10-04T02:42:54-04:00

print(repr(current_time))
# datetime.datetime(2021, 10, 4, 2, 42, 54, 40600, tzinfo=zoneinfo.ZoneInfo(key='US/Eastern'))

print(current_time_in_new_timezone.isoformat(timespec='seconds'))
# 2021-10-03T23:42:54-07:00

print(repr(current_time_in_new_timezone))
# datetime.datetime(2021, 10, 3, 23, 42, 54, 40600, tzinfo=zoneinfo.ZoneInfo(key='US/Pacific'))

2
对于任何使用 Python <3.9 的人,您可以安装时区信息的后移版本 https://pypi.org/project/backports.zoneinfo/。 - jpobst

10

如何将日期时间/时间戳从一个时区转换到另一个时区?

有两个步骤:

  1. Create an aware datetime objects from the system time and timezone e.g., to get the current system time in the given timezone:

    #!/usr/bin/env python
    from datetime import datetime
    import pytz
    
    server_timezone = pytz.timezone("US/Eastern")
    server_time = datetime.now(server_timezone) # you could pass *tz* directly
    

    Note: datetime.now(server_timezone) works even during ambiguous times e.g., during DST transitions while server_timezone.localize(datetime.now()) may fail (50% chance).

    If you are sure that your input time exists in the server's timezone and it is unique then you could pass is_dst=None to assert that:

    server_time = server_timezone.localize(naive_time, is_dst=None)
    

    It raises an exception for invalid times.
    If it is acceptable to ignore upto a day error (though typically an error due to DST is around an hour) then you could drop is_dst parameter:

    server_time = server_timezone.normalize(server_timezone.localize(naive_time))
    

    .normalize() is called to adjust non-existing times (local time in the gap, during "spring forward" transitions). If the time zone rules haven't changed; your server shouldn't generate non-existing times. See "Can I just always set is_dst=True?"

  2. Convert an aware datetime object to the target timezone tz:

    tz = pytz.timezone("US/Pacific")
    server_time_in_new_timezone = server_time.astimezone(tz)
    

1
如果您想将一个时戳从一个时区转换到另一个时区,可以使用以下代码:
from datetime import datetime
from zoneinfo import ZoneInfo


from_timezone = ZoneInfo('Europe/Moscow')  # UTC-3
to_timezone = ZoneInfo('Asia/Tbilisi')  # UTC-4

dt = datetime.fromtimestamp(timestamp, to_timezone)
result_timestamp = int(dt.replace(tzinfo=from_timezone).timestamp())

例如,如果您使用时间戳 = 529635600(1986年10月14日04:00:00莫斯科时间)并运行此代码,则会得到result_timestamp = 529639200(1986年10月14日05:00:00第比利斯时间)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接