为什么datetime.datetime.utcnow()不包含时区信息?

453
datetime.datetime.utcnow()

为什么这个datetime没有任何时区信息,明明它是一个明确的UTC datetime
我本来期望它会包含tzinfo

如何将字符串类型的普通ISO格式日期字段转换为UTC格式? - Navi
10个回答

339
请注意,从Python 3.2开始,datetime模块包含datetime.timezonedatetime.utcnow()的文档中写道:

通过调用datetime.now(timezone.utc)可以获得一个具有意识的当前UTC日期时间。

datetime.utcnow()不会设置tzinfo来指示它是UTC时间,但datetime.now(datetime.timezone.utc)会返回具有设置了tzinfo的UTC时间。
所以你可以这样做:
>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)

自从Python 3.11版本以来,还存在datetime.UTC,它等同于datetime.timezone.utc。因此,你也可以使用datetime.datetime.now(datetime.UTC)

3
哪个更好?datetime.now(timezone.utc)还是datetime.utcnow(timezone.utc) - Jesse Webb
16
datetime.utcnow() 不需要参数。所以应该使用 datetime.now(timezone.utc) - Craig McQueen
27
@Babu:datetime.utcnow()没有设置tzinfo来表示它是UTC时间。但是,datetime.now(datetime.timezone.utc)会返回已设置tzinfo的UTC时间。 - Craig McQueen
3
我认为现在这是正确的解决方案,当已经明确使用UTC时,就不需要添加外部依赖项来指定时区元数据。 - Jacopofar
2
没有回答问题 - 为什么它不包含时区信息? - Beau B.
显示剩余6条评论

247

这意味着它没有时区信息,因此您不能使用 datetime.astimezone 方法。

您可以像这样为其指定时区:

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

现在你可以更改时区

print(u.astimezone(pytz.timezone("America/New_York")))

要获取给定时区的当前时间,您可以直接将 tzinfo 传递给 datetime.now()

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

它适用于任何时区,包括那些观察夏令时的时区,即适用于可能在不同时间具有不同UTC偏移量(非固定UTC偏移量)的时区。不要使用tz.localize(datetime.now()),它可能会在本地时间不明确的夏令时结束转换期间失败。


294
但是它没有必要是时区无关的——它被指定为协调世界时(UTC)。为什么需要寻找第三方库来使其正常工作呢? - Mark Ransom
8
我同意;对我来说,“天真”的时代完全没有用处。目前Python列表中有关将pytz添加到stdlib的讨论;问题不在于许可证,而在于时区数据更新频率非常高(而Python本身无法更新)。此外,pytz没有按预期方式实现tzinfo接口,因此如果尝试在astimezone中使用某些城市时区,则可能会出现错误。因此,datetime不仅没有本地化的时区,而且唯一广泛可用的tzinfo实现也不符合所谓的标准。 - bobince
5
为什么pytz和标准datetime库对你不起作用?Python核心和pytz作为独立项目的发展降低了核心团队的后勤复杂性。是的,减少Python核心团队的复杂性会增加所有需要处理时区问题的Python用户的复杂性,但我相信他们做出了这个决定有很好的理由。规则“标准库没有tzinfo实例…”很好,因为它很简单,为什么要在这里做一个例外呢? - Derek Litz
19
可以这样简写:u=datetime.utcnow().replace(tzinfo=pytz.utc) - Craig McQueen
5
不要使用tz.localize(datetime.now()),而应该使用datetime.now(tz) - jfs
显示剩余11条评论

86
标准的Python库在Python 3.2之前没有包含任何tzinfo类。对于这个原因,我只能猜测。个人认为,不包括UTC的tzinfo类是一个错误,因为它足够无争议,适合有一个标准的实现。虽然库中没有实现,但在tzinfo文档中提供了一个示例。
from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

一旦你有了一个UTC的tzinfo对象,你仍然不能使用它与utcnow一起使用。为了将当前时间作为明确的datetime对象:

from datetime import datetime 

now = datetime.now(utc)

在 Python 3.2 版本中,他们终于在库中加入了一个UTC的tzinfo类:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)

在Python 3.9中,他们为所有其他时区创建了tzinfo类。有关详细信息,请参阅PEP 615--支持IANA时区数据库的标准库


10
这个班级为什么一开始就没有提供(更重要的是,为什么不用于由utcnow()创建的“datetime”对象)?这还真难说...... - André Caron
5
@rgove,这就是Python 3应该公平竞争的纠正错误的方式。他们不应该担心向后兼容性。我最近几天读到另一个例子 - “struct”模块会自动将Unicode转换为字节串,最终决定破坏与早期Python 3版本的兼容性,以防止错误决策得以实施。 - Mark Ransom
2
我感到非常惊讶,Python的tzinfo文档包括实现它的代码示例,但datetime本身却没有包含这个功能!https://docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc - Mr. Lance E Sloan
此外,这里展示的UTC类示例几乎与pytz模块的实现完全相同。它添加了一些有用的方法。 - Mr. Lance E Sloan
1
@LS 是的,pytz 是一个很好的资源。当我编辑我的答案并加入示例代码时,已经有其他人建议了它,我不想抢他们的风头。 - Mark Ransom
这是正确的答案。为了向后兼容,utcnow()被保留不变。 - Smart Manoj

21

pytz模块是一种选择,还有另一个python-dateutil,虽然也是第三方软件包,但根据您的其他依赖项和操作系统,它可能已经可用。

我只是想提供这种方法作为参考-如果您已经安装了python-dateutil用于其他目的,您可以使用其tzinfo而不是重复使用pytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

我倾向于认同调用utcnow应该包含UTC时区信息。我怀疑这个信息没有被包含是因为本地的datetime库默认使用naive日期时间以实现跨平台兼容性。


1
NameError: 未定义名称 'dt' - xApple
我正在使用datetime.datetime.utcfromtimestamp()方法,并需要添加tzinfo,第二种解决方案适用于我:utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc()) - Ian Lee
1
请注意:与始终有效的 datetime.now(pytz_tz) 不同,datetime.now(dateutil.tz.tzlocal()) 可能在 DST 转换期间失败。未来,PEP 495 -- 本地时间消歧 可能会改善 dateutil 的情况。 - jfs
@IanLee:你可以使用utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(注意:dateutil带有非固定UTC偏移量(例如dateutil.tz.tzlocal())可能会失败,请改用基于pytz的解决方案)。 - jfs
由于我的程序已经导入了dateutil.parser,因此我最喜欢这个解决方案。它非常简单:utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc())。哇! - Mr. Lance E Sloan
谢谢,@IanLee。仍然让我困惑的部分是,一个名为utcfromtimestamp的方法,它以POSIX时间戳作为参数,而根据定义,该时间戳是UTC时间,但仍然产生了一个naïve datetime。对我来说,这似乎是一个简单的疏忽。 - Michael Scheper

16

在Python 3.2+中添加时区信息

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'

1
Python 2.7.13(默认,2017年1月19日,14:48:08) - Marcin Owsiany
@MarcinOwsiany 尝试: from datetime import datetime, timezone 然后使用 datetime.now(tz=timezone.utc) 调用。 - s2t2
这是获取UTC时区感知的日期时间对象的最佳方法,而不使用任何第三方Python模块,如pytz。| 只需添加一件事。如果您想获取本地时区感知的日期时间对象而不使用任何额外的模块,则只需添加astimezone():**d.astimezone()**。 - Saurav Kumar

10

Julien Danjou写了一篇很好的文章,解释了为什么你永远不应该处理时区。以下是节选内容:

事实上,Python datetime API总是返回无时区意识(datetime)对象,这非常不幸。确实,一旦你得到这样一个对象,就没有办法知道它所处的时区,因此这些对象本身相当“无用”。

遗憾的是,即使你使用utcnow(),你仍然看不到时区信息,正如你所发现的那样。

建议:

  • 始终使用带有时区信息的意识清醒的 datetime 对象。这样可以确保您可以直接比较它们(未知和已知时区的 datetime 对象不可比较),并将它们正确返回给用户。利用 pytz 来获取时区对象。

  • 使用 ISO 8601 作为输入和输出字符串格式。使用 datetime.datetime.isoformat() 将时间戳作为字符串格式化,使用该格式,该格式包括时区信息。

  • 如果需要解析包含 ISO 8601 格式的时间戳的字符串,则可以依赖于 iso8601,它会返回具有正确时区信息的时间戳。这使得时间戳直接可比较。


4
这是一个有点误导性的建议。经验法则是,永远不要涉及时区。始终存储和传输tz无关的utc对象(epoch对象)。时区应该只在UI表示的时候计算。 - nehem
1
这听起来已经与Julien的想法非常吻合了。他上面提到的具体建议中哪些是误导性的? - Joe D'Andrea
2
如果所有的日期时间计算都使用UTC,那么时区无关对象就不是问题。应该将所有的日期时间存储和处理为UTC,并且只有在需要人为操作时(例如在UI层),才将其转换为特定的时区。 - cowbert

4
datetime.datetime.utcnow()方法返回的UTC时间作为纯日期时间对象显然存在问题,必须进行修复。如果您的系统本地时区不是UTC,则可能会导致意外结果,因为datetime库假定纯日期时间对象代表系统本地时间。例如,datetime.datetime.utcnow().timestamp() 在我的计算机上会比正确值提前4个小时。另外,自Python 3.6起,可以在纯日期时间实例上调用datetime.astimezone()方法,但是datetime.datetime.utcnow().astimezone(any_timezone)会给出错误的结果,除非您的系统本地时区是UTC。

我们中使用 datetime.datetime.utcnow() 自从它被引入到 Python 中以来,在大多数情况下并没有使用 tz-aware 对象 - 我们只存储和处理 UTC,并且仅在 UI 层面为人类用户进行转换。如果您出于某种原因想要带有 tzinfo 的 UTC,请仅使用具有 tz 支持的现代 datetime 对象。 - cowbert

3

应该包括now()。相关的问题

因此,在那之前,now()优于today()utcnow()

from datetime import datetime, timezone
utc = timezone.utc
date = datetime.now(utc)
print(date) # 2022-04-06 05:40:13.025347+00:00

今天最简单和最准确的方法。原因是:date = datetime.now(timezone.utc) # datetime.datetime(2023, 10, 13, 8, 17, 21, 862183, tzinfo=datetime.timezone.utc)date = datetime.utcnow() #datetime.datetime(2023, 10, 13, 8, 17, 35, 668704) - undefined

-7
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]

-16

UTC日期不需要任何时区信息,因为它们是协调世界时(UTC),根据定义,这意味着它们没有偏移量。


13
据我所知,从http://docs.python.org/library/datetime.html可以看出,没有时区信息的datetime是指未指定时区的情况。在这里,时区已经被指定,因此从逻辑上讲应该出现。一个没有关联时区的日期/时间与一个明确在UTC中的日期/时间之间存在很大的区别(在我看来,理想情况下,它们应该是不同的类型,但这是另外一回事...)。 - Jon Skeet
2
@JonSkeet 我认为你没有理解Ignacio的观点,即UTC不是一个时区。令人惊讶的是,当我写这篇文章时,这个答案的得分是-9... - C S
6
@CS:嗯,Ignacio从未说过......严格来说,UTC不是一个时区,但通常被视为一个时区,以使生活变得更加简单(包括在Python中,例如使用pytz.utc)。请注意,一个值其与UTC的偏移量未知,与一个已知偏移量为0的值之间存在很大的区别。后者是utcnow()应该返回的内容,我认为如此。这将符合文档中所述的“意识到对象用于表示特定时刻,该时刻不容解释”。 - Jon Skeet

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