This program generates printable text files of monthly calendars for the month and year you enter. Dates and calendars are a tricky topic in programming because there are so many different rules for determining the number of days in a month, which years are leap years, and which day of the week a particular date falls on. Fortunately, Python’s datetime
module handles these details for you. This program focuses on generating the multiline string for the monthly calendar page.
When you run calendarmaker.py, the output will look like this:
Calendar Maker, by Al Sweigart [email protected]
Enter the year for the calendar:
> 2029
Enter the month for the calendar, 1-12:
> 12
December 2029
...Sunday.....Monday....Tuesday...Wednesday...Thursday....Friday....Saturday..
+----------+----------+----------+----------+----------+----------+----------+
|25 |26 |27 |28 |29 |30 | 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 | 1 | 2 | 3 | 4 | 5 |
| | | | | | | |
| | | | | | | |
| | | | | | | |
+----------+----------+----------+----------+----------+----------+----------+
Saved to calendar_2029_12.txt
Note that the getCalendarFor()
function returns a giant multiline string of the calendar for the given month and year. In this function, the calText
variable stores this string, which adds the lines, spaces, and dates to it. To track the date, the currentDate
variable holds a datetime.date()
object, which gets set to the next or previous date by adding or subtracting datetime.timedelta()
objects. You can learn about Python’s date and time modules by reading Chapter 17 of Automate the Boring Stuff with Python at https://automatetheboringstuff.com/2e/chapter17/.
1. """Calendar Maker, by Al Sweigart [email protected]
2. Create monthly calendars, saved to a text file and fit for printing.
3. This code is available at https://nostarch.com/big-book-small-python-programming
4. Tags: short"""
5.
6. import datetime
7.
8. # Set up the constants:
9. DAYS = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
10. 'Friday', 'Saturday')
11. MONTHS = ('January', 'February', 'March', 'April', 'May', 'June', 'July',
12. 'August', 'September', 'October', 'November', 'December')
13.
14. print('Calendar Maker, by Al Sweigart [email protected]')
15.
16. while True: # Loop to get a year from the user.
17. print('Enter the year for the calendar:')
18. response = input('> ')
19.
20. if response.isdecimal() and int(response) > 0:
21. year = int(response)
22. break
23.
24. print('Please enter a numeric year, like 2023.')
25. continue
26.
27. while True: # Loop to get a month from the user.
28. print('Enter the month for the calendar, 1-12:')
29. response = input('> ')
30.
31. if not response.isdecimal():
32. print('Please enter a numeric month, like 3 for March.')
33. continue
34.
35. month = int(response)
36. if 1 <= month <= 12:
37. break
38.
39. print('Please enter a number from 1 to 12.')
40.
41.
42. def getCalendarFor(year, month):
43. calText = '' # calText will contain the string of our calendar.
44.
45. # Put the month and year at the top of the calendar:
46. calText += (' ' * 34) + MONTHS[month - 1] + ' ' + str(year) + '\n'
47.
48. # Add the days of the week labels to the calendar:
49. # (!) Try changing this to abbreviations: SUN, MON, TUE, etc.
50. calText += '...Sunday.....Monday....Tuesday...Wednesday...Thursday....Friday....Saturday..\n'
51.
52. # The horizontal line string that separate weeks:
53. weekSeparator = ('+----------' * 7) + '+\n'
54.
55. # The blank rows have ten spaces in between the | day separators:
56. blankRow = ('| ' * 7) + '|\n'
57.
58. # Get the first date in the month. (The datetime module handles all
59. # the complicated calendar stuff for us here.)
60. currentDate = datetime.date(year, month, 1)
61.
62. # Roll back currentDate until it is Sunday. (weekday() returns 6
63. # for Sunday, not 0.)
64. while currentDate.weekday() != 6:
65. currentDate -= datetime.timedelta(days=1)
66.
67. while True: # Loop over each week in the month.
68. calText += weekSeparator
69.
70. # dayNumberRow is the row with the day number labels:
71. dayNumberRow = ''
72. for i in range(7):
73. dayNumberLabel = str(currentDate.day).rjust(2)
74. dayNumberRow += '|' + dayNumberLabel + (' ' * 8)
75. currentDate += datetime.timedelta(days=1) # Go to next day.
76. dayNumberRow += '|\n' # Add the vertical line after Saturday.
77.
78. # Add the day number row and 3 blank rows to the calendar text.
79. calText += dayNumberRow
80. for i in range(3): # (!) Try changing the 4 to a 5 or 10.
81. calText += blankRow
82.
83. # Check if we're done with the month:
84. if currentDate.month != month:
85. break
86.
87. # Add the horizontal line at the very bottom of the calendar.
88. calText += weekSeparator
89. return calText
90.
91.
92. calText = getCalendarFor(year, month)
93. print(calText) # Display the calendar.
94.
95. # Save the calendar to a text file:
96. calendarFilename = 'calendar_{}_{}.txt'.format(year, month)
97. with open(calendarFilename, 'w') as fileObj:
98. fileObj.write(calText)
99.
100. print('Saved to ' + calendarFilename)
After you’ve entered the code and run it a few times, try re-creating this program from scratch without looking at the source code in this book. It doesn’t have to be exactly the same as this program; you can invent your own version! On your own, you can also try to figure out how to do the following:
Try to find the answers to the following questions. Experiment with some modifications to the code and rerun the program to see what effect the changes have.
'Jan'
instead of 'January'
?year = int(response)
on line 21?print(calText)
on line 93?