Python月初・月末(初日・最終日)、最終X曜日の日付を取得する方法

 

Pythonで月の初日・最終日、および最終X曜日(最終月曜日、最終金曜日など)の日付を取得する方法を詳しく解説します。実用的なサンプルコードとともに様々なパターンを学びましょう。

月初・月末の基本取得

基本的な月初・月末の取得

from datetime import date, datetime, timedelta
import calendar

def get_month_first_last(year, month):
    """指定年月の初日と最終日を取得"""
    # 月初(1日)
    first_day = date(year, month, 1)
    
    # 月末(最終日)
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    return {
        'first_day': first_day,
        'last_day': last_day,
        'days_in_month': last_day_num
    }

# 使用例
result = get_month_first_last(2024, 2)
print(f"2024年2月")
print(f"初日: {result['first_day']}")
print(f"最終日: {result['last_day']}")
print(f"日数: {result['days_in_month']}日")

現在月の初日・最終日

from datetime import date, datetime
import calendar

def get_current_month_bounds():
    """現在月の初日と最終日を取得"""
    today = date.today()
    year = today.year
    month = today.month
    
    first_day = date(year, month, 1)
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    return {
        'today': today,
        'first_day': first_day,
        'last_day': last_day,
        'days_remaining': (last_day - today).days
    }

# 使用例
current_bounds = get_current_month_bounds()
print(f"今日: {current_bounds['today']}")
print(f"今月の初日: {current_bounds['first_day']}")
print(f"今月の最終日: {current_bounds['last_day']}")
print(f"月末まで: {current_bounds['days_remaining']}日")

前月・次月の初日・最終日

from datetime import date, timedelta
import calendar

def get_previous_month_bounds(reference_date=None):
    """前月の初日・最終日を取得"""
    if reference_date is None:
        reference_date = date.today()
    
    # 前月の年月を計算
    if reference_date.month == 1:
        prev_year = reference_date.year - 1
        prev_month = 12
    else:
        prev_year = reference_date.year
        prev_month = reference_date.month - 1
    
    first_day = date(prev_year, prev_month, 1)
    _, last_day_num = calendar.monthrange(prev_year, prev_month)
    last_day = date(prev_year, prev_month, last_day_num)
    
    return {
        'first_day': first_day,
        'last_day': last_day,
        'year': prev_year,
        'month': prev_month
    }

def get_next_month_bounds(reference_date=None):
    """次月の初日・最終日を取得"""
    if reference_date is None:
        reference_date = date.today()
    
    # 次月の年月を計算
    if reference_date.month == 12:
        next_year = reference_date.year + 1
        next_month = 1
    else:
        next_year = reference_date.year
        next_month = reference_date.month + 1
    
    first_day = date(next_year, next_month, 1)
    _, last_day_num = calendar.monthrange(next_year, next_month)
    last_day = date(next_year, next_month, last_day_num)
    
    return {
        'first_day': first_day,
        'last_day': last_day,
        'year': next_year,
        'month': next_month
    }

# 使用例
prev_month = get_previous_month_bounds()
next_month = get_next_month_bounds()

print(f"前月: {prev_month['first_day']} ~ {prev_month['last_day']}")
print(f"次月: {next_month['first_day']} ~ {next_month['last_day']}")

最終X曜日の取得

基本的な最終曜日の取得

from datetime import date, timedelta
import calendar

def get_last_weekday_of_month(year, month, weekday):
    """指定年月の最終X曜日を取得
    
    Args:
        year: 年
        month: 月
        weekday: 曜日 (0=月曜日, 1=火曜日, ..., 6=日曜日)
    """
    # 月の最終日を取得
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    # 最終日から遡って指定曜日を探す
    days_back = (last_day.weekday() - weekday) % 7
    target_date = last_day - timedelta(days=days_back)
    
    return target_date

# 使用例
year = 2024
month = 1

weekdays = ['月', '火', '水', '木', '金', '土', '日']
for i, weekday_name in enumerate(weekdays):
    last_weekday = get_last_weekday_of_month(year, month, i)
    print(f"最終{weekday_name}曜日: {last_weekday}")

複数月の最終曜日を一括取得

def get_last_weekdays_for_year(year, weekday):
    """年間の各月の最終X曜日を取得"""
    last_weekdays = []
    
    for month in range(1, 13):
        last_weekday = get_last_weekday_of_month(year, month, weekday)
        last_weekdays.append({
            'year': year,
            'month': month,
            'date': last_weekday,
            'day': last_weekday.day
        })
    
    return last_weekdays

# 使用例:2024年の各月の最終金曜日
last_fridays = get_last_weekdays_for_year(2024, 4)  # 4 = 金曜日
print("=== 2024年 各月の最終金曜日 ===")
for info in last_fridays:
    print(f"{info['month']:2d}月: {info['date']} ({info['day']:2d}日)")

営業日としての最終平日

def get_last_business_day_of_month(year, month):
    """月の最終営業日を取得(土日を除く)"""
    # 月の最終日から遡って平日を探す
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    # 土日でない日を探す
    while last_day.weekday() >= 5:  # 5=土曜日, 6=日曜日
        last_day -= timedelta(days=1)
    
    return last_day

def get_first_business_day_of_month(year, month):
    """月の最初の営業日を取得(土日を除く)"""
    first_day = date(year, month, 1)
    
    # 土日でない日を探す
    while first_day.weekday() >= 5:
        first_day += timedelta(days=1)
    
    return first_day

# 使用例
year = 2024
month = 1

first_business = get_first_business_day_of_month(year, month)
last_business = get_last_business_day_of_month(year, month)

print(f"{year}年{month}月")
print(f"最初の営業日: {first_business}")
print(f"最終営業日: {last_business}")

高度な日付計算

N番目の曜日の取得

def get_nth_weekday_of_month(year, month, weekday, n):
    """月のN番目の指定曜日を取得
    
    Args:
        year: 年
        month: 月  
        weekday: 曜日 (0=月曜日, ..., 6=日曜日)
        n: 何番目か (1=第1, 2=第2, ..., -1=最後, -2=最後から2番目)
    """
    if n > 0:
        # N番目を前から探す
        first_day = date(year, month, 1)
        days_until_weekday = (weekday - first_day.weekday()) % 7
        first_occurrence = first_day + timedelta(days=days_until_weekday)
        
        target_date = first_occurrence + timedelta(weeks=n-1)
        
        # 月を超えていないかチェック
        if target_date.month != month:
            return None
        
        return target_date
    
    elif n < 0:
        # 最後からN番目を探す
        last_weekday = get_last_weekday_of_month(year, month, weekday)
        target_date = last_weekday + timedelta(weeks=n+1)
        
        # 月を下回っていないかチェック
        if target_date.month != month or target_date.year != year:
            return None
        
        return target_date
    
    else:
        return None

# 使用例
year = 2024
month = 1

print(f"=== {year}年{month}月の月曜日 ===")
for i in range(1, 6):
    nth_monday = get_nth_weekday_of_month(year, month, 0, i)  # 0 = 月曜日
    if nth_monday:
        print(f"第{i}月曜日: {nth_monday}")
    else:
        print(f"第{i}月曜日: なし")

print(f"\n最後から数えた月曜日:")
last_monday = get_nth_weekday_of_month(year, month, 0, -1)
second_last_monday = get_nth_weekday_of_month(year, month, 0, -2)
print(f"最終月曜日: {last_monday}")
print(f"最後から2番目: {second_last_monday}")

月の境界日の詳細情報

def get_month_boundary_info(year, month):
    """月の境界日の詳細情報を取得"""
    # 基本情報
    first_day = date(year, month, 1)
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    # 曜日名
    weekday_names = ['月', '火', '水', '木', '金', '土', '日']
    
    # 営業日情報
    first_business = get_first_business_day_of_month(year, month)
    last_business = get_last_business_day_of_month(year, month)
    
    # 週末情報
    first_weekend = None
    last_weekend = None
    
    # 最初の週末を探す
    current = first_day
    while current.month == month:
        if current.weekday() >= 5:  # 土日
            first_weekend = current
            break
        current += timedelta(days=1)
    
    # 最後の週末を探す
    current = last_day
    while current.month == month:
        if current.weekday() >= 5:  # 土日
            last_weekend = current
            break
        current -= timedelta(days=1)
    
    return {
        'year': year,
        'month': month,
        'first_day': {
            'date': first_day,
            'weekday': weekday_names[first_day.weekday()],
            'is_weekend': first_day.weekday() >= 5
        },
        'last_day': {
            'date': last_day,
            'weekday': weekday_names[last_day.weekday()],
            'is_weekend': last_day.weekday() >= 5
        },
        'first_business_day': first_business,
        'last_business_day': last_business,
        'first_weekend': first_weekend,
        'last_weekend': last_weekend,
        'total_days': last_day_num
    }

# 使用例
boundary_info = get_month_boundary_info(2024, 1)
print(f"=== {boundary_info['year']}年{boundary_info['month']}月 境界情報 ===")
print(f"初日: {boundary_info['first_day']['date']} ({boundary_info['first_day']['weekday']})")
print(f"最終日: {boundary_info['last_day']['date']} ({boundary_info['last_day']['weekday']})")
print(f"最初の営業日: {boundary_info['first_business_day']}")
print(f"最終営業日: {boundary_info['last_business_day']}")
print(f"最初の週末: {boundary_info['first_weekend']}")
print(f"最後の週末: {boundary_info['last_weekend']}")

実用的な応用例

給与支払日の計算

def calculate_salary_payment_dates(year):
    """年間の給与支払日を計算(月末営業日)"""
    payment_dates = []
    
    for month in range(1, 13):
        payment_date = get_last_business_day_of_month(year, month)
        
        payment_info = {
            'month': month,
            'payment_date': payment_date,
            'day_of_month': payment_date.day,
            'weekday': ['月', '火', '水', '木', '金', '土', '日'][payment_date.weekday()]
        }
        
        payment_dates.append(payment_info)
    
    return payment_dates

# 使用例
salary_dates = calculate_salary_payment_dates(2024)
print("=== 2024年 給与支払予定日(月末営業日)===")
for info in salary_dates:
    print(f"{info['month']:2d}月: {info['payment_date']} ({info['weekday']})")

会議日程の自動設定

def schedule_monthly_meetings(year, weekday, week_number):
    """毎月の定期会議日程を設定
    
    Args:
        year: 年
        weekday: 曜日 (0=月曜日, ..., 6=日曜日)
        week_number: 第何週 (1-4, または -1で最終週)
    """
    meeting_schedule = []
    weekday_names = ['月', '火', '水', '木', '金', '土', '日']
    
    for month in range(1, 13):
        meeting_date = get_nth_weekday_of_month(year, month, weekday, week_number)
        
        if meeting_date:
            meeting_info = {
                'month': month,
                'date': meeting_date,
                'weekday': weekday_names[weekday],
                'week_description': f"第{week_number}週" if week_number > 0 else "最終週"
            }
            meeting_schedule.append(meeting_info)
    
    return meeting_schedule

# 使用例:毎月第2火曜日の会議
meetings = schedule_monthly_meetings(2024, 1, 2)  # 1=火曜日, 2=第2週
print("=== 2024年 月例会議日程(第2火曜日)===")
for meeting in meetings:
    print(f"{meeting['month']:2d}月: {meeting['date']} ({meeting['weekday']})")

四半期末日の計算

def get_quarter_end_dates(year):
    """四半期末日を取得"""
    quarters = [
        {'quarter': 'Q1', 'month': 3},
        {'quarter': 'Q2', 'month': 6},
        {'quarter': 'Q3', 'month': 9},
        {'quarter': 'Q4', 'month': 12}
    ]
    
    quarter_ends = []
    
    for quarter_info in quarters:
        month = quarter_info['month']
        _, last_day_num = calendar.monthrange(year, month)
        quarter_end = date(year, month, last_day_num)
        
        # 四半期末営業日も計算
        business_end = get_last_business_day_of_month(year, month)
        
        quarter_data = {
            'quarter': quarter_info['quarter'],
            'calendar_end': quarter_end,
            'business_end': business_end,
            'days_difference': (quarter_end - business_end).days
        }
        
        quarter_ends.append(quarter_data)
    
    return quarter_ends

# 使用例
quarter_ends = get_quarter_end_dates(2024)
print("=== 2024年 四半期末日 ===")
for quarter in quarter_ends:
    print(f"{quarter['quarter']}: {quarter['calendar_end']} "
          f"(営業日: {quarter['business_end']})")

祝日を考慮した営業日計算

def get_business_day_with_holidays(year, month, holidays=None):
    """祝日を考慮した営業日計算"""
    if holidays is None:
        # 簡略化した日本の祝日(一部)
        holidays = [
            date(year, 1, 1),   # 元日
            date(year, 2, 11),  # 建国記念の日
            date(year, 4, 29),  # 昭和の日
            date(year, 5, 3),   # 憲法記念日
            date(year, 5, 4),   # みどりの日
            date(year, 5, 5),   # こどもの日
        ]
    
    # 月の範囲を取得
    first_day = date(year, month, 1)
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    # 最初の営業日を取得(祝日考慮)
    first_business = first_day
    while (first_business.weekday() >= 5 or  # 週末
           first_business in holidays):       # 祝日
        first_business += timedelta(days=1)
        if first_business.month != month:
            first_business = None
            break
    
    # 最後の営業日を取得(祝日考慮)
    last_business = last_day
    while (last_business.weekday() >= 5 or   # 週末
           last_business in holidays):        # 祝日
        last_business -= timedelta(days=1)
        if last_business.month != month:
            last_business = None
            break
    
    # 営業日数をカウント
    business_days_count = 0
    current = first_day
    while current <= last_day:
        if (current.weekday() < 5 and        # 平日
            current not in holidays):         # 祝日でない
            business_days_count += 1
        current += timedelta(days=1)
    
    return {
        'first_business_day': first_business,
        'last_business_day': last_business,
        'business_days_count': business_days_count,
        'holidays_in_month': [h for h in holidays if h.month == month and h.year == year]
    }

# 使用例
business_info = get_business_day_with_holidays(2024, 1)
print("=== 2024年1月 営業日情報(祝日考慮)===")
print(f"最初の営業日: {business_info['first_business_day']}")
print(f"最後の営業日: {business_info['last_business_day']}")
print(f"営業日数: {business_info['business_days_count']}日")
print(f"月内の祝日: {business_info['holidays_in_month']}")

月次レポート生成用の日付取得

def generate_monthly_report_dates(year, month):
    """月次レポート用の重要日付を生成"""
    # 基本的な境界日
    first_day = date(year, month, 1)
    _, last_day_num = calendar.monthrange(year, month)
    last_day = date(year, month, last_day_num)
    
    # 営業日
    first_business = get_first_business_day_of_month(year, month)
    last_business = get_last_business_day_of_month(year, month)
    
    # 月の中間点
    mid_day = date(year, month, last_day_num // 2)
    
    # 各週の最終営業日
    weekly_ends = []
    month_calendar = calendar.monthcalendar(year, month)
    
    for week in month_calendar:
        # 週の最後の有効な日を取得
        week_days = [day for day in week if day != 0]
        if week_days:
            week_end = date(year, month, week_days[-1])
            # 営業日でない場合は前の営業日を探す
            while week_end.weekday() >= 5:
                week_end -= timedelta(days=1)
                if week_end.month != month:
                    week_end = None
                    break
            if week_end:
                weekly_ends.append(week_end)
    
    report_dates = {
        'report_period': f"{year}年{month}月",
        'period_start': first_day,
        'period_end': last_day,
        'business_period_start': first_business,
        'business_period_end': last_business,
        'mid_month': mid_day,
        'weekly_business_ends': weekly_ends,
        'total_days': last_day_num,
        'business_days': len([d for d in weekly_ends if d is not None])
    }
    
    return report_dates

# 使用例
report_dates = generate_monthly_report_dates(2024, 1)
print(f"=== {report_dates['report_period']} レポート用日付 ===")
print(f"期間: {report_dates['period_start']} ~ {report_dates['period_end']}")
print(f"営業期間: {report_dates['business_period_start']} ~ {report_dates['business_period_end']}")
print(f"月中間: {report_dates['mid_month']}")
print("週次営業日終了:")
for i, week_end in enumerate(report_dates['weekly_business_ends'], 1):
    print(f"  第{i}週: {week_end}")

複数月の比較分析

def compare_multiple_months(year, months):
    """複数月の境界日情報を比較"""
    comparison_data = []
    
    for month in months:
        month_info = get_month_boundary_info(year, month)
        
        # 追加の分析情報
        first_day = month_info['first_day']['date']
        last_day = month_info['last_day']['date']
        
        # 週末の日数計算
        weekend_count = 0
        current = first_day
        while current <= last_day:
            if current.weekday() >= 5:
                weekend_count += 1
            current += timedelta(days=1)
        
        comparison_info = {
            'month': month,
            'first_day_weekday': month_info['first_day']['weekday'],
            'last_day_weekday': month_info['last_day']['weekday'],
            'total_days': month_info['total_days'],
            'weekend_days': weekend_count,
            'business_days': month_info['total_days'] - weekend_count,
            'starts_weekend': month_info['first_day']['is_weekend'],
            'ends_weekend': month_info['last_day']['is_weekend']
        }
        
        comparison_data.append(comparison_info)
    
    return comparison_data

# 使用例:第1四半期の比較
q1_months = [1, 2, 3]
q1_comparison = compare_multiple_months(2024, q1_months)

print("=== 2024年第1四半期 月別比較 ===")
print("月  初日曜日 最終日曜日 総日数 営業日 週末日")
print("-" * 45)
for info in q1_comparison:
    print(f"{info['month']:2d}月 {info['first_day_weekday']:>4} "
          f"{info['last_day_weekday']:>6} {info['total_days']:>4}日 "
          f"{info['business_days']:>4}日 {info['weekend_days']:>4}日")

日付計算のユーティリティクラス

class MonthBoundaryCalculator:
    def __init__(self):
        self.weekday_names = ['月', '火', '水', '木', '金', '土', '日']
    
    def get_month_info(self, year, month):
        """月の包括的な情報を取得"""
        return {
            'boundaries': get_month_first_last(year, month),
            'business_days': {
                'first': get_first_business_day_of_month(year, month),
                'last': get_last_business_day_of_month(year, month)
            },
            'last_weekdays': {
                name: get_last_weekday_of_month(year, month, i)
                for i, name in enumerate(self.weekday_names)
            }
        }
    
    def get_year_summary(self, year):
        """年間の月境界情報サマリー"""
        year_data = []
        
        for month in range(1, 13):
            month_info = self.get_month_info(year, month)
            
            summary = {
                'month': month,
                'days': month_info['boundaries']['days_in_month'],
                'first_business': month_info['business_days']['first'],
                'last_business': month_info['business_days']['last'],
                'last_friday': month_info['last_weekdays']['金']
            }
            
            year_data.append(summary)
        
        return year_data

# 使用例
calculator = MonthBoundaryCalculator()
year_summary = calculator.get_year_summary(2024)

print("=== 2024年 年間サマリー ===")
print("月  日数 初回営業日   最終営業日   最終金曜日")
print("-" * 50)
for summary in year_summary:
    print(f"{summary['month']:2d}月 {summary['days']:2d}日 "
          f"{summary['first_business']} {summary['last_business']} "
          f"{summary['last_friday']}")

まとめ

  • 月初・月末date(year, month, 1)calendar.monthrange()で簡単取得
  • 最終X曜日:月末から逆算して指定曜日を検索
  • 営業日計算:週末と祝日を考慮した実用的な実装
  • 応用例:給与日、会議日程、四半期末、レポート生成
  • ユーティリティ:再利用可能なクラス設計

これらの方法を活用することで、ビジネスアプリケーションでの複雑な日付計算が効率的に実装できます。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座