Шаг 58.
Python: сборник рецептов.
Числа, даты и время. Поиск диапазона дат для текущего месяца

    На этом шаге мы рассмотрим реализацию этой задачи.

Задача

    У вас есть код, которому необходимо пройти в цикле по каждой дате текущего месяца, и вам нужен эффективный способ поиска диапазонов дат.

Решение

    Прохождение в цикле по датам не требует предварительного создания списка всех дат. Вы можете просто вычислить стартовую и конечную даты в диапазоне, а затем использовать объекты datetime.timedelta, инкрементируя дату.

    Вот функция, которая принимает любой объект datetime и возвращает кортеж, содержащий первую дату месяца и начальную дату следующего месяца:

>>> from datetime import datetime, date, timedelta
>>> import calendar
>>> def get_month_range(start_date=None):
	if start_date is None:
		start_date = date.today().replace(day=1)
		_, days_in_month = calendar.monthrange(start_date.year, start_date.month)
	end_date = start_date + timedelta(days=days_in_month)
	return (start_date, end_date)

>>> a_day = timedelta(days=1)
>>> first_day, last_day = get_month_range()
>>> while first_day < last_day:
	print(first_day)
	first_day += a_day

	
2026-04-01
2026-04-02
2026-04-03
2026-04-04
2026-04-05
2026-04-06
2026-04-07
2026-04-08
2026-04-09
2026-04-10
2026-04-11
2026-04-12
2026-04-13
2026-04-14
2026-04-15
2026-04-16
2026-04-17
2026-04-18
2026-04-19
2026-04-20
2026-04-21
2026-04-22
2026-04-23
2026-04-24
2026-04-25
2026-04-26
2026-04-27
2026-04-28
2026-04-29
2026-04-30
>>> 


Обсуждение

    Этот рецепт работает так: сначала вычисляется дата, соответствующая первому дню месяца. Быстрый способ сделать это - использовать метод replace() объектов date или datetime, чтобы присвоить атрибуту days значение 1. Приятно, что метод replace() создает объект того же типа, к которому он был применен. В данном случае, поскольку на входе у нас был экземпляр date, результат тоже является экземпляром date. Точно так же мы бы получили экземпляр datetime, если бы на входе у нас был экземпляр datetime.

    Затем функция calendar.monthrange() используется для нахождения количества дней в рассматриваемом месяце. Модуль calendar весьма полезен для получения базовых данных о календарях. Функция monthrange() возвращает кортеж, который содержит день недели и количество дней в месяце.

    Когда мы знаем количество дней в месяце, конечная дата вычисляется путем добавления соответствующего timedelta к стартовой дате. Тонкий, но важный аспект этого рецепта - конечная дата не включается в диапазон (на самом деле это первая дата следующего месяца). Это отражает присущее срезам и диапазонам Python поведение, которое также не подразумевает включение последнего элемента.

    Чтобы пройти в цикле по диапазону дат, используются стандартные математические операции и операторы сравнения. Например, экземпляр timedelta может быть использован для инкрементирования даты. Оператор < используется для проверки того, не достигнута ли конечная дата.

    В идеальном случае стоит создать функцию, которая будет работать как встроенная range(), но с датами. К счастью, есть чрезвычайно простой способ сделать это с помощью генератора:

>>> for d in date_range(datetime(2012, 9, 1), datetime(2012,10,1), timedelta(hours=6)):
	print(d)

	
2012-09-01 00:00:00
2012-09-01 06:00:00
2012-09-01 12:00:00
2012-09-01 18:00:00
2012-09-02 00:00:00
2012-09-02 06:00:00
2012-09-02 12:00:00
2012-09-02 18:00:00
2012-09-03 00:00:00
2012-09-03 06:00:00
2012-09-03 12:00:00
2012-09-03 18:00:00
2012-09-04 00:00:00
2012-09-04 06:00:00
2012-09-04 12:00:00
2012-09-04 18:00:00
2012-09-05 00:00:00
2012-09-05 06:00:00
2012-09-05 12:00:00
2012-09-05 18:00:00
2012-09-06 00:00:00
2012-09-06 06:00:00
2012-09-06 12:00:00
2012-09-06 18:00:00
2012-09-07 00:00:00
2012-09-07 06:00:00
2012-09-07 12:00:00
2012-09-07 18:00:00
2012-09-08 00:00:00
2012-09-08 06:00:00
2012-09-08 12:00:00
2012-09-08 18:00:00
2012-09-09 00:00:00
2012-09-09 06:00:00
2012-09-09 12:00:00
2012-09-09 18:00:00
2012-09-10 00:00:00
2012-09-10 06:00:00
2012-09-10 12:00:00
2012-09-10 18:00:00
2012-09-11 00:00:00
2012-09-11 06:00:00
2012-09-11 12:00:00
2012-09-11 18:00:00
2012-09-12 00:00:00
2012-09-12 06:00:00
2012-09-12 12:00:00
2012-09-12 18:00:00
2012-09-13 00:00:00
2012-09-13 06:00:00
2012-09-13 12:00:00
2012-09-13 18:00:00
2012-09-14 00:00:00
2012-09-14 06:00:00
2012-09-14 12:00:00
2012-09-14 18:00:00
2012-09-15 00:00:00
2012-09-15 06:00:00
2012-09-15 12:00:00
2012-09-15 18:00:00
2012-09-16 00:00:00
2012-09-16 06:00:00
2012-09-16 12:00:00
2012-09-16 18:00:00
2012-09-17 00:00:00
2012-09-17 06:00:00
2012-09-17 12:00:00
2012-09-17 18:00:00
2012-09-18 00:00:00
2012-09-18 06:00:00
2012-09-18 12:00:00
2012-09-18 18:00:00
2012-09-19 00:00:00
2012-09-19 06:00:00
2012-09-19 12:00:00
2012-09-19 18:00:00
2012-09-20 00:00:00
2012-09-20 06:00:00
2012-09-20 12:00:00
2012-09-20 18:00:00
2012-09-21 00:00:00
2012-09-21 06:00:00
2012-09-21 12:00:00
2012-09-21 18:00:00
2012-09-22 00:00:00
2012-09-22 06:00:00
2012-09-22 12:00:00
2012-09-22 18:00:00
2012-09-23 00:00:00
2012-09-23 06:00:00
2012-09-23 12:00:00
2012-09-23 18:00:00
2012-09-24 00:00:00
2012-09-24 06:00:00
2012-09-24 12:00:00
2012-09-24 18:00:00
2012-09-25 00:00:00
2012-09-25 06:00:00
2012-09-25 12:00:00
2012-09-25 18:00:00
2012-09-26 00:00:00
2012-09-26 06:00:00
2012-09-26 12:00:00
2012-09-26 18:00:00
2012-09-27 00:00:00
2012-09-27 06:00:00
2012-09-27 12:00:00
2012-09-27 18:00:00
2012-09-28 00:00:00
2012-09-28 06:00:00
2012-09-28 12:00:00
2012-09-28 18:00:00
2012-09-29 00:00:00
2012-09-29 06:00:00
2012-09-29 12:00:00
2012-09-29 18:00:00
2012-09-30 00:00:00
2012-09-30 06:00:00
2012-09-30 12:00:00
2012-09-30 18:00:00
>>> 

    Повторимся, самое большое преимущество такой реализации в том, что датами и временем можно манипулировать с помощью стандартных математических операторов и операторов сравнения.

    На следующем шаге мы рассмотрим конвертирование строк в даты и время.




Предыдущий шаг Содержание Следующий шаг