Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 15 из 20 Начальный 18 мин 110 XP

calendar: месяц и год в текст

Модуль calendar рисует месяц одной строкой, говорит, каким днём недели была дата, проверяет год на високосность и сообщает размер месяца — без единого объекта datetime.

Редакция Питоники

Часовые пояса из прошлого урока закрыли самый хитрый вопрос дат. Теперь — про самый наглядный: календарь как картинка. В отчёте, в консольной утилите, в ответе на вопрос «а сколько в октябре будней?» нужен не момент времени, а месяц целиком: сетка дней, дни недели, границы месяца. Для этого в стандартной библиотеке есть отдельный модуль — calendar.

calendar работает не с объектами, а с числами и словами: год, номер месяца, номер дня недели. В этом его сила — не нужно собирать datetime, чтобы узнать мелочь про дату.

calendar.month: месяц одной строкой

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

сетка октября 2026
import calendar

print(calendar.month(2026, 10))
Вывод
    October 2026
Mo Tu We Th Fr Sa Su
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Разбираем картинку. Первая строка — заголовок с названием месяца и годом (названия по-английски — об этом ниже). Вторая — шапка дней недели Mo Tu We Th Fr Sa Su: неделя начинается с понедельника. Дальше — недели месяца строками: числа выровнены в колонки по два символа, а дни соседних месяцев заменены пробелами. 21 октября 2026 стоит под We — среда. И заметьте: после сетки в выводе проскочила пустая строка — причина у неё честная, см. врезку.

calendar.weekday: день недели по трём числам

номер дня недели по дате
import calendar

print(calendar.weekday(2026, 10, 21))   # среда
print(calendar.weekday(2026, 10, 26))   # понедельник
Вывод
2
0

calendar.weekday(год, месяц, день) возвращает номер дня недели: 0 — понедельник, 6 — воскресенье. Это те же номера, что даёт метод d.weekday() из урока про дни недели, только объект datetime собирать не нужно — достаточно трёх чисел. 21 октября 2026 — номер 2, то есть среда; 26-е — номер 0, понедельник.

calendar.isleap: високосный год

Второй частый вопрос про календарь — високосный ли год. Проверять делением на 4 руками — классический источник ошибок, потому что правило длиннее:

isleap знает исключения
import calendar

print(calendar.isleap(2028))
print(calendar.isleap(2026))
print(calendar.isleap(1900))
print(calendar.isleap(2000))
Вывод
True
False
False
True

Правило в три шага: год високосный, если делится на 4; но кратные 100 — обычные; и только кратные 400 — снова високосные. Поэтому 2028-й — високосный, 2026-й — нет, 1900-й — нет (делится на 100, но не на 400), а 2000-й — да. calendar.isleap(год) возвращает честный True или False и принимает только год — не месяц и не день.

calendar.monthrange: где начинается месяц и сколько в нём дней

Последняя функция закрывает сразу два вопроса: чем является первое число месяца и сколько дней в месяце вообще.

границы октября
import calendar

print(calendar.monthrange(2026, 10))
print(calendar.monthrange(2026, 10)[1])
Вывод
(calendar.THURSDAY, 31)
31

Кортеж из двух чисел: первый элемент — день недели, на который приходится 1-е число (октябрь 2026 начался с четверга), второй — сколько дней в месяце. Индекс [1] достаёт длину месяца — October, как и положено, 31 день. Если у вас более старый Python (до 3.12), первый элемент напечатается просто числом 3 — это тот же четверг: с 3.12 calendar возвращает именованные дни вроде THURSDAY, и число за ним то же самое.

ВызовЧто вернётДля октября 2026
calendar.month(2026, 10)строка-сетка месяца« October 2026 …»
calendar.weekday(2026, 10, 21)номер дня недели, 0 — пн2
calendar.isleap(2026)True или FalseFalse
calendar.monthrange(2026, 10)[1]число дней в месяце31
calendar.monthcalendar(2026, 10)[0]первая неделя списком[0, 0, 0, 1, 2, 3, 4]

Что дальше

calendar отвечает на вопросы про месяц, но не считает жизненные промежутки: сколько человеку лет, сколько дней до экзамена. Это задачи следующего урока — возраст и разница в днях, где timedelta из урока про разницы превращается в конкретные числа. А если нужен месяц с русскими названиями — сетку calendar можно обрамить заголовком из словаря месяцев урока 9.

Сетка как данные, а не как картинка

До сих пор мы смотрели на месяц глазами: month() рисует картинку, и это удобно для отчёта или подсказки в консоли. Но как только месяц нужен для счёта, картинка не годится — её придётся парсить пробелами. И тут выручает вторая функция: monthcalendar возвращает не строку, а список списков по семь чисел, где чужие дни заменены нулями.

месяц как список недель
import calendar

weeks = calendar.monthcalendar(2026, 10)

print(len(weeks))
print(weeks[0])
print(weeks[-1])
Вывод
5
[0, 0, 0, 1, 2, 3, 4]
[26, 27, 28, 29, 30, 31, 0]

Пять недель — столько поместилось в октябрь 2026 года. Первая неделя начинается с нулей: 1 октября 2026 года приходится на четверг, а нули стоят в местах понедельника, вторника и среды. Последняя неделя заканчивается нулём в воскресенье, потому что 31 октября — пятница, и дальше месяца нет. Числа вместо дат — сознательное решение: нулем обозначен день, которого в этом месяце нет, и его легко отсечь проверкой if day.

сколько будней в месяце
import calendar

def workdays_in_month(year, month):
    total = 0
    for week in calendar.monthcalendar(year, month):
        for day in week:
            if day and calendar.weekday(year, month, day) < 5:
                total += 1
    return total

print(workdays_in_month(2026, 10))
print(workdays_in_month(2026, 2))
print(workdays_in_month(2026, 11))
Вывод
22
20
21

В октябре 2026 года 22 будня, в феврале 20 — февраль короче на целые девять дней, из которых пять будних, а в ноябре 21: ноябрь начинается в воскресенье, поэтому первый рабочий день — понедельник 2-го, и месяц заканчивается в понедельник 30-го, то есть последняя неделя умещается целиком. Мы этим считал трудоёмкость спринта: сколько рабочих дней у команды до конца месяца. Отдельная функция с циклом из урока про рабочие дни даёт тот же ответ, но месяц — это готовый список, и пересчитывать его не нужно.

Месяц по-русски

Заголовок calendar.month всегда английский, и это не лень модуля, а его решение: модуль не зависит от настроек языка системы. Но месяц с русскими названиями получается сам, если взять сетку из monthcalendar, а шапку собрать вручную из словаря — того самого, что разбирали в уроке про русские даты.

сетка с русскими днями недели
import calendar

months = {10: 'Октябрь'}
weekdays = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']

print(months[10])
print(' '.join(weekdays))
for week in calendar.monthcalendar(2026, 10):
    row = [f'{d:>2}' if d else '  ' for d in week]
    print(' '.join(row).rstrip())
Вывод
Октябрь
Пн Вт Ср Чт Пт Сб Вс
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Заголовок «Октябрь» из словаря, шапка дней недели — список из двухбуквенных сокращений, а числа — шириной в два символа, чтобы колонки встали ровно. Куски дни соседних месяцев заменены двумя пробелами, а rstrip() в конце убирает хвостовые пробелы у последней строки, где месяц обрывается на 31-м числе. Две детали, из-за которых это перестаёт быть «просто напечатать»: календарь в ru-локали меняет формат дат, а список месяцев в Python короткий до «янв» — приходится разворачивать словарь вручную.

Глобальная настройка, о которой забывают

В модуле есть настройка первого дня недели — и она глобальная, то есть влияет на весь процесс, а не на конкретный вызов. Меняют её через setfirstweekday, и после этого меняется всё: и нарисованная сетка, и номера дней в ней, и monthcalendar.

смена первого дня недели
import calendar

print(calendar.month(2026, 10).splitlines()[1])
calendar.setfirstweekday(calendar.SUNDAY)
print(calendar.month(2026, 10).splitlines()[1])
calendar.setfirstweekday(calendar.MONDAY)
print(calendar.month(2026, 10).splitlines()[1])
Вывод
Mo Tu We Th Fr Sa Su
Su Mo Tu We Th Fr Sa
Mo Tu We Th Fr Sa Su

Заголовок переехал: неделя теперь начинается с воскресенья, и весь месяц сдвинулся на одну колонку. Третья строка возвращает всё как было — поэтому в рабочем коде мы всегда ставим setfirstweekday обратно, иначе следующий импорт из этого модуля получит неделю с другой нумерацией. Отдельно про цифры: setfirstweekday меняет и weekday(), то есть номера дней, которые раньше означали «понедельник», теперь означают «воскресенье». Код, который сравнивал weekday() с магическим числом, после такой смены молча начинал считать не то.

локальная настройка через объект Calendar
import calendar

cal = calendar.Calendar(firstweekday=calendar.SUNDAY)

print(cal.monthdayscalendar(2026, 10)[0])
print(calendar.month(2026, 10).splitlines()[1])
Вывод
[0, 0, 0, 0, 1, 2, 3]
Mo Tu We Th Fr Sa Su

Собственный экземпляр Calendar с нужным первым днём недели работает, а модуль остался нетронутым: вторая строка по-прежнему показывает привычную шапку с понедельника. В месячных данных появился ноль в воскресеньем месте — 1 октября 2026 года было в четверг, поэтому первая неделя теперь начинается с 4-го и в ней 1, 2, 3 октября. Для приложения, которое печатает календарь, — честный выход; для отчёта, который читают люди, мы всё равно оставляем международный порядок с понедельника.

Кортеж monthrange на разбор

monthrange отдаёт кортеж, и с ним удобно работать по частям. Отдельно стоит сказать про первый элемент: начиная с Python 3.12 это не число, а именованный день недели из перечисления — печать показывает calendar.THURSDAY вместо 3. Число то же самое, но код, который сравнивал его с литералом, пришлось бы поправить при обновлении Python.

разбор кортежа monthrange
import calendar

first, days = calendar.monthrange(2026, 10)

print(first)
print(days)
print(int(first))
Вывод
3
31
3

Распаковка в две переменные работает и на 3.12, и на более ранних версиях, а int(first) возвращает обычное число в любом случае — так код переживает смену представления. Мы держим в проекте именно распаковку с int: она не сломается ни при переходе на новый Python, ни при возврате к старому, а разница между версиями — ровно то, из-за чего такие мелочи обычно и ломаются.

ВызовЧто вернётДля октября 2026
calendar.month(2026, 10)строка-сетка месяца« October 2026 …»
calendar.weekday(2026, 10, 21)номер дня недели, 0 — пн2
calendar.isleap(2026)True или FalseFalse
calendar.monthrange(2026, 10)[1]число дней в месяце31
calendar.monthcalendar(2026, 10)[0]первая неделя списком[0, 0, 0, 1, 2, 3, 4]
calendar.monthrange(2028, 2)[1]дней в феврале високосного года29
calendar.monthrange(2026, 2)[1]дней в феврале обычного года28

Правило выбора между двумя способами ответа на один вопрос: если нужно нарисовать, берите month() — результат готов к печати. Если нужно посчитать, берите monthcalendar() — результат готов к циклу. Смешивать их не стоит: парсить пробелы из нарисованной сетки — это работа, которую модуль за вас уже сделал.

calendar умеет то, до чего объекты datetime не договариваются: нарисовать месяц целиком, как на бумажном календаре.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

import calendar

print(calendar.weekday(2028, 2, 29))
import calendar

print(calendar.isleap(2024) + calendar.isleap(2025))
import calendar

print(calendar.weekday(2026, 10, 25) < 5)
import calendar

weeks = calendar.monthcalendar(2026, 10)
print(len(weeks))
print(weeks[0])
import calendar

first, days = calendar.monthrange(2026, 10)
print(int(first))
print(days)
Проверь себя
0 / 9

1. Что возвращает calendar.month(2026, 10)?

2. calendar.weekday(2026, 10, 21) вернул 2. Какой это день недели?

3. Почему calendar.isleap(1900) возвращает False?

4. Что лежит в кортеже, который возвращает calendar.monthrange(2026, 10)?

5. Почему print(calendar.month(2026, 10)) печатает пустую строку после сетки?

6. Что означают нули в списке, который возвращает calendar.monthcalendar?

7. Чем опасен вызов calendar.setfirstweekday(calendar.SUNDAY) в прикладном коде?

8. Что вернёт calendar.monthrange(2026, 10)[0] на Python 3.12 и новее?

9. Сколько рабочих дней было в октябре 2026 года по подсчёту через monthcalendar?

Карточки терминов
Запомнено: 0 / 10
Практика

Соберите мини-справку по календарю тремя вызовами calendar: напечатайте сетку ноября 2026, выведите номер дня недели для 1 января 2027 и число дней в феврале 2028. Порядок вывода — именно такой.

practice.py
Вопросы и ответы по уроку

Что делает модуль calendar в Python?

Это модуль стандартной библиотеки для календарных задач без объектов datetime: calendar.month печатает сетку месяца текстом, calendar.weekday возвращает номер дня недели по дате, calendar.isleap проверяет год на високосность, calendar.monthrange даёт день недели первого числа и длину месяца. Все функции принимают обычные числа.

Урок 15: calendar в Python

Как напечатать месяц календарём в консоли Python?

Одной строкой: import calendar, затем calendar.month(год, номер_месяца) — например calendar.month(2026, 10) вернёт сетку октября 2026 с неделями от понедельника. Чтобы напечатать сразу без лишней пустой строки, используйте calendar.prmonth(2026, 10) или print(calendar.month(2026, 10), end='').

Почему названия месяцев в calendar на английском?

calendar.month всегда рисует английский заголовок вроде October 2026: модуль не зависит от языковых настроек системы. Русское название над сеткой удобно собрать самому словарём месяцев и strftime-кодами из урока про русские даты, а саму сетку оставить как есть — числа в ней интернациональны.

Урок 9: русские даты

С какого дня недели начинается неделя в calendar?

По умолчанию с понедельника: первый столбец сетки — Mo, а calendar.weekday считает понедельник нулём. Перенастроить можно вызовом calendar.setfirstweekday(calendar.SUNDAY) — тогда неделя начнётся с воскресенья и в сетке, и в monthcalendar.

Как посчитать рабочие дни в месяце через calendar?

Возьмите calendar.monthcalendar(год, месяц) и пройдите по неделям: нули пропускайте, а для каждого дня считайте calendar.weekday(год, месяц, день) < 5. В октябре 2026 года получится 22 будня, в феврале — 20, в ноябре — 21.

Урок 17: рабочие дни

Как получить месяц с русскими названиями дней недели?

Сетку берите из calendar.monthcalendar(год, месяц) — это данные, а не картинка, — а шапку рисуйте сами из списка ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'], а название месяца — из словаря месяцев урока про русские даты. Каждый день форматируйте шириной в два символа, чтобы колонки встали ровно.

Урок 9: русские даты

Почему monthrange в Python 3.12 возвращает calendar.THURSDAY, а не число 3?

С 3.12 дни недели в модуле calendar стали элементами перечисления, и первый элемент кортежа monthrange — именованный день. Значение то же самое, поэтому int(first) даёт обычное число 3 и код переживает смену представления. Старый код, сравнивавший результат с литералом, при обновлении Python пришлось бы поправить.

Как нарисовать календарь в HTML?

В модуле есть класс HTMLCalendar: cal = calendar.HTMLCalendar(), затем cal.formatmonth(2026, 10) возвращает готовый фрагмент HTML-таблицы с классом month. Для простого текста хватает calendar.month, а для HTML этот класс экономит всю разметку вручную.

Понравился урок? Сошлитесь на него

«calendar умеет то, до чего объекты datetime не договариваются: нарисовать месяц целиком, как на бумажном календаре.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.