何をする?
MixJuice経由でIchigoJamから祝日データを取得し、
日曜と祝日をハイライトしたカレンダーを出力します。
※IchigoJamはjig.jpの登録商標です。
日本の祝日を取得するAPI
日本の祝日を取得するWeb APIを探したところ、以下のものが見つかりました。
国民の祝日:日本
指定した年・月・日の祝日のリストが得られます。
例えば、以下のURLにアクセスすると
https://api.national-holidays.jp/202108
以下の結果が得られました。
[{"date": "2021-08-08", "name": "山の日", "type": "国民の祝日"}, {"date": "2021-08-09", "name": "振替休日", "type": "振替休日"}]
見やすく整形すると、以下のような感じです。
[
{
"date": "2021-08-08",
"name": "山の日",
"type": "国民の祝日"
},
{
"date": "2021-08-09",
"name": "振替休日",
"type": "振替休日"
}
]
今回見つかった中で唯一指定の月の休日の取得に対応しており、今回の目的との相性が良さそうです。
さらに、HTTPSに対応しています。
Firefoxで直接APIのURLにアクセスした時はAccess-Control-Allow-Origin
ヘッダは確認できませんでしたが、
IchigoJam webのバーチャルMixJuiceからのアクセスを試してみると
Access-Control-Allow-Origin: *
ヘッダが入り、データの取得が可能でした。
記事執筆時点で、1948年~2022年のデータの取得に対応しているようです。
Holidays JP API
指定の月の祝日のリストが取得できます。
例えば、以下のURLにアクセスすると
https://holidays-jp.github.io/api/v1/2021/date.json
以下の結果が得られました。
{
"2021-01-01": "元日",
"2021-01-11": "成人の日",
"2021-02-11": "建国記念の日",
"2021-02-23": "天皇誕生日",
"2021-03-20": "春分の日",
"2021-04-29": "昭和の日",
"2021-05-03": "憲法記念日",
"2021-05-04": "みどりの日",
"2021-05-05": "こどもの日",
"2021-07-22": "海の日",
"2021-07-23": "スポーツの日",
"2021-08-08": "山の日",
"2021-08-09": "休日 山の日",
"2021-09-20": "敬老の日",
"2021-09-23": "秋分の日",
"2021-11-03": "文化の日",
"2021-11-23": "勤労感謝の日"
}
今回見つかった他のAPIとは違い、結果に最初から改行やインデントが入っていました。
HTTPSに対応し、Access-Control-Allow-Origin: *
ヘッダも入っているため、
IchigoJam webのバーチャルMixJuiceでの利用がしやすそうです。
記事執筆時点では2015年~2022年のデータの取得に対応しているようで、
今回見つかった中では一番対応範囲が狭いです。
祝日判定web API
祝日のリストを得る機能は見当たらず、指定した日1日が祝日かどうかを判定できます。
例えば、
http://s-proj.com/utils/checkHoliday.php?kind=ph&date=20210809
というURLにアクセスすると
holiday
という結果が返り、
http://s-proj.com/utils/checkHoliday.php?kind=ph&date=20210810
というURLにアクセスすると
else
という結果が返ってきました。
今回見つかった他のAPIと違い、結果はJSONではなくシンプルなプレーンテキストで返るようです。
ヘッダにAccess-Control-Allow-Origin
は含まれていませんでした。
また、HTTPSへの対応も不完全です。(接続はできるが、証明書が期限切れの自己署名証明書)
IchigoJam webのバーチャルMixJuiceでの利用は難しいかもしれません。
データの対応範囲は2000年~となっていますが、試してみると1955年までは結果が返ってきました。
記事執筆時において、上限は2022年のようです。
Nager.Date
Worldwide Public Holidays - Nager.Date
日本だけでなく、世界各国の祝日のリストの取得ができます。
例えば、
https://date.nager.at/api/v3/PublicHolidays/2021/JP
というURLにアクセスすると、
{"date":"2021-01-01","localName":"元日","name":"New Year's Day","countryCode":"JP","fixed":false,"global":true,"counties":null,"launchYear":null,"types":["Public"]}
という形式のデータの配列が得られました。
見やすく整形すると、以下のような感じです。
{
"date": "2021-01-01",
"localName": "元日",
"name": "New Year's Day",
"countryCode": "JP",
"fixed": false,
"global": true,
"counties": null,
"launchYear": null,
"types": [
"Public"
]
}
日付と名前の他に様々な属性がついており、結果が長くなっています。
また、HTTPSに対応しています。
Firefoxで直接APIのURLにアクセスした時はAccess-Control-Allow-Origin
ヘッダは確認できませんでしたが、
IchigoJam webのバーチャルMixJuiceからのアクセスを試してみると
access-control-allow-origin: *
ヘッダが入り、データの取得が可能でした。
対応範囲については、1年~9999年について結果が返ってきました。
今回見つけた中で一番対応範囲が広いようです。
祝日と日曜日が重なる場合は、振替休日の日付が祝日の日付として返るようです。
また、2019年5月1日「天皇の即位の日」が載っていない、
2021年8月8日(振替休日9日)ではなく8月11日が「山の日」となっているなど、正確性に難があるようです。
まとめ
API | HTTPS対応 | CORS対応 | 指定年取得 | 指定月取得 | 指定日取得 | シンプルな結果 | 正確性 | 対応年範囲 |
---|---|---|---|---|---|---|---|---|
国民の祝日:日本 | ○ | ○ | ○ | ○ | ○ | ○ | ○ | 1948~2022 |
Holidays JP API | ○ | ◎ | ○ | × | × | ○ | ○ | 2015~2022 |
祝日判定web API | × | × | × | × | ○ | ◎ | ○ | 1955(2000?)~2022 |
Nager.Date | ○ | ○ | ○ | × | × | × | × | 1~9999 |
(CORS対応:ヘッダにAccess-Control-Allow-Origin: *
が含まれる)
(正確性:2019年5月1日「天皇の即位の日」および2021年8月8日「山の日」をチェックしました)
カレンダーの出力
今回は、年と月を入力させ、その年のその月のカレンダーを出力することにします。
カレンダーを出力するには、「その月の1日が何曜日か」と「その月が何日までか」の情報を使います。
「その月が何日までか」は、昨日の記事の方法で求められます。
「その月の1日が何曜日か」は、ツェラーの公式を用いて求められます。
これらの情報をもとに、1日の位置を合わせ、月末まで日付を表す数を出力します。
10 ' カレンダー
20 INPUT "ナン ネン?",Y
30 INPUT "ナン ガツ?",M
40 IF M<=2 Z=Y-1:N=M+12 ELSE Z=Y:N=M
50 C=Z/100:D=Z%100
60 E=26*(N+1)/10:F=5*C+C/4
70 H=(1+E+D+D/4+F+6)%7
80 G=(30+M%2)^(M>7):IF M=2 U=Y%400=0OR(Y%4=0ANDY%100):G=28+U
90 ?" Su Mo Tu We Th Fr Sa"
100 IF H>0 FOR I=1 TO H:?" ";:NEXT
110 FOR I=1 TO G
120 ?" ";:IF I<10 ?" ";
130 ?I;
140 H=H+1:IF H>6 H=0:?CHR$(10);
150 NEXT
160 IF H>0 ?CHR$(10);
祝日リストの取得と利用
使用するAPI
今回は、情報が正確で対応範囲が広い、国民の祝日:日本を使用することにしました。
月単位で取得できるAPIもありますが、データをまとめて取得できる年単位のAPIを使用することにしました。
祝日の日付の取得
今回は祝日の日付のみ取得できればいいので、以下の方法で取得することにしました。
- 読み込んだ文字が数字なら、数値の読み取りを進める。
- 読み込んだ文字が数字でなく、直前の文字が数字のとき、2個前に読み込んだ数値が取得中の年であれば、1個前に読み込んだ数値を月、今読み込んだ数値を日として、祝日のリストに加える。
さらに、今回読み込む数値は日付を表す正の数のみです。
そこで、読み取り中の数値が正であれば、直前の文字が数字だと判定します。
祝日のリストは、ビットマップで表現します。
1個の月を配列の2個の要素(16ビット×2=32ビット)で表し、各ビットを祝日なら1、祝日でなければ0とします。
ビットは0番目~31番目が使えるので、最大31の日を表すのにピッタリです。
以下が具体的なコードです。
ビットマップと、読み込んだ数値を格納する変数N
(年)、T
(月)、H
(日)を初期化します。
60 FORI=2TO25:[P+I]=0:NEXT:N=0:T=0:H=0
クエリを送信し、進捗状況表示用の変数L
を初期化します。
70 ?CHR$(10);"MJ GETS api.national-holidays.jp/";Y:L=0
1文字読み込みます。
数値を読み込んだ後文字を読み込めず0.5秒経つか、]
を読み込んだら、次の処理に行きます。
80 C=INKEY():IF(TANDTICK()>29)ORC=#5D?CHR$(10);:GOTO140ELSEIFC=0GOTO80
読み込んだ文字が数字であれば、読み込んだ数値H
を更新します。
90 CLT:IF#30<=CANDC<=#39H=H*10+C-#30:GOTO80
読み込んだ文字が数字でないとき、ここ以降の処理に入ります。
数値を読み込んでいないとき、そのまま次の文字を読み込みに行きます。
100 IFH=0GOTO80
年が読み込み中の年で、月が有効なものである時、ビットマップに祝日情報を追加します。
110 IFN=YAND1<=TANDT<=12X=P+T*2+(H>15):[X]=[X]|1<<(H&15)ELSEGOTO130
進捗状況を出力します。
新しい月に移った時、この月の数になるように*
を出力します。
120 IFL<T?"*";:L=L+1:GOTO120
読み込んだ数値の更新と初期化を行い、次の文字を読み込みに行きます。
130 N=T:T=H:H=0:GOTO80
休日を考慮したカレンダーの出力
今回は、休日(日曜日と祝日)を白黒反転した数字で表示することにしました。
これを実現するため、#E0
からの部分に白黒反転した数字のフォントを用意します。
20 FORI=0TO79:POKE#700+I,~PEEK(#180+I):NEXT
どの年のどの月のカレンダーを出力するのかを読み込みます。
30 INPUT"ナン ネン?",Y:INPUT"ナン ガツ?",M
ツェラーの公式でこの月の1日の曜日を求めます。
H
を祝日フラグに使うため、曜日を表す変数をW
としています。
140 IFM<3Z=Y-1:N=M+12ELSEZ=Y:N=M
150 C=Z/100:D=Z%100:E=26*(N+1)/10:F=5*C+C/4:W=(1+E+D+D/4+F+6)%7:G=(30+M%2)^(M>7):IFM=2U=Y%400=0OR(Y%4=0ANDY%100):G=28+U
曜日と、1日の前の空白を出力します。
160 ?" Su Mo Tu We Th Fr Sa":IFW>0FORI=1TOW:?" ";:NEXT
休日かを判定し、それに応じて日付を出力します。
- 1日~9日 (1桁) のとき
- 休日のときは、白1色(
#8F
)を出力します。 - 平日のときは、空白(
#20
)を出力します。
- 休日のときは、白1色(
- 10日~ (2桁) のとき
- 休日の時は、
#E0
~#E9
の白黒反転した数字を出力します。 - 平日の時は、
#30
~#39
の通常の数字を出力します。
- 休日の時は、
170 FORI=1TOG:?" ";:X=P+M*2+(I>15):H=W=0OR[X]>>(I&15)&1:IFI<10?CHR$(#20+H*#6F);ELSE?CHR$(#30+#B0*H+I/10);
日付の1桁目を出力します。
休日の時は#E0
~#E9
の白黒反転した数字を出力し、平日の時は#30
~#39
の通常の数字を出力します。
180 ?CHR$(#30+#B0*H+I%10);:W=W+1:IFW>6W=0:?CHR$(10);
月末まで日付を出力後、改行がない場合は改行を出力します。
190 NEXT:IFW?CHR$(10);
祝日リストのキャッシュ
今回扱う祝日リストは比較的長いデータであり、MixJuiceでの取得には無視できない時間がかかります。
そこで、取得した祝日リストをキャッシュし、カレンダー出力の時に毎回取得しなくていいようにしました。
IchigoJamの配列は102要素あり、1年のデータの格納には24要素を使うので、4年分のデータを格納できます。
そこで、さらに1年あたり1要素を使用し、
「何年のデータか」と「そのデータをどこに格納したか」を管理することにしました。
今回は、下2ビットを「どこに格納したか」、それ以外のビットを「何年のデータか」に割り当てました。
4年分のデータを取得・格納した後、また別の年のデータを格納するには、
格納した4年分のデータのいずれかを消去して上書きしないといけません。
ここでどのデータを消去するかによって、キャッシュの利便性が変わってくる可能性があります。
できれば、この後使うデータを残し、使わないデータを消去したいです。
今回は、「格納されたデータの中で、最後に使ったのが一番昔のもの」を消去することにしました。
これはLRU (Least Recently Used) と呼ばれ、一般的に使われる方法の一つです。
以下が具体的なコードです。
キャッシュの管理用データを初期化します。
「何年のデータか」を全て0年とし、「どこに格納したか」の情報を準備します。
今回は、最近使ったデータほど大きい添字に格納することにしました。
20 LET[0],0,1,2,3
カレンダーを出力する年Y
のデータがキャッシュにあるかを探します。
見つかった場合は、祝日データを格納する位置P
を取得し、
管理用データをシフトして見つかった管理用データを[3]
に移動します。
ちなみに、使いたいデータがキャッシュから見つかることを「ヒット」といいます。
40 P=0:FORI=0TO3:IF[I]/4=Y:P=[I]%4*24+2:T=[I]:FORJ=ITO3:[J]=[J+1]:NEXT:[3]=T:I=9
50 NEXT:IFPGOTO140
データが見つからなかった場合は、
最後に使ったのが一番昔のデータ[0]
のかわりに新しい年のデータを格納することにします。
管理用データをシフトし、新しいデータの年と格納位置を[3]
にセットします。
ちなみに、使いたいデータがキャッシュから見つからないことを「ミス」といいます。
60 T=[0]:[0]=[1]:[1]=[2]:[2]=[3]:[3]=Y*4+T%4:P=T%4*24+2
作成したプログラム
10 ' キュウジツ カレンダー
20 LET[0],0,1,2,3:FORI=0TO79:POKE#700+I,~PEEK(#180+I):NEXT
30 INPUT"ナン ネン?",Y:INPUT"ナン ガツ?",M
40 P=0:FORI=0TO3:IF[I]/4=Y:P=[I]%4*24+2:T=[I]:FORJ=ITO3:[J]=[J+1]:NEXT:[3]=T:I=9
50 NEXT:IFPGOTO140
60 T=[0]:[0]=[1]:[1]=[2]:[2]=[3]:[3]=Y*4+T%4:P=T%4*24+2:FORI=2TO25:[P+I]=0:NEXT:N=0:T=0:H=0
70 ?CHR$(10);"MJ GETS api.national-holidays.jp/";Y:L=0
80 C=INKEY():IF(TANDTICK()>29)ORC=#5D?CHR$(10);:GOTO140ELSEIFC=0GOTO80
90 CLT:IF#30<=CANDC<=#39H=H*10+C-#30:GOTO80
100 IFH=0GOTO80
110 IFN=YAND1<=TANDT<=12X=P+T*2+(H>15):[X]=[X]|1<<(H&15)ELSEGOTO130
120 IFL<T?"*";:L=L+1:GOTO120
130 N=T:T=H:H=0:GOTO80
140 IFM<3Z=Y-1:N=M+12ELSEZ=Y:N=M
150 C=Z/100:D=Z%100:E=26*(N+1)/10:F=5*C+C/4:W=(1+E+D+D/4+F+6)%7:G=(30+M%2)^(M>7):IFM=2U=Y%400=0OR(Y%4=0ANDY%100):G=28+U
160 ?" Su Mo Tu We Th Fr Sa":IFW>0FORI=1TOW:?" ";:NEXT
170 FORI=1TOG:?" ";:X=P+M*2+(I>15):H=W=0OR[X]>>(I&15)&1:IFI<10?CHR$(#20+H*#6F);ELSE?CHR$(#30+#B0*H+I/10);
180 ?CHR$(#30+#B0*H+I%10);:W=W+1:IFW>6W=0:?CHR$(10);
190 NEXT:IFW?CHR$(10);
200 GOTO30
実行結果
祝日のデータを取得し、休日をハイライトしたカレンダーを出力することができました。
IchigoJam・MixJuice実機においては、
IchigoJam BASIC 1.0.0、MixJuiceからの送信間隔30ms (?"MJ SPW 30"
)において、
データを欠けずに取得することができました。
IchigoJam web・バーチャルMixJuiceにおいては、データが欠けてしまい、正しく取得できませんでした。