試験までの日数をカウントダウンする（日付差の計算）

本記事は大阪ハイテクノロジー専門学校のロボット学科が作るアドベントカレンダーのいち記事です。他の記事も是非御覧ください↓
https://qiita.com/advent-calendar/2024/osaka-hightech


# はじめに
「○月△日まであと×日！」と数えるのが大変なので、自動で計算してくれるやつをM5Stackで作ります。

# 暗算するのも大変
そもそも日付差の計算が大変な理由は、
* うるう年の存在
* 月またぎ、年またぎ
* 1月~12月の日数がバラバラ

かと思います。
今日(2024/12/3)からクリスマス(2024/12/25)なら、シンプルに25-3=22日後ですね。
では来年の成人の日(2025/1/13)は何日後・・・？

# 計算のテクニック
プログラムの実装自体は泥臭い感じになるのですが、それでも多少のテクニックを使うことで実装を簡単にすることができます。
今回はこちらのサイトを参考にしました。
>『生れてから今日まで、何日生きてきただろう！』
https://www2.kaiyodai.ac.jp/~kurokawa/lecture/lbp/m/tb09b2.htm

例えば、来年の2025/2/12を対象日とします。
![今日.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/2cd3e86c-c7bc-a048-04f1-92f4de9c377c.png)

まず、①と③はそれぞれの年の元旦を基準にした日数です。
区間内の月を列挙しそれぞれの日数と、(今日-1)[日]を加算します。

次に、②はそれぞれの年の元旦同士の差です。区間内の年数をnとすれば、365×n[日]、そこにうるう年が含まれるごとに+1[日]します。

実際に計算してみましょう。2024年はうるう年なので注意！

①：1月から11月の日数と(3-1)[日]なので、31+29+31+30+31+30+31+31+30+31+30+(3-1)=337[日]
※小の月（30日以下の月）は「西向く侍（士）」で覚えた人も多いと思います。2,4,6,9,11月ですね。

②：366[日]

③：1月と(12-1)[日]なので、31+(12-1)=42[日]

そして、日数差は図の通り、②+③-①で求まるので、
②+③-①=366+42-337=71[日]
となります！

一応、計算サイトでも確認。。
> 日数計算（日付−日付）
https://keisan.casio.jp/exec/system/1177658154

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/c495193e-086f-d1f8-e12b-2c91939cc87e.png)
いいですね！


# プログラムの実装
いよいよプログラムの実装に移ります。
今回はM5Stackで動作させるため、M5Flow上でブロックプログラミングをしてみます。
なお、プログラムを簡単にするため、うるう年判定は4で割り切れる年だけとします **（2100年をまたぐとバグる）**。

早速プログラムを試したい人はこちらからどうぞ！
https://github.com/sousci/assort/blob/main/day_remain_v1%5Bqiita%5D.m5f

※日数表示用にラベル1~3を画面に配置しています
![uiflow_block_1733282844243.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/818c96d8-9357-0cc4-3a30-11fb9ded0bb6.png)


## 各月の日数を取得する関数[month2days]
①,③を計算するにあたり、各月の日数を取得する関数を定義します。
引数として、月と年の2つのデータを取ります。年を取得する理由は、うるう年で2月が29日になるためです。後は ~~パワープレイ。~~ 条件分岐。

この辺、リストにする方が良さそうですが、ブロックがわかりませんでした。

![uiflow_block_1733283610784.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/a0afa978-63dd-b30e-8490-5faf33d25444.png)

## 日数差を計算する関数[calcRemain]
上のmonth2days関数を使いながら、日数差を計算するメインの関数を定義します。
引数として、現在の年月日(curr_y, curr_m, curr_d)と対象の年月日(tar_y, tar_m, tar_d)を取ります。

![uiflow_block_1733284208595.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/68ae3b32-5fd1-bcbe-eab2-c005e2d08570.png)

ざっくりとした流れは、
* ②の計算(remain_days2)
* ①の計算(remain_days1)
* ③の計算(remain_days3) ※①の計算と同じ
* ②+③-①する

②の計算は、カウンタ変数'k'を現在の年にして対象の年の一つ前までループし、'k'がうるう年でなければ365[日]、うるう年なら366[日]をremain_days2に加算します。シンプルですね。

①の計算は、'k'を1にセットし、現在の月の一つ前までループします。'k'をmonth2days関数の引数に取って、その戻り値をremain_days1に加算していきます。
続いて、現在の日から-1[日]したものを更にremain_days1に加算します。

③の計算は、①の計算を現在の日付ではなく対象の日付に置き換えるだけです。変数名を見比べてください。

①～③が計算できたら、②+③-①をcalcRemain関数の戻り値とします。

## その他のプログラム
5分おきに、NTPサーバーからリアルタイムで時刻の取得し、都度残りの日数を計算します。
![uiflow_block_1733285572805.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/5bf8cda4-3db8-d6de-1761-e627d0369bc1.png)


# 動作確認
最新のファームウェアを書き込んで、APIを入力して・・・Run!!
![IMG_9330.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/40c7ff4b-4f02-a845-35a1-1c322019d728.jpeg)

remain:71と出ていますね！よい感じです。

# （おまけ）7セグも光らせてみた
M5Stackの画面だと物足りないので、大型の7セグを光らせてみました。
このために基板も作りました。ものづくり最高！
![IMG_9251.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606237/827c899d-a278-a3fc-f1df-20d10ffeaf53.jpeg)

ダイナミック点灯はM5Stackにとって負担なので、デコーダーIC(TC4511)を使ってます。
7セグの動作電圧が7.2VでM5Stackの出力電圧と合わないので、トランジスタアレイも挟んでます。

思ったより大仕事になってしまった。。。 ~~日数カウントダウンのプログラムの10倍かかった~~