Python UTC datetime对象的ISO格式不包含Z (Zulu或零偏移量)

为什么python 2.7不包括Z字符(Zulu或零偏移量)在UTC datetime对象的isoformat字符串结束不像JavaScript?

>>> datetime.datetime.utcnow().isoformat()
'2013-10-29T09:14:03.895210'

而在javascript中

>>>  console.log(new Date().toISOString());
2013-10-29T09:38:41.341Z
265669 次浏览

Python datetime对象默认情况下没有时区信息,如果没有它,Python实际上违反了ISO 8601规范(如果没有给出时区信息,则假定为本地时间)。你可以使用pytz包来获取一些默认时区,或者你自己直接子类tzinfo:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
def tzname(self,**kwargs):
return "UTC"
def utcoffset(self, dt):
return timedelta(0)

然后你可以手动将时区信息添加到utcnow():

>>> datetime.utcnow().replace(tzinfo=simple_utc()).isoformat()
'2014-05-16T22:51:53.015001+00:00'

请注意,这确实符合ISO 8601格式,该格式允许使用Z+00:00作为UTC后缀。注意,后者实际上更好地符合标准,一般来说,时区是如何表示的(UTC是一个特殊情况)。

Python日期时间有点笨拙。使用arrow

> str(arrow.utcnow())
'2014-05-17T01:18:47.944126+00:00'

Arrow拥有本质上与datetime相同的api,但带有时区和一些额外的细节,这些应该在主库中。

与Javascript兼容的格式可以通过以下方式实现:

arrow.utcnow().isoformat().replace("+00:00", "Z")
'2018-11-30T02:46:40.714281Z'

Javascript Date.parse将悄悄地从时间戳中删除微秒。

选择:isoformat()

Python的datetime不支持军事时区后缀,例如UTC的'Z'后缀。下面是简单的字符串替换方法:

In [1]: import datetime


In [2]: d = datetime.datetime(2014, 12, 10, 12, 0, 0)


In [3]: str(d).replace('+00:00', 'Z')
Out[3]: '2014-12-10 12:00:00Z'

str(d)本质上与d.isoformat(sep=' ')相同

看:Datetime, Python标准库

选择:strftime()

或者你可以使用strftime来达到同样的效果:

In [4]: d.strftime('%Y-%m-%dT%H:%M:%SZ')
Out[4]: '2014-12-10T12:00:00Z'

注意:此选项仅在您知道指定的日期为UTC时有效。

看:datetime.strftime ()


附加:人可读时区

更进一步,你可能对显示人类可读的时区信息感兴趣,pytzstrftime %Z时区标志:

In [5]: import pytz


In [6]: d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=pytz.utc)


In [7]: d
Out[7]: datetime.datetime(2014, 12, 10, 12, 0, tzinfo=<UTC>)


In [8]: d.strftime('%Y-%m-%d %H:%M:%S %Z')
Out[8]: '2014-12-10 12:00:00 UTC'

通过结合以上所有答案,我得到了以下函数:

from datetime import datetime, tzinfo, timedelta
class simple_utc(tzinfo):
def tzname(self,**kwargs):
return "UTC"
def utcoffset(self, dt):
return timedelta(0)




def getdata(yy, mm, dd, h, m, s) :
d = datetime(yy, mm, dd, h, m, s)
d = d.replace(tzinfo=simple_utc()).isoformat()
d = str(d).replace('+00:00', 'Z')
return d




print getdata(2018, 02, 03, 15, 0, 14)

在这篇文章中有很多很好的答案,但我希望格式与JavaScript完全相同。这就是我正在用的,效果很好。

In [1]: import datetime


In [1]: now = datetime.datetime.utcnow()


In [1]: now.strftime('%Y-%m-%dT%H:%M:%S') + now.strftime('.%f')[:4] + 'Z'
Out[3]: '2018-10-16T13:18:34.856Z'
>>> import arrow


>>> now = arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS')
>>> now
'2018-11-28T21:34:59.235'
>>> zulu = "{}Z".format(now)
>>> zulu
'2018-11-28T21:34:59.235Z'

或者,一下子就得到它:

>>> zulu = "{}Z".format(arrow.utcnow().format('YYYY-MM-DDTHH:mm:ss.SSS'))
>>> zulu
'2018-11-28T21:54:49.639Z'

下面的javascript和python脚本给出相同的输出。我觉得这就是你想要的。

JavaScript

new Date().toISOString()

Python

from datetime import datetime


datetime.utcnow().isoformat()[:-3]+'Z'

它们给出的输出是格式化为ISO字符串的UTC (zulu)时间,其中有一个3毫秒的有效数字,并附加一个Z。

2019-01-19T23:20:25.459Z

在Python >= 3.2中,你可以简单地使用:

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2019-03-14T07:55:36.979511+00:00'
pip install python-dateutil
>>> a = "2019-06-27T02:14:49.443814497Z"
>>> dateutil.parser.parse(a)
datetime.datetime(2019, 6, 27, 2, 14, 49, 443814, tzinfo=tzutc())

我用钟摆:

import pendulum




d = pendulum.now("UTC").to_iso8601_string()
print(d)


>>> 2019-10-30T00:11:21.818265Z

你的目标不应该是添加一个Z字符,它应该是生成一个UTC“意识”。ISO 8601格式的datetime字符串。解决方案是将UTC时区对象传递给datetime.now(),而不是使用datetime.utcnow():

from datetime import datetime, timezone


datetime.now(timezone.utc)
>>> datetime.datetime(2020, 1, 8, 6, 6, 24, 260810, tzinfo=datetime.timezone.utc)


datetime.now(timezone.utc).isoformat()
>>> '2020-01-08T06:07:04.492045+00:00'

看起来很好,所以让我们看看Django和dateutil是怎么想的:

from django.utils.timezone import is_aware
is_aware(datetime.now(timezone.utc))
>>> True


from dateutil.parser import isoparse
is_aware(isoparse(datetime.now(timezone.utc).isoformat()))
>>> True

注意,你需要从dateutil.parser中使用isoparse(),因为datetime.fromisoformat()的Python文档说它“不支持解析任意ISO 8601字符串”。

好的,Python datetime对象和ISO 8601字符串都是UTC "aware"。现在让我们看看JavaScript是如何看待datetime字符串的。借用这个答案,我们得到:

let date = '2020-01-08T06:07:04.492045+00:00';
const dateParsed = new Date(Date.parse(date))


document.write(dateParsed);
document.write("\n");
// Tue Jan 07 2020 22:07:04 GMT-0800 (Pacific Standard Time)


document.write(dateParsed.toISOString());
document.write("\n");
// 2020-01-08T06:07:04.492Z


document.write(dateParsed.toUTCString());
document.write("\n");
// Wed, 08 Jan 2020 06:07:04 GMT

注:

我有几个目标来解决这个问题:

  • 生成UTC“意识”;ISO 8601格式的datetime字符串
  • 使用只有 Python标准库函数创建datetime对象和字符串
  • 使用Django timezone实用函数、dateutil解析器和JavaScript函数验证datetime对象和字符串

注意,这种方法包含Z后缀,而使用utcnow()。但是它是基于Python文档中的建议的,并且它在Django和JavaScript中都是合格的。

参见:

简短的回答

datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")

长回答

“Z"不包含的原因是datetime.now()甚至datetime.utcnow()返回的是无时区的日期时间,也就是说没有与时区信息相关的日期时间。为了获得一个时区感知的datetime,你需要将一个时区作为参数传递给datetime.now。例如:

from datetime import datetime, timezone


datetime.utcnow()
#> datetime.datetime(2020, 9, 3, 20, 58, 49, 22253)
# This is timezone naive


datetime.now(timezone.utc)
#> datetime.datetime(2020, 9, 3, 20, 58, 49, 22253, tzinfo=datetime.timezone.utc)
# This is timezone aware

一旦你有了一个时区感知的时间戳,isoformat就会包含一个时区标识。因此,您可以通过以下方式获得ISO 8601时间戳:

datetime.now(timezone.utc).isoformat()
#> '2020-09-03T20:53:07.337670+00:00'

“+ 00:00"是UTC的有效ISO 8601时区名称。如果你想拥有"Z"而不是"+00:00",你必须自己做替换:

datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
#> '2020-09-03T20:53:07.337670Z'

只使用标准库,不假设时区已经是UTC,并返回问题中要求的确切格式:

dt.astimezone(timezone.utc).replace(tzinfo=None).isoformat(timespec='milliseconds') + 'Z'

不过,这确实需要Python 3.6或更高版本。