| #! /usr/bin/env python | 
| """ | 
| Description: | 
|     Programm zur Ausgabe einer Ansicht eines Monats wie gängige Kalender-Format | 
|   | 
| Usage: | 
|   | 
| ``` | 
| $ python month_of_year.py 12 2024 | 
| ``` | 
| """ | 
| from typing import Final | 
| import sys | 
|   | 
|   | 
| # Calender setup | 
| weekdays: Final[tuple[str, ...]] = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa") | 
| first_day_of_week: Final[int] = 1  # it is monday, common in Europa | 
| months: Final[tuple[ tuple[str, int], ...]] = (("", 0), | 
|                                              ("Januar", 31), ("Februar", 28), ("März", 31), | 
|                                              ("April", 30), ("Mai", 31), ("Juni", 30), | 
|                                              ("July", 31), ("August", 31), ("September", 30), | 
|                                              ("Oktober", 31), ("November", 30), ("Dezember", 31)) | 
|   | 
|   | 
| def is_leap_year(year: int) -> bool: | 
|     return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) | 
|   | 
|   | 
| def weekday_of_date(day: int, month: int, year: int) -> int: | 
|     y0 = year - ((14 - month) // 12) | 
|     x = y0 + (y0 // 4) - (y0 // 100) + (y0 // 400) | 
|     m0 = month + 12 * ((14 - month) // 12) - 2 | 
|     return (day + x + (31 * m0) // 12) % 7 | 
|   | 
|   | 
| def count_days_in_month(month: int, year: int) -> int: | 
|     count_days = months[month][1] | 
|     if month == 2 and is_leap_year(year): | 
|         count_days += 1 | 
|     return count_days | 
|   | 
|   | 
| def build_calendar_head(month: int, year: int, calendar_width: int, fill_char: str =' ') -> str: | 
|     head_line = f"{months[month][0]} {year}" | 
|     return head_line.center(calendar_width, fill_char) | 
|   | 
|   | 
| def build_weekdays_line(column_width) -> str: | 
|     wd_line = "" | 
|     for i in range(0, 7): | 
|         d = (i + first_day_of_week) % 7 | 
|         wd_line += f"{weekdays[d]}".rjust(column_width, ' ') | 
|     return wd_line | 
|   | 
|   | 
| def format_day(day: int, column_width: int) -> str: | 
|     return f"{day}".rjust(column_width) | 
|   | 
|   | 
| def build_month_view(month: int, year: int, column_width: int) -> str: | 
|     month_view = "" | 
|     day = 1 | 
|     # the first week | 
|     day_of_first_date = weekday_of_date(day, month, year) | 
|     # how many columns before the first day of this month | 
|     diff = (7 + day_of_first_date - first_day_of_week) % 7 | 
|     # -> empty column | 
|     for _ in range(diff, 0, -1): | 
|         month_view += ("-" + "#"*(column_width - 1) ) | 
|     # -> next days in the first week | 
|     while day + diff <= 7: | 
|         month_view += format_day(day, column_width) | 
|         day += 1 | 
|     month_view += "\n" | 
|     # next weeks | 
|     days_in_month = count_days_in_month(month, year) | 
|     while day <= days_in_month: | 
|         month_view += format_day(day, column_width) | 
|         if (day + diff) % 7 == 0 and (day < days_in_month): | 
|             month_view += "\n" | 
|         day += 1 | 
|     return month_view | 
|   | 
|   | 
| def build_calendar(month: int, year: int, column_width: int = 3) -> str: | 
|     calendar_width = 7 * column_width | 
|     head_line = build_calendar_head(month, year, calendar_width, fill_char="-") | 
|     calendar_view = f"{head_line}\n" | 
|     weekdays_line = build_weekdays_line(column_width) | 
|     calendar_view += f"{weekdays_line}\n" | 
|     month_view = build_month_view(month, year, column_width) | 
|     calendar_view += f"{month_view}" | 
|     return calendar_view | 
|   | 
|   | 
| if __name__ == "__main__": | 
|     month = int(sys.argv[1]) | 
|     year = int(sys.argv[2]) | 
|     cal = build_calendar(month, year, 5) | 
|     print(cal) |