Pythonで年内通算日を計算したい（年跨ぎもあるよ）

Pythonで年内通算日（年間通算日）を求めるのに、書式コードを探すのにちょっと時間がかかったので計算方法も合わせてメモしておく。

## 基本
まずは単純にその年の通算日を求めます。

### strftime() と strptime() の書式コードは%j
↓ここを参照
https://docs.python.org/ja/3/library/datetime.html#strftime-and-strptime-format-codes

`strftime('%j')`で変換すると0埋めした3桁の通算日が返却されます。
もちろんint()で数値化可能。

```python:test1.py
import datetime

def calc():

    day = datetime.date(2023, 4, 1)
    total_day = day.strftime('%j')

    print(f'2023/04/01の通算日 = {total_day}')

    total_int = int(total_day)
    print(f'intで扱う : {total_int}')

if __name__ == '__main__':
    calc()
```

```shell:実行結果
$ python test1.py
2023/04/01の通算日 = 091
intで扱う : 91
```

### うるう年も考慮される
年を書き換えて、うるう年の通算日を見てみましょう。

```Python:test2.py
import datetime

def calc():

    day = datetime.date(2020, 4, 1)
    total_day = day.strftime('%j')

    print(f'2020/04/01の通算日 = {total_day}')

if __name__ == '__main__':
    calc()
```
```shell:実行結果
$ python test2.py
2020/04/01の通算日 = 092
```

2023/04/01の通算日は91日だったので、うるう年の2/29もちゃんと加算されていることが分かります。

## 年跨ぎで通算日を扱いたい
年末～年始付近の通算日を扱う場合、年跨ぎで366(367)以上の値にしたり前年末の値をマイナスにしたりしたい場合がある方向け。

### 翌年の1月を366以上の値にしたい
基本的には翌年の通算日に365を足せばいいんですが、前年がうるう年の場合を考慮する必要があるため、前年12/31の通算日(365 or 366)を足すことになります。

```python:test_spanning_year1.py
import datetime

def calc(year):

    next_year = year + 1  # 次の年を定義

    day = datetime.date(year, 12, 1)  # 前年12/1
    additional_day = datetime.date(year, 12, 31)  # 加算分の12/31
    spanning_year = datetime.date(next_year, 1, 3)  # 年跨ぎで計算したい日

    total_day = day.strftime('%j')
    additional_total_day = int(additional_day.strftime('%j'))
    next_year_total_day = int(spanning_year.strftime('%j'))
    sppaning_total_day = additional_total_day + next_year_total_day

    print(f'{year}/12/01の通算日 = {total_day}')
    print(f'{year}/12/31の通算日 = {additional_total_day}')
    print(f'{next_year}/01/03の通算日 = {sppaning_total_day}')

if __name__ == '__main__':
    print('======== 通常の年 ========')
    calc(2022)
    print('====== うるう年考慮 ======')
    calc(2020)
```

```shell:実行結果
$ python test_spanning_year1.py
======== 通常の年 ========
2022/12/01の通算日 = 335
2022/12/31の通算日 = 365
2023/01/03の通算日 = 368
====== うるう年考慮 ======
2020/12/01の通算日 = 336
2020/12/31の通算日 = 366
2021/01/03の通算日 = 369
```

2020年のうるう年が考慮されて、1日多く加算されていることが分かります。

### 前年の12月をマイナスの値にしたい

最後はちょっとめんどくさい計算になります。
まず、マイナスの値を取りえるということは、**ゼロをどこにするか**を決める必要があります。
ゼロを無視して1/1が1日目、前年12/31が-1日目としても気にならないのであればそれで良いのですが、グラフに表す場合に1日差が2日差に見えてしまうなど、どうしても不都合が出てくる場合もあるかと思います。

今回は前年をマイナスで表現したいため、
・前年12/31を-1日目とする
・1/1を0日目とする
・1/1以降の通算日は (通常の通算日－1) 日とする
として計算します。

前年をマイナスで表現する場合は、基本的には（通算日－365－1）をすれば表現できますが、前項の＋365の例と同様に前年がうるう年だった場合を考慮する必要があるため、計算式は（通算日－前年12/31の通算日－1）となります。

```python:test_spanning_year2.py
import datetime

def calc(year):

    prev_year = year - 1  # 前の年を定義

    day = datetime.date(year, 1, 5)  # 通常年の1/5
    subtract_day = datetime.date(prev_year, 12, 31)  # 減算分の12/31
    spanning_year = datetime.date(prev_year, 12, 27)  # 年跨ぎで計算したい日

    total_day = int(day.strftime('%j')) - 1  # 1/1が0となるため、マイナス1する
    subtract_total_day = int(subtract_day.strftime('%j'))
    prev_year_total_day = int(spanning_year.strftime('%j'))
    sppaning_total_day = prev_year_total_day - subtract_total_day - 1

    print(f'{year}/01/05の通算日 = {total_day}')
    print(f'{prev_year}/12/31の通算日 = {subtract_total_day}')
    print(f'{prev_year}/12/27の通算日 = {sppaning_total_day}')

if __name__ == '__main__':
    print('======== 通常の年 ========')
    calc(2023)
    print('====== うるう年考慮 ======')
    calc(2021)
```

```shell:実行結果
$ python test_spanning_year2.py
======== 通常の年 ========
2023/01/05の通算日 = 4
2022/12/31の通算日 = 365
2022/12/27の通算日 = -5
====== うるう年考慮 ======
2021/01/05の通算日 = 4
2020/12/31の通算日 = 366
2020/12/27の通算日 = -5
```

結果が合っているか判断しづらいので、想定の値の表を作って確認しましょう。
どちらの年も1/1が0、12/31が-1となればいいので、正しい通算日は以下の表のようになります。

| 12/27 | 12/28 | 12/29 | 12/30 | 12/31 | 1/1 | 1/2 | 1/3 | 1/4 | 1/5 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|  -5 |  -4 | -3  |  -2 | -1  |  0 |  1 | 2  | 3  | 4  |

計算結果と合っていますね！

## 以上、
Pythonでの年内通算日の計算方法でした。
どこかで誰かの役に立てば幸いです。