Вопрос-Ответ

How do I parse an ISO 8601-formatted date and time?

Как мне проанализировать дату и время в формате 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, за следующими исключениями:



  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().


Ответ 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.

python datetime