Работа с датой и временем в Python

Введение

Примеры

Как спарсить строку в 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 предусматривает наличие встроенных методов и внешних библиотек для создания, модификации, разбора и управления датой и временем.