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

Введение

Примеры

  • Как спарсить строку в datetime с часовым поясом

    Python 3.2+ поддерживает формат %z при разборе строки в объекте datetime.

    UTC в форме +HHMM или -HHMM (пустая строка, если объект не содержит информации о временной зоне).

    Python 3.x 3.2

    import datetime
    dt = datetime.datetime.strptime("2016-04-15T08:27:18-0500", "%Y-%m-%dT%H:%M:%S%z")

    

    Для других версий Python можно использовать внешнюю библиотеку, такую ​​как dateutil, которая проанализирует текст в объект datetime.

    import dateutil.parser
    dt = 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 datetime
    
    today = 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-15
    Yesterday: 2016-04-14
    Tomorrow: 2016-04-16
    Difference between tomorrow and yesterday: 2 days, 0:00:00

    

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

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

    import datetime
    
    # Date object
    today = datetime.date.today()
    new_year = datetime.date(2017, 01, 01) #datetime.date(2017, 1, 1)
    
    # Time object
    noon = datetime.time(12, 0, 0) #datetime.time(12, 0)
    
    # Current datetime
    now = datetime.datetime.now()
    
    # Datetime object
    millenium_turn = datetime.datetime(2000, 1, 1, 0, 0, 0) #datetime.datetime(2000, 1, 1, 0, 0)

    Арифметические операции для этих объектов поддерживаются только для одного и того же типа данных. Если выполнять арифметические действия с разными типами объектов, то это приведет к ошибке TypeError.

    # subtraction of noon from today
    noon-today
    Traceback (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 instead
    print('Time since the millenium at midnight: ',
    datetime.datetime(today.year, today.month, today.day) - millenium_turn)
    
    # Or this
    print('Time since the millenium at noon: ',
    datetime.datetime.combine(today, noon) - millenium_turn)
  • Итерация по датам

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

    import datetime
    
    # The size of each step in days
    day_delta = datetime.timedelta(days=1)
    
    start_date = datetime.date.today()
    end_date = start_date + 7*day_delta
    
    for i in range((end_date - start_date).days):
    print(start_date + i*day_delta)

    Получится:

    2016-07-21
    2016-07-22
    2016-07-23
    2016-07-24
    2016-07-25
    2016-07-26
    2016-07-27

    

  • Спарсить строку с двухбуквенным часовым поясом и перевести в UTC

    Используя библиотеку dateutil можно парсить временные метки с коротким буквенным именем часового пояса.

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

    Например, CST. Это может быть Центральным стандартным временем, Стандартным временем в Китае, Стандартным временем Кубы и т.д. Полный список можно посмотреть здесь.

    from dateutil import tz
    from dateutil.parser import parse
    
    ET = 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 parse
    import pytz
    
    EST = 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, timezone
    JST = timezone(timedelta(hours=+9))
    
    dt = datetime(2015, 1, 1, 12, 0, 0, tzinfo=JST)
    print(dt)
    # 2015-01-01 12:00:00+09:00
    
    print(dt.tzname())
    # UTC+09:00
    
    dt = 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, timedelta
    from dateutil import tz
    
    JST = tz.tzoffset('JST', 9 * 3600) # 3600 seconds per hour
    dt = datetime(2015, 1, 1, 12, 0, tzinfo=JST)
    print(dt)
    # 2015-01-01 12:00:00+09:00
    print(dt.tzname)
    # 'JST'

    Часовые пояса с переходом на летнее время

    Для часовых зон с летним временем в стандартных библиотеках python нет стандартных классов. Поэтому нужно использовать стороннюю библиотеку. Есть pytz и dateutil — популярные библиотеки, в которых представлены классы часовых поясов.

    Помимо стандартных часовых поясов, в dateutil есть классы часовых поясов, в которых используется летнее время (см. Документацию для модуля tz ). Можете использовать метод tz.gettz() для получения объекта часового пояса, который затем можно передать непосредственно в datetime:

    from datetime import datetime
    from dateutil import tz
    local = tz.gettz() # Local time
    PT = tz.gettz('US/Pacific') # Pacific time
    
    dt_l = datetime(2015, 1, 1, 12, tzinfo=local) # I am in EST
    dt_pst = datetime(2015, 1, 1, 12, tzinfo=PT)
    dt_pdt = datetime(2015, 7, 1, 12, tzinfo=PT) # DST is handled automatically
    print(dt_l)
    # 2015-01-01 12:00:00-05:00
    print(dt_pst)
    # 2015-01-01 12:00:00-08:00
    print(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, timedelta
    import pytz
    
    PT = 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:00
    print(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 PST
    print(dt_new)
    # 2015-11-01 03:30:00-07:00
    dt_corrected = PT.normalize(dt_new)
    print(dt_corrected)
    # 2015-11-01 02:30:00-08:00

    

  • Извлечение даты и времени из текста (неопределенный анализ даты и времени)

    Можно извлечь дату из текста, используя синтаксический анализатор dateutil в режиме «fuzzy», в котором компоненты строки, не идентифицирующиеся как часть даты, игнорируются.

    

    from dateutil.parser import parse
    
    dt = 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 datetime
    from dateutil import tz
    
    utc = 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 iso8601
    iso8601.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 time
    from datetime import datetime
    seconds_since_epoch=time.time() #1469182681.709
    
    utc_date=datetime.utcfromtimestamp(seconds_since_epoch) # datetime.datetime(2016, 7, 22, 10, 18, 1, 709000)
  • Вычисление количества месяцев с даты

    Использование модуля calendar

    import calendar
    from datetime import date
    
    def 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 datetime
    import dateutil.relativedelta
    
    d = datetime.datetime.strptime("2013-03-31", "%Y-%m-%d")
    d2 = d - dateutil.relativedelta.relativedelta(months=1) #datetime.datetime(201)
  • Вычисление разницы во времени

    Модуль timedelta пригодится для вычисления разницы во времени:

    

    from datetime import datetime, timedelta
    now = datetime.now()
    then = datetime(2016, 5, 23) # datetime.datetime(2016, 05, 23, 0, 0, 0)

    При создании нового объекта datetime указывать время необязательно.

    delta = now - then

    

    delta — это тип timedelta

    

    print(delta.days)
    # 60
    print(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 datetime
    
    datetime.now().isoformat()
    # Out: '2016-07-31T23:08:20.886783'

    С часовым поясом и с микросекундами

    from datetime import datetime
    from dateutil.tz import tzlocal
    
    datetime.now(tzlocal()).isoformat()
    # Out: '2016-07-31T23:09:43.535074-07:00'

    С часовым поясом без микросекунд

    from datetime import datetime
    from dateutil.tz import tzlocal
    
    datetime.now(tzlocal()).replace(microsecond=0).isoformat()
    # Out: '2016-07-31T23:10:30-07:00'

Синтаксис

Параметры

Примечания

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