【问题标题】:Python UTC datetime object's ISO format doesn't include Z (Zulu or Zero offset)Python UTC 日期时间对象的 ISO 格式不包括 Z(祖鲁语或零偏移)
【发布时间】:2013-11-08 09:25:56
【问题描述】:

为什么 python 2.7 不像 JavaScript 那样在 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

【问题讨论】:

  • Python 日期时间值没有时区信息。如果您想将时区信息存储在时间戳中,请尝试 pytz 或 Babel。
  • datetime.datetime.utcnow().isoformat() + 'Z'
  • ..而缺少 Z 令人惊讶地导致某些事情无法正常工作,例如 API 调用
  • 更糟糕的是,如果datetime的最后一部分为0,它会截断它...

标签: python python-2.7 datetime timestamp iso8601


【解决方案1】:

选项:isoformat()

Python 的 datetime 不支持 military timezone 后缀,例如 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 Standard Library

选项: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'

【讨论】:

  • 分隔符不应该是T而不是空白吗?
  • 它应该是第 2 行中的 d = datetime.datetime(2014, 12, 10, 12, 0, 0, tzinfo=datetime.timezone.utc)。顺便说一句,在 RFC3339 中它表示空格是“好的”(而不是“T”)。
【解决方案2】:

Python datetime 对象默认没有时区信息,没有它,Python 实际上违反了 ISO 8601 规范 (if no time zone info is given, assumed to be local time)。您可以使用pytz package 获取一些默认时区,或者自己直接继承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 是一个特例。)

【讨论】:

  • 如何包含Z 而不是+00:00
  • 警告!!! pytz 违反了 Python 的 tzinfo 协议,使用起来很危险!见blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html
  • @ivan_pozdeev 感谢您的链接,非常有趣。请注意,允许dateutil 工作的更改直到带有 PEP 495 的 Python 3.6 才实现,所以说pytz 没有使用标准接口是不公平的,因为接口发生了变化。在更改之前,使其工作的唯一方法是 pytz 的方式。
【解决方案3】:

以下 javascript 和 python 脚本提供相同的输出。我想这就是你要找的。​​p>

JavaScript

new Date().toISOString()

Python

from datetime import datetime

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

他们给出的输出是 utc (zelda) 时间,格式为 ISO 字符串,有效数字为 3 毫秒,并附加一个 Z。

2019-01-19T23:20:25.459Z

【讨论】:

  • 但是如果 now() 是 0 毫秒,分钟和 : 也会被剥离。
  • @МихаилМуругов 当您运行.isoformat() 时,它会创建一个包含固定数量字符的字符串,包括尾随零,因此它确实可以可靠地工作
  • @PeterVanderMeer datetime(2021, 2, 19, 23, 21, 15).isoformat() -> '2021-02-19T23:21:15', datetime(2021, 2, 19, 23, 21, 15, 256).isoformat() -> '2021-02-19T23:21:15.000256'。不同的表示。我的错误是我写的是分钟,而不是秒。
  • 使用时区偏移,您需要使用[:-6]+'Z'
【解决方案4】:

您的目标不应该是添加 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

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

好的,Python datetime 对象和 ISO 8601 字符串都可以“感知”UTC。现在让我们看看 JavaScript 对日期时间字符串的看法。借用this answer 我们得到:

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

注意事项:

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

  • 生成 ISO 8601 格式的 UTC“感知”日期时间字符串
  • 使用 Python 标准库函数创建日期时间对象和字符串
  • 使用 Django timezone 实用函数、dateutil 解析器和 JavaScript 函数验证日期时间对象和字符串

请注意,此方法包含 Z 后缀并且使用utcnow()。但它基于recommendation in the Python documentation,并且通过了 Django 和 JavaScript 的测试。

另见:

【讨论】:

  • 坚持使用基础库函数并跟进旧帖子的奖励。
【解决方案5】:

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

>>> from datetime import datetime, timezone
>>> datetime.now(timezone.utc).isoformat()
'2019-03-14T07:55:36.979511+00:00'

【讨论】:

  • 非常好的解决方案。对于他们关于Z 的问题,您可以剪辑偏移量:.isoformat()[:-6] + 'Z'
【解决方案6】:

简答

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'

【讨论】:

  • 我喜欢这个答案,因为其他大多数人要么不演示添加Z,要么使用天真的日期时间对象。我觉得使用有意识的对象是要走的路。当我看到datetime.utcnow().timestamp()datetime.now(timezone.utc).timestamp() 之间的区别后,我决定尝试坚持有意识的对象。
  • 另外,如果你真的想匹配 Javascript 中返回的值,你可以这样做:datetime.now(timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z')。它有点长,但也许也很容易阅读?
  • 鉴于 API 的限制,我认为这是最好的答案,但是这个库确实应该有一种方法来实现这一点。为非常常见的操作手动替换字符串组件感觉很脆弱,应该没有必要。
【解决方案7】:

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 会悄悄地从时间戳中减少微秒。

【讨论】:

  • 这不能回答问题,因为字符串不以“Z”结尾。
【解决方案8】:

帖子上有很多很好的答案,但我希望格式与 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'

【讨论】:

    【解决方案9】:
    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())
    

    【讨论】:

      【解决方案10】:

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

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

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

      【讨论】:

        【解决方案11】:
        >>> 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'
        

        【讨论】:

        • 现在您可以使用转义字符来简化操作。 zulu = arrow.utcnow().format("YYYY-MM-DDTHH:mm:ss:SSS[Z]")
        【解决方案12】:

        我用钟摆:

        import pendulum
        
        
        d = pendulum.now("UTC").to_iso8601_string()
        print(d)
        
        >>> 2019-10-30T00:11:21.818265Z
        

        【讨论】:

          【解决方案13】:

          通过结合上面的所有答案,我得到了以下功能:

          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)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-06-21
            • 2020-02-03
            • 2020-10-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-11-27
            • 2020-04-22
            相关资源
            最近更新 更多