В Python есть несколько встроенных библиотек для работы со временем и интервалами времени: time, datatime, calendar, timeit. Но когда какой модуль использовать? Рассмотрим на примерах.
Как в Python вывести строку, содержащую текущие дату и время? Как вычислить время, прошедшее между двумя днями? Как измерить длительность выполнения фрагмента кода? Как сгенерировать календарь на весь год или на один месяц? Как определить даты всех третьих четвергов одного года? На эти и другие вопросы вы получите ответы в данном руководстве.
Чтобы не обращаться далее к операции импорта, перечислим сразу все встроенные модули, рассматриваемые в этом руководстве:
1. Работа со шкалой времени: модуль time
1.1. Понятие epoch
Работа с модулем time в существенной мере зависит от используемой операционной системы. Время в библиотеке привязано к фиксированной начальной точке – эпохе (epoch). Узнаем эту начальную точку:
В Unix-системах точкой отсчета (epoch) является 1 января 1970 г. Функция gmtime() вернула объект именованного кортежа struct_time.
С помощью функции time() время, прошедшее с этой начальной точки, можно также вывести в секундах (seconds since the epoch):
Так как точка epoch для разных операционных систем может отличаться, число секунд, возвращаемое функцией time(), также может быть различным.
Время до точки epoch тоже существует, но значения секунд seconds since the epoch отрицательны. Если мы передадим функции gmtime отрицательное значение секунд, мы перенесемся в прошлое относительно момента времени epoch:
1.2. Секунды, struct_time и преобразование друг в друга
Итак, модуль time оперирует двумя основными типами объектов: struct_time и секундами с начала эпохи. Для взаимных преобразований используются следующие функции:
gmtime(): из секунд в struct_time для UTC.
localtime(): из секунд в struct_time для местного времени.
calendar.timegm() (не модуль time): из struct_time для UTC в секунды.
mktime(): из struct_time местного времени в секунды.
UTC – стандартное обозначение всемирного координированного времени без учета часовых поясов. Начало отсчета epoch привязано к UTC, то есть не зависит от местного времени. Поэтому UTC удобнее использовать для общения с внешним миром.
В отсутствии аргумента функции gmtime() и localtime() возвращают значение для текущего времени – соответственно UTC и местное время.
Для преобразования объекта struct_time в секунды можно или передать сам объект, или кортеж целых чисел. Порядок элементов в кортеже:
Год tm_year
Месяц tm_mon – целое число (1 – Январь, 12 – Декабрь)
День месяца tm_day
Час tm_hour – целое число в диапазоне от 0 до 23
Минута tm_min
Секунда tm_sec
День недели tm_wday – целое число от 0 (Понедельник) до 6 (Воскресенье)
День года tm_yday
Целочисленный флаг tm_isdst для учета перехода на летнее время (daylight saving time, DST): 1 – переход на летнее время учитывается, 0 – не учитывается, -1 – неизвестно
Очевидно, что составлять такой кортеж вручную – задача неблагодарная, ведь нужно знать и день недели, и номер дня в году. Обычно используются «готовые» объекты (для «ручного» формирования дат удобнее применять описанный далее модуль datetime):
Для перевода не местного, а международного времени в секунды необходимо использовать модуль calendar:
Заметим, что, в отличие от представления в виде секунд, struct_time не хранит составляющих времени, меньших, чем секунда.
Удобство использования struct_time заключается в том, что это именованный кортеж. Значит, можно писать более ясный код: вместо индексов элементы объекта вызываются по ключам с говорящими именами:
Кроме вышеперечисленных параметров-меток, struct_time содержит скрытые. Так, местным законодательством каждой страны регулируется переход на летнее время. Узнать, действует ли сейчас летнее время, можно следующим образом (в России в 2014 году произведен переход на постоянное «зимнее» время):
Считаем часовой пояс:
Смещение местного времени относительно UTC в секундах:
1.3. Строки временных меток
Распространенная задача – преобразование объектов указанных типов в строки вида timestamp, например, Mon Dec 2 18:30:20 2019. Для этого применяются функции ctime() и asctime():
ctime() – принимает время в секундах
asctime() – принимает struct_time (по умолчанию используется местное время)
Хотя строковый вывод функций ctime() и asctime() довольно удобен, может потребоваться альтернативный формат. Для гибкого форматирования в библиотеку time включена функция strftime(). Функция принимает строку шаблона форматирования со спецификаторами и сам объект времени.
Функция strftime() также удобна для автоматической локализации строк:
%a, %A – аббревиатура и полное название дня недели (Чт, Четверг)
%b, %B – то же для месяца с учетом склонения (ноя, ноября)
%с – локализованная строка временной метки
%d – день месяца (28)
%H, %I – Час в 24- и 12-часовом представлении (17, 05)
%j – номер дня года (в представлении от 001 до 366)
%m – двузначное представление месяца (от 01 до 12)
%M – двузначное представление минут (от 00 до 59)
%p – местный эквивалент AM и PM
%S – двузначное представление секунд
%W – двузначное представление номера недели, первый день – Пн (%U для Вс)
%w – двузначное представление номера дня недели
%x, %X – принятый способ представления даты и времени.
%y, %Y – двузначное (без века) и четырехзначное представление года
%z, %Z – обозначение часового пояса в четырехзначном формате со знаком плюс или минус и в виде названия часового пояса
Пример одновременного использования нескольких спецификаторов:
Что, если у нас есть строка, содержащая метку времени, а мы хотим распарсить ее в объект struct_time, чтобы обработать его в Python? Для этого есть функция strptime(). Первый аргумент – строка, второй – правило, описанное через те же спецификаторы:
Функция strptime() позволяет кратко задавать struct_time, не используя все девять позиций кортежа. Неизвестные элементы вычисляются или на их место подставляются значения по умолчанию.
1.4. Приостановка выполнения кода и оценка производительности
Одна из наиболее часто используемых функций модуля time – функция sleep(), выполняющая задержку исполнения программного кода на переданное число секунд (можно использовать дробные значения):
Функция sleep() нередко используется для тестирования кода, намеренного внесения задержек на различных этапах выполнения программы.
Для оценки производительности однократно запускаемых команд применяется функция perf_counter(), обеспечивающая лучшее разрешение по времени на коротких интервалах:
В Python версии 3.7 добавлена функция perf_counter_ns() – работает так же, но длительность выводится в наносекундах, что удобнее для совсем малых интервалов времени и быстро исполняемых команд.
Более удобные методы для измерения производительности фрагмента кода предоставляет модуль timeit.
2. Оценка производительности: timeit
В момент запуска программы в фоновом режиме также запускается множество сторонних процессов. Модуль timeit за счет многократного запуска фрагмента нивелирует неоднородность длительности его выполнения.
У модуля timeit есть интерфейс командной строки и интерфейс для вызова в коде. Во втором случае выводится время в секундах, которое длится общее количество запусков. Так как значение number по умолчанию составляет 1 млн повторений, можно считать, что при дефолтном запуске выводится среднее время операции в микросекундах. При вызове timeit в командной строке достаточное количество повторений определяется автоматически.
Сравним скорость выполнения операция конкатенации при использовании генератора и функции map():
Сравним с вызовом через интерпретатор Python:
Кроме куска кода, функции timeit() можно передать строку setup, однократно выполняемую перед началом повторения кода stmt. В setup, например, можно вынести импорт библиотек:
В блокнотах Jupyter команда timeit относится к числу магических. С одним знаком процента она действует в пределах строки кода, с двумя – в границах ячейки:
Магические команды %time и %%time делают те же операции без многократного повторения. Это приводит к завышенным результатам, но позволяет быстрее получить оценку производительности:
3. Работа с датами: datetime
Вернемся к вопросу перемещения во времени. Модуль datetime поддерживает различные операции для работы с датами, например, определение интервала между двумя днями.
Структура представления времени в datetime похожа на struct_time в модуле time:
Выведем отдельно дату и время:
Аналогично извлекаются год, месяц и т.д.:
Модуль datetime также удобен для «ручного» задания дат и автоматизации арифметических операций с датами. Узнаем интервал времени между двумя главными датами сюжета фильма «Назад в будущее 2»:
Добавление найденной разности к первой дате «возвращает» нас в «будущее»:
Узнаем, какое число будет через четыре недели. Для форматирования строк в модуле datetime имеется функция strftime() с теми же спецификаторами, что и в модуле time:
Если вам важнее оперировать не датами, а неделями, днями недели, месяцами, годами, то вам нужен модуль calendar.
4. Работа с календарем: calendar
Модуль calendar содержит функции для работы с календарем. В частности, умеет генерировать строки и HTML для вывода каленадарей месяцев и годов. Для наглядности напечатаем календарь на декабрь 2019 года:
Всё хорошо, кроме того, что в заголовке используется шаблон названия месяцев в родительном падеже (так он обозначен в указанной выше локали системы). Мы можем вручную переобозначить константу именования месяцев:
Или использовать сокращения:
При помощи calendar можно не только «рисовать» календари, но и осуществлять итерации по их составляющим.
В качестве примера рассмотрим следующую практическую задачу. Во многих музеях существует один день месяца, когда посещение музея для всех лиц или отдельных категорий граждан происходит без взимания платы. Например, в Эрмитаже это третий четверг месяца. Вычислим даты бесплатных дней посещения Эрмитажа на 2020 год:
5. Сторонние библиотеки
Конечно, даже в самом подробном руководстве не описать всего, что рассказано в документациях встроенных библиотек. Если вам не хватает их функционала, имеется ряд сторонних решений:
dateutil – расширение стандартного модуля datetime для более специфичных операций, например, парсинга дат и их составляющих
pytz – для сложных манипуляций с часовыми поясами и летним временем
delorean – библиотека, названная в честь машины времени из фильма «Назад в будущее», упрощающая работу с датами
arrow – библиотека, стремящаяся заменить собой все вышеперечисленные, объединив их лучшие качества и заполнив пробелы