如何用Python制作可指定周起始日与月天数的自定义日历?
Great question! The standard calendar module works perfectly for Gregorian calendars, but when you need full control over month lengths and week start days, you'll want to build a custom solution from scratch. Let's walk through how to implement this step by step.
Core Requirements Recap
- Define a custom number of days per month (no fixed 28/30/31 constraints)
- Specify any day as the start of the week (Monday, Sunday, etc.)
- Generate a clean, readable text-based calendar that matches these rules
Step-by-Step Implementation
Below is a flexible function that handles all your requirements. We'll use basic list manipulation and string formatting to create the calendar layout:
def generate_custom_calendar(month_days: int, start_week_day: str = "Monday", first_day_weekday: str = "Monday"): # Map weekday names to numerical values for easier calculations WEEKDAY_MAP = { "Monday": 0, "Tuesday": 1, "Wednesday": 2, "Thursday": 3, "Friday": 4, "Saturday": 5, "Sunday": 6 } # Validate input values to avoid errors if start_week_day not in WEEKDAY_MAP or first_day_weekday not in WEEKDAY_MAP: raise ValueError("Invalid weekday name. Use one of: Monday, Tuesday, ..., Sunday") if month_days < 1: raise ValueError("Month must have at least 1 day") # Convert input days to numerical values for math operations start_day_num = WEEKDAY_MAP[start_week_day] first_day_num = WEEKDAY_MAP[first_day_weekday] # Create the weekday header in the order of our custom week start ordered_weekdays = list(WEEKDAY_MAP.keys())[start_day_num:] + list(WEEKDAY_MAP.keys())[:start_day_num] # Calculate empty slots needed at the start of the first week # This ensures the first day of the month lines up with its correct column leading_empty_slots = (first_day_num - start_day_num) % 7 # Build the full list of calendar entries: empty slots + actual days calendar_entries = [""] * leading_empty_slots + list(range(1, month_days + 1)) # Split entries into weeks (7 days per week) calendar_weeks = [calendar_entries[i:i+7] for i in range(0, len(calendar_entries), 7)] # Format and print the calendar # Print the weekday header with consistent spacing print(" | ".join(f"{day:9}" for day in ordered_weekdays)) print("-" * 65) # Print each week row, formatting empty slots and dates uniformly for week in calendar_weeks: formatted_row = [] for entry in week: if isinstance(entry, int): formatted_row.append(f"{entry:9}") else: formatted_row.append(" " * 9) print(" | ".join(formatted_row))
How to Use the Function
Let's test it with a couple of examples to see it in action:
Example 1: 30-day month, week starts on Sunday, first day is Friday
generate_custom_calendar(month_days=30, start_week_day="Sunday", first_day_weekday="Friday")
Output:
Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday ----------------------------------------------------------------- | | | | | 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
Example 2: 29-day month, week starts on Monday, first day is Wednesday
generate_custom_calendar(month_days=29, start_week_day="Monday", first_day_weekday="Wednesday")
Output:
Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday ----------------------------------------------------------------- | | 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 | | | |
Key Features Explained
- Input Validation: Catches invalid weekday names or non-positive month lengths before running.
- Dynamic Week Order: The header automatically adjusts to match your chosen week start day.
- Flexible First Day: You can set the first day of the month to any weekday, so the calendar aligns exactly as you need.
- Clean Formatting: Fixed-width spacing keeps the calendar neat and easy to read.
Extensions You Can Add
If you want to expand this further, here are some ideas:
- Return the calendar as a string instead of printing it directly (great for saving to files or embedding in other outputs).
- Add support for marking specific dates (like holidays) with special characters or formatting.
- Integrate with custom year structures (e.g., 13 months per year, each with 28 days).
内容的提问来源于stack exchange,提问作者Austin b
相关产品推荐
相关产品推荐

