【python】設定した月日と曜日から直近過去および未来の同じ月日と曜日の年を取得する

■概要

月、日、および曜日を指定して、直近同じ月日と曜日の年をyyyy-mm-dd(曜日)形式で表示する。

また、指定した月日と曜日が処理日と同じ場合は、処理日の曜日も表示する。


・概要図

入力月日と曜日
2月24日(月)
入力月日と曜日 2月24日(月)
処理日
2025年2月24日(月)
処理日 2025年2月24日(月)
2020年2月24日(月)
2020年2月24日(月)
2031年2月24日(月)
2031年2月24日(月)
入力月日と曜日
1月4日(木)
入力月日と曜日 1月4日(木)
2024年1月4日(木)
2024年1月4日(木)
2029年1月4日(木)
2029年1月4日(木)
凡例
凡例
入力値
入力値
出力値
出力値
時系列
時系列
Text is not SVG – cannot display

▼ディレクトリ構成

実装するディレクトリ構成は以下の通り。

※以下の記述について、単体テストファイルについては、割愛する(GitHub参照)。

■フローチャート

前述の「▼ディレクトリ構成」にて提示したファイル名に対する、各処理のフローチャートを以下に示す。

※「get_next_and_last_day_const.py」については、定数のみのためフローチャートは割愛する(後述の「■サンプルコード」参照)。

▼main.py

・メイン処理

開始
開始
処理日を取得
処理日を取得
閏年フラグ初期化
閏年フラグ初期化
初期年を設定
初期年を設定
yes
yes
no
no
閏年フラグをセット
閏年フラグをセット
曜日判定処理
曜日判定処理
true
true
false
false
処理日 = 入力月日
処理日 = 入力月日
日付チェック処理
日付チェック処理
true
true
false
false
2/29
2/29
日付検索処理(過去)
日付検索処理(過去)
結果出力処理(処理日)
結果出力処理(処理日)
日付検索処理(未来)
日付検索処理(未来)
処理日 < 入力月日
処理日 < 入力月日
日付検索処理(過去)
日付検索処理(過去)
日付検索処理(未来)
日付検索処理(未来)
true
true
true
true
false
false
false
false
処理日 > 入力月日
処理日 > 入力月日
日付検索処理(過去)
日付検索処理(過去)
日付検索処理(未来)
日付検索処理(未来)
true
true
false
false
終了
終了
処理曜日 = 入力曜日
処理曜日 = 入力曜日
true
true
false
false
Text is not SVG – cannot display

▼get_next_and_last_day_util.py

・曜日判定処理

開始
開始
終了
終了
return
曜日リストに含まれるか判定
return曜日リストに含まれるか判定
Text is not SVG – cannot display

・日付フォーマット処理

try
try
except
except
開始
開始
終了
終了
return
日付変換
return…
例外出力
例外出力
return
None
return…
例外エラー
例外エラー
コンソール
コンソール
Text is not SVG – cannot display

・結果出力処理

開始
開始
終了
終了
結果出力
結果出力
結果
結果
コンソール
コンソール
Text is not SVG – cannot display

・対象日付検索処理

開始
開始
終了
終了
閏年
閏年
yes
yes
閏年取得処理
閏年取得処理
インプットの曜日と検索した年月日の曜日が
一致するまで繰り返し
インプットの曜日と検索した年月日の曜日が 一致するまで繰り返し
閏年取得処理
閏年取得処理
インプットの曜日と検索した年月日の曜日が
一致するまで繰り返し
インプットの曜日と検索した年月日の曜日が 一致するまで繰り返し
年を加算or減算
年を加算or減算
結果出力処理
結果出力処理
no
no
Text is not SVG – cannot display

・閏年取得処理

開始
開始
終了
終了
閏年になるまで繰り返し
閏年になるまで繰り返し
年を加算or減算
年を加算or減算
return
閏年
return…
Text is not SVG – cannot display

■サンプルコード

・main.py

from datetime import datetime

from util.get_next_and_last_day_util import is_week_format, date_formatter, \
    search_day, print_day


def main(month: int, day: int, weekday: str) -> None:
    # 処理日を取得
    dt_now = datetime.now()

    # 閏年フラグ初期化
    is_leap_year = False

    # デフォルトの年は処理日付の年
    dt_default_year = dt_now.year

    # 2/29なら閏年判定
    if month == 2 and day == 29:
        is_leap_year = True
    else:
        # 日付のフォーマットチェックしてNGなら終了
        if date_formatter(dt_default_year, month, day) is None:
            return

    # 曜日判定処理して曜日でないなら処理終了
    if not is_week_format(weekday):
        return

    # 処理日と入力した月日と曜日が一致した場合
    if (dt_now.month, dt_now.day) == (month, day):

        # 直近過去の日付を検索(ex 処理年が2024なら2023スタート)
        search_day(
            dt_default_year - 1, month, day, weekday, -1, is_leap_year)

        # 曜日が一緒の場合
        if dt_now.strftime('%A') == weekday:
            # 処理日を出力
            print_day(date_formatter(dt_default_year, month, day))

        # 直近未来の日付を検索(ex 処理年が2024なら2025からスタート)
        search_day(
            dt_default_year + 1, month, day, weekday, 1, is_leap_year)

    # 処理日 < 入力した月日の場合
    elif (dt_now.month, dt_now.day) < (month, day):
        # 直近過去の日付を検索(ex 処理年が2024なら2023スタート)
        search_day(
            dt_default_year - 1, month, day, weekday, -1, is_leap_year)

        # 直近未来の日付を検索(ex 処理年が2024なら2024からスタート)
        search_day(
            dt_default_year, month, day, weekday, 1, is_leap_year)

    # 処理日 > 入力した月日の場合
    elif (dt_now.month, dt_now.day) > (month, day):
        # 直近過去の日付を検索(ex 処理年が2024なら2024スタート)
        search_day(
            dt_default_year, month, day, weekday, -1, is_leap_year)

        # 直近未来の日付を検索(ex 処理年が2024なら2025からスタート)
        search_day(
            dt_default_year + 1, month, day, weekday, 1, is_leap_year)


if __name__ == '__main__':
    # ここの日付と曜日を操作する
    main(2, 24, 'Monday')

・get_next_and_last_day_util.py

import calendar
from datetime import datetime
from typing import Optional

from get_next_and_last_day.const.get_next_and_last_day_const import WEEK_LIST


def is_week_format(week: str) -> bool:
    """
    曜日判定処理
     paramが曜日リストに含まれているか判定
    :param week: 曜日
    :return: True/False
    """
    return week in WEEK_LIST


def date_formatter(year: int, month: int, day: int) -> Optional[datetime]:
    """
    日付フォーマット処理
    :param year: 年
    :param month: 月
    :param day: 日
    :return: True: 日付型/ False: None
    """
    try:
        # 日付変換して返す
        return datetime.strptime(
            f'{str(year)}-{str(month)}-{str(day)}', '%Y-%m-%d')

    except Exception as e:
        # 日付に変換できなかった場合の例外処理
        print(e)
        return None


def print_day(target_date: datetime) -> None:
    """
    結果出力処理
    :param target_date: 検索結果の日付
    :return: None
    """
    print(target_date.strftime('%Y-%m-%d (%A)'))


def search_day(
        year: int, month: int, day: int, weekday: str,
        point: int, is_leap_year: bool) -> None:
    """
    対象日付検索処理
    :param year: 年
    :param month: 月
    :param day: 日
    :param weekday: 曜日
    :param point: 加算減算値(-1/1)
    :param is_leap_year: 閏年判定フラグ(True/False)
    :return: None
    """
    # 閏年なら、閏年判定で取得した年で判定ループ
    if is_leap_year:
        year = get_leapdays(year, point)
        # インプットの曜日と検索した年月日の曜日が一致するまで繰り返し
        while weekday != date_formatter(year, month, day).strftime('%A'):
            year = get_leapdays(year + point, point)
    # 閏年以外ならインクリメント/デクリメント
    else:
        # インプットの曜日と検索した年月日の曜日が一致するまで繰り返し
        while weekday != date_formatter(year, month, day).strftime('%A'):
            year = year + point

    print_day(date_formatter(year, month, day))


def get_leapdays(year: int, point: int):
    """
    閏年取得処理
    :param year: 年
    :param point: 加算減算値(-1/1)
    :return: 年(閏年)
    """

    # 閏年になるまで繰り返し
    while not calendar.isleap(year):
        year = year + point

    return year

■実行結果

▼処理日=入力日(曜日も一致)

処理日:2025年2月24日(月)

入力日:2月24日(月)

結果:

2020-02-24 (Monday)
2025-02-24 (Monday)
2031-02-24 (Monday)

▼処理日=入力日(曜日は不一致)

処理日:2025年2月24日(月)

入力日:2月24日(木)

結果:

2022-02-24 (Thursday)
2028-02-24 (Thursday)

▼処理日>入力日

処理日:2025年2月24日(月)

入力日:1月4日(木)

結果:

2024-01-04 (Thursday)
2029-01-04 (Thursday)

▼処理日<入力日

処理日:2025年2月24日(月)

入力日:9月23日(火)

結果:

2014-09-23 (Tuesday)
2025-09-23 (Tuesday)

▼処理日<入力日(閏年)

処理日:2025年2月24日(月)

入力日:2月29日(水)

結果:

2012-02-29 (Wednesday)
2040-02-29 (Wednesday)

■参考

本処理を実装するにあたり、参考とさせていただいたURLを以下に示す。


■GitHub

ソース全量はGitHub参照。

コメント