Python’s calendar module provides calendar computations and text/HTML rendering. import calendar. month: calendar.month(year, month, w=0, l=0) → text calendar string for one month. monthcalendar: calendar.monthcalendar(year, month) → list of weeks; each week is a list of 7 ints (0 = day outside month). monthrange: calendar.monthrange(year, month) → (weekday_of_first, num_days); weekday 0=Monday…6=Sunday. isleap: calendar.isleap(year) → bool. leapdays: calendar.leapdays(y1, y2) → count of leap years in [y1, y2). weekday: calendar.weekday(year, month, day) → 0=Monday…6=Sunday. weekheader: calendar.weekheader(n) → abbreviated weekday header string of width n. calendar func: calendar.calendar(year) → full year as text. HTMLCalendar: calendar.HTMLCalendar(firstweekday=0) — .formatmonth(year, month) / .formatyear(year). TextCalendar: calendar.TextCalendar(firstweekday=0). setfirstweekday: calendar.setfirstweekday(6) — set Sunday as first day. timegm: calendar.timegm(struct_time) → UTC epoch (unlike time.mktime which uses local time). day_name: list(calendar.day_name) → [‘Monday’, …, ‘Sunday’]. day_abbr: abbreviated 3-letter names. month_name: list(calendar.month_name) → ['', 'January', ..., 'December'] (index 0 is empty string). month_abbr: abbreviated. Claude Code generates appointment calendars, fiscal week analyzers, availability grids, and date range iterators.
CLAUDE.md for calendar
## calendar Stack
- Stdlib: import calendar
- Range: first_wd, ndays = calendar.monthrange(year, month)
- Weeks: calendar.monthcalendar(year, month) # list of 7-item week rows
- Leap: calendar.isleap(year)
- WkDay: calendar.weekday(y, m, d) # 0=Mon…6=Sun
- Epoch: calendar.timegm(struct_time) # UTC (not local)
- Names: calendar.day_name[0] # "Monday"
- calendar.month_name[1] # "January"
calendar Date and Calendar Pipeline
# app/calutil.py — month grids, ranges, availability, fiscal, holidays
from __future__ import annotations
import calendar
import datetime
from dataclasses import dataclass
from typing import Iterator
# Alias Weekday constants for readability
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
WEEKDAYS = {MON, TUE, WED, THU, FRI}
WEEKEND = {SAT, SUN}
# ─────────────────────────────────────────────────────────────────────────────
# 1. Month and year metadata
# ─────────────────────────────────────────────────────────────────────────────
def month_days(year: int, month: int) -> int:
"""
Return the number of days in a given month.
Example:
month_days(2024, 2) # 29 (leap year)
month_days(2025, 2) # 28
"""
_, n = calendar.monthrange(year, month)
return n
def first_weekday(year: int, month: int) -> int:
"""
Return the weekday of the 1st of the month (0=Monday, 6=Sunday).
Example:
first_weekday(2025, 1) # 2 (Wednesday)
"""
wd, _ = calendar.monthrange(year, month)
return wd
def is_leap(year: int) -> bool:
"""Return True if year is a leap year."""
return calendar.isleap(year)
def leap_years(start: int, end: int) -> list[int]:
"""
Return list of leap years in [start, end] inclusive.
Example:
leap_years(2020, 2032) # [2020, 2024, 2028, 2032]
"""
return [y for y in range(start, end + 1) if calendar.isleap(y)]
def weekday_name(wd: int, abbr: bool = False) -> str:
"""
Return the name for a weekday integer (0=Monday).
Example:
weekday_name(0) # "Monday"
weekday_name(4, abbr=True) # "Fri"
"""
seq = calendar.day_abbr if abbr else calendar.day_name
return seq[wd]
def month_name(month: int, abbr: bool = False) -> str:
"""
Return the name for a month integer (1=January).
Example:
month_name(1) # "January"
month_name(12, True) # "Dec"
"""
seq = calendar.month_abbr if abbr else calendar.month_name
return seq[month]
# ─────────────────────────────────────────────────────────────────────────────
# 2. Date iteration
# ─────────────────────────────────────────────────────────────────────────────
def iter_month_dates(year: int, month: int) -> Iterator[datetime.date]:
"""
Yield every date in the given month as datetime.date objects.
Example:
fridays = [d for d in iter_month_dates(2025, 3) if d.weekday() == FRI]
"""
_, n = calendar.monthrange(year, month)
for day in range(1, n + 1):
yield datetime.date(year, month, day)
def iter_weekdays_in_month(year: int, month: int, weekday: int) -> list[datetime.date]:
"""
Return all dates in the month that fall on the given weekday.
Example:
mondays = iter_weekdays_in_month(2025, 1, MON)
"""
return [d for d in iter_month_dates(year, month) if d.weekday() == weekday]
def iter_date_range(start: datetime.date, end: datetime.date) -> Iterator[datetime.date]:
"""
Yield each date from start to end inclusive.
Example:
for d in iter_date_range(datetime.date(2025,1,1), datetime.date(2025,1,7)):
print(d)
"""
current = start
step = datetime.timedelta(days=1)
while current <= end:
yield current
current += step
def business_days_in_month(year: int, month: int, holidays: set[datetime.date] | None = None) -> int:
"""
Count Mon–Fri days in a month, excluding any provided holiday dates.
Example:
business_days_in_month(2025, 1) # 23
"""
skip = holidays or set()
return sum(
1 for d in iter_month_dates(year, month)
if d.weekday() in WEEKDAYS and d not in skip
)
def nth_weekday_of_month(year: int, month: int, weekday: int, n: int) -> datetime.date:
"""
Return the nth occurrence (1-based) of `weekday` in the given month.
Use n=-1 for the last occurrence.
Example:
# 3rd Monday in January 2025
nth_weekday_of_month(2025, 1, MON, 3)
# Last Friday in December 2025
nth_weekday_of_month(2025, 12, FRI, -1)
"""
days = iter_weekdays_in_month(year, month, weekday)
if not days:
raise ValueError(f"No weekday {weekday} in {year}-{month:02d}")
if n == -1:
return days[-1]
if n < 1 or n > len(days):
raise IndexError(f"Only {len(days)} occurrences of weekday {weekday}")
return days[n - 1]
# ─────────────────────────────────────────────────────────────────────────────
# 3. Week grid
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class MonthGrid:
"""
A structured representation of a month's calendar, week by week.
Attributes:
year, month: integers
weeks: list of 7-int lists; 0 means padding outside the month
"""
year: int
month: int
weeks: list[list[int]]
@classmethod
def build(cls, year: int, month: int, firstweekday: int = MON) -> "MonthGrid":
"""
Build a MonthGrid for the given year/month.
Example:
grid = MonthGrid.build(2025, 2)
for week in grid.weeks:
print(week)
"""
cal = calendar.Calendar(firstweekday)
return cls(year=year, month=month, weeks=cal.monthdayscalendar(year, month))
def all_days(self) -> list[int]:
"""Return a flat list of all day numbers (0 = outside month)."""
return [d for week in self.weeks for d in week]
def real_days(self) -> list[int]:
"""Return only the non-zero day numbers."""
return [d for d in self.all_days() if d]
def week_of(self, day: int) -> int:
"""Return 0-based week index containing the given day number."""
for i, week in enumerate(self.weeks):
if day in week:
return i
raise ValueError(f"Day {day} not in {self.year}-{self.month:02d}")
def to_text(self) -> str:
"""Return a simple text representation of the grid."""
header = calendar.weekheader(3)
lines = [f"{month_name(self.month)} {self.year}", header]
for week in self.weeks:
lines.append(" ".join(f"{d:3d}" if d else " " for d in week))
return "\n".join(lines)
# ─────────────────────────────────────────────────────────────────────────────
# 4. Fiscal calendar helpers
# ─────────────────────────────────────────────────────────────────────────────
def fiscal_quarter(month: int, fiscal_year_start: int = 1) -> int:
"""
Return fiscal quarter (1–4) for a calendar month number.
fiscal_year_start: 1=Jan (default), 4=Apr (UK), 7=Jul (Australia), 10=Oct (US federal).
Example:
fiscal_quarter(3, fiscal_year_start=1) # 1 (Q1 Jan–Mar)
fiscal_quarter(3, fiscal_year_start=10) # 2 (Q2 Jan–Mar in Oct FY)
"""
offset = (month - fiscal_year_start) % 12
return offset // 3 + 1
def quarter_months(quarter: int, fiscal_year_start: int = 1) -> list[int]:
"""
Return the three month numbers in a given fiscal quarter.
Example:
quarter_months(1) # [1, 2, 3]
quarter_months(1, fiscal_year_start=4) # [4, 5, 6]
"""
start_offset = (quarter - 1) * 3
return [((fiscal_year_start - 1 + start_offset + i) % 12) + 1 for i in range(3)]
def iso_week_number(d: datetime.date) -> int:
"""
Return the ISO week number (1–53) for a date.
Example:
iso_week_number(datetime.date(2025, 1, 1)) # 1
"""
return d.isocalendar()[1]
def months_between(start: datetime.date, end: datetime.date) -> int:
"""
Return the number of complete calendar months from start to end.
Example:
months_between(datetime.date(2025,1,15), datetime.date(2025,6,15)) # 5
"""
return (end.year - start.year) * 12 + (end.month - start.month)
# ─────────────────────────────────────────────────────────────────────────────
# 5. Availability and schedule
# ─────────────────────────────────────────────────────────────────────────────
@dataclass
class Availability:
"""
Track which days of a month are available (True) or blocked (False).
Example:
avail = Availability.full_month(2025, 3)
avail.block_weekends()
avail.block_day(datetime.date(2025, 3, 17)) # block a holiday
free = avail.free_days()
"""
year: int
month: int
days: dict[int, bool] # day_number → available
@classmethod
def full_month(cls, year: int, month: int) -> "Availability":
_, n = calendar.monthrange(year, month)
return cls(year=year, month=month, days={d: True for d in range(1, n + 1)})
def block_weekends(self) -> None:
for d in list(self.days):
date = datetime.date(self.year, self.month, d)
if date.weekday() in WEEKEND:
self.days[d] = False
def block_day(self, date: datetime.date) -> None:
if date.year == self.year and date.month == self.month:
self.days[date.day] = False
def free_days(self) -> list[int]:
return [d for d, avail in sorted(self.days.items()) if avail]
def free_count(self) -> int:
return sum(1 for v in self.days.values() if v)
def utilization(self) -> float:
total = len(self.days)
return (total - self.free_count()) / total if total else 0.0
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== calendar demo ===")
print("\n--- month metadata ---")
print(f" month_days(2024, 2) = {month_days(2024, 2)}")
print(f" month_days(2025, 2) = {month_days(2025, 2)}")
print(f" is_leap(2024) = {is_leap(2024)}")
print(f" first_weekday(2025, 1) = {first_weekday(2025, 1)} ({weekday_name(first_weekday(2025,1))})")
print(f" leap_years(2020,2032) = {leap_years(2020, 2032)}")
print("\n--- day/month names ---")
print(f" day_name = {list(calendar.day_name)}")
print(f" month_name(6) = {month_name(6)}")
print(f" month_name(6, abbr=True)= {month_name(6, abbr=True)}")
print("\n--- iter_weekdays_in_month ---")
mondays_jan = iter_weekdays_in_month(2025, 1, MON)
print(f" Mondays in Jan 2025: {[d.day for d in mondays_jan]}")
print("\n--- nth_weekday_of_month ---")
thanks = nth_weekday_of_month(2025, 11, THU, 4)
print(f" 4th Thursday Nov 2025 (US Thanksgiving): {thanks}")
last_fri = nth_weekday_of_month(2025, 12, FRI, -1)
print(f" Last Friday Dec 2025: {last_fri}")
print("\n--- business_days_in_month ---")
print(f" Business days Jan 2025: {business_days_in_month(2025, 1)}")
print(f" Business days Feb 2025: {business_days_in_month(2025, 2)}")
print("\n--- MonthGrid ---")
grid = MonthGrid.build(2025, 2)
print(grid.to_text())
print(f" real_days count: {len(grid.real_days())}")
print(f" week_of(14): {grid.week_of(14)}")
print("\n--- fiscal_quarter ---")
for m in range(1, 13):
print(f" month {m:2d}: Q{fiscal_quarter(m)} Oct-FY Q{fiscal_quarter(m, 10)}")
print("\n--- quarter_months ---")
for q in range(1, 5):
print(f" Q{q}: {quarter_months(q)}")
print("\n--- iso_week_number ---")
d = datetime.date(2025, 12, 31)
print(f" ISO week of {d}: {iso_week_number(d)}")
print("\n--- months_between ---")
start = datetime.date(2025, 1, 15)
end = datetime.date(2025, 8, 14)
print(f" months_between({start}, {end}) = {months_between(start, end)}")
print("\n--- Availability ---")
avail = Availability.full_month(2025, 3)
avail.block_weekends()
avail.block_day(datetime.date(2025, 3, 17))
print(f" free days in Mar 2025 (no weekends, -Mar17): {avail.free_days()}")
print(f" free count: {avail.free_count()}")
print(f" utilization (blocked): {avail.utilization():.0%}")
print("\n--- calendar.timegm (UTC epoch) ---")
import time
st = time.strptime("2025-01-01 00:00:00", "%Y-%m-%d %H:%M:%S")
print(f" calendar.timegm('2025-01-01') = {calendar.timegm(st)}")
print("\n=== done ===")
For the datetime alternative — datetime.date provides rich date objects with timedelta arithmetic, ISO formatting, and strptime/strftime; the calendar module provides calendar-grid computations (week arrays, weekday offsets, leap logic, month ranges) that would be tedious with datetime alone — use datetime as the primary date type for comparisons, arithmetic, and ISO formatting; use calendar functions like monthrange(), monthcalendar(), and timegm() for grid layout, iteration scaffolding, and the UTC-correct epoch conversion that time.mktime() cannot provide. For the dateutil alternative — dateutil (PyPI) adds recurrence rules (rrule), fuzzy date parsing, relativedelta for month/year arithmetic, and full IANA timezone database support; calendar covers only the built-in stdlib subset with no external dependencies — use dateutil for advanced scheduling (every third Tuesday, monthly on the last day), fuzzy natural-language date parsing, or IANA timezone handling in production applications; use calendar for lightweight calendar grid rendering, fiscal quarter math, and scripts where adding a PyPI dependency is undesirable. The Claude Skills 360 bundle includes calendar skill sets covering month_days()/first_weekday()/is_leap()/leap_years()/weekday_name()/month_name() metadata helpers, iter_month_dates()/iter_weekdays_in_month()/iter_date_range()/business_days_in_month()/nth_weekday_of_month() date iteration tools, MonthGrid.build() with to_text()/week_of()/real_days(), fiscal_quarter()/quarter_months()/iso_week_number()/months_between() fiscal helpers, and Availability class with block_weekends()/block_day()/free_days()/utilization(). Start with the free tier to try calendar grid generation patterns and calendar module pipeline code generation.