【问题标题】:How to convert a timezone aware string to datetime in Python without dateutil?如何在没有 dateutil 的情况下在 Python 中将时区感知字符串转换为日期时间?
【发布时间】:2012-11-01 17:07:24
【问题描述】:

我必须将"2012-11-01T04:16:13-04:00" 等可识别时区的字符串转换为 Python datetime 对象。

我看到dateutil 模块有一个解析函数,但我真的不想使用它,因为它添加了一个依赖项。

那我该怎么做呢?我尝试了类似以下的方法,但没有运气。

datetime.datetime.strptime("2012-11-01T04:16:13-04:00", "%Y-%m-%dT%H:%M:%S%Z")

【问题讨论】:

  • 当依赖项精确地满足您的要求时添加依赖项有什么问题?当然,如果没有额外的模块也能达到同样的效果,那么模块就没有理由存在了,对吗?添加依赖项对您来说有多难?
  • 我认为这可能是个人恩惠?我真的不想在项目中引入整个大模块,因为我只需要一个很小的单个函数。
  • 向项目添加依赖项的具体成本是多少,与使代码难以理解的成本相比。忽略您仅当前需要一个功能的事实 - 专注于成本。

标签: python datetime timezone rfc3339


【解决方案1】:

从 Python 3.7 开始,datetime.datetime.fromisoformat() 可以处理您的格式:

>>> import datetime
>>> datetime.datetime.fromisoformat('2012-11-01T04:16:13-04:00')
datetime.datetime(2012, 11, 1, 4, 16, 13, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=72000)))

在较旧的 Python 版本中,您无法做到这一点,而且需要大量艰苦的手动时区定义。

Python 不包含时区数据库,因为它会很快过时。相反,Python 依赖于可以具有更快发布周期的外部库来为您提供正确配置的时区。

作为副作用,这意味着时区解析也需要是一个外部库。如果dateutil 对您来说太重了,请改用iso8601,它会很好地解析您的特定格式:

>>> import iso8601
>>> iso8601.parse_date('2012-11-01T04:16:13-04:00')
datetime.datetime(2012, 11, 1, 4, 16, 13, tzinfo=<FixedOffset '-04:00'>)

iso8601 是一个 惊人的 4KB 小。比较 python-dateutil 的 148KB。

从 Python 3.2 开始,Python 可以处理简单的基于偏移的时区,%z 将在时间戳中解析 -hhmm+hhmm 时区偏移。这意味着对于 ISO 8601 时间戳,您必须删除时区中的 :

>>> from datetime import datetime
>>> iso_ts = '2012-11-01T04:16:13-04:00'
>>> datetime.strptime(''.join(iso_ts.rsplit(':', 1)), '%Y-%m-%dT%H:%M:%S%z')
datetime.datetime(2012, 11, 1, 4, 16, 13, tzinfo=datetime.timezone(datetime.timedelta(-1, 72000)))

Python issue 15873 中正在跟踪缺乏正确的 ISO 8601 解析。

【讨论】:

  • 在我看来 datetime 可以包含类似 iso8601 的东西来处理 ISO 8601 时区——一些解析和两个 tzinfo 子类。
  • @eryksun: ISO8601 对时区非常简单,但是一旦你在 python 标准库中包含了这些偏移量,你就会被为什么现实生活中的时区(不仅仅是一个偏移量)的误解所淹没) 不工作,等等。
  • 定义FixedOffset 类并没有那么痛苦。这是code example
  • Python 3.9, fromisoformat 在字符串中的 Z 或时间中的小数处失败`
  • @Jashwant:在任何 Python 版本中都会失败。如果您必须使用Z 接受字符串,请使用isoformattedstring.replace("Z", "+00:00")。不知道您所说的时间小数是什么意思。
【解决方案2】:

这是使用 dateutil 包的日期时间对象的 Python Doc..

from dateutil.parser import parse

get_date_obj = parse("2012-11-01T04:16:13-04:00")
print get_date_obj

【讨论】:

  • 这应该是在没有外部库的情况下这样做的正确答案
  • @Paullo python-dateutil 正是“外部库”。
  • 最佳答案对我来说没有尾随的“Z”,但这个答案可以。
【解决方案3】:

原问题中的代码有两个问题:时区中不应有:,并且“时区作为偏移量”的格式字符串是小写%z不上%Z

这适用于我在 Python v3.6

>>> from datetime import datetime
>>> t = datetime.strptime("2012-11-01T04:16:13-0400", "%Y-%m-%dT%H:%M:%S%z")
>>> print(t)
2012-11-01 04:16:13-04:00

【讨论】:

  • 错了,为什么print(t)在utc偏移上加冒号?
  • @moooeeeep 因为默认日期时间使用isoformat(sep=' ') 用于__str__ 函数,它将UTC 偏移量打印为“+HH:MM”。使用print(t.strftime("%Y-%m-%dT%H:%M:%S%z")) 将打印时区中没有“:”。
  • 在时区中有一个冒号并没有错。许多来源以字符串形式显示他们的时间:2012-11-01T04:16:13-04:00。 OP 正在寻求解析该表单。
【解决方案4】:

您可以创建一个时区不感知对象并替换 tzinfo 并使其成为时区感知 DateTime 对象。

from datetime import datetime
import pytz

unware_time = datetime.strptime("2012-11-01 04:16:13", "%Y-%m-%d %H:%M:%S")
aware_time = unaware_time.replace(tzinfo=pytz.UTC)

【讨论】:

  • 这是最简单的方法,但一直困扰我的是你创建了两次datetime 对象,因为replace 只是简单地替换了 tzinfo,它创建一个全新的对象。另外,从 Python 3.2 开始,你可以使用datetime.timezone.utc,不需要 pytz。
  • 谢谢!超级有用。我可以试试这个关于 datetime.timezone.utc 的其他评论...但实际上我只需要一些有用的东西。
【解决方案5】:

你可以这样转换。

date = datetime.datetime.strptime('2019-3-16T5-49-52-595Z','%Y-%m-%dT%H-%M-%S-%f%z')
date_time = date.strftime('%Y-%m-%dT%H:%M:%S.%fZ')

【讨论】:

    【解决方案6】:

    Mohideen bin Mohammed 提出的使用 dateutil 的建议绝对是最好的解决方案,即使它确实需要一个小型库。在那里使用其他方法容易出现各种形式的失败。这是一个很好的函数。

    from dateutil.parser import parse
    
    
    def parse_date_convert(date, fmt=None):
        if fmt is None:
            fmt = '%Y-%m-%d %H:%M:%S' # Defaults to : 2022-08-31 07:47:30
        get_date_obj = parse(str(date))
        return str(get_date_obj.strftime(fmt))
    
    dates = ['2022-08-31T07:47:30Z','2022-08-31T07:47:29.098Z','2017-05-27T07:20:18.000-04:00','2012-11-01T04:16:13-04:00']
    
    for date in dates:
        print(f'Before: {date}  After: {parse_date_convert(date)}')
    

    结果:

    Before: 2022-08-31T07:47:30Z  After: 2022-08-31 07:47:30
    Before: 2022-08-31T07:47:29.098Z  After: 2022-08-31 07:47:29
    Before: 2017-05-27T07:20:18.000-04:00  After: 2017-05-27 07:20:18
    Before: 2012-11-01T04:16:13-04:00  After: 2012-11-01 04:16:13
    

    尝试过各种形式,例如像这样用切片分割替换 T Z:

    dates = ['2022-08-31T07:47:30Z','2022-08-31T07:47:29.098Z','2017-05-27T07:20:18.000-04:00','2012-11-01T04:16:13-04:00']
    
    for date in dates:
        print(f'Before: {date}  After: {date.replace("T", " ").replace("Z", "")}')
    

    你仍然得到低于标准的结果。像下面这样

    Before: 2022-08-31T07:47:30Z  After: 2022-08-31 07:47:30
    Before: 2022-08-31T07:47:29.098Z  After: 2022-08-31 07:47:29.098
    Before: 2017-05-27T07:20:18.000-04:00  After: 2017-05-27 07:20:18.000-04:00
    Before: 2012-11-01T04:16:13-04:00  After: 2012-11-01 04:16:13-04:00
    

    【讨论】:

      【解决方案7】:

      我是 Python 新手,但找到了一种转换方法

      2017-05-27T07:20:18.000-04:00

      2017-05-27T07:20:18 无需下载新的实用程序。

      from datetime import datetime, timedelta
      
      time_zone1 = int("2017-05-27T07:20:18.000-04:00"[-6:][:3])
      >>returns -04
      
      item_date = datetime.strptime("2017-05-27T07:20:18.000-04:00".replace(".000", "")[:-6], "%Y-%m-%dT%H:%M:%S") + timedelta(hours=-time_zone1)
      

      我确信有更好的方法可以做到这一点,而无需过多地切割字符串,但这完成了工作。

      【讨论】:

        猜你喜欢
        • 2012-10-22
        • 1970-01-01
        • 2020-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-29
        相关资源
        最近更新 更多