Что найти?

Преобразование строки в datetime в Python

/
/

Одна из многих распространенных проблем, с которыми мы сталкиваемся при разработке программного обеспечения, – это обработка даты и времени. Например, после получения строки даты и времени из API нам необходимо преобразовать ее в удобочитаемый формат.

Опять же, если один и тот же API используется в разных часовых поясах, преобразование будет другим. Хорошая библиотека даты и времени должна преобразовывать время в соответствии с часовым поясом. Это лишь один из многих нюансов, которые необходимо учитывать при работе с датами и временем.

К счастью, Python поставляется со встроенным модулем datetime для работы с датой и временем. Как вы, наверное, догадались, в нем есть различные функции для управления датой и временем. Используя этот модуль, мы можем легко проанализировать любую строку даты и времени, и преобразовать ее в объект datetime.

Как преобразовать?

Модуль datetime состоит из трех разных типов объектов: даты, времени и datetime. Очевидно, что объект date содержит дату, time – время, а datetime – дату и время.

Например, следующий код напечатает текущую дату и время:

import datetime

print ('Current date/time: {}'.format(datetime.datetime.now()))

Запуск этого кода напечатает что-то похожее на это:

$ python3 datetime-print-1.py
Current date/time: 2018-06-29 08:15:27.243860

Если пользовательское форматирование не задано, используется строковый формат по умолчанию, т.е. формат для «2018-06-29 08: 15: 27.243860» находится в формате ISO 8601 (ГГГГ-ММ-ДДТЧЧ: ММ: СС.мммммм). Если наша входная строка для создания объекта datetime имеет тот же формат ISO 8601, мы можем легко преобразовать ее в объект datetime.

Давайте посмотрим на код ниже:

import datetime

date_time_str = '2018-06-29 08:15:27.243860'
date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S.%f')

print('Date:', date_time_obj.date())
print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)

Запустив его, он напечатает дату, время и дату-время:

$ python3 datetime-print-2.py
Date: 2018-06-29
Time: 08:15:27.243860
Date-time: 2018-06-29 08:15:27.243860

В этом примере мы используем новый метод под названием strptime. Этот метод принимает два аргумента: первый – это строковое представление даты и времени, а второй – формат входной строки. Указание формата, подобного этому, значительно ускоряет синтаксический анализ, поскольку datetime не нужно пытаться интерпретировать формат самостоятельно, что намного дороже в вычислительном отношении. Возвращаемое значение имеет тип datetime.

В нашем примере «2018-06-29 08: 15: 27.243860» – это входная строка, а «% Y-% m-% d% H:% M:% S.% f» – это формат нашей строки даты. Возвращаемое значение datetime сохраняется в переменной date_time_obj. Поскольку это объект datetime, мы можем вызывать методы date() и time() непосредственно на нем. Как видно из выходных данных, он печатает часть «дата» и «время» входной строки.

Вам может быть интересно, что означает формат «% Y-% m-% d% H:% M:% S.% f». Они известны как токены формата. Каждый токен представляет собой отдельную часть даты и времени, такую как день, месяц, год и т.д. Для быстрого ознакомления вот что мы используем в приведенном выше коде:

  • % Y: год (4 цифры);
  • % m: месяц;
  • % d: день месяца;
  • % H: час (24 часа);
  • % M: минуты;
  • % S: секунды;
  • % f: микросекунды.

Ожидается, что все эти токены, кроме года, будут заполнены нулями.

Итак, если известен формат строки, ее можно легко преобразовать в объект datetime с помощью strptime. Приведу еще один нетривиальный пример:

import datetime

date_time_str = 'Jun 28 2018 7:40AM'
date_time_obj = datetime.datetime.strptime(date_time_str, '%b %d %Y %I:%M%p')

print('Date:', date_time_obj.date())
print('Time:', date_time_obj.time())
print('Date-time:', date_time_obj)

Из следующего вывода вы можете видеть, что строка была успешно проанализирована, поскольку она правильно распечатывается объектом datetime:

$ python3 datetime-print-3.py
Date: 2018-06-28
Time: 07:40:00
Date-time: 2018-06-28 07:40:00

Вот еще несколько примеров часто используемых форматов времени и токенов, используемых для синтаксического анализа:

"Jun 28 2018 at 7:40AM" -> "%b %d %Y at %I:%M%p"
"September 18, 2017, 22:19:55" -> "%B %d, %Y, %H:%M:%S"
"Sun,05/12/99,12:30PM" -> "%a,%d/%m/%y,%I:%M%p"
"Mon, 21 March, 2015" -> "%a, %d %B, %Y"
"2018-03-12T10:12:45Z" -> "%Y-%m-%dT%H:%M:%SZ"

Вы можете проанализировать строку даты и времени любого формата, используя таблицу, упомянутую в документации strptime.

Работа с часовыми поясами

При работе с часовыми поясами обработка даты и времени становится более сложной. Все вышеупомянутые примеры, которые мы обсуждали, являются наивными объектами datetime, то есть эти объекты не содержат данных, связанных с часовым поясом. У объекта datetime есть одна переменная, которая содержит информацию о часовом поясе, tzinfo.

import datetime as dt

dtime = dt.datetime.now()

print(dtime)
print(dtime.tzinfo)

Этот код напечатает:

$ python3 datetime-tzinfo-1.py
2018-06-29 22:16:36.132767
None

Вывод tzinfo – None, поскольку это объект datetime. Для преобразования часового пояса для Python доступна библиотека pytz. Теперь давайте воспользуемся библиотекой, чтобы преобразовать указанную выше метку времени в UTC.

import datetime as dt
import pytz

dtime = dt.datetime.now(pytz.utc)

print(dtime)
print(dtime.tzinfo)

Вывод:

$ python3 datetime-tzinfo-2.py
2018-06-29 17:08:00.586525+00:00
UTC

+00: 00 – разница между отображаемым временем и временем UTC. В этом примере значение tzinfo также совпадает с UTC, отсюда смещение 00:00. В этом случае объект datetime является объектом с учетом часового пояса.

Точно так же мы можем преобразовать строки даты и времени в любой другой часовой пояс. Например, мы можем преобразовать строку «2018-06-29 17: 08: 00.586525 + 00: 00» в часовой пояс «America / New_York», как показано ниже:

import datetime as dt
import pytz

date_time_str = '2018-06-29 17:08:00'
date_time_obj = dt.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S')

timezone = pytz.timezone('America/New_York')
timezone_date_time_obj = timezone.localize(date_time_obj)

print(timezone_date_time_obj)
print(timezone_date_time_obj.tzinfo)

Выход:

$ python3 datetime-tzinfo-3.py
2018-06-29 17:08:00-04:00
America/New_York

Сначала мы преобразовали строку в объект datetime, date_time_obj. Затем мы преобразовали его в объект datetime с включенным часовым поясом, timezone_date_time_obj. Поскольку мы установили часовой пояс, как «America и New_York», время вывода показывает, что он на 4 часа отстает от времени UTC. Вы можете проверить эту страницу Википедии, чтобы найти полный список доступных часовых поясов.

Преобразование часовых поясов

Мы можем преобразовать часовой пояс объекта datetime из одного региона в другой, как показано в примере ниже:

import datetime as dt
import pytz

timezone_nw = pytz.timezone('America/New_York')
nw_datetime_obj = dt.datetime.now(timezone_nw)

timezone_london = pytz.timezone('Europe/London')
london_datetime_obj = nw_datetime_obj.astimezone(timezone_london)


print('America/New_York:', nw_datetime_obj)
print('Europe/London:', london_datetime_obj)

Сначала мы создали один объект datetime с текущим временем и установили его как часовой пояс «America и New_York». Затем, используя метод astimezone(), мы преобразовали это datetime в часовой пояс «Европа и Лондон». Обе даты будут печатать разные значения, например:

$ python3 datetime-tzinfo-4.py
America/New_York: 2018-06-29 22:21:41.349491-04:00
Europe/London: 2018-06-30 03:21:41.349491+01:00

Как и ожидалось, даты и время отличаются, поскольку разница между ними составляет около 5 часов.

Использование сторонних библиотек

Модуль datetime в Python может преобразовывать все разные типы строк в объект. Но основная проблема в том, что для этого вам нужно создать соответствующую строку кода форматирования, которую может понять strptime. Создание этой строки требует времени и затрудняет чтение кода. Вместо этого мы можем использовать другие сторонние библиотеки, чтобы упростить задачу.

В некоторых случаях эти сторонние библиотеки также имеют лучшую встроенную поддержку для управления и сравнения даты и времени, а некоторые даже имеют встроенные часовые пояса, поэтому вам не нужно включать дополнительный пакет.

Давайте рассмотрим некоторые из этих библиотек в следующих разделах.

dateutil

Модуль dateutil является расширением модуля datetime. Одно из преимуществ состоит в том, что нам не нужно передавать код синтаксического анализа. Например:

from dateutil.parser import parse

datetime = parse('2018-06-29 22:21:41')

print(datetime)

Эта функция синтаксического анализа автоматически проанализирует строку и сохранит ее в переменной datetime. Парсинг выполняется автоматически. Вам не нужно указывать какую-либо строку формата. Попробуем разобрать разные типы строк с помощью dateutil:

from dateutil.parser import parse

date_array = [
    '2018-06-29 08:15:27.243860',
    'Jun 28 2018 7:40AM',
    'Jun 28 2018 at 7:40AM',
    'September 18, 2017, 22:19:55',
    'Sun, 05/12/1999, 12:30PM',
    'Mon, 21 March, 2015',
    '2018-03-12T10:12:45Z',
    '2018-06-29 17:08:00.586525+00:00',
    '2018-06-29 17:08:00.586525+05:00',
    'Tuesday , 6th September, 2017 at 4:30pm'
]

for date in date_array:
    print('Parsing: ' + date)
    dt = parse(date)
    print(dt.date())
    print(dt.time())
    print(dt.tzinfo)
    print('\n')

Вывод:

$ python3 dateutil-1.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29
08:15:27.243860
None

Parsing: Jun 28 2018 7:40AM
2018-06-28
07:40:00
None

Parsing: Jun 28 2018 at 7:40AM
2018-06-28
07:40:00
None

Parsing: September 18, 2017, 22:19:55
2017-09-18
22:19:55
None

Parsing: Sun, 05/12/1999, 12:30PM
1999-05-12
12:30:00
None

Parsing: Mon, 21 March, 2015
2015-03-21
00:00:00
None

Parsing: 2018-03-12T10:12:45Z
2018-03-12
10:12:45
tzutc()

Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29
17:08:00.586525
tzutc()

Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29
17:08:00.586525
tzoffset(None, 18000)

Parsing: Tuesday , 6th September, 2017 at 4:30pm
2017-09-06
16:30:00
None

Вы можете видеть, что практически любой тип строки можно легко проанализировать с помощью модуля dateutil.

Хотя это удобно, вспомните из предыдущего, что необходимость предсказывать формат делает код намного медленнее, поэтому, если ваш код требует высокой производительности, это может быть неправильным подходом для вашего приложения.

Maya

Maya также упрощает синтаксический анализ строки и изменение часовых поясов. Здесь показаны несколько простых примеров:

import maya

dt = maya.parse('2018-04-29T17:45:25Z').datetime()
print(dt.date())
print(dt.time())
print(dt.tzinfo)

Вывод:

$ python3 maya-1.py
2018-04-29
17:45:25
UTC

Для преобразования времени в другой часовой пояс:

import maya

dt = maya.parse('2018-04-29T17:45:25Z').datetime(to_timezone='America/New_York', naive=False)
print(dt.date())
print(dt.time())
print(dt.tzinfo)

Вывод:

$ python3 maya-2.py
2018-04-29
13:45:25
America/New_York

Разве не так просто использовать? Давайте попробуем Maya с тем же набором строк, который мы использовали с dateutil:

import maya

date_array = [
    '2018-06-29 08:15:27.243860',
    'Jun 28 2018 7:40AM',
    'Jun 28 2018 at 7:40AM',
    'September 18, 2017, 22:19:55',
    'Sun, 05/12/1999, 12:30PM',
    'Mon, 21 March, 2015',
    '2018-03-12T10:12:45Z',
    '2018-06-29 17:08:00.586525+00:00',
    '2018-06-29 17:08:00.586525+05:00',
    'Tuesday , 6th September, 2017 at 4:30pm'
]

for date in date_array:
    print('Parsing: ' + date)
    dt = maya.parse(date).datetime()
    print(dt)
    print(dt.date())
    print(dt.time())
    print(dt.tzinfo)

Вывод:

$ python3 maya-3.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29 08:15:27.243860+00:00
2018-06-29
08:15:27.243860
UTC

Parsing: Jun 28 2018 7:40AM
2018-06-28 07:40:00+00:00
2018-06-28
07:40:00
UTC

Parsing: Jun 28 2018 at 7:40AM
2018-06-28 07:40:00+00:00
2018-06-28
07:40:00
UTC

Parsing: September 18, 2017, 22:19:55
2017-09-18 22:19:55+00:00
2017-09-18
22:19:55
UTC

Parsing: Sun, 05/12/1999, 12:30PM
1999-05-12 12:30:00+00:00
1999-05-12
12:30:00
UTC

Parsing: Mon, 21 March, 2015
2015-03-21 00:00:00+00:00
2015-03-21
00:00:00
UTC

Parsing: 2018-03-12T10:12:45Z
2018-03-12 10:12:45+00:00
2018-03-12
10:12:45
UTC

Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29 17:08:00.586525+00:00
2018-06-29
17:08:00.586525
UTC

Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29 12:08:00.586525+00:00
2018-06-29
12:08:00.586525
UTC

Parsing: Tuesday , 6th September, 2017 at 4:30pm
2017-09-06 16:30:00+00:00
2017-09-06
16:30:00
UTC

Как видите, все форматы даты были успешно проанализированы.

Но вы заметили разницу? Если мы не предоставляем информацию о часовом поясе, он автоматически преобразует ее в UTC. Итак, важно отметить, что мы должны предоставить параметры to_timezone и naive, если время не в формате UTC.

Arrow

Arrow – еще одна библиотека для работы с datetime в Python. И, как и раньше с Maya, он также автоматически определяет формат даты и времени. После интерпретации он возвращает объект даты и времени в Python из объекта стрелки.

Давайте попробуем это с той же примерной строкой, которую мы использовали для Maya:

import arrow

dt = arrow.get('2018-04-29T17:45:25Z')
print(dt.date())
print(dt.time())
print(dt.tzinfo)

Вывод:

$ python3 arrow-1.py
2018-04-29
17:45:25
tzutc()

А вот как можно использовать стрелку для преобразования часовых поясов с помощью метода to:

import arrow

dt = arrow.get('2018-04-29T17:45:25Z').to('America/New_York')
print(dt)
print(dt.date())
print(dt.time())

Вывод:

$ python3 arrow-2.py
2018-04-29T13:45:25-04:00
2018-04-29
13:45:25

Как видите, строка даты и времени преобразована в регион «Америка и Нью-Йорк».

Теперь давайте снова воспользуемся тем же набором строк, который мы использовали выше:

import arrow

date_array = [
    '2018-06-29 08:15:27.243860',
    #'Jun 28 2018 7:40AM',
    #'Jun 28 2018 at 7:40AM',
    #'September 18, 2017, 22:19:55',
    #'Sun, 05/12/1999, 12:30PM',
    #'Mon, 21 March, 2015',
    '2018-03-12T10:12:45Z',
    '2018-06-29 17:08:00.586525+00:00',
    '2018-06-29 17:08:00.586525+05:00',
    #'Tuesday , 6th September, 2017 at 4:30pm'
]

for date in date_array:
    dt = arrow.get(date)
    print('Parsing: ' + date)
    print(dt)
    print(dt.date())
    print(dt.time())
    print(dt.tzinfo)

Этот код завершится ошибкой для закомментированных строк даты и времени, что составляет более половины наших примеров. Результатом для других строк будет:

$ python3 arrow-3.py
Parsing: 2018-06-29 08:15:27.243860
2018-06-29T08:15:27.243860+00:00
2018-06-29
08:15:27.243860
tzutc()

Parsing: 2018-03-12T10:12:45Z
2018-03-12T10:12:45+00:00
2018-03-12
10:12:45
tzutc()

Parsing: 2018-06-29 17:08:00.586525+00:00
2018-06-29T17:08:00.586525+00:00
2018-06-29
17:08:00.586525
tzoffset(None, 0)

Parsing: 2018-06-29 17:08:00.586525+05:00
2018-06-29T17:08:00.586525+05:00
2018-06-29
17:08:00.586525
tzoffset(None, 18000)

Чтобы правильно проанализировать строки даты и времени, вам необходимо передать соответствующие токены формата, чтобы дать библиотеке подсказки относительно того, как их анализировать. Например, «MMM» для названия месяцев, например «Jan, Feb, Mar» и т.д. вы можете проверить это руководство для всех доступных токенов.

Заключение

В этой статье мы показали различные способы синтаксического анализа строки в объект datetime в Python. Вы можете выбрать библиотеку datetime в Python по умолчанию или любую из сторонних библиотек, упомянутых в этой статье, среди многих других.

Основная проблема с пакетом datetime по умолчанию заключается в том, что нам нужно вручную указать код синтаксического анализа почти для всех форматов строк даты и времени. Итак, если ваш строковый формат изменится в будущем, вам, вероятно, также придется изменить свой код. Но многие сторонние библиотеки, такие как упомянутые здесь, обрабатывают это автоматически.

Еще одна проблема, с которой мы сталкиваемся, – это часовые пояса. Лучший способ справиться с ними – всегда сохранять время в базе данных в формате UTC, а затем при необходимости преобразовывать его в местный часовой пояс пользователя.

Эти библиотеки не только хороши для синтаксического анализа строк, но их можно использовать для множества различных типов операций, связанных с датой и временем.

Оставить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

This div height required for enabling the sticky sidebar