Вступление
Одна из многих распространенных проблем, с которыми мы сталкиваемся при разработке программного обеспечения, - это обработка даты и времени. Например, после получения строки даты и времени из API нам необходимо преобразовать ее в удобочитаемый формат. Опять же, если один и тот же API используется в разных часовых поясах, преобразование будет другим. Хорошая библиотека даты и времени должна преобразовывать время в соответствии с часовым поясом. Это лишь один из многих нюансов, которые необходимо учитывать при работе с датами и временем.
К счастью, Python поставляется со встроенным модулем datetime
для
работы с датой и временем. Как вы, наверное, догадались, в нем есть
различные функции для управления датой и временем. Используя этот
модуль, мы можем легко проанализировать любую строку даты и времени и
преобразовать ее в объект datetime
Преобразование строк с использованием datetime
Модуль datetime
состоит из трех разных типов объектов: date
, time
и 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"
.
Они известны как токены формата . Каждый токен представляет собой
отдельную часть даты и времени, например день, месяц, год и т. Д.
Ознакомьтесь с документацией
strptime
, чтобы найти список всех различных типов кода формата, поддерживаемых в
Python. Для быстрого ознакомления вот что мы используем в приведенном
выше коде:
%Y
: год (4 цифры)%m
: месяц%d
: день месяца%H
: час (24 часа)%M
: минуты%S
: секунды%f
: микросекунды
Ожидается, что все эти токены, кроме года, будут заполнены нулями.
Итак, если известен формат строки, ее можно легко strptime
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
является объектом с учетом часового пояса .
Точно так же мы можем преобразовать строки даты и времени в любой другой часовой пояс. Например, мы можем преобразовать строку «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
в часовой пояс «Европа
/ Лондон». Оба 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
может
понять strptime. Создание этой строки требует времени и затрудняет
чтение кода. Вместо этого мы можем использовать другие сторонние
библиотеки, чтобы упростить задачу.
В некоторых случаях эти сторонние библиотеки также имеют лучшую встроенную поддержку для управления и сравнения даты и времени, а некоторые даже имеют встроенные часовые пояса, поэтому вам не нужно включать дополнительный пакет.
Давайте рассмотрим некоторые из этих библиотек в следующих разделах.
dateutil
Модуль dateutil является
расширением модуля datetime
Одно из преимуществ состоит в том, что нам
не нужно передавать код синтаксического анализа для синтаксического
анализа строки. Например:
from dateutil.parser import parse
datetime = parse('2018-06-29 22:21:41')
print(datetime)
Этот parse
функция автоматически разобрать строку и сохранить ее в
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
модуля 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_timezone
и naive
если время не
в формате UTC.
Стрела
Arrow - еще одна библиотека
для работы с datetime в Python. И, как и раньше с maya
, он также
автоматически определяет формат даты и времени. После интерпретации он
возвращает объект datetime
Python из объекта 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
Как видите, строка даты и времени преобразована в регион "Америка / Нью-Йорк".
Теперь давайте снова воспользуемся тем же набором строк, который мы использовали выше:
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, а затем при необходимости преобразовывать его в местный часовой пояс пользователя.
Эти библиотеки не только хороши для синтаксического анализа строк, но их можно использовать для множества различных типов операций, связанных с датой и временем. Я рекомендую вам просмотреть документы, чтобы подробно изучить функции.