#はじめに
以前カレンダーの壁紙を制作しようと思い立ち,祝祭日をどのように決めるかを調べていたら,
ht_dekoさんの記事で以下の記事をがありました。
Delphi で "指定した日が祝日かどうか" を調べる @ht_deko
https://qiita.com/ht_deko/items/a0cbbbd899c1c016ae67
春分の日や秋分の日を計算で出すという便利な関数ですが,記事の最後に**「祝日は祝日テーブルに登録するのがベストだと思われます。」**と書かれておりました。春分の日と秋分の日が国立天文台の発表を待たないと確定せず,それは1年先のものまでしかわからないということに原因があるのだということが分かりました。
また,2021年についてみてみると東京オリンピックの関係で祝日が動いていています(2020/11/27参院可決)。これをいちいちプログラムで変更するのは面倒だなと思いました。
大臣官房総務課 国民の祝日について
https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
令和3年(2021年)の祝日について
令和3年(2021年)に限り、「海の日」は7月22日に、「スポーツの日」は7月23日に、「山の日」は8月8日(※)になります。
平成三十二年東京オリンピック競技大会・東京パラリンピック競技大会特別措置法等の一部を改正する法律の施行に伴い、改正後の令和三年東京オリンピック競技大会・東京パラリンピック競技大会特別措置法(平成27年法律第33号)第32条第2項の規定に基づき、令和3年(2021年)における海の日、スポーツの日及び山の日は上記の通りとなります。
(※)国民の祝日に関する法律(昭和23年法律第178号)第3条第2項の規定に基づき、8月9日は休日となります。
そこで,公式の休日の情報を取得しておいて,@ht_dekoさんの作られた
function IsSpecialHoliday(ADate: TDate; var AName: string): Boolean;
と仕様を合わせた関数を作ろうと考えました。
#データフォーマット
DATA●GO.JP データカタログサイト
https://www.data.go.jp/data/dataset/cao_20190522_0002
によると,昭和30年(1955年)から令和2年(2020年)国民の祝日等(いわゆる振替休日等を含む)(csv形式:19KB)は2021年12月現在で,
https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv
より公開されています。改正後の令和三年東京オリンピック競技大会・東京パラリンピック競技大会特別措置法にも対応しています。
なお,ライセンスはCC-BYとなっております。
CC BY 4.0
https://creativecommons.org/licenses/by/4.0/deed.ja
営利目的でも利用できますね。クレジット表示は必要です。
ダウンロードして分かったのですが,Shift-JISでエンコードされており,半角カンマで区切られた空白のない文字列です。
日付のフォーマットは yyyy/m/d です。例えば以下のようになっています。
2021/11/23,勤労感謝の日
休日全てのデータがあり振替休日は休日とだけ書かれています。
#ロジック
休日祭日のデータを取得しファイルに保存する関数と,ファイルに保存された休日祭日のデータを利用して指定した日が休日祭日かどうかを返す関数を実装します。
これらを一つのユニットにまとめて,
- 起動時に保存されたファイルがあればテーブルに読みだし,
- データが取得された場合にはそれを更新し,
- 休日祭日かどうかを返す関数は読みだされたデータを利用するようにして,
- 終了時にテーブルを開放する
ことにしました。
このデータをTHttpClient(XE8以降で実装された命令)で取得し,指定された場所に保存します。THttpClientはOSの機能でhttpsにも簡単にアクセスできるので便利ですね。シンプルに実装しました。非同期にする場合にはHOSOKAWA @pik様の
THttpClient の落とし穴(解決編)
https://qiita.com/pik/items/95dfebdb659b30918196
がありますので,こちらを参考にしてください。
#コード
unit p_kato_Holiday;
interface
function SpecialHolidayGetFromHTTP:boolean; // 祝祭日ファイルを更新する ファイルがない場合には必ず実行すること
function IsSpecialHoliday(ADate: TDate; var AName: string): Boolean; // ADateが祝祭日かを返す AName:祝祭日名
implementation
uses
System.Classes, System.SysUtils, System.Net.HttpClient,
Generics.Collections;
type
// 休日祭日のテーブル構造
TSpecialHoliday=record
Date:TDate; // 日付
Name:string; // 祝日名
end;
var
SpecialHolidays:TList<TSpecialHoliday>; // 休日祭日のテーブル 起動時に生成され終了時に破棄される
const
SpecialHolidayFile='SpecialHoliday.txt'; // 祝日を保存するファイル 特殊フォルダにする場合は変数にしてinitialization時に取得するように変更する
const
CAOURL='https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv';
// https://www.data.go.jp/data/dataset/cao_20190522_0002 内閣府データカタログサイトより取得
function GetURLAsString(const AURL: string): string; // URLで指定したファイルの内容を返す
(*
System.Net.HttpClient.THTTPClient
http://docwiki.embarcadero.com/Libraries/ja/System.Net.HttpClient.THTTPClient
山本隆の開発日誌 TNetHTTPRequest/TNetHTTPClientでWebサーバーにアクセスする
https://www.gesource.jp/weblog/?p=7090
*)
var
ResponseContent: TMemoryStream;
HTTPClient:THttpClient;
st:TStringList;
begin
Result:='';
HTTPClient:=THttpClient.Create;
HTTPClient.AllowCookies := True;
ResponseContent := TMemoryStream.Create;
st:=TStringList.Create;
try
HTTPClient.Get(AURL, ResponseContent);
st.LoadFromStream(ResponseContent,TEncoding.GetEncoding('Shift_JIS')); // 2020/12/01現在データはSJIS
Result:=st.Text;
finally
st.Free;
ResponseContent.Free;
HTTPClient.Free;
end;
end;
function StrToDate(s:string;var ADate:TDate):boolean;// YYYY/MM/DD を TDateに変換 成功したらTrueを返す
var
dt:TStringList;
y,m,d:word;
begin
Result:=False;
dt:=TStringList.Create;
dt.Delimiter:='/';
dt.StrictDelimiter:=True;
dt.DelimitedText:=s;
if dt.Count=3 then begin
y:=StrToIntDef(dt[0],0);
m:=StrToIntDef(dt[1],0);
d:=StrToIntDef(dt[2],0);
try
ADate:=EncodeDate(y,m,d);
Result:=True;
finally
end;
end;
dt.Free;
end;
function SpecialHolidayGet(AStringList:TStringList):boolean; // 祭日休日情報の取得
var
i:integer;
st:TStringList;
sd:TSpecialHoliday;
begin
SpecialHolidays.Clear;
Result:=True;
if AStringList.Count>1 then begin
st:=TStringList.Create;
st.Delimiter:=',';
st.StrictDelimiter:=True;
for i:=1 to AStringList.Count-1 do begin
st.DelimitedText:=AStringList[i];
if st.Count=2 then begin
if StrToDate(st[0],sd.Date) then begin // YYYY/MM/DD を TDateに変換
sd.Name:=st[1];
SpecialHolidays.Add(sd);
end else begin
Result:=False;
end;
end;
end;
st.Free;
end;
end;
function SpecialHolidayGetFromHTTP:boolean; // ファイルを保存できたかを確認する
var
s:string;
st:TStringList;
begin
Result:=False;
s:=GetURLAsString(CAOURL);
if s<>'' then begin
st:=TStringList.Create;
try
st.Text:=s;
st.SaveToFile(SpecialHolidayFile,TEncoding.UTF8);
if s<>'' then begin
Result:=SpecialHolidayGet(st); // 祭日休日情報の取得
end;
finally
st.Free;
end;
end;
end;
function SpecialHolidayGetFromFile:boolean; // ファイルを保存できたかを確認する
var
st:TStringList;
begin
Result:=False;
if FileExists(SpecialHolidayFile) then begin
st:=TStringList.Create;
try
st.LoadFromFile(SpecialHolidayFile,TEncoding.UTF8);
if st.Text<>'' then begin
Result:=SpecialHolidayGet(st); // 祭日休日情報の取得
end;
finally
st.Free;
end;
end;
end;
//
function IsSpecialHoliday(ADate: TDate; var AName: string): Boolean;
var
i:integer;
begin
for i:=0 to SpecialHolidays.Count-1 do begin
if SPecialHolidays[i].Date=ADate then begin
AName :=SPecialHolidays[i].Name;
Result:=True;
exit;
end;
end;
AName :='';
Result:=False;
end;
initialization
SpecialHolidays:=TList<TSpecialHoliday>.Create; // 休日祭日配列を生成
SpecialHolidayGetFromFile; // 保存されていれば休日祭日配列ファイルを読み込む
finalization
SpecialHolidays.Free; // 休日祭日配列を破棄
end.
#使い方
利用するフォームで
uses
p_kato_Holidays;
を記述し,休日祭日を取得してファイルに保存するときに
SpecialHolidayGetFromHTTP;
を実行します。ファイルをまだ持っていないときには必ず実行するようにしてください。
IsSpecialHoliday(ADate: TDate; var AName: string): Boolean;
でADateが祝日休日かどうかを返します。祝日休日ならANameに祝日祭日名が入ります。振替休日のときは休日が入ります。
祝日でも休日でもないなら,ANameには空白が入ります。
皆様のお役に立てば幸いです。
#おまけ
カレンダーコンポーネントの文字色を設定するには
@igy様の カレンダーコンポーネントの文字色を設定 https://qiita.com/igy/items/54867fffdb6dac19fa0b にその方法が書かれています。ぜひご確認ください。
#参考資料
この記事を書くために参考にした資料で本文でご紹介できなかったものは以下の通りです。
08_THttpClient または TNetHttpClient を使用する接続処理 Mr.XRAY
http://mrxray.on.coocan.jp/Delphi/plSamples/772_Indy_HTTPGet.htm#08
山本隆の開発日誌 TNetHTTPRequest/TNetHTTPClientでWebサーバーにアクセスする
https://www.gesource.jp/weblog/?p=7090
#謝辞
deco(@ht_deco)様 HOSOKAWA(@pik)様 XRAY様 山本隆様 igy様 皆様のおかげでこの原稿を書くことができました。ありがとうございます。