次の13日の金曜日がいつか求めるプログラム

#初めに
来週が13日の金曜日で、その次の13日の金曜日が次いつ来るかが気になったので、13日の金曜日を求めるプログラムをPython3で書きました。
(執筆時2020年3月07日)

N番煎じ感が否めないですが、何か参考になれば幸いです。
また、こうした方がいいなどのアドバイスがありましたらご教示お願いします。

#使ったモジュール
今回はcalenderモジュールとdatetimeモジュールを使いました。

calenderモジュールはその名の通り、カレンダー情報を取得できます。
datetimeモジュールは現在の年月を取得するのに使いました。

それぞれのモジュールの使い方は下記ドキュメントを参考にしてください
[calender](https://docs.python.org/ja/3/library/calendar.html)
[datetime](https://docs.python.org/ja/3/library/datetime.html?highlight=datetime#module-datetime)

#ソースコード
この記事のタイトルでは次の13日の金曜日を求めるとしていますが、自分で指定した期間(年)の13日の金曜日をすべて求められるようにしています。一覧を求めたうえで、現在の日付から最も近い13日の金曜日を求めています。

どこで何の処理をしているかはコメントにて書きましたので、そちらを見ていただけると幸いです。


```python:cal_13_Fri.py
import calendar
import datetime

#何年から何年までの13日の金曜日を調べるか
#----------------------------------------------------------------------------------
start_year = 1930
end_year = 2200
#----------------------------------------------------------------------------------

#初期化処理
#----------------------------------------------------------------------------------
dt_now = datetime.datetime.now()
now_year = dt_now.year
now_month = dt_now.month
now_date = dt_now.day
next_13_fri = 0#現在の日付を取得して、次の13日の金曜日を求めるための変数
list_13_fir = []#13日の金曜日の年月のリスト
cal = calendar.Calendar()
#----------------------------------------------------------------------------------

for i in range(start_year,end_year+1):#年
    for j in range(1,13):#月
        cal_y_m = cal.monthdayscalendar(i,j)#i年j月のカレンダー情報を取得
        len_cal_y_m = len(cal_y_m)
        for k in range(len_cal_y_m):#週
            if cal_y_m[k][4] == 13:#配列の2番目の引数4は金曜日を表す
                list_13_fir.append("{}年{}月13日は金曜日です".format(i,j))
                #現在の年月を取得し次の13日の金曜日を求める
                #----------------------------------------------------------------------------------
                if i == now_year :#同じ年に13月の金曜日があるか判定
                    if j >= now_month and next_13_fri == 0:
                        if j == now_month and now_date > 13:#現在の月に13日の金曜日があるけど、日付がすぎてしまっている場合
                            pass
                        else:
                            next_13_fri =  "次の13日の金曜日は{}年{}月です".format(i,j)
                elif i > now_year:#年をまたぐ場合はこっち
                    if next_13_fri == 0:
                        next_13_fri =  "次の13日の金曜日は{}年{}月です".format(i,j) 
                #----------------------------------------------------------------------------------
            elif cal_y_m[k][4] > 13:#13日以上は計算する必要がないので、break
                break
print(list_13_fir)
print(next_13_fri)

```

#実行結果
ソースコード上だと指定した期間の13日の金曜日も求めるのですが、長くなってしまうので次の13日の金曜日だけ表示させます。

```python:cal_13_Fri.py
次の13日の金曜日は2020年3月です
```

結果を見ると正しく求められてそうです。
ちなみにこの次の13日の金曜日は11月にあるみたいです。


#(補足)ツェラーの公式
この記事を書くにあたり、曜日を求める公式がないかと調べていたところ、ツェラーの公式というものがありました。
[Wikipedia-ツェラーの公式](https://ja.wikipedia.org/wiki/%E3%83%84%E3%82%A7%E3%83%A9%E3%83%BC%E3%81%AE%E5%85%AC%E5%BC%8F)
以下Wikipediaからの抜粋です。
>
$y 年 {\displaystyle m} 月 {\displaystyle d} 日の曜日を求める。$
$ただし、1月と2月は、前年のそれぞれ13月・14月として扱う。$
$たとえば、2020年1月1日・2月1日は、2019年13月1日・14月1日とする。$
$また、紀元前 {\displaystyle {\hat {y}}} 年は西暦 {\displaystyle 1-{\hat {y}}} 年として扱う。$
たとえば、紀元前1年・前2年・前3年は、0年・－1年・－2年となる。
>
$曜日を表す {\displaystyle h} は次の式で求められる：$

>
${\displaystyle h=\left\\{d+\left\lfloor {\frac {26(m+1)}{10}}\right\rfloor +Y+\left\lfloor {\frac {Y}{4}}\right\rfloor +{\mathit {\Gamma }}\right\\}\mod 7} $ 

>
>
hは、0～6で土曜日～金曜日を表す。
>
$\mathit \Gamma はグレゴリオ暦 (Gregorian) かユリウス暦 (Julian) かで変わる項で、$
>
```math
\Gamma = \begin{cases} 
  - 2 C + \left\lfloor \frac C 4 \right\rfloor & \mbox{: Gregorian } ( 1582 \lessapprox y ) \\
  - C + 5 & \mbox{: Julian } ( 4 \lessapprox y \lessapprox 1582 )
\end{cases}
```

>
ただし
>
```math
C = \left\lfloor \frac y { 100 } \right\rfloor \\
{\displaystyle Y=y\mod 100}  。
```
コンピュータの多くの環境では負数の剰余を保証しないので、整数の合同関係を使って

>
```math
{\displaystyle {\mathit {\Gamma }}={\begin{cases}5C+\left\lfloor {\frac {C}{4}}\right\rfloor &{\mbox{: Gregorian }}(1582\lessapprox y)\\6C+5&{\mbox{: Julian }}(4\lessapprox y\lessapprox 1582)\end{cases}}} \mathit \Gamma = \begin{cases} 
  5 C + \left\lfloor \frac C 4 \right\rfloor & \mbox{: Gregorian } ( 1582 \lessapprox y ) \\
  6 C + 5 & \mbox{: Julian } ( 4 \lessapprox y \lessapprox 1582 )
\end{cases}
と変形する。
```

>
$\lfloor x\rfloor はxを超えない（x以下）の最大の整数（床関数）、{\displaystyle x\mod n}は {\displaystyle x} を {\displaystyle n} で割った剰余である。$

他にも細かな注意事項がありますが、詳しくはWikipediaを見てください。

##ソースコード
(2020/03/08 追記)コメントにてジェネレータ関数というものを教えていただいたので、それを使ってツェラーの公式を使った場合のコードを書いてみました。

```python:Friday13_Zeller.py

def friday13(start_year,end_year):
    day = 13
    for year in range(start_year,end_year+1):
        for month in range(1,13):
            if month < 3:#1月、2月は前年の13月、14月として扱っている
                temp_year =  year - 1
                temp_month = month + 12
            else:
                temp_year =  year
                temp_month = month
            Y = temp_year % 100
            C = int(Y/100)
            h = (day + int(26*(temp_month+1)/10)+Y+int(Y/4)+5*C+int(C/4))%7#ツェラーの公式
            
            if h == 6:
                yield year, month 

for year, month in friday13(start_year=2020,end_year=2200):
    print("{}年{}月の13日は金曜日です".format(year,month))
    if input("次を探しますか?(y or n) > ") != "y":
        break

```