Рубрики
Без рубрики

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

В этом уроке мы будем преобразовывать строки в datetime в Python, имея дело с часовыми поясами. Мы также будем использовать dateutil, Maya и Arrow для преобразования строк в datetime.

Автор оригинала: N Kaushik.

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

Вступление

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

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

Преобразование Строк С Использованием datetime

Модуль datetime состоит из трех различных типов объектов: date , time и date time . Очевидно, что объект data содержит дату, 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 (ГГГГ-ММ-DDTHH:ММ:SS.mmmmmm). Если наша входная строка для создания объекта 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" . Они известны как токены формата . Каждый маркер представляет собой различную часть даты-времени, такую как день, месяц, год и т. Д. Ознакомьтесь с документацией strptime для получения списка всех различных типов кода формата, поддерживаемых в Python. Для краткой справки вот что мы используем в приведенном выше коде:

  • %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 . Вы можете установить его, как описано в этих инструкциях . Теперь давайте воспользуемся библиотекой 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 является объектом timezone-aware .

Точно так же мы можем преобразовать строки даты и времени в любой другой часовой пояс. Например, мы можем преобразовать строку “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 . Затем мы преобразовали его в объект timezone-enabled datetime , time zone_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 в часовой пояс “Европа/Лондон”. Оба datetime s будут печатать разные значения, такие как:

$ 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 часах друг от друга.

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

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

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

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

dateutil

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

from dateutil.parser import parse

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

print(datetime)

Эта функция parse автоматически проанализирует строку и сохранит ее в переменной 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 также позволяет очень легко анализировать строку и изменять часовые пояса. Здесь приведены несколько простых примеров:

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_time zone и naive параметры, если время не находится в UTC.

Стрела

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

Давайте попробуем это сделать с тем же примером строки, который мы использовали для 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()

А вот как вы можете использовать arrow для преобразования часовых поясов с помощью метода 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

Как вы можете видеть, строка даты и времени преобразуется в регион “America/New_York”.

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

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)

Чтобы правильно разобрать строки даты и времени, которые я закомментировал, вам нужно будет передать соответствующие токены формата, чтобы дать библиотеке подсказки о том, как их разобрать. Например, “МММ” для названия месяцев, например “Январь, февраль, март” и т. Д. Вы можете проверить это руководство на наличие всех доступных токенов.

Вывод

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

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

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

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