具体而言,假设我已知服务器所在的时区(系统时间视角),并输入了一个时区,那么如何计算出系统时间在这个新时区的时间(不考虑夏令时等因素)?
import datetime
current_time = datetime.datetime.now() #system time
server_timezone = "US/Eastern"
new_timezone = "US/Pacific"
current_time_in_new_timezone = ???
具体而言,假设我已知服务器所在的时区(系统时间视角),并输入了一个时区,那么如何计算出系统时间在这个新时区的时间(不考虑夏令时等因素)?
import datetime
current_time = datetime.datetime.now() #system time
server_timezone = "US/Eastern"
new_timezone = "US/Pacific"
current_time_in_new_timezone = ???
如果你知道原始时区和要转换的新时区,那么转换过程非常简单:
创建两个 pytz.timezone
对象,一个表示当前时区,另一个表示新时区,例如 pytz.timezone("US/Pacific")
。你可以在 pytz
库中找到所有官方时区列表: import pytz; pytz.all_timezones
将需要转换的日期时间/时间戳本地化到当前时区,例如:
current_timezone = pytz.timezone("US/Eastern")
localized_timestamp = current_timezone.localize(timestamp)
.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
即使是看似简单的操作,如将日期时间/时间戳转换为日期,也可能变得不明显。正如这个有用的文档所指出的:
日期时间表示时间点。它是绝对的:不依赖于任何东西。相反,日期是一个日历概念。它是一个时间段,其界限取决于考虑日期的时区。正如您可以看到的那样,这两个概念根本不同。
了解这种差异是避免基于时间的错误的关键步骤。祝您好运。
使用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'))
如何将日期时间/时间戳从一个时区转换到另一个时区?
有两个步骤:
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?"
Convert an aware datetime object to the target timezone tz
:
tz = pytz.timezone("US/Pacific")
server_time_in_new_timezone = server_time.astimezone(tz)
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())
datetime.now(server_timezone)
而不是server_timezone.localize(datetime.now())
。 (2)考虑使用is_dst
参数。 (3)在server_timezone.localize()
之后,您可能需要使用server_timezone.normalize()
来修复不存在的本地时间。请参见我的答案中的更多细节和参考资料。 - jfspytz
不再推荐使用。请参见: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