如何在 Python 中获取“时区感知”的 datetime.today() 值?
- 2025-02-12 10:04:00
- admin 原创
- 88
问题描述:
我试图从 的值中减去一个日期值datetime.datetime.today()
来计算某件事发生的时间。但它抱怨道:
TypeError: can't subtract offset-naive and offset-aware datetimes
返回值datetime.datetime.today()
似乎不是“时区感知的”,而我的其他日期值是。如何获取datetime.datetime.today()
时区感知的返回值?
理想的解决方案是让它自动知道时区。
现在,它给我的是当地时间,恰好是 PST,即 UTC - 8 小时。最坏的情况是,我有没有办法手动将时区值输入到datetime
返回的对象中datetime.datetime.today()
并将其设置为 UTC-8?
解决方案 1:
在标准库中,如果不创建自己的时区类,则无法跨平台创建感知时区的方法。(编辑: Python 3.9zoneinfo
在标准库中引入了此功能。)
在 Windows 上,有win32timezone.utcnow()
,但那是 pywin32 的一部分。我宁愿建议使用pytz 库,它具有大多数时区的不断更新的数据库。
使用本地时区可能非常棘手(请参阅下面的“进一步阅读”链接),因此您可能更愿意在整个应用程序中使用 UTC,特别是对于算术运算(例如计算两个时间点之间的差异)。
您可以像这样获取当前日期/时间:
import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)
请注意datetime.today()
,datetime.now()
返回的是当地时间,而不是 UTC 时间,因此应用于.replace(tzinfo=pytz.utc)
它们是不正确的。
另一个好方法是:
datetime.now(pytz.utc)
它稍微短一点,但功能相同。
进一步阅读/观察为什么在许多情况下更喜欢 UTC:
pytz 文档
每个开发人员都应该知道的时间知识——许多实际用例的开发提示
时间和时区的问题 - Computerphile – 关于使用时区的复杂性的有趣、令人大开眼界的解释(视频)
解决方案 2:
在 Python ≥ 3.9 中:zoneinfo
使用 IANA 时区数据库:
在 Python 3.9 或更高版本中,您可以使用标准库指定特定时区,zoneinfo
如下所示:
>>> import datetime
>>> from zoneinfo import ZoneInfo
>>> datetime.datetime.now(ZoneInfo("America/Los_Angeles"))
datetime.datetime(2020, 11, 27, 6, 34, 34, 74823, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles'))
zoneinfo
从操作系统获取时区数据库。如果操作系统没有 IANA 时区数据库(尤其是 Windows 没有),则从第一方 PyPI 包(tzdata
如果已安装)中检索信息。
在 Python ≥ 3.2 中:
如果需要指定 UTC 作为时区,标准库在 Python 3.2 或更高版本中提供了支持:
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2020, 11, 27, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)
您还可以使用以下方法获取包含本地时间偏移的日期时间astimezone
:
>>> datetime.datetime.now(datetime.timezone.utc).astimezone()
datetime.datetime(2020, 11, 27, 15, 34, 34, 74823, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'CET'))
在 Python 3.6 或更高版本中,你可以将最后一行缩短为:
>>> datetime.datetime.now().astimezone()
datetime.datetime(2020, 11, 27, 15, 34, 34, 74823, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'CET'))
如果您想要一个仅使用标准库并且可在 Python 2 和 Python 3 中运行的解决方案,请参阅jfs 的回答。
解决方案 3:
获取特定时区的当前时间:
import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))
记得先安装pytz
。
解决方案 4:
从 Python 3.3 开始,仅使用标准库的单行代码就可以工作。你可以datetime
使用以下命令获取本地时区感知对象astimezone
(如johnchen902 所建议的):
from datetime import datetime, timezone
aware_local_now = datetime.now(timezone.utc).astimezone()
print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00
print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))
解决方案 5:
这是一个适用于 Python 2 和 3 的 stdlib 解决方案:
from datetime import datetime
now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight
其中today
是一个感知型日期时间实例,代表 UTC 时间的开始(午夜),并且utc
是一个 tzinfo 对象(示例来自文档):
from datetime import tzinfo, timedelta
ZERO = timedelta(0)
class UTC(tzinfo):
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTC()
相关:获取给定 UTC 时间的午夜(一天的开始)的几种方法的性能比较。注意:获取具有非固定 UTC 偏移量的时区的午夜更加复杂。
解决方案 6:
构造表示当前时间的时区感知日期时间对象的另一种方法:
import datetime
import pytz
pytz.utc.localize( datetime.datetime.utcnow() )
您可以pytz
通过运行以下命令从 PyPI 安装:
$ pipenv install pytz
解决方案 7:
如果您正在使用Django,您可以设置非 tz 感知的日期(仅UTC)。
在 settings.py 中注释以下行:
USE_TZ = True
解决方案 8:
按照 Python datetime.datetime.now() 中的说明使用具有时区意识的dateutil :
from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())
python-dateutil
要作为依赖项安装,请运行:
pip install python-dateutil
解决方案 9:
以下是使用 stdlib 生成它的一种方法:
import time
from datetime import datetime
FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)
date将存储本地日期和与 UTC 的偏移量,而不是 UTC 时区的日期,因此如果您需要确定日期是在哪个时区生成的,则可以使用此解决方案。在此示例中,在我的本地时区中:
date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))
date.tzname()
'UTC+02:00'
关键是将%z
指令添加到表示 FORMAT 中,以指示生成的时间结构的 UTC 偏移量。其他表示格式可以参阅 datetime 模块文档
如果您需要 UTC 时区的日期,则可以将time.localtime()替换为time.gmtime()
date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)
date
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)
date.tzname()
'UTC'
编辑
这仅适用于 python3。z 指令在 python 2 _strptime.py 代码中不可用
解决方案 10:
需要强调的是,自 Python 3.6 以来,您只需要标准库即可获取表示本地时间(操作系统的设置)的时区感知日期时间对象。使用astimezone()
import datetime
datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))
datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'
# I'm on CET/CEST
(参见@johnchen902 的评论)。
但请注意,有一个小警告,不要期望 timedelta 时区具有任何“DST 感知”。
解决方案 11:
pytz是一个 Python 库,允许使用 Python 2.3 或更高版本进行精确的跨平台时区计算。
使用 stdlib,这是不可能的。
请参阅SO上的类似问题。
解决方案 12:
这是一个使用可读时区并与 today() 配合使用的解决方案:
from pytz import timezone
datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()
您可以按如下方式列出所有时区:
import pytz
pytz.all_timezones
pytz.common_timezones # or
解决方案 13:
获取时区中可感知时区的日期utc
足以使日期减法起作用。
但是如果您想要在当前时区中获取时区感知日期,tzlocal
则可以这样做:
from tzlocal import get_localzone # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())
PSdateutil
具有类似的功能(dateutil.tz.tzlocal
)。但尽管名称相同,但其代码库却完全不同,正如JF Sebastian 所指出的那样,这可能会导致错误的结果。
解决方案 14:
另一个选择,在我看来是一个更好的选择,是使用Pendulum
而不是pytz
。考虑以下简单代码:
>>> import pendulum
>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>
要安装 Pendulum 并查看其文档,请转到此处。它有大量选项(如简单的 ISO8601、RFC3339 和许多其他格式支持)、更好的性能并且往往会产生更简单的代码。
解决方案 15:
特别是对于非 UTC 时区:
唯一具有自己的方法的时区是timezone.utc
,但是如果需要,您可以使用timedelta
&来伪造具有任何 UTC 偏移量的时区timezone
,并使用 强制执行.replace
。
In [1]: from datetime import datetime, timezone, timedelta
In [2]: def force_timezone(dt, utc_offset=0):
...: return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
...:
In [3]: dt = datetime(2011,8,15,8,15,12,0)
In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'
In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'
使用timezone(timedelta(hours=n))
时区是真正的灵丹妙药,它还有很多其他有用的应用程序。
解决方案 16:
来自“howchoo”的 Tyler 写了一篇非常棒的文章,帮助我更好地了解了 Datetime 对象,链接如下
使用日期时间
本质上,我只是在两个日期时间对象的末尾添加了以下内容
.replace(tzinfo=pytz.utc)
例子:
import pytz
import datetime from datetime
date = datetime.now().replace(tzinfo=pytz.utc)
解决方案 17:
我尝试了这里的大多数其他解决方案,但它们都不起作用。这是在 Python3.x 上有效的方法
import datetime, pytz
def get_current_datetime(timezone_str='America/Toronto'):
utc_now = datetime.datetime.now()
timezone = pytz.timezone(timezone_str)
now_tz = utc_now.replace(tzinfo=pytz.utc).astimezone(timezone)
return now_tz
解决方案 18:
如果您在 python 中获取当前时间和日期,然后在 python 中导入日期和时间、pytz 包,之后您将获得当前日期和时间,就像这样。
from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))
解决方案 19:
使用如下所示的时区来实现时区感知的日期时间。默认值为 UTC:
from django.utils import timezone
today = timezone.now()
解决方案 20:
尝试pnp_datetime,所有使用和返回的时间都带有时区,并且不会导致任何偏移量无关和偏移量感知问题。
>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)
扫码咨询,免费领取项目管理大礼包!