読み込み中...

Pythonで休日判定を行うための基礎知識と実装例10選

休日判定 徹底解説 Python
この記事は約27分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●Pythonで休日判定をマスターしよう!

Pythonを使って休日判定を行うスキルは、多くのプログラマーにとって非常に重要です。

ビジネスロジックや予約システム、給与計算など、様々な場面で活用できる技術だからです。

休日判定を正確に行うことで、システムの信頼性が向上し、ユーザー満足度も高まります。

○休日判定とは?その重要性と基礎知識

休日判定とは、ある特定の日付が休日であるかどうかを判断するプロセスです。

単純そうに見えますが、実際にはかなり複雑な作業になります。

国や地域によって祝日が異なり、さらに年によっても変動することがあるからです。

日本の場合、土曜日、日曜日に加えて、国民の祝日、振替休日、国民の休日などが休日として扱われます。

これを正確に判定することは、多くのビジネスプロセスにおいて欠かせません。

例えば、営業日の計算、配送日の設定、給与計算など、様々な場面で休日判定が必要となります。

休日判定を行う際には、次の点に注意が必要です。

  1. 土日の判定
  2. 祝日の判定
  3. 振替休日の考慮
  4. 国民の休日(祝日と祝日に挟まれた平日)の判定
  5. 年によって変動する祝日への対応

これらを全て手動で管理するのは非常に煩雑で、ミスも起きやすいです。

そこで、Pythonを使って自動化することで、効率的かつ正確に休日判定を行うことができます。

○Pythonでの休日判定に必要なライブラリとデータ

Pythonで休日判定を行うには、主に次のライブラリとデータが必要となります。

  1. datetime -> 日付と時間を扱うための標準ライブラリです。日付の演算や曜日の取得などに使用します。
  2. jpholiday -> 日本の祝日を判定するためのサードパーティライブラリです。日本の祝日を簡単に取得できます。
  3. calendar -> Pythonの標準ライブラリで、カレンダー関連の機能を提供します。月ごとのカレンダーの生成などに使用できます。
  4. 祝日データ -> jpholidayライブラリを使用しない場合は、独自に祝日データを用意する必要があります。CSVファイルやデータベースなどで管理することが一般的です。

これらのライブラリとデータを組み合わせることで、柔軟で正確な休日判定システムを構築することができます。

●jpholidayライブラリを使いこなそう

jpholidayライブラリは、日本の祝日を簡単に判定できる非常に便利なツールです。

このライブラリを使いこなすことで、休日判定の精度が格段に向上します。

○サンプルコード1:jpholidayのインストールと基本機能

まずは、jpholidayライブラリをインストールしましょう。

コマンドラインで次のコマンドを実行します。

pip install jpholiday

インストールが完了したら、基本的な使い方を見ていきましょう。

import jpholiday
import datetime

# 今日の日付を取得
today = datetime.date.today()

# 今日が祝日かどうかを判定
if jpholiday.is_holiday(today):
    print(f"{today}は祝日です。")
else:
    print(f"{today}は祝日ではありません。")

# 祝日名を取得
holiday_name = jpholiday.is_holiday_name(today)
if holiday_name:
    print(f"祝日名: {holiday_name}")

このコードを実行すると、今日の日付が祝日かどうかを判定し、祝日の場合はその名前も表示します。

jpholidayライブラリのis_holiday()関数は、指定された日付が祝日であればTrueを返します。

is_holiday_name()関数は、祝日名を返します。

○サンプルコード2:jpholidayで指定日の休日判定

特定の日付が休日かどうかを判定するには、次のようなコードが使えます。

import jpholiday
import datetime

def is_holiday(date):
    if date.weekday() >= 5:  # 土曜日または日曜日
        return True
    return jpholiday.is_holiday(date)

# 特定の日付を指定
target_date = datetime.date(2024, 1, 1)

if is_holiday(target_date):
    print(f"{target_date}は休日です。")
else:
    print(f"{target_date}は平日です。")

# 1週間分の日付を判定
for i in range(7):
    date = target_date + datetime.timedelta(days=i)
    if is_holiday(date):
        print(f"{date}は休日です。")
    else:
        print(f"{date}は平日です。")

このコードでは、is_holiday()関数を定義して、土日と祝日を両方考慮した休日判定を行っています。

weekday()メソッドは月曜日を0、日曜日を6として曜日を返すので、5以上(土曜日または日曜日)の場合は休日と判定します。

○サンプルコード3:祝日リストの取得と活用法

jpholidayライブラリを使って、特定の年の祝日リストを取得し、活用する方法を見てみましょう。

import jpholiday
import datetime

def get_holidays(year):
    holidays = jpholiday.year_holidays(year)
    return [(date, name) for date, name in holidays]

# 2024年の祝日リストを取得
year = 2024
holiday_list = get_holidays(year)

print(f"{year}年の祝日リスト:")
for date, name in holiday_list:
    print(f"{date}: {name}")

# 特定の月の祝日を抽出
target_month = 5  # 5月
may_holidays = [holiday for holiday in holiday_list if holiday[0].month == target_month]

print(f"\n{year}年{target_month}月の祝日:")
for date, name in may_holidays:
    print(f"{date}: {name}")

# 祝日の数を数える
holiday_count = len(holiday_list)
print(f"\n{year}年の祝日の数: {holiday_count}日")

このコードでは、year_holidays()関数を使って特定の年の祝日リストを取得しています。

そして、全ての祝日を表示した後、特定の月(この例では5月)の祝日だけを抽出して表示しています。

最後に、その年の祝日の総数も計算しています。

●datetimeライブラリで日付操作をマスター

Pythonの標準ライブラリであるdatetimeは、日付や時間を扱う上で欠かせない存在です。

休日判定においても、datetimeを使いこなすことで、より柔軟で高度な判定が可能になります。

datetimeライブラリを使いこなせば、カレンダーの世界がぐっと広がりますよ。

○サンプルコード4:datetimeを使った日付と曜日の取得

datetimeライブラリを使って、日付や曜日を取得する方法を見ていきましょう。

from datetime import datetime, timedelta

# 現在の日時を取得
now = datetime.now()
print(f"現在の日時: {now}")

# 日付のみを取得
today = now.date()
print(f"今日の日付: {today}")

# 曜日を取得(0:月曜日, 6:日曜日)
weekday = today.weekday()
weekday_names = ["月", "火", "水", "木", "金", "土", "日"]
print(f"今日の曜日: {weekday_names[weekday]}曜日")

# 1週間後の日付を計算
next_week = today + timedelta(days=7)
print(f"1週間後の日付: {next_week}")

実行結果

現在の日時: 2024-08-08 12:34:56.789012
今日の日付: 2024-08-08
今日の曜日: 木曜日
1週間後の日付: 2024-08-15

上記のコードでは、datetime.now()を使って現在の日時を取得し、date()メソッドで日付部分のみを抽出しています。

weekday()メソッドは曜日を数値で返すので、曜日名のリストと組み合わせて、人間にわかりやすい形式で曜日を表示しています。

○サンプルコード5:土日を考慮した休日判定ロジック

次に、土日を考慮した休日判定ロジックを実装してみましょう。

from datetime import datetime, timedelta

def is_weekend(date):
    return date.weekday() >= 5  # 土曜日(5)または日曜日(6)

def get_next_business_day(date):
    next_day = date + timedelta(days=1)
    while is_weekend(next_day):
        next_day += timedelta(days=1)
    return next_day

# テスト用の日付
test_dates = [
    datetime(2024, 8, 8),  # 木曜日
    datetime(2024, 8, 9),  # 金曜日
    datetime(2024, 8, 10),  # 土曜日
    datetime(2024, 8, 11),  # 日曜日
]

for date in test_dates:
    if is_weekend(date):
        print(f"{date.date()} は週末です。次の営業日は {get_next_business_day(date).date()} です。")
    else:
        print(f"{date.date()} は平日です。")

実行結果

2024-08-08 は平日です。
2024-08-09 は平日です。
2024-08-10 は週末です。次の営業日は 2024-08-12 です。
2024-08-11 は週末です。次の営業日は 2024-08-12 です。

上記のコードでは、is_weekend()関数で土日かどうかを判定し、get_next_business_day()関数で次の営業日を取得しています。

土日が続く場合も適切に処理できるようになっています。

○サンプルコード6:時間帯を考慮した高度な判定方法

最後に、時間帯も考慮した高度な休日判定方法を実装してみましょう。

from datetime import datetime, time

def is_business_hours(dt):
    # 営業時間を9:00から17:30とする
    business_start = time(9, 0)
    business_end = time(17, 30)

    # 土日は営業時間外とする
    if dt.weekday() >= 5:
        return False

    return business_start <= dt.time() < business_end

# テスト用の日時
test_datetimes = [
    datetime(2024, 8, 8, 8, 59),   # 木曜日 8:59
    datetime(2024, 8, 8, 9, 0),    # 木曜日 9:00
    datetime(2024, 8, 8, 12, 0),   # 木曜日 12:00
    datetime(2024, 8, 8, 17, 29),  # 木曜日 17:29
    datetime(2024, 8, 8, 17, 30),  # 木曜日 17:30
    datetime(2024, 8, 10, 12, 0),  # 土曜日 12:00
]

for dt in test_datetimes:
    if is_business_hours(dt):
        print(f"{dt} は営業時間内です。")
    else:
        print(f"{dt} は営業時間外です。")

実行結果

2024-08-08 08:59:00 は営業時間外です。
2024-08-08 09:00:00 は営業時間内です。
2024-08-08 12:00:00 は営業時間内です。
2024-08-08 17:29:00 は営業時間内です。
2024-08-08 17:30:00 は営業時間外です。
2024-08-10 12:00:00 は営業時間外です。

このコードでは、is_business_hours()関数で日付だけでなく時間も考慮した判定を行っています。

営業時間を9:00から17:30と設定し、土日は常に営業時間外としています。

datetimeライブラリを使いこなすことで、単純な日付判定から、時間帯まで考慮した複雑な判定まで、幅広いシチュエーションに対応することができます。

●独自の休日判定メソッドを作成しよう

ここからは、より高度な休日判定メソッドを作成していきます。

実際のビジネスシーンでは、標準的な休日だけでなく、会社独自の休日や特別な営業日などを考慮する必要があるでしょう。

そんな複雑なケースにも対応できる、柔軟な休日判定メソッドを作っていきましょう。

○サンプルコード7:カスタム休日データの作成と判定

まずは、カスタムの休日データを作成し、それを用いた休日判定メソッドを実装してみましょう。

from datetime import datetime, date

class HolidayCalendar:
    def __init__(self):
        self.holidays = set()
        self.working_holidays = set()

    def add_holiday(self, holiday_date):
        self.holidays.add(holiday_date)

    def add_working_holiday(self, date):
        self.working_holidays.add(date)

    def is_holiday(self, check_date):
        if isinstance(check_date, datetime):
            check_date = check_date.date()

        # 土日判定
        if check_date.weekday() >= 5:
            # 土日でも稼働日なら休日ではない
            return check_date not in self.working_holidays

        # 平日でも休日として登録されていれば休日
        return check_date in self.holidays

# カレンダーの作成と休日の設定
calendar = HolidayCalendar()

# 2024年の祝日(一部)
calendar.add_holiday(date(2024, 1, 1))   # 元日
calendar.add_holiday(date(2024, 1, 8))   # 成人の日
calendar.add_holiday(date(2024, 2, 11))  # 建国記念の日
calendar.add_holiday(date(2024, 2, 12))  # 振替休日

# 特別な稼働日
calendar.add_working_holiday(date(2024, 2, 10))  # 土曜日だが営業日

# テスト
test_dates = [
    date(2024, 1, 1),   # 元日
    date(2024, 1, 2),   # 平日
    date(2024, 2, 10),  # 特別稼働日(土曜日)
    date(2024, 2, 11),  # 建国記念の日(日曜日)
    date(2024, 2, 12),  # 振替休日(月曜日)
]

for test_date in test_dates:
    if calendar.is_holiday(test_date):
        print(f"{test_date} は休日です。")
    else:
        print(f"{test_date} は営業日です。")

実行結果

2024-01-01 は休日です。
2024-01-02 は営業日です。
2024-02-10 は営業日です。
2024-02-11 は休日です。
2024-02-12 は休日です。

このコードでは、HolidayCalendarクラスを作成し、休日と特別な稼働日を登録できるようにしています。

is_holiday()メソッドでは、土日判定に加えて、登録された休日と稼働日を考慮した判定を行っています。

○サンプルコード8:複雑な条件による休日判定ロジック

次に、より複雑な条件を含む休日判定ロジックを実装してみましょう。

例えば、毎月第3金曜日を休日とするような規則を追加します。

from datetime import datetime, date, timedelta

class AdvancedHolidayCalendar(HolidayCalendar):
    def __init__(self):
        super().__init__()
        self.monthly_holidays = []

    def add_monthly_holiday(self, weekday, week_number):
        self.monthly_holidays.append((weekday, week_number))

    def is_holiday(self, check_date):
        if super().is_holiday(check_date):
            return True

        # 毎月の特定の日が休日かチェック
        for weekday, week_number in self.monthly_holidays:
            if self.is_nth_weekday(check_date, weekday, week_number):
                return True

        return False

    @staticmethod
    def is_nth_weekday(check_date, weekday, n):
        # その月の最初の日を取得
        first_day = date(check_date.year, check_date.month, 1)
        # 求めたい曜日の最初の日を計算
        offset = (weekday - first_day.weekday()) % 7
        target_day = first_day + timedelta(days=offset + 7 * (n - 1))
        return check_date == target_day

# カレンダーの作成と休日の設定
calendar = AdvancedHolidayCalendar()

# 基本の休日を設定
calendar.add_holiday(date(2024, 1, 1))   # 元日
calendar.add_holiday(date(2024, 1, 8))   # 成人の日

# 毎月第3金曜日を休日に設定
calendar.add_monthly_holiday(4, 3)  # 金曜日は4、第3週

# テスト
test_dates = [
    date(2024, 1, 1),   # 元日
    date(2024, 1, 19),  # 1月第3金曜日
    date(2024, 2, 16),  # 2月第3金曜日
    date(2024, 3, 15),  # 3月第3金曜日
    date(2024, 1, 12),  # 1月第2金曜日(休日ではない)
]

for test_date in test_dates:
    if calendar.is_holiday(test_date):
        print(f"{test_date} は休日です。")
    else:
        print(f"{test_date} は営業日です。")

実行結果

2024-01-01 は休日です。
2024-01-19 は休日です。
2024-02-16 は休日です。
2024-03-15 は休日です。
2024-01-12 は営業日です。

このコードでは、AdvancedHolidayCalendarクラスを作成し、毎月の特定の日(例:第3金曜日)を休日として設定できるようにしています。

is_nth_weekday()メソッドを使って、指定された日が第n週の特定の曜日かどうかを判定しています。

○サンプルコード9:判定結果の効果的な出力方法

休日判定の結果を視覚的に理解しやすくするため、カレンダー形式で表示する方法を紹介します。

月単位でカレンダーを出力し、休日を赤色で強調表示することで、一目で休日を確認できるようになります。

import calendar
from datetime import date

class CalendarPrinter:
    def __init__(self, holiday_calendar):
        self.holiday_calendar = holiday_calendar

    def print_month(self, year, month):
        cal = calendar.monthcalendar(year, month)
        print(f"{year}年 {month}月".center(20))
        print("月 火 水 木 金 土 日")

        for week in cal:
            for day in week:
                if day == 0:
                    print("   ", end="")
                else:
                    current_date = date(year, month, day)
                    if self.holiday_calendar.is_holiday(current_date):
                        print(f"\033[91m{day:2d}\033[0m ", end="")  # 赤色で表示
                    else:
                        print(f"{day:2d} ", end="")
            print()

# カレンダーの作成と休日の設定
calendar = AdvancedHolidayCalendar()
calendar.add_holiday(date(2024, 1, 1))   # 元日
calendar.add_holiday(date(2024, 1, 8))   # 成人の日
calendar.add_monthly_holiday(4, 3)  # 毎月第3金曜日を休日に設定

# カレンダーの表示
printer = CalendarPrinter(calendar)
printer.print_month(2024, 1)  # 2024年1月のカレンダーを表示

実行結果

    2024年 1月    
月 火 水 木 金 土 日
 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日、8日、19日(第3金曜日)が赤色で表示されます。

このコードでは、CalendarPrinterクラスを作成し、print_month()メソッドで指定された年月のカレンダーを表示します。

休日判定には先ほど作成したAdvancedHolidayCalendarクラスを使用しています。

カレンダーの表示には標準ライブラリのcalendarモジュールを利用しています。

monthcalendar()メソッドで月のカレンダーデータを取得し、それを元に整形して表示しています。

休日と判定された日は、ANSIエスケープシーケンスを使用して赤色で表示されます。

ただし、全ての環境でこの色付けが機能するわけではありません。Windows環境など、一部の環境では別途設定が必要な場合があります。

この方法を使えば、複雑な休日ルールがある場合でも、視覚的に休日を確認することができます。

例えば、営業日カレンダーの作成や、休暇計画の立案などに活用できるでしょう。

さらに発展させるなら、複数月を一度に表示したり、祝日名を表示したりすることも可能です。

また、HTMLやPDFなどの形式で出力すれば、より見やすいカレンダーを作成することができます。

●AIと機械学習で休日判定を進化させる

休日判定の精度と効率を飛躍的に向上させる方法として、AIと機械学習の活用が注目を集めています。

従来の規則ベースの判定方法では対応が難しかった複雑なパターンや、将来の休日予測にも応用が可能です。

Python環境でAIを活用した休日判定を実装する方法を探ってみましょう。

○サンプルコード10:機械学習を用いた未来の休日予測

休日パターンを学習し、将来の休日を予測する機械学習モデルを作成します。

過去の休日データを使用して学習を行い、未来の日付に対する休日判定を行います。

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from datetime import datetime, timedelta

# 過去の休日データを生成(実際はより大規模で正確なデータを使用)
start_date = datetime(2020, 1, 1)
end_date = datetime(2023, 12, 31)
date_range = pd.date_range(start=start_date, end=end_date)

holidays = pd.DataFrame({
    'date': date_range,
    'is_holiday': [1 if d.weekday() >= 5 or d.day == 1 else 0 for d in date_range],
    'year': date_range.year,
    'month': date_range.month,
    'day': date_range.day,
    'weekday': date_range.weekday
})

# 特徴量とターゲットを分離
X = holidays[['year', 'month', 'day', 'weekday']]
y = holidays['is_holiday']

# データを訓練セットとテストセットに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# ランダムフォレストモデルを訓練
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# モデルの精度を評価
accuracy = model.score(X_test, y_test)
print(f"モデルの精度: {accuracy:.2f}")

# 2024年の休日を予測
future_dates = pd.date_range(start=datetime(2024, 1, 1), end=datetime(2024, 12, 31))
future_features = pd.DataFrame({
    'year': future_dates.year,
    'month': future_dates.month,
    'day': future_dates.day,
    'weekday': future_dates.weekday
})

predictions = model.predict(future_features)

# 結果を表示
future_holidays = pd.DataFrame({
    'date': future_dates,
    'predicted_holiday': predictions
})

print("\n2024年の予測休日:")
print(future_holidays[future_holidays['predicted_holiday'] == 1].head())

上記のコードでは、scikit-learnライブラリを使用してランダムフォレスト分類器を訓練しています。

過去の休日データを基に学習を行い、2024年の休日を予測しています。

実際の運用では、より詳細で正確な過去のデータを使用し、モデルの精度を高めることが重要です。

機械学習を活用することで、複雑な休日パターンや特殊な休日ルールにも対応できる可能性が広がります。

また、予測精度を向上させるために、祝日情報や地域特有のイベント情報などを追加の特徴量として使用することも考えられます。

●休日判定のトラブルシューティングとベストプラクティス

休日判定プログラムを開発・運用する中で、様々な問題に直面することがあります。

ここでは、よく遭遇するエラーとその解決策、そしてパフォーマンスを最適化するためのTipsを紹介します。

○よくあるエラーとその解決策

□タイムゾーンの問題

異なるタイムゾーンのデータを扱う際に発生するエラーです。

解決策として、pytzライブラリを使用してタイムゾーンを明示的に指定することをお勧めします。

from datetime import datetime
import pytz

# 日本時間で現在の日時を取得
jst = pytz.timezone('Asia/Tokyo')
now = datetime.now(jst)
print(f"日本時間: {now}")

□祝日データの更新忘れ

法改正などによる祝日の変更に対応できていないケースがあります。

jpholidayライブラリを使用している場合は、定期的にライブラリを更新することが重要です。

pip install --upgrade jpholiday

□日付形式の不一致

文字列として渡された日付データの形式が想定と異なる場合にエラーが発生します。

datetime.strptime()を使用して、明示的に日付形式を指定することで解決できます。

from datetime import datetime

date_string = "2024-08-08"
date_object = datetime.strptime(date_string, "%Y-%m-%d").date()
print(f"変換後の日付: {date_object}")

○パフォーマンス最適化のためのTips

□キャッシュの活用

頻繁に参照される日付の休日判定結果をメモリ上にキャッシュすることで、処理速度を向上させることができます。

from functools import lru_cache

@lru_cache(maxsize=None)
def is_holiday(date):
    # 休日判定のロジック
    pass

□バッチ処理の利用

大量の日付に対して休日判定を行う場合、1件ずつ処理するのではなく、バッチ処理を利用することでパフォーマンスが向上します。

import pandas as pd

def batch_holiday_check(dates):
    # 日付のリストに対して一括で休日判定を行う
    pass

dates = pd.date_range(start="2024-01-01", end="2024-12-31")
results = batch_holiday_check(dates)

□並列処理の活用

大規模なデータセットを処理する場合、multiprocessingライブラリを使用して並列処理を行うことで、処理速度を大幅に向上させることができます。

from multiprocessing import Pool

def process_chunk(chunk):
    # チャンクごとの処理を記述
    pass

if __name__ == '__main__':
    with Pool() as p:
        results = p.map(process_chunk, data_chunks)

まとめ

本記事では、Pythonを使った休日判定の基礎から応用まで、幅広いトピックをカバーしました。

jpholidayやdatetimeといった基本的なライブラリの使用方法から、独自の休日判定ロジックの作成、さらにはAIと機械学習を活用した高度な手法まで、段階的に解説してきました。

今回紹介した技術やアプローチを参考に、プロジェクトのニーズに合わせた最適な休日判定システムを構築していただければ幸いです。