28 июня 2017 г.

Fast Python. Парсинг ISO дат

Преобразование ISO-даты из строки в объект datetime.datetime (или datetime.date), наверное, одна из самых распространенных и постоянных задач в web-разработке на Python. Количество способов сделать это просто поражает воображение,

In [1]: value = '2017-06-28T16:59:27+0000'

In [2]: import datetime

In [3]: datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S%z')
Out[3]: datetime.datetime(2017, 6, 28, 16, 59, 27, tzinfo=datetime.timezone.utc)

In [4]: from dateutil.parser import parse

In [5]: parse(value)
Out[5]: datetime.datetime(2017, 6, 28, 16, 59, 27, tzinfo=tzutc())

In [6]: import dateparser

In [7]: dateparser.parse(value)
Out[7]: datetime.datetime(2017, 6, 28, 16, 59, 27, tzinfo=)

Использовались функции: datetime.datetime.strptime, dateutil.parser.parse и dateparser.parse.

А ведь еще есть,

In [8]: import arrow

In [9]: arrow.get(value, 'YYYY-MM-DDTHH:mm:ssZZ')
Out[9]: <Arrow [2017-06-28T16:59:27+00:00]>

In [10]: import maya

In [11]: maya.parse(value)
Out[11]: <MayaDT epoch=1498669167.0>

-//-: arrow.get и maya.parse.

И это только вершина айсберга, потому что библиотек для работ с датами в Python не просто много, а очень много (что говорить, если у Django вообще есть своя parse_datetime функция, основанная на регекспе). У той или иной библиотеки работы с датами есть свои плюсы и особенности, но что делать, если нужно быстро конвертировать строки с датами в стандартные объекты datetime.datetime? Как всегда ответ прост: использовать что-то написанное на C с биндингами в Python, в нашем случае - это ciso8601,

In [12]: ciso8601.parse_datetime(value)
Out[12]: datetime.datetime(2017, 6, 28, 16, 59, 27, tzinfo=)

Результаты %timeit - красноречивы,

In [13]: %timeit datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S%z')
17.9 µs ± 516 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [14]: %timeit parse(value)
99 µs ± 1.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [15]: %timeit dateparser.parse(value)
1.61 ms ± 39.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [16]: %timeit ciso8601.parse_datetime(value)
2.08 µs ± 67.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Итого: Если нужно быстро парсить ISO даты: используйте ciso8601.parse_datetime, если нужно быстро парсить не ISO даты: сначала попробуйте стандартный datetime.datetime.strptime, потом dateutil.parser.parse и только в случае особой и острой необходимости dateparser.parse и прочие библиотеки, основанные на нем.

ps. Результаты приведены для Python 3.6.1, на Python 2.7.13 соблюдаются такие же показатели, но в 2.7.13 в стандартном strptime не было возможности разобрать +0000, потому что ValueError: 'z' is a bad directive in format '%Y-%m-%dT%H:%M:%S%z'.

blog comments powered by Disqus