Как мне проанализировать дату и время в формате ISO 8601?
Мне нужно разобрать строки RFC 3339 типа "2008-09-03T20:56:35.450686Z"
в типе Python datetime
.
Я нашел strptime
в стандартной библиотеке Python, но это не очень удобно.
Как лучше всего это сделать?
Переведено автоматически
Ответ 1
isoparse
функция из python-dateutil
В пакете python-dateutil есть dateutil.parser.isoparse
для анализа не только строк даты и времени RFC 3339, подобных приведенной в вопросе, но и других строк даты и времени ISO 8601, которые не соответствуют RFC 3339 (например, без смещения 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 fromisoformat
значение ≤ 3.10 намеренно отсутствует. В Python 3.11, fromisoformat
поддерживает почти все строки в допустимом ISO 8601. Смотрите fromisoformat
документы для этого предостережения. (См. Этот ответ).
Ответ 2
Начиная с Python 3.11, стандартная библиотека datetime.fromisoformat
поддерживает любые допустимые входные данные ISO 8601. В более ранних версиях он анализирует только определенное подмножество, см. Предостережение в документации. Если вы используете Python 3.10 или более ранней версии для строк, которые не попадают в это подмножество (как в вопросе), смотрите другие ответы для функций вне стандартной библиотеки. В документах:
метод класса
datetime.fromisoformat(date_string)
:Возвращает a,
datetime
соответствующий date_string в любом допустимом формате ISO 8601, за следующими исключениями:
- Смещения часовых поясов могут составлять доли секунды.
T
Разделитель может быть заменен любым символом unicode.- Порядковые даты в настоящее время не поддерживаются.
- Дробные часы и минуты не поддерживаются.
Примеры:
>>> 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().
Ответ 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, но также хотите строго проверить, что получаемое вами datetime соответствует 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
.