与 JavaScript 不同,为什么 python 2.7 在 UTC 日期时间对象的 isoformat 字符串的末尾不包含 Z 字符(Zulu 或零偏移量)?
>>> datetime.datetime.utcnow().isoformat()
'2013-10-29T09:14:03.895210'
而在 javascript
>>> console.log(new Date().toISOString());
2013-10-29T09:38:41.341Z
与 JavaScript 不同,为什么 python 2.7 在 UTC 日期时间对象的 isoformat 字符串的末尾不包含 Z 字符(Zulu 或零偏移量)?
>>> datetime.datetime.utcnow().isoformat()
'2013-10-29T09:14:03.895210'
而在 javascript
>>> console.log(new Date().toISOString());
2013-10-29T09:38:41.341Z
isoformat()
Pythondatetime
不支持军用时区后缀,例如 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=' ')
请参阅:日期时间,Python 标准库
strftime()
或者你可以使用strftime
来达到同样的效果:
In [4]: d.strftime('%Y-%m-%dT%H:%M:%SZ')
Out[4]: '2014-12-10T12:00:00Z'
注意:此选项仅在您知道指定的日期为 UTC 时才有效。
更进一步,您可能有兴趣pytz
使用strftime
%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'
Pythondatetime
对象默认没有时区信息,没有它,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 格式,该格式允许UTCZ
或+00:00
作为 UTC 的后缀。请注意,后者实际上更符合标准,通常表示时区(UTC 是一个特例。)
以下 javascript 和 python 脚本提供相同的输出。我想这就是你要找的。
JavaScript
new Date().toISOString()
Python
from datetime import datetime
datetime.utcnow().isoformat()[:-3]+'Z'
他们给出的输出是 UTC (zulu) 时间,格式为带有 3 毫秒有效数字并附加 Z 的 ISO 字符串。
2019-01-19T23:20:25.459Z
您的目标不应该是添加 Z 字符,而应该是生成 ISO 8601 格式的 UTC“感知”日期时间字符串。解决方案是将 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
请注意,您需要使用isoparse()
from,dateutil.parser
因为 Python 文档datetime.fromisoformat()
说它“不支持解析任意 ISO 8601 字符串”。
好的,Pythondatetime
对象和 ISO 8601 字符串都是 UTC“感知”的。现在让我们看看 JavaScript 对日期时间字符串的看法。借用这个答案,我们得到:
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
笔记:
我有几个目标来解决这个问题:
timezone
使用 Django实用函数、dateutil
解析器和 JavaScript 函数验证日期时间对象和字符串请注意,此方法不包括 Z 后缀,也不使用utcnow()
. 但它基于Python 文档中的建议,并且通过了 Django 和 JavaScript 的测试。
也可以看看:
在 Python >= 3.2 中,您可以简单地使用:
>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2019-03-14T07:55:36.979511+00:00'
datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
不包括“Z”的原因是因为datetime.now()
甚至datetime.utcnow()
返回时区天真的日期时间,也就是说没有关联时区信息的日期时间。要获得时区感知日期时间,您需要将时区作为参数传递给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'
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'
JavascriptDate.parse
会悄悄地从时间戳中删除微秒。
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())
帖子上有很多很好的答案,但我希望格式与 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'
仅使用标准库,不假设时区已经是 UTC,并返回问题中请求的确切格式:
dt.astimezone(timezone.utc).replace(tzinfo=None).isoformat(timespec='milliseconds') + 'Z'
不过,这确实需要 Python 3.6 或更高版本。
>>> 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'
我用钟摆:
import pendulum
d = pendulum.now("UTC").to_iso8601_string()
print(d)
>>> 2019-10-30T00:11:21.818265Z
通过结合上面的所有答案,我得到了以下功能:
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)