Введение
Примеры
Как спарсить строку в datetime с часовым поясом
Python 3.2+ поддерживает формат %z
при разборе строки в объекте datetime
.
UTC в форме +HHMM
или -HHMM
(пустая строка, если объект не содержит информации о временной зоне).
Python 3.x 3.2
import datetimedt = datetime.datetime.strptime("2016-04-15T08:27:18-0500", "%Y-%m-%dT%H:%M:%S%z")
Для других версий Python можно использовать внешнюю библиотеку, такую как dateutil
, которая проанализирует текст в объект datetime
.
import dateutil.parserdt = dateutil.parser.parse("2016-04-15T08:27:18-0500")
Теперь переменная dt
представляет собой объект datetime
со следующим значением:
datetime.datetime(2016, 4, 15, 8, 27, 18, tzinfo=tzoffset(None, -18000))
Простые арифметические операции с датами
Даты не существуют изолированно. Обычно нужно найти количество времени между датами или определить, какая дата будет завтра. Это можно сделать с помощью объектов timedelta
:
import datetimetoday = datetime.date.today()print('Today:', today)yesterday = today - datetime.timedelta(days=1)print('Yesterday:', yesterday)tomorrow = today + datetime.timedelta(days=1)print('Tomorrow:', tomorrow)print('Time between tomorrow and yesterday:', tomorrow - yesterday)
Получится:
Today: 2016-04-15Yesterday: 2016-04-14Tomorrow: 2016-04-16Difference between tomorrow and yesterday: 2 days, 0:00:00
Использование базовых объектов
Модуль datetime
содержит три основных типа объектов: отдельно дату, отдельно время и дату вместе со временем.
import datetime# Date objecttoday = datetime.date.today()new_year = datetime.date(2017, 01, 01) #datetime.date(2017, 1, 1)# Time objectnoon = datetime.time(12, 0, 0) #datetime.time(12, 0)# Current datetimenow = datetime.datetime.now()# Datetime objectmillenium_turn = datetime.datetime(2000, 1, 1, 0, 0, 0) #datetime.datetime(2000, 1, 1, 0, 0)
Арифметические операции для этих объектов поддерживаются только для одного и того же типа данных. Если выполнять арифметические действия с разными типами объектов, то это приведет к ошибке TypeError
.
# subtraction of noon from todaynoon-todayTraceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: unsupported operand type(s) for -: 'datetime.time' and 'datetime.date'However, it is straightforward to convert between types.# Do this insteadprint('Time since the millenium at midnight:', datetime.datetime(today.year, today.month, today.day) - millenium_turn)# Or thisprint('Time since the millenium at noon:', datetime.datetime.combine(today, noon) - millenium_turn)
Итерация по датам
Если нужно перебрать диапазон дат от даты начала до некоторой даты окончания, то можно использовать библиотеку datetime
и объект timedelta
:
import datetime# The size of each step in daysday_delta = datetime.timedelta(days=1)start_date = datetime.date.today()end_date = start_date + 7*day_deltafor i in range((end_date - start_date).days): print(start_date + i*day_delta)
Получится:
2016-07-212016-07-222016-07-232016-07-242016-07-252016-07-262016-07-27
Спарсить строку с двухбуквенным часовым поясом и перевести в UTC
Используя библиотеку dateutil
можно парсить временные метки с коротким буквенным именем часового пояса.
Для дат, с короткими названиями зон или аббревиатурами, которые неоднозначны или недоступны в стандартной базе данных, необходимо указать возможные буквенные обозначения между аббревиатурой часового пояса и объектом tzinfo
.
Например, CST. Это может быть Центральным стандартным временем, Стандартным временем в Китае, Стандартным временем Кубы и т.д. Полный список можно посмотреть здесь.
from dateutil import tzfrom dateutil.parser import parseET = tz.gettz('US/Eastern')CT = tz.gettz('US/Central')MT = tz.gettz('US/Mountain')PT = tz.gettz('US/Pacific')us_tzinfos = {'CST': CT, 'CDT': CT, 'EST': ET, 'EDT': ET, 'MST': MT, 'MDT': MT, 'PST': PT, 'PDT': PT}dt_est = parse('2014-01-02 04:00:00 EST', tzinfos=us_tzinfos)dt_pst = parse('2016-03-11 16:00:00 PST', tzinfos=us_tzinfos)
Дальнейшие действия:
dt_est# datetime.datetime(2014, 1, 2, 4, 0, tzinfo=tzfile('/usr/share/zoneinfo/US/Eastern'))dt_pst# datetime.datetime(2016, 3, 11, 16, 0, tzinfo=tzfile('/usr/share/zoneinfo/US/Pacific'))
При использовании с этим методом часового пояса pytz
— часовой пояс правильно не локализуется:
from dateutil.parser import parseimport pytzEST = pytz.timezone('America/New_York')dt = parse('2014-02-03 09:17:00 EST', tzinfos={'EST': EST})
Привязка часового пояса pytz
к дате и времени:
dt.tzinfo # Will be in Local Mean Time!# <DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>
Если вы используете этот метод, вы должны повторно использовать localize
для простых значений даты и времени:
dt_fixed = dt.tzinfo.localize(dt.replace(tzinfo=None))dt_fixed.tzinfo # Now it's EST.# <DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>)
Создание даты и времени в разных часовых поясах
Все объекты datetime
по умолчанию не относятся ни к одному часовому поясу. Чтобы привязать их к определенному часовому поясу — им нужно назначить объект tzinfo
, который привязывает функцию даты и времени к стандарту UTC.
Пересчёт часовых поясов
Для часовых поясов, которые фиксировано смещаются относительно UTC, в Python 3.2+ в модуле datetime
есть класс timezone
в котором реализован tzinfo
, принимающий параметры timedelta
и имя (необязательно):
Python 3.x 3.2
from datetime import datetime, timedelta, timezoneJST = timezone(timedelta(hours=+9))dt = datetime(2015, 1, 1, 12, 0, 0, tzinfo=JST)print(dt)# 2015-01-01 12:00:00+09:00print(dt.tzname())# UTC+09:00dt = datetime(2015, 1, 1, 12, 0, 0, tzinfo=timezone(timedelta(hours=9), 'JST'))print(dt.tzname)# 'JST'
Для Python-версий ниже 3.2 необходимо использовать стороннюю библиотеку. Например, dateutil
. dateutil
предоставляет эквивалентный класс tzoffset
, который (tzoffset с версии 2.5.3) принимает аргументы вида dateutil.tz.tzoffset(tzname, offset)
, где offset
указано в секундах:
Python 3.x 3.2
Python 2.x 2.7
from datetime import datetime, timedeltafrom dateutil import tzJST = tz.tzoffset('JST', 9 * 3600) # 3600 seconds per hourdt = datetime(2015, 1, 1, 12, 0, tzinfo=JST)print(dt)# 2015-01-01 12:00:00+09:00print(dt.tzname)# 'JST'
Часовые пояса с переходом на летнее время
Для часовых зон с летним временем в стандартных библиотеках python нет стандартных классов. Поэтому нужно использовать стороннюю библиотеку. Есть pytz
и dateutil
— популярные библиотеки, в которых представлены классы часовых поясов.
Помимо стандартных часовых поясов, в dateutil
есть классы часовых поясов, в которых используется летнее время (см. Документацию для модуля tz ). Можете использовать метод tz.gettz()
для получения объекта часового пояса, который затем можно передать непосредственно в datetime
:
from datetime import datetimefrom dateutil import tzlocal = tz.gettz() # Local timePT = tz.gettz('US/Pacific') # Pacific timedt_l = datetime(2015, 1, 1, 12, tzinfo=local) # I am in ESTdt_pst = datetime(2015, 1, 1, 12, tzinfo=PT)dt_pdt = datetime(2015, 7, 1, 12, tzinfo=PT) # DST is handled automaticallyprint(dt_l)# 2015-01-01 12:00:00-05:00print(dt_pst)# 2015-01-01 12:00:00-08:00print(dt_pdt)# 2015-07-01 12:00:00-07:00
ВНИМАНИЕ. Начиная с версии 2.5.3, dateutil
обрабатывает неоднозначные даты и всегда будет назначен по умолчанию для более поздней даты. Невозможно создать объект dateutil
с текущим часовым поясом, например, 2015-11-01 1:30 EDT-4
, так как это происходит во время перехода на летнее время.
В крайних случаях при использовании pytz
это можно сделать вручную, но в pytz
часовые пояса не должны быть непосредственно привязаны через конструктор. Вместо этого временная зона pytz
должна быть присоединена к часовому поясу с использованием метода localize:
from datetime import datetime, timedeltaimport pytzPT = pytz.timezone('US/Pacific')dt_pst = PT.localize(datetime(2015, 1, 1, 12))dt_pdt = PT.localize(datetime(2015, 11, 1, 0, 30))print(dt_pst)# 2015-01-01 12:00:00-08:00print(dt_pdt)# 2015-11-01 00:30:00-07:00
Если вы рассчитываете дату и время в часовом поясе pytz
— вы должны либо выполнить вычисления для UTC (чтобы получить абсолютное истекшее время), либо используйте normalize()
:
dt_new = dt_pdt + timedelta(hours=3) # This should be 2:30 AM PSTprint(dt_new)# 2015-11-01 03:30:00-07:00dt_corrected = PT.normalize(dt_new)print(dt_corrected)# 2015-11-01 02:30:00-08:00
Извлечение даты и времени из текста (неопределенный анализ даты и времени)
Можно извлечь дату из текста, используя синтаксический анализатор dateutil
в режиме «fuzzy», в котором компоненты строки, не идентифицирующиеся как часть даты, игнорируются.
from dateutil.parser import parsedt = parse("Today is January 1, 2047 at 8:21:00AM", fuzzy=True)print(dt)
dt
теперь является объектом datetime
и вы увидите datetime.datetime(2047, 1, 1, 8, 21)
.
Переключение между часовыми поясами
Чтобы переключаться между часовыми поясами, вам нужны объекты datetime
, в которых есть информация о часовом поясе.
from datetime import datetimefrom dateutil import tzutc = tz.tzutc()local = tz.tzlocal()utc_now = datetime.utcnow()utc_now # Not timezone-aware.utc_now = utc_now.replace(tzinfo=utc)utc_now # Timezone-aware.local_now = utc_now.astimezone(local)local_now # Converted to local time.
Спарсить произвольный timestamp в ISO 8601 с минимумом подключаемых библиотек
У Python ограниченная поддержка анализа временных меток ISO 8601. Для того, чтобы использовать strptime
— вам нужно точно знать его формат. Сложность в том, что в datetime
с временной меткой ISO 8601 есть 6-разрядная дробь:
str(datetime.datetime(2016, 7, 22, 9, 25, 59, 555555))# '2016-07-22 09:25:59.555555'
Если дробная часть равна 0, то она не выводится:
str(datetime.datetime(2016, 7, 22, 9, 25, 59, 0))# '2016-07-22 09:25:59'
Но для этих двух вариантов используется другой формат для strptime
.Кроме того, strptime
не поддерживает ни минуты, ни обозначения часовых поясов с минутами. Например, 2016-07-22 09: 25: 59 + 0300 проанализируется, а стандартный формат вида 2016-07-22 09:25:59 +03: 00
нет.
Существует библиотека из одного файла — iso8601
, которая правильно анализирует только временные метки ISO 8601.
Он поддерживает дроби, часовые пояса и T разделитель внутри единственной функции:
import iso8601iso8601.parse_date('2016-07-22 09:25:59')# datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<iso8601.Utc>)iso8601.parse_date('2016-07-22 09:25:59+03:00')# datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<FixedOffset '+03:00' ...>)iso8601.parse_date('2016-07-22 09:25:59Z')# datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<iso8601.Utc>)iso8601.parse_date('2016-07-22T09:25:59.000111+03:00')# datetime.datetime(2016, 7, 22, 9, 25, 59, 111, tzinfo=<FixedOffset '+03:00' ...>)
Если часовой пояс не установлен, значение iso8601.parse_date
соответствует UTC. Зона по умолчанию может быть изменена с помощью ключевого слова default_zone
. Если вместо значения по умолчанию передаётся None
, то тогда те значения даты и времени, у которых нет определенного часового значения, устанавливаются вместо простых значений даты и времени:
iso8601.parse_date('2016-07-22T09:25:59', default_timezone=None)# datetime.datetime(2016, 7, 22, 9, 25, 59)iso8601.parse_date('2016-07-22T09:25:59Z', default_timezone=None)# datetime.datetime(2016, 7, 22, 9, 25, 59, tzinfo=<iso8601.Utc>)
Преобразование timestamp в datetime
Модуль datetime
может преобразовать метку POSIX timestamp
в объект ITC datetime
.
Эпоха: 1 января 1970 года, полночь.
import timefrom datetime import datetimeseconds_since_epoch=time.time() #1469182681.709utc_date=datetime.utcfromtimestamp(seconds_since_epoch) # datetime.datetime(2016, 7, 22, 10, 18, 1, 709000)
Вычисление количества месяцев с даты
Использование модуля calendar
import calendarfrom datetime import datedef monthdelta(date, delta): m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12 if not m: m = 12 d = min(date.day, calendar.monthrange(y, m)[1]) return date.replace(day=d,month=m, year=y)next_month = monthdelta(date.today(), 1) #datetime.date(2016, 10, 23)
Использование модуля dateutils
import datetimeimport dateutil.relativedeltad = datetime.datetime.strptime("2013-03-31", "%Y-%m-%d")d2 = d - dateutil.relativedelta.relativedelta(months=1) #datetime.datetime(201)
Вычисление разницы во времени
Модуль timedelta
пригодится для вычисления разницы во времени:
from datetime import datetime, timedeltanow = datetime.now()then = datetime(2016, 5, 23) # datetime.datetime(2016, 05, 23, 0, 0, 0)
При создании нового объекта datetime
указывать время необязательно.
delta = now - then
delta
— это тип timedelta
print(delta.days)# 60print(delta.seconds)# 40826
Чтобы получить n дней после даты:
def get_n_days_after_date(date_format="%d %B %Y", add_days=120): date_n_days_after = datetime.datetime.now() + timedelta(days=add_days) return date_n_days_after.strftime(date_format)
Чтобы получить n дней до даты:
def get_n_days_before_date(self, date_format="%d %B %Y", days_before=120): date_n_days_ago = datetime.datetime.now() - timedelta(days=days_before) return date_n_days_ago.strftime(date_format)
Получение временной метки ISO 8601
Без часового пояса с микросекундами
from datetime import datetimedatetime.now().isoformat()# Out: '2016-07-31T23:08:20.886783'
С часовым поясом и с микросекундами
from datetime import datetimefrom dateutil.tz import tzlocaldatetime.now(tzlocal()).isoformat()# Out: '2016-07-31T23:09:43.535074-07:00'
С часовым поясом без микросекунд
from datetime import datetimefrom dateutil.tz import tzlocaldatetime.now(tzlocal()).replace(microsecond=0).isoformat()# Out: '2016-07-31T23:10:30-07:00'
Синтаксис
Параметры
Примечания
Python предусматривает наличие встроенных методов и внешних библиотек для создания, модификации, разбора и управления датой и временем.