如何解析 ISO 8601 格式的日期和时间?

2024-11-19 08:38:00
admin
原创
11
摘要:问题描述:我需要将RFC 3339字符串解析"2008-09-03T20:56:35.450686Z"为 Pythondatetime类型。我在Python标准库中找到了strptime,但是不太方便。做到这一点的最好方法是什么?解决方案 1:isoparse来自python-dateut...

问题描述:

我需要将RFC 3339字符串解析"2008-09-03T20:56:35.450686Z"为 Pythondatetime类型。

我在Python标准库中找到了strptime,但是不太方便。

做到这一点的最好方法是什么?


解决方案 1:

isoparse来自python-dateutil的函数

python -dateutildateutil.parser.isoparse不仅要解析 RFC 3339 日期时间字符串(比如问题中的字符串),还要解析其他不符合 RFC 3339的 ISO 8601日期和时间字符串(比如没有 UTC 偏移量的字符串,或者仅代表日期的字符串)。

>>> import dateutil.parser
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686Z') # RFC 3339 format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())
>>> dateutil.parser.isoparse('2008-09-03T20:56:35.450686') # ISO 8601 extended format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903T205635.450686') # ISO 8601 basic format
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)
>>> dateutil.parser.isoparse('20080903') # ISO 8601 basic format, date only
datetime.datetime(2008, 9, 3, 0, 0)

python -dateutil包中也有dateutil.parser.parse。与相比isoparse,它的严格程度可能较低,但它们都相当宽容,并将尝试解释您传入的字符串。如果您想消除任何误读的可能性,则需要使用比这两个函数更严格的方法。

与 Python 3.7+ 内置的比较datetime.datetime.fromisoformat

dateutil.parser.isoparse是一个完整的 ISO-8601 格式解析器,但在 Python ≤ 3.10 中fromisoformat故意提供。在 Python 3.11 中,fromisoformat支持几乎所有有效的 ISO 8601 字符串。fromisoformat有关此警告性警告,请参阅 的文档。(请参阅此答案)。

解决方案 2:

自 Python 3.11 起,标准库datetime.datetime.fromisoformat支持任何有效的 ISO 8601 输入。在早期版本中,它仅解析特定子集,请参阅文档中的警告说明。如果您在不属于该子集的字符串上使用 Python 3.10 或更早版本(如问题中所示),请参阅标准库之外的函数的其他答案。文档:

类方法 datetime.fromisoformat(date_string)

返回与任何有效 ISO 8601 格式的date_stringdatetime相对应的值,但以下情况除外:

  1. 时区偏移可能有小数秒。

  2. 分隔符T可以被任何单个 Unicode 字符替换。

  3. 目前不支持序数日期。

  4. 不支持小数小时和小数分钟。

例子:

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04')
datetime.datetime(2011, 11, 4, 0, 0)
>>> datetime.fromisoformat('20111104')
datetime.datetime(2011, 11, 4, 0, 0)
>>> datetime.fromisoformat('2011-11-04T00:05:23')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-11-04T00:05:23Z')
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('20111104T000523')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-W01-2T00:05:23.283')
datetime.datetime(2011, 1, 4, 0, 5, 23, 283000)
>>> datetime.fromisoformat('2011-11-04 00:05:23.283')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000)
>>> datetime.fromisoformat('2011-11-04 00:05:23.283+00:00')
datetime.datetime(2011, 11, 4, 0, 5, 23, 283000, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('2011-11-04T00:05:23+04:00')   
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone(datetime.timedelta(seconds=14400)))

3.7 版本中的新功能。

在 3.11 版更改:以前,此方法仅支持 date.isoformat() 或 datetime.isoformat() 可以发出的格式。

如果您只需要日期而不是日期时间,则可以使用datetime.date.fromisoformat

>>> from datetime import date
>>> date.fromisoformat("2024-01-31")
datetime.date(2024, 1, 31)

解决方案 3:

请注意,在 Python 2.6+ 和 Py3K 中,%f 字符会捕获微秒。

>>> datetime.datetime.strptime("2008-09-03T20:56:35.450686Z", "%Y-%m-%dT%H:%M:%S.%fZ")

请参阅此处的问题

解决方案 4:

从 Python 3.7 开始,您基本上可以(下面的警告)使用它datetime.datetime.strptime来解析 RFC 3339 日期时间,如下所示:

from datetime import datetime

def parse_rfc3339(datetime_str: str) -> datetime:
    try:
        return datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S.%f%z")
    except ValueError:
        # Perhaps the datetime has a whole number of seconds with no decimal
        # point. In that case, this will work:
        return datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S%z")

这有点尴尬,因为我们需要尝试两种不同的格式字符串,以支持带有秒小数部分(如2022-01-01T12:12:12.123Z)和不带有秒小数部分(如2022-01-01T12:12:12Z)的日期时间,这两种日期时间在 RFC 3339 下都是有效的。但只要我们完成那一点复杂的逻辑,就可以了。

关于此方法需要注意以下几点:

  • 从技术上讲,它并不完全支持 RFC 3339,因为 RFC 3339 奇怪地允许您使用空格而不是T来分隔日期和时间,尽管 RFC 3339 声称是 ISO 8601 的配置文件,而 ISO 8601 不允许这样做。如果您想支持 RFC 3339 的这个愚蠢的怪癖,您可以datetime_str = datetime_str.replace(' ', 'T')在函数的开头添加。

  • 我上面的实现比严格的 RFC 3339 解析器稍微宽松一些,因为它允许时区偏移,例如+0500不带冒号,而 RFC 3339 不支持。如果您不仅想解析已知的 RFC-3339 日期时间,还想严格验证您获取的日期时间是否为 RFC 3339,请使用其他方法或添加您自己的逻辑来验证时区偏移格式。

  • 此功能肯定不支持所有ISO 8601,它包含比 RFC 3339广泛的格式。(例如,2009-W01-1是有效的 ISO 8601 日期。)

  • 它在 Python 3.6 或更早版本中不起作用,因为在那些旧版本中,说明%z符仅匹配时区偏移量(如+0500-0430或 )+0000,而不是 RFC 3339 时区偏移量(如+05:00-04:30或 )Z

解决方案 5:

尝试iso8601模块;它正是这样做的。

python.org wiki 上的WorkingWithTime页面上还提到了其他几个选项。

解决方案 6:

Python >= 3.11

fromisoformat现在Z直接解析:

from datetime import datetime

s = "2008-09-03T20:56:35.450686Z"

datetime.fromisoformat(s)
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=datetime.timezone.utc)

Python 3.7 至 3.10

其中一条评论中有一个简单的选项:'Z''+00:00'- 替换并使用fromisoformat

from datetime import datetime

s = "2008-09-03T20:56:35.450686Z"

datetime.fromisoformat(s.replace('Z', '+00:00'))
# datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=datetime.timezone.utc)

为什么偏爱fromisoformat

尽管strptime's%z可以将字符解析'Z'为 UTC,fromisoformat但速度却快了约 40 倍(对于 Python 3.11 甚至快了约 60 倍):

from datetime import datetime
from dateutil import parser

s = "2008-09-03T20:56:35.450686Z"

# Python 3.11+
%timeit datetime.fromisoformat(s)
85.1 ns ± 0.473 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

# Python 3.7 to 3.10
%timeit datetime.fromisoformat(s.replace('Z', '+00:00'))
134 ns ± 0.522 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

%timeit parser.isoparse(s)
4.09 µs ± 5.2 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

%timeit datetime.strptime(s, '%Y-%m-%dT%H:%M:%S.%f%z')
5 µs ± 9.26 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

%timeit parser.parse(s)
28.5 µs ± 99.2 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

(GNU/Linux 上的 Python 3.11.3 x64)

另请参阅:更快的 strptime

解决方案 7:

从 Python 3.7 开始,strptime 支持 UTC 偏移中的冒号分隔符(源)。因此,您可以使用:

import datetime

def parse_date_string(date_string: str) -> datetime.datetime
    try:
       return datetime.datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S.%f%z')
    except ValueError:
       return datetime.datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S%z')

编辑:

正如 Martijn 所指出的,如果您使用 isoformat() 创建了 datetime 对象,那么您只需使用 即可datetime.fromisoformat()

编辑2:

正如马克·阿默里 (Mark Amery) 指出的那样,我添加了一个 try..except 块来解释丢失的秒数小数部分。

解决方案 8:

您遇到的具体错误是什么?是像下面这样的吗?

>>> datetime.datetime.strptime("2008-08-12T12:20:30.656234Z", "%Y-%m-%dT%H:%M:%S.Z")
ValueError: time data did not match format:  data=2008-08-12T12:20:30.656234Z  fmt=%Y-%m-%dT%H:%M:%S.Z

如果是,您可以用“。”分割输入字符串,然后将微秒添加到您获得的日期时间。

尝试一下:

>>> def gt(dt_str):
        dt, _, us= dt_str.partition(".")
        dt= datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S")
        us= int(us.rstrip("Z"), 10)
        return dt + datetime.timedelta(microseconds=us)

>>> gt("2008-08-12T12:20:30.656234Z")
datetime.datetime(2008, 8, 12, 12, 20, 30, 656234)

解决方案 9:

import re
import datetime
s = "2008-09-03T20:56:35.450686Z"
d = datetime.datetime(*map(int, re.split(r'[^d]', s)[:-1]))

解决方案 10:

如今,Arrow还可以用作第三方解决方案:

>>> import arrow
>>> date = arrow.get("2008-09-03T20:56:35.450686Z")
>>> date.datetime
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=tzutc())

解决方案 11:

只需使用python-dateutil模块:

>>> import dateutil.parser as dp
>>> t = '1984-06-02T19:05:00.000Z'
>>> parsed_t = dp.parse(t)
>>> print(parsed_t)
datetime.datetime(1984, 6, 2, 19, 5, tzinfo=tzutc())

文档

解决方案 12:

我发现ciso8601是解析 ISO 8601 时间戳的最快方法。

它还完全支持 RFC 3339,并具有用于严格解析 RFC 3339 时间戳的专用函数。

使用示例:

>>> import ciso8601
>>> ciso8601.parse_datetime('2014-01-09T21')
datetime.datetime(2014, 1, 9, 21, 0)
>>> ciso8601.parse_datetime('2014-01-09T21:48:00.921000+05:30')
datetime.datetime(2014, 1, 9, 21, 48, 0, 921000, tzinfo=datetime.timezone(datetime.timedelta(seconds=19800)))
>>> ciso8601.parse_rfc3339('2014-01-09T21:48:00.921000+05:30')
datetime.datetime(2014, 1, 9, 21, 48, 0, 921000, tzinfo=datetime.timezone(datetime.timedelta(seconds=19800)))

GitHub Repo README显示了它们与其他答案中列出的所有其他库相比的加速情况。

我的个人项目涉及大量 ISO 8601 解析。能够切换通话并加快速度真是太好了。:)

编辑:从那时起,我就成为了 ciso8601 的维护者。它现在比以前更快了!

解决方案 13:

如果您使用 Django,它提供了dateparse 模块,该模块接受一堆类似于 ISO 格式的格式,包括时区。

如果您没有使用 Django 并且不想使用此处提到的其他库之一,那么您可以将dateparse 的 Django 源代码调整到适合您的项目。

解决方案 14:

如果你不想使用dateutil,你可以尝试这个函数:

def from_utc(utcTime,fmt="%Y-%m-%dT%H:%M:%S.%fZ"):
    """
    Convert UTC time string to time.struct_time
    """
    # change datetime.datetime to time, return time.struct_time type
    return datetime.datetime.strptime(utcTime, fmt)

测试:

from_utc("2007-03-04T21:08:12.123Z")

结果:

datetime.datetime(2007, 3, 4, 21, 8, 12, 123000)

解决方案 15:

我编写了一个 ISO 8601 标准的解析器并将其放在 GitHub 上: https: //github.com/boxed/iso8601。此实现支持规范中的所有内容,除了持续时间、间隔、周期间隔和 Python datetime 模块支持的日期范围之外的日期。

包含测试!:P

解决方案 16:

这适用于 Python 3.2 及更高版本的 stdlib(假设所有时间戳都是 UTC):

from datetime import datetime, timezone, timedelta
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").replace(
    tzinfo=timezone(timedelta(0)))

例如,

>>> datetime.utcnow().replace(tzinfo=timezone(timedelta(0)))
... datetime.datetime(2015, 3, 11, 6, 2, 47, 879129, tzinfo=datetime.timezone.utc)

解决方案 17:

我是 iso8601utils 的作者。可以在 GitHub或PyPI上找到它。以下是解析示例的方法:

>>> from iso8601utils import parsers
>>> parsers.datetime('2008-09-03T20:56:35.450686Z')
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686)

解决方案 18:

在所有受支持的 Python 版本中,将类似 ISO 8601 的日期字符串转换为 UNIX 时间戳或对象的一种直接方法是使用SQLite 的日期解析器,datetime.datetime而无需安装第三方模块。

#!/usr/bin/env python
from __future__ import with_statement, division, print_function
import sqlite3
import datetime

testtimes = [
    "2016-08-25T16:01:26.123456Z",
    "2016-08-25T16:01:29",
]
db = sqlite3.connect(":memory:")
c = db.cursor()
for timestring in testtimes:
    c.execute("SELECT strftime('%s', ?)", (timestring,))
    converted = c.fetchone()[0]
    print("%s is %s after epoch" % (timestring, converted))
    dt = datetime.datetime.fromtimestamp(int(converted))
    print("datetime is %s" % dt)

输出:

2016-08-25T16:01:26.123456Z is 1472140886 after epoch
datetime is 2016-08-25 12:01:26
2016-08-25T16:01:29 is 1472140889 after epoch
datetime is 2016-08-25 12:01:29

解决方案 19:

另一种方法是使用 ISO-8601 的专门解析器,即使用dateutil 解析器的isoparse函数:

from dateutil import parser

date = parser.isoparse("2008-09-03T20:56:35.450686+01:00")
print(date)

输出:

2008-09-03 20:56:35.450686+01:00

标准 Python 函数datetime.fromisoformat的文档中也提到了此函数:

第三方包 dateutil 中提供了功能更齐全的 ISO 8601 解析器 dateutil.parser.isoparse。

解决方案 20:

Django 的parse_datetime()函数支持具有 UTC 偏移量的日期:

parse_datetime('2016-08-09T15:12:03.65478Z') =
datetime.datetime(2016, 8, 9, 15, 12, 3, 654780, tzinfo=<UTC>)

因此它可以用于解析整个项目中字段中的 ISO 8601 日期:

from django.utils import formats
from django.forms.fields import DateTimeField
from django.utils.dateparse import parse_datetime

class DateTimeFieldFixed(DateTimeField):
    def strptime(self, value, format):
        if format == 'iso-8601':
            return parse_datetime(value)
        return super().strptime(value, format)

DateTimeField.strptime = DateTimeFieldFixed.strptime
formats.ISO_INPUT_FORMATS['DATETIME_INPUT_FORMATS'].insert(0, 'iso-8601')

解决方案 21:

如果pandas无论如何都要使用,我可以推荐。Timestamppandas那里你可以

ts_1 = pd.Timestamp('2020-02-18T04:27:58.000Z')    
ts_2 = pd.Timestamp('2020-02-18T04:27:58.000')

咆哮:令人难以置信的是,2021 年了我们仍然需要担心日期字符串解析之类的事情。

解决方案 22:

因为 ISO 8601 允许存在多种可选冒号和破折号的变体,所以基本上CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]。如果要使用 strptime,则需要先删除这些变体。

目标是生成一个 utc datetime 对象。


如果您只想要一个适用于带有 Z 后缀的 UTC 的基本情况,例如2016-06-29T19:36:29.3453Z

datetime.datetime.strptime(timestamp.translate(None, ':-'), "%Y%m%dT%H%M%S.%fZ")

如果您想要处理时区偏移,例如2016-06-29T19:36:29.3453-04002008-09-03T20:56:35.450686+05:00使用以下内容。这些将把所有变体转换为没有变量分隔符的内容,使其 20080903T205635.450686+0500更加一致/更易于解析。

import re
# this regex removes all colons and all 
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((d{2}[:]d{2})|(d{4}))$))", '', timestamp)
datetime.datetime.strptime(conformed_timestamp, "%Y%m%dT%H%M%S.%f%z" )

如果您的系统不支持%zstrptime 指令(您会看到类似 的内容ValueError: 'z' is a bad directive in format '%Y%m%dT%H%M%S.%f%z'),则需要手动偏移时间Z(UTC)。请注意,%z在 Python 版本低于 3 的系统上可能无法使用,因为它依赖于 c 库支持,而 c 库支持会因系统/Python 构建类型(即 Jython、Cython 等)而异。

import re
import datetime

# this regex removes all colons and all 
# dashes EXCEPT for the dash indicating + or - utc offset for the timezone
conformed_timestamp = re.sub(r"[:]|([-](?!((d{2}[:]d{2})|(d{4}))$))", '', timestamp)

# split on the offset to remove it. use a capture group to keep the delimiter
split_timestamp = re.split(r"[+|-]",conformed_timestamp)
main_timestamp = split_timestamp[0]
if len(split_timestamp) == 3:
    sign = split_timestamp[1]
    offset = split_timestamp[2]
else:
    sign = None
    offset = None

# generate the datetime object without the offset at UTC time
output_datetime = datetime.datetime.strptime(main_timestamp +"Z", "%Y%m%dT%H%M%S.%fZ" )
if offset:
    # create timedelta based on offset
    offset_delta = datetime.timedelta(hours=int(sign+offset[:-2]), minutes=int(sign+offset[-2:]))
    # offset datetime with timedelta
    output_datetime = output_datetime + offset_delta

解决方案 23:

如今,有了Maya: Datetimes for Humans™,它来自流行的 Requests: HTTP for Humans™ 包的作者:

>>> import maya
>>> str = '2008-09-03T20:56:35.450686Z'
>>> maya.MayaDT.from_rfc3339(str).datetime()
datetime.datetime(2008, 9, 3, 20, 56, 35, 450686, tzinfo=<UTC>)

解决方案 24:

datetime.fromisoformat()在 Python 3.11 中得到改进,可以解析大多数 ISO 8601 格式

datetime.fromisoformat()现在可用于解析大多数 ISO 8601 格式,但仅支持小数小时和分钟的格式除外。以前,此方法仅支持 datetime.isoformat() 可以发出的格式。

>>> from datetime import datetime
>>> datetime.fromisoformat('2011-11-04T00:05:23Z')
datetime.datetime(2011, 11, 4, 0, 5, 23, tzinfo=datetime.timezone.utc)
>>> datetime.fromisoformat('20111104T000523')
datetime.datetime(2011, 11, 4, 0, 5, 23)
>>> datetime.fromisoformat('2011-W01-2T00:05:23.283')
datetime.datetime(2011, 1, 4, 0, 5, 23, 283000)

解决方案 25:

如果解析无效的日期字符串,python-dateutil 将抛出异常,因此您可能需要捕获该异常。

from dateutil import parser
ds = '2012-60-31'
try:
  dt = parser.parse(ds)
except ValueError, e:
  print '"%s" is an invalid date' % ds

解决方案 26:

对于与 2.X 标准库兼容的东西,请尝试:

calendar.timegm(time.strptime(date.split(".")[0]+"UTC", "%Y-%m-%dT%H:%M:%S%Z"))

calendar.timegm 是 time.mktime 缺失的 gm 版本。

解决方案 27:

感谢伟大的Mark Amery 的回答,我设计了一个函数来解释日期时间的所有可能的 ISO 格式:

class FixedOffset(tzinfo):
    """Fixed offset in minutes: `time = utc_time + utc_offset`."""
    def __init__(self, offset):
        self.__offset = timedelta(minutes=offset)
        hours, minutes = divmod(offset, 60)
        #NOTE: the last part is to remind about deprecated POSIX GMT+h timezones
        #  that have the opposite sign in the name;
        #  the corresponding numeric value is not used e.g., no minutes
        self.__name = '<%+03d%02d>%+d' % (hours, minutes, -hours)
    def utcoffset(self, dt=None):
        return self.__offset
    def tzname(self, dt=None):
        return self.__name
    def dst(self, dt=None):
        return timedelta(0)
    def __repr__(self):
        return 'FixedOffset(%d)' % (self.utcoffset().total_seconds() / 60)
    def __getinitargs__(self):
        return (self.__offset.total_seconds()/60,)

def parse_isoformat_datetime(isodatetime):
    try:
        return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S.%f')
    except ValueError:
        pass
    try:
        return datetime.strptime(isodatetime, '%Y-%m-%dT%H:%M:%S')
    except ValueError:
        pass
    pat = r'(.*?[+-]d{2}):(d{2})'
    temp = re.sub(pat, r'', isodatetime)
    naive_date_str = temp[:-5]
    offset_str = temp[-5:]
    naive_dt = datetime.strptime(naive_date_str, '%Y-%m-%dT%H:%M:%S.%f')
    offset = int(offset_str[-4:-2])*60 + int(offset_str[-2:])
    if offset_str[0] == "-":
        offset = -offset
    return naive_dt.replace(tzinfo=FixedOffset(offset))

解决方案 28:

最初我尝试过:

from operator import neg, pos
from time import strptime, mktime
from datetime import datetime, tzinfo, timedelta

class MyUTCOffsetTimezone(tzinfo):
    @staticmethod
    def with_offset(offset_no_signal, signal):  # type: (str, str) -> MyUTCOffsetTimezone
        return MyUTCOffsetTimezone((pos if signal == '+' else neg)(
            (datetime.strptime(offset_no_signal, '%H:%M') - datetime(1900, 1, 1))
          .total_seconds()))

    def __init__(self, offset, name=None):
        self.offset = timedelta(seconds=offset)
        self.name = name or self.__class__.__name__

    def utcoffset(self, dt):
        return self.offset

    def tzname(self, dt):
        return self.name

    def dst(self, dt):
        return timedelta(0)


def to_datetime_tz(dt):  # type: (str) -> datetime
    fmt = '%Y-%m-%dT%H:%M:%S.%f'
    if dt[-6] in frozenset(('+', '-')):
        dt, sign, offset = strptime(dt[:-6], fmt), dt[-6], dt[-5:]
        return datetime.fromtimestamp(mktime(dt),
                                      tz=MyUTCOffsetTimezone.with_offset(offset, sign))
    elif dt[-1] == 'Z':
        return datetime.strptime(dt, fmt + 'Z')
    return datetime.strptime(dt, fmt)

但这在负时区时不起作用。不过,在 Python 3.7.3 中,这个方法运行良好:

from datetime import datetime


def to_datetime_tz(dt):  # type: (str) -> datetime
    fmt = '%Y-%m-%dT%H:%M:%S.%f'
    if dt[-6] in frozenset(('+', '-')):
        return datetime.strptime(dt, fmt + '%z')
    elif dt[-1] == 'Z':
        return datetime.strptime(dt, fmt + 'Z')
    return datetime.strptime(dt, fmt)

一些测试,请注意,输出仅在微秒的精度上有所不同。我的机器上精度达到 6 位,但 YMMV:

for dt_in, dt_out in (
        ('2019-03-11T08:00:00.000Z', '2019-03-11T08:00:00'),
        ('2019-03-11T08:00:00.000+11:00', '2019-03-11T08:00:00+11:00'),
        ('2019-03-11T08:00:00.000-11:00', '2019-03-11T08:00:00-11:00')
    ):
    isoformat = to_datetime_tz(dt_in).isoformat()
    assert isoformat == dt_out, '{} != {}'.format(isoformat, dt_out)
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用