Проверить строку даты-времени ISO-8601 в Python?
Я хочу написать функцию, которая принимает строку и возвращает True
если это действительная дата-время ISO-8601 - с точностью до микросекунд, включая смещение часового пояса--False
иначе.
Я нашел другие вопросы, которые обеспечивают различные способы анализа строк даты и времени, но я хочу вернуться True
только в случае формата ISO-8601. Разбор мне не поможет, если я не смогу выдать ошибку для форматов, которые не соответствуют ISO-8601.
(Я использую красивую библиотеку стрелок в другом месте моего кода. Решение, которое использует arrow
будет приветствоваться.)
РЕДАКТИРОВАТЬ: Похоже, что общего решения "является ли эта строка допустимым дата-время ISO 8601" не существует среди распространенных пакетов даты-времени Python.
Итак, чтобы сделать этот вопрос более узким, конкретным и ответственным, я остановлюсь на строке формата, которая проверит строку даты и времени в этой форме:
'2016-12-13T21:20:37.593194+00:00'
В настоящее время я использую:
format_string = '%Y-%m-%dT%H:%M:%S.%f%z'
datetime.datetime.strptime(my_timestamp, format_string)
Это дает:
ValueError: time data '2016-12-13T21:20:37.593194+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%f%z'
Проблема, кажется, лежит с двоеточием в смещении UTC (+00:00
). Если я использую смещение без двоеточия (например, '2016-12-13T21:20:37.593194+0000'
), это анализирует должным образом, как и ожидалось. Это видимо потому что datetime
"s %z
токен не относится к форме смещения UTC, в которой есть двоеточие, только к форме без, даже если оба действительны в соответствии со спецификацией.
3 ответа
Последние версии Python (начиная с 3.7) имеют fromisoformat()
функция в datetime
стандартная библиотека. См. https://docs.python.org/3.7/library/datetime.html
Итак, это поможет:
from datetime import datetime
def datetime_valid(dt_str):
try:
datetime.fromisoformat(dt_str)
except:
return False
return True
Обновить:
Я узнал, что Python не распознает суффикс Z как действительный. Поскольку я хотел поддержать это в своем API, теперь я использую:
from datetime import datetime
def datetime_valid(dt_str):
try:
datetime.fromisoformat(dt_str)
except:
try:
datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
except:
return False
return True
return True
предоставить множество вариантов для проверки даты и времени в формате ISO8601 (например, 2008-08-30T01:45:36 или 2008-08-30T01:45:36.123Z). Регулярное выражение для типа XML-схемы dateTime имеет вид:
>>> regex = r'^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$'
Таким образом, для проверки вы можете сделать:
>>> import re
>>> match_iso8601 = re.compile(regex).match
>>> def validate_iso8601(str_val):
... try:
... if match_iso8601( str_val ) is not None:
... return True
... except:
... pass
... return False
Некоторые примеры:
>>> validate_iso8601('2017-01-01')
False
>>> validate_iso8601('2008-08-30T01:45:36.123Z')
True
>>> validate_iso8601('2016-12-13T21:20:37.593194+00:00')
True
Вот грубое, но функциональное решение (для более узкого вопроса), использующее datetime.strptime()
:
import datetime
def is_expected_datetime_format(timestamp):
format_string = '%Y-%m-%dT%H:%M:%S.%f%z'
try:
colon = timestamp[-3]
if not colon == ':':
raise ValueError()
colonless_timestamp = timestamp[:-3] + timestamp[-2:]
datetime.datetime.strptime(colonless_timestamp, format_string)
return True
except ValueError:
return False
Учитывая ограничения, которые вы наложили на проблему, вы можете легко решить ее с помощью регулярного выражения.
>>> import re
>>> re.match(r'^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{6}[+-]\d\d:\d\d$', '2016-12-13T21:20:37.593194+00:00')
<_sre.SRE_Match object; span=(0, 32), match='2016-12-13T21:20:37.593194+00:00'>
Если вам нужно передать все варианты ISO 8601, это будет гораздо более сложное регулярное выражение, но это все же можно сделать. Если вам также необходимо проверить числовые диапазоны, например, проверить, что час находится в диапазоне от 0 до 23, вы можете поместить круглые скобки в регулярное выражение, чтобы создать группы совпадений, а затем проверить каждую группу.
In [1] import dateutil.parser as dp
In [2]: import re
...: def validate_iso8601_us(str_val):
...: try:
...: dp.parse(str_val)
...: if re.search('\.\d\d\d\d\d\d',str_val):
...: return True
...: except:
...: pass
...: return False
...:
In [3]: validate_iso8601_us('2019/08/15T16:03:5.12345')
Out[3]: False
In [4]: validate_iso8601_us('2019/08/15T16:03:5.123456')
Out[4]: True
In [5]: validate_iso8601_us('2019/08/15T16:03:5.123456+4')
Out[5]: True
In [6]: validate_iso8601_us('woof2019/08/15T16:03:5.123456+4')
Out[6]: False