Batu Lab NotesPractical developer guides

Generate a Monday-first month grid with calendar

By Batu ยท English technical notes

Also published in our Blogger archive.

A month grid is easier to generate from calendar.Calendar than from manually calculated padding. This example creates a calendar with firstweekday=calendar.MONDAY and asks it for February 2024 as complete weeks. monthdayscalendar() returns a list of seven-item lists. Days outside the requested month are represented by zero, which the display code renders as ..

The first assertion confirms that the first Thursday of February follows three Monday-first padding cells. The final assertion confirms the placement of leap day, 29 February, and the padding after it. These assertions establish the layout for this selected month and first-weekday setting; they do not prove formatting behavior for every locale or month.

calendar.Calendar uses Monday as its default first weekday, but passing the constant makes the intended convention explicit. The rows are numeric and locale-independent. If a user interface requires translated weekday names, retrieve or supply labels separately, and be careful with global calendar settings such as setfirstweekday() when concurrent code may run. Calendar and monthdayscalendar() are standard-library APIs available in supported Python versions. Their week structure and zero-padding behavior are documented in the official calendar module documentation and Calendar class reference.

AI assistance disclosure: this article was drafted with AI assistance and should be reviewed for the application's presentation requirements.

import calendar

month_calendar = calendar.Calendar(firstweekday=calendar.MONDAY)
weeks = month_calendar.monthdayscalendar(2024, 2)

assert weeks[0] == [0, 0, 0, 1, 2, 3, 4]
assert weeks[-1] == [26, 27, 28, 29, 0, 0, 0]

for week in weeks:
    cells = [f"{day:2}" if day else " ." for day in week]
    print(" ".join(cells))
 .  .  .  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  .  .  .