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

Why does datetime.datetime.utcnow() not contain timezone information?

1
datetime.datetime.utcnow()

为什么这个datetime没有任何时区信息,因为它明确是UTC datetime

我希望这会包含tzinfo


这意味着它是时区的天真,所以你不能用datetime.astimezone

你可以给它一个像这样的时区

1
2
3
4
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

现在你可以改变时区了

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

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

1
2
3
4
5
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

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

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


请注意,对于Python 3.2及更高版本,datetime模块包含datetime.timezonedatetime.utcnow()的文档说:

An aware current UTC datetime can be obtained by calling datetime.now(timezone.utc).

所以你可以这样做:

1
2
3
>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)


标准Python库不包含任何tzinfo类(但请参阅pep 431)。我只能猜测原因。就个人而言,我认为不包括UTC的tzinfo类是错误的,因为那个没有足够的争议,有一个标准的实现。

编辑:虽然库中没有实现,但在tzinfo文档中有一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()

要使用它,要将当前时间作为识别日期时间对象:

1
2
3
from datetime import datetime

now = datetime.now(utc)

Python 3.2+中有datetime.timezone.utc

1
2
3
from datetime import datetime, timezone

now = datetime.now(timezone.utc)


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

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

1
2
3
4
5
6
7
8
9
10
11
12
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时区信息。我怀疑这不包括在内,因为本机日期时间库默认为天真的日期时间以实现交叉兼容性。


Julien Danjou写了一篇很好的文章,解释了为什么你永远不应该处理时区。摘录:

Indeed, Python datetime API always returns unaware datetime objects,
which is very unfortunate. Indeed, as soon as you get one of this
object, there is no way to know what the timezone is, therefore these
objects are pretty"useless" on their own.

唉,即使你可以使用utcnow(),你仍然不会看到你所发现的时区信息。

建议:

  • Always use aware datetime objects, i.e. with timezone information. That
    makes sure you can compare them directly (aware and unaware datetime
    objects are not comparable) and will return them correctly to users.
    Leverage pytz to have timezone objects.

  • Use ISO 8601 as the input and
    output string format. Use datetime.datetime.isoformat() to return
    timestamps as string formatted using that format, which includes the
    timezone information.

  • If you need to parse strings containing ISO 8601 formatted timestamps,
    you can rely on iso8601, which returns timestamps with correct
    timezone information. This makes timestamps directly comparable.


在Python 3.2+中添加timezone信息

1
2
3
4
5
import datetime

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


1
2
3
4
5
6
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]

UTC日期不需要任何时区信息,因为它们是UTC,根据定义,它们没有偏移量。