5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

休日表示付きカレンダー

Posted at

何をする?

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

Holidays JP API (日本の祝日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)を出力します。
  • 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においては、データが欠けてしまい、正しく取得できませんでした。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?