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, он, предположительно, менее строгий, но оба они довольно снисходительны и попытаются интерпретировать строку, которую вы передаете. Если вы хотите исключить возможность каких-либо неправильных прочтений, вам нужно использовать что-то более строгое, чем любая из этих функций.
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 или более ранней версии для строк, которые не попадают в это подмножество (как в вопросе), смотрите другие ответы для функций вне стандартной библиотеки. В документах:
Начиная с версии Python 3.7, вы можете в принципе (предостережения ниже) обойтись без использования datetime.datetime.strptime для анализа даты и времени RFC 3339, вот так:
from datetime import datetime
defparse_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.