指定した日が祝日かどうかを調べる

 面倒です。振替休日やらハッピーマンデーやら、春分の日 / 秋分の日やら...。大手のシステムでは "祝日テーブル" を持たせるのが普通のようですが、"意地で" ロジックによる休日計算をやってみました。

 ...とはいえ、規則性がないので case 文と if 文で処理する以外方法はありません。

function IsSpecialHoliday(ADate: TDate; var AName: string): Boolean;
// ------------------------------------------------------------
// http://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
// http://koyomi.vis.ne.jp/
// http://www.asahi-net.or.jp/~CI5M-NMR/misc/equinox.html#Rule
// ------------------------------------------------------------
// ADateが祝日かどうかを返す。
// 祝日=True,祝日ではない=False
// AName には祝日の名前を返す
var
  DName: string;
  i:Integer;
  {FreqOfWeek Begin}
  function FreqOfWeek(AYear, AMonth: Word; AWeekNo, ADayOfWeeek: Byte): TDateTime;
    // AYear年AMonth月の第AWeekNo「ADayOfWeeek曜日」の日付を返す
    // ADayOfWeeek 日曜日=1..土曜日=7
  var
    dDay: Word;
    dDoW: Word;
    dWeekNo: Byte;
  begin
    dDoW := DayOfWeek(EncodeDate(AYear, AMonth, 1));
    dWeekNo := AWeekNo;
    if ADayOfWeeek >= dDoW then
      dWeekNo := dWeekNo - 1;
    dDay := (dWeekNo * 7) + (ADayOfWeeek - dDoW) + 1;
    result := EncodeDate(AYear, AMonth, dDay);
  end;
  {FreqOfWeek End}
  {LeapYearCount Begin}
  function LeapYearCount(SYear, EYear: Word): Integer;
    // SYearからEYear迄に何回閏年があるかを返す
  var
    i: Integer;
    Cnt: Integer;
  begin
    Cnt := 0;
    for i := sYear to eYear do
      begin
        if (i mod 4) <> 0 then
          Continue;
        if IsLeapYear(i) then
          Inc(Cnt);
      end;
    result := Cnt;
  end;
  {LeapYearCount End}
  {VernalEquinox End}
  function VernalEquinox(AYear: Word): TDateTime;
    // Ayearの春分の日を求める
  var
    dDay: Word;
  begin
    dDay := Trunc((21.147 + ((AYear - 1940) * 0.2421904) - (LeapYearCount(1940, AYear) - 1)));
    result := EncodeDate(AYear, 3, dDay);
  end;
  {VernalEquinox End}
  {AutumnalEquinox End}
  function AutumnalEquinox(AYear: Word): TDateTime;
    // Ayearの秋分の日を求める
  var
    dDay: Word;
  begin
    case AYear of
      1979:
        dDay := 24;
    else
      dDay := Trunc((23.5412 + ((AYear - 1940) * 0.2421904) - (LeapYearCount(1940, AYear) - 1)));
    end;
    result := EncodeDate(AYear, 9, dDay);
  end;
  {AutumnalEquinox End}
  {_IsSpecialHoliday Begin}
  function _IsSpecialHoliday(ADate: TDate; var AName: string): Boolean;
    // ADateが祝日かどうかを返す。
    // 祝日=True,祝日ではない=False
    // AName には祝日の名前を返す
    // '国民の休日'はここでは算出されない
  var
    dYear,
      dMonth,
      dDay: Word;
  begin
    AName := '';
    result := False;
    DecodeDate(ADate, dYear, dMonth, dDay);
    case dMonth of
      1:
        begin
          // '元日' 1948~
          if (dYear >= 1948and (dDay = 1then
            begin
              result := True;
              AName := '元日';
              Exit;
            end;
          // '成人の日①' 1948~1999
          if (dYear >= 1948and (dYear <= 1999and (dDay = 15then
            begin
              result := True;
              AName := '成人の日';
              Exit;
            end;
          // '成人の日②' 2000~
          // 第2月曜日(ハッピーマンデー)
          if (dYear >= 2000then
            begin
              if ADate = FreqOfWeek(dYear, dMonth, 22then
                begin
                  result := True;
                  AName := '成人の日';
                  Exit;
                end;
            end;
        end;
      2:
        begin
          // '建国記念の日' 1967~
          if (dYear >= 1967and (dDay = 11then
            begin
              result := True;
              AName := '建国記念の日';
              Exit;
            end;
          // '天皇誕生日②' 2020~
          if (dYear >= 2020and (dDay = 23then
            begin
              result := True;
              AName := '天皇誕生日';
              Exit;
            end;
          // ※昭和天皇の大喪の礼(1989/02/24)
          if (dYear = 1989and (dDay = 24then
            begin
              result := True;
              AName := '昭和天皇の大喪の礼';
              Exit;
            end;
        end;
      3:
        begin
          // '春分の日' 1949~
          if (dYear >= 1949then
            begin
              if ADate = VernalEquinox(dYear) then
                begin
                  result := True;
                  AName := '春分の日';
                  Exit;
                end;
            end;
        end;
      4:
        begin
          // '天皇誕生日' 1948~1988
          if (dYear >= 1948and (dYear <= 1988and (dDay = 29then
            begin
              result := True;
              AName := '天皇誕生日';
              Exit;
            end;
          // 'みどりの日①' 1989~2006
          if (dYear >= 1989and (dYear <= 2006and (dDay = 29then
            begin
              result := True;
              AName := 'みどりの日';
              Exit;
            end;
          // '昭和の日' 2007~
          if (dYear >= 2007and (dDay = 29then
            begin
              result := True;
              AName := '昭和の日';
              Exit;
            end;
          // ※皇太子明仁親王の結婚の儀(1959/04/10)
          if (dYear = 1959and (dDay = 10then
            begin
              result := True;
              AName := '皇太子明仁親王の結婚の儀';
              Exit;
            end;
        end;
      5:
        begin
          // '天皇の即位' 2019/05/01
          if (dYear = 2019and (dDay = 1then
            begin
              result := True;
              AName := '天皇の即位';
              Exit;
            end;
          // '憲法記念日' 1948~
          if (dYear >= 1948and (dDay = 3then
            begin
              result := True;
              AName := '憲法記念日';
              Exit;
            end;
          // 'みどりの日②' 2007~
          if (dYear >= 2007and (dDay = 4then
            begin
              result := True;
              AName := 'みどりの日';
              Exit;
            end;
          // 'こどもの日' 1948~
          if (dYear >= 1948and (dDay = 5then
            begin
              result := True;
              AName := 'こどもの日';
              Exit;
            end;
        end;
      6:
        begin
          // ※皇太子徳仁親王の結婚の儀(1993/06/09)
          if (dYear = 1993and (dDay = 9then
            begin
              result := True;
              AName := '皇太子徳仁親王の結婚の儀';
              Exit;
            end;
        end;
      7:
        begin
          // '海の日①' 1996~2002
          if (dYear >= 1996and (dYear <= 2002and (dDay = 20then
            begin
              result := True;
              AName := '海の日';
              Exit;
            end;
          // '海の日②' 2003~
          // 第3月曜日 (五輪祝日移動法 により 2020 年は 23 日)
          if (dYear >= 2003then
            begin
              if ((dYear =  2020and (dDay = 23)) or
                 ((dYear <> 2020and (ADate = FreqOfWeek(dYear, dMonth, 32))) then
                begin
                  result := True;
                  AName := '海の日';
                  Exit;
                end;
            end;
          // '体育の日③' 2020 (五輪祝日移動法)
          if (dYear = 2020and (dDay = 24then
            begin
              result := True;
              AName := 'スポーツの日';
              Exit;
            end;
        end;
      8:
        begin
          // '山の日' 2016~
          // 第3月曜日 (五輪祝日移動法 により 2020 年は 10 日)
          if ((dYear >= 2016and (dYear <> 2020and (dDay = 11)) or ((dYear = 2020and (dDay = 10)) then
            begin
              result := True;
              AName := '山の日';
              Exit;
            end;
        end;
      9:
        begin
          // '敬老の日①' 1966~2002
          if (dYear >= 1966and (dYear <= 2002and (dDay = 15then
            begin
              result := True;
              AName := '敬老の日';
              Exit;
            end;
          // '敬老の日②' 2003~
          // 第3月曜日
          if (dYear >= 2003then
            begin
              if ADate = FreqOfWeek(dYear, dMonth, 32then
                begin
                  result := True;
                  AName := '敬老の日';
                  Exit;
                end;
            end;
          // '秋分の日' 1948~
          if (dYear >= 1948then
            begin
              if ADate = AutumnalEquinox(dYear) then
                begin
                  result := True;
                  AName := '秋分の日';
                  Exit;
                end;
            end;
        end;
      10:
        begin
          // '体育の日①' 1966~1999
          if (dYear >= 1966and (dYear <= 1999and (dDay = 10then
            begin
              result := True;
              AName := '体育の日';
              Exit;
            end;
          // ※即位礼正殿の儀(2019/10/22)
          if (dYear = 2019and (dDay = 22then
            begin
              result := True;
              AName := '即位礼正殿の儀';
              Exit;
            end;
          // '体育の日②' 2000~
          // 第2月曜日(ハッピーマンデー) (五輪祝日移動法 により 2020 年を除く)
          if (dYear >= 2000and (dYear <> 2020then
            begin
              if ADate = FreqOfWeek(dYear, dMonth, 22then
                begin
                  result := True;
                  if (dYear < 2020then
                    AName := '体育の日'
                  else
                    AName := 'スポーツの日';
                  Exit;
                end;
            end;
        end;
      11:
        begin
          // '文化の日' 1948~
          if (dYear >= 1948and (dDay = 3then
            begin
              result := True;
              AName := '文化の日';
              Exit;
            end;
          // '勤労感謝の日' 1948~
          if (dYear >= 1948and (dDay = 23then
            begin
              result := True;
              AName := '勤労感謝の日';
              Exit;
            end;
          // ※即位礼正殿の儀(1990/11/12)
          if (dYear = 1990and (dDay = 12then
            begin
              result := True;
              AName := '即位礼正殿の儀';
              Exit;
            end;
        end;
      12:
        begin
          // '天皇誕生日①' 1948~2018
          if (dYear >= 1989and (dYear <= 2018and (dDay = 23then
            begin
              result := True;
              AName := '天皇誕生日';
              Exit;
            end;
        end;
    end;
  end;
  {_IsSpecialHoliday End}
begin
  result := False;
  AName := '';
  if _IsSpecialHoliday(ADate, DName) then
    begin
      result := True;
      AName := DName;
    end
  else if (ADate >= EncodeDate(1973412)) and (DayOfWeek(ADate) = 2and
          _IsSpecialHoliday(ADate - 1, DName) then
    begin
      // 振替休日① 1973/04/12以降
      // 日曜日と祝祭日が重なった場合には'振替休日'となる
      result := True;
      AName := '振替休日';
    end
  else if (ADate >= EncodeDate(198854)) and (DayOfWeek(ADate) <> 1and
          _IsSpecialHoliday(ADate - 1, DName) and _IsSpecialHoliday(ADate + 1, DName) then
    begin
      // 国民の休日 1988/05/04以降
      // 祝日と祝日に挟まれた平日は'国民の休日'となる。
      result := True;
      AName := '国民の休日';
    end
  else if (ADate >= EncodeDate(200856)) and (DayOfWeek(ADate) <> 1and
          _IsSpecialHoliday(ADate - DayOfWeek(ADate) + 1, DName) then
    begin
      // 振替休日② 2008/05/06以降
      // '祝日'が日曜日に当たるときは、その日後においてその日に最も近い'祝日'でない日を休日とする
      result := True;
      AName := '振替休日';
      for i:=1 to DayOfWeek(ADate) - 2 do
        begin
          if not _IsSpecialHoliday(ADate - i, DName) then
            begin
              result := False;
              AName := '';
              Break;
            end;
        end;
    end;
end;

 あー面倒臭いったら。えーと、9 月の '国民の祝日' の計算で '敬老の日' と '秋分の日' が逆転した時の計算がありませんが、'それは有り得ない' のでやってないのです (手抜きじゃないんですよ^^;)。 一番早い第 3 月曜日は15日、一番遅いのが 21 日です。秋分の日は早い日で 22 日なので、カブる事は有り得ませんから...。

 適当にプロジェクトを作ったらフォームに Edit と Memo と Button を貼って、Button の OnClick イベントに以下のコードを記述してみて下さい。

procedure TForm1.Button1Click(Sender: TObject);
var
  Dmy, DName: String;
  DDate: TDate;
  Adj_Leap, i: Integer;
begin
  Memo1.Lines.Clear;
  DDate := StrToDate(Edit1.Text + '/01/01');
  Adj_Leap := Integer(IsLeapYear(StrToInt(Edit1.Text)));
  for i:=0 to 364 + Adj_Leap do
    begin
      Dmy := FormatDateTime('YYYY/MM/DD (ddd)', DDate);
      if IsSpecialHoliday(DDate, DName) then
        begin
          Dmy := Dmy + Format(' [%s]', [DName]);
          Memo1.Lines.Add(Dmy);
        end;
      DDate := DDate + 1;
    end;
end;

 Edit1 に年を入れて Button1 を押すと Memo1 にその年のカレンダーが出力されます。

 追記:


 BACK