VBAによる「祝日判定処理」を「休日判定処理」に拡張してみた
3ヶ月半ほど前の5月28日に、VBAの祝日判定コードを書いたところ思っていた以上にアクセスされているようで、「以外に需要があるものだ」と少々驚いています。(推定で平日30件たらず?ですけど・・・)
そこで、図に乗った空腹おやじは、前回の祝日判定を拡張し、休日判定出来るようにすることにしました。(「休日でなければ・・・」という判定をすれば、営業日とか稼働日の判定にも使えそうです。)
基本は、前回の祝日判定処理を利用して、祝日定義同様に
- 月日固定の休日
- 月週曜日固定の休日
- 曜日固定の休日
を定義する処理を追加し、祝日判定の時と同様に日付指定でBool値判定。
更に、
- 特定の祝日、休日を除外できるようにする
といったところでしょうか。
クラスモジュール(CCompanyHoliday)のソースは、長いので下の方に・・・
とりあえず祝日定義は、祝日法が変わらなければ、いじる必要が必要ないと思うので
まずは、休日定義を以下の6個のメソッド内で設定してから使ってみてください。
- getCompanyHolidayInfoW(曜日固定の会社休日情報生成)
- getCompanyHolidayInfoMD(月日固定の会社休日情報生成)
- getCompanyHolidayInfoWN(月週曜日固定の会社休日情報生成)
- getCompanyHolidayInfoMDExclude(月日固定の会社出勤情報生成)
- getCompanyHolidayInfoWNExclude(月週曜日固定の会社出勤情報生成)
実行例は、こんな感じ。
Public Sub Test(ByVal lYear As Long) Dim cch As CCompanyHoliday Dim dt() As Date Dim i As Long Set cch = New CCompanyHoliday Call cch.getNationalHolidays(lYear, dt) For i = 0 To UBound(dt) Debug.Print dt(i), cch.getNationalHolidayName(dt(i)) Next i Set cch = Nothing End Sub
これをイミディエイトウィンドウで実行してみると
call Test(2018)
2018/01/01 元日
2018/01/02 年始休暇
2018/01/03 年始休暇
2018/01/06 会社休日
2018/01/07 会社休日
2018/01/08 成人の日
2018/01/13 会社休日
2018/01/14 会社休日
2018/01/20 会社休日
2018/01/21 会社休日
2018/01/27 会社休日
2018/01/28 会社休日
2018/02/03 会社休日
2018/02/04 会社休日
2018/02/10 会社休日
2018/02/11 建国記念の日
2018/02/12 振替休日
2018/02/17 会社休日
2018/02/18 会社休日
2018/02/24 会社休日
2018/02/25 会社休日
2018/03/03 会社休日
2018/03/04 会社休日
2018/03/10 会社休日
2018/03/11 会社休日
2018/03/17 会社休日
2018/03/18 会社休日
2018/03/21 春分の日
2018/03/24 会社休日
2018/03/25 会社休日
2018/03/31 会社休日
2018/04/01 会社休日
2018/04/07 会社休日
2018/04/08 会社休日
2018/04/14 会社休日
2018/04/15 会社休日
2018/04/21 会社休日
2018/04/22 会社休日
2018/04/28 会社休日
2018/04/30 振替休日
2018/05/03 憲法記念日
2018/05/04 みどりの日
2018/05/05 こどもの日
2018/05/06 会社休日
2018/05/12 会社休日
2018/05/13 会社休日
2018/05/19 会社休日
2018/05/20 会社休日
2018/05/26 会社休日
2018/05/27 会社休日
2018/06/02 会社休日
2018/06/03 会社休日
2018/06/09 会社休日
2018/06/10 会社休日
2018/06/16 会社休日
2018/06/17 会社休日
2018/06/18 特別休日
2018/06/23 会社休日
2018/06/24 会社休日
2018/06/30 会社休日
2018/07/01 会社休日
2018/07/07 会社休日
2018/07/08 会社休日
2018/07/14 会社休日
2018/07/15 会社休日
2018/07/16 海の日
2018/07/21 会社休日
2018/07/22 会社休日
2018/07/28 会社休日
2018/07/29 会社休日
2018/08/04 会社休日
2018/08/05 会社休日
2018/08/11 山の日
2018/08/12 お盆休暇
2018/08/13 お盆休暇
2018/08/14 お盆休暇
2018/08/15 お盆休暇
2018/08/16 お盆休暇
2018/08/17 お盆休暇
2018/08/18 お盆休暇
2018/08/19 会社休日
2018/08/26 会社休日
2018/09/01 創業記念日
2018/09/02 会社休日
2018/09/08 会社休日
2018/09/09 会社休日
2018/09/15 会社休日
2018/09/16 会社休日
2018/09/17 敬老の日
2018/09/22 会社休日
2018/09/23 秋分の日
2018/09/24 振替休日
2018/09/29 会社休日
2018/09/30 会社休日
2018/10/06 会社休日
2018/10/07 会社休日
2018/10/08 体育の日
2018/10/13 会社休日
2018/10/14 会社休日
2018/10/20 会社休日
2018/10/21 会社休日
2018/10/24 特別休日
2018/10/25 特別休日
2018/10/27 会社休日
2018/10/28 会社休日
2018/11/03 文化の日
2018/11/04 会社休日
2018/11/10 会社休日
2018/11/11 会社休日
2018/11/17 会社休日
2018/11/18 会社休日
2018/11/23 勤労感謝の日
2018/11/24 会社休日
2018/11/25 会社休日
2018/12/01 会社休日
2018/12/02 会社休日
2018/12/08 会社休日
2018/12/09 会社休日
2018/12/15 会社休日
2018/12/16 会社休日
2018/12/22 会社休日
2018/12/23 天皇誕生日
2018/12/24 振替休日
2018/12/29 年末休暇
2018/12/30 年末休暇
2018/12/31 年末休暇
クラスモジュール(CCompanyHoliday)のソースは、▶こちらをクリックで展開します。(注:1055行ありますので・・・ご注意下さい。m(_ _)m)
2021/4/11
都合により、こちら に移動しました。
CCompanyHoliday.cls のみ
https://github.com/Z1000R/determining-and-retrieving-holidays/blob/main/Source/CCompanyHoliday.cls
その他諸々を含めて一式
github.com
お約束
掲載したコードの使用については、特に制限は設けません。MITライセンスとします。
ご自由にお使い下さい。
使用にあたって、私への連絡等は不要です。
ただし、使用した結果、何らかのトラブル、損害、その他諸々の事象が発生しても、私は一切関与しません。
ソースコードを組み込んだ方が責任を取れる範囲内で使って下さい。
バグ、要望、気が付いた事などあれば、コメントしていただければと思います。
Manjaro Linux pacmanのミラー(筑波)がエラー403
現在、2018年7月21日 21時過ぎですが
筑波のミラーがまたエラーのようです。
理研のミラーは大丈夫のようです。
マウントできなくなったHDDから、testdiskを使用して、ファイルをサルベージしてみた
USB接続で使用していた4TBのHDDが、突然使用できなくなりました。
マウントできないと・・・
根本的な原因は不明なのですが、ラベルが書き換わって、openSUSE-Leapとなっていましたし、
中途半端に、ddコマンドが実行されたような感じでしょうか?
openSUSEは、インストール用としてUSBメモリに書き込むために以前使ったことはありますが
現在は、内蔵HDDには残っていません。
主にバックアップ用として使用していたので
いろいろと大切なファイル(そう、あんなファイルやこんなファイル・・・)を保存していたので
困ってしまいました。
唯一救いだったのが、OSのインストールされたHDDではなく
外付けのHDDだったので、不要な書き込みによる上書きの心配が少なかったことでしょうか。
諦めてフォーマットしてしまおうかと思ってみたものの
少しくらいは悪あがきしてみて、駄目なら諦めようと調べてみると
testdiskというツールがあるらしく、それを使ってみることにした。
HDDの情報が書き換わったPC(Manjaro Linux)でサルベージするのも嫌だったので
別のPC(Antergos)で作業することに。
pacmanで検索すると、testdiskが見つかったので、即インストール。
端末から、コマンドを入力して開始。
最初にログの作成方法について聞いてきた。
初めてなので、新規にログを作成するので、「Create」が選択された状態でEnter
(ログは、自分のHOMEディレクトリに作成されました。 /home/z1000)
次に認識されたHDDのリストが表示されるので、対象となるHDDを選択して
[ Proceed ]を選択して、Enter
次は、パーティションテーブルの種類。
今回のLinuxの場合には、[ Intel ]を選べばOKなようです。
事前の準備が住んだところで、まずは分析
[ Analyse ]を選択して、Enter
現状で、OSが認識している状況が表示される(?)ようです。
[ Quick Search ]を選択して、Enter
そうすると、以前の状況らしきものが見えてきた。
キーボードから「P」を入力すると、ファイルリストが表示されるようです。
2018/7/3 修正
赤い文字のファイルやディレクトリは、復元できないものなのかもしれない。
赤い文字のファイルも復元はできるようです。
↑↓で、ディレクトリやファイルを選択でき、
←→で、ディレクトリの親や子に移動できるようです。
サルベージしたいファイルやディレクトリを選択し、
キーボードの「C」を押下すると、コピー先を聞いてきます。
あとは、移動先を選択し、Enterを押下すればコピーが始まります。
選択したファイルは、階層付きでコピーされました。
この調子で、他のファイルも・・・
2018/7/3 追記
USBメモリのファイルを削除して、同様に操作したら、
削除したファイルを復元できました。
復元されたファイルの所有者、グループはrootでした。
ファイル名によっては、元のファイル名を復元できない場合もあるようです。
(省略形のような名前で復元されたりする場合があるようです。)
ExcelのVBAで使えるDLLを、C++(Visual Studio 2017)で作る。・・・その2
次回予告までしておきながら、他にVBA関係の記事を書いていたこともあり、既に2ケ月以上が過ぎ、
「いつ頃までに、まとめられるかは、不明・・・」の記載通りになってしまった。
前回も書きましたけど、64bit版のVBAではどうなるかわかりませんので!!!
今回の内容は次の通り。
受け渡しするデータの型について
とりあえず必要なのは、VBAとC++のデータ型の対応。VBA独自の型は、C++に渡せないし、逆にC++にしかない型は、VBAが受け取れない。
(条件付きであれば、例外もあるけど・・・)
VBAの主な型を中心に対応を見てみると次のような感じ。
VBAの型 | C++の型 | 備考 |
---|---|---|
Byte | unsigned char BYTE |
|
Integer | short SHORT |
|
Long | int long INT LONG |
|
Single | float | |
Double | double | |
Boolean | BOOL |
C++の型 | VBAの型 | 備考 |
---|---|---|
HANDLE | Long | |
char CHAR |
Byte | 最上位ビットがOFFならばそのまま使用可 |
unsigned short WORD |
Integer | 最上位ビットがOFFならばそのまま使用可 |
unsigned int unsigned long UINT ULONG DWORD |
Long | 最上位ビットがOFFならばそのまま使用可 |
VBAにあってC++にない型(抜粋) |
---|
Currency |
Date |
Decimal |
String |
Object |
Variant |
C++にあってVBAにない型(抜粋) |
---|
WCHAR |
LONGLONG |
ULONGLONG |
VBAのString型に対応するC++の型はありません。
ちょっと特殊です。データの受け渡しにはchar型のポインタまたは、BSTR型を使用します。
これについては、次回(?)にでも・・・
処理する値を渡せるようにすること
DLLに何らかのデータ処理をしてもらうためには、必要なデータを渡してあげないと出来ません。データはVBAの関数と同様にパラメータに引数を渡します。
ただ、C++とVBAでは、構文が違うので予め覚えておく必要があります。
値渡しと参照渡しについて
- 値渡し
- 引数のアドレスをプロシージャに渡すのではなく引数の値を渡す方法。
VBAでは、ByValを指定する事により値渡しとなる。 - 参照渡し
- 引数の値をプロシージャに渡すのではなく引数のアドレスを渡す方法。
VBAでは、デフォルトで参照渡しである。明示的に指定する場合には、ByRefを指定する。
C++の場合
- 値渡し
- データ型の後ろに変数を指定する。
void doAnything(int hoge); intがデータ型で、hogeが変数 - 値渡し
- データ型の後ろに"*"を付けその後ろに変数を指定する。(ポインタですな。)
void doSomething(long* fuga); longがデータ型で、fugaが変数
処理した結果や値を返してもらえるようにすること
値を返してもらう方法は、大きく分けて2つ。一つは、関数の復帰値による方法。
これは、VBAでいえば、ファンクションプロシージャによる復帰値で結果を得る方法と同じ。
C++の場合には、関数名の前に復帰値のデータ型を指定する。
復帰値の型 関数名(パラメータリスト)
もう一つは、渡したパラメータにデータを入れてもらい、返してもらう方法。
こちらは、VBAでいえば、パラメータにByRefを指定し、返してもらう方法と同じ。
どちらを使うかは、状況に応じて使い分ければよろしいかと。
場合によっては、両方というのもありです。
数値型の場合の受け渡しの例
VBAからLongの値を渡して、2倍した結果を返してもらう「GetNumberI」という関数と、300倍した値を返してもらう「GetNumberI2」を作ってみます。
GetNumberIは、パラメータを値渡しして、復帰値で結果をもらいます。
GetNumberI2は、パラメータを参照渡しし、そのパラメータ値を変更して返してもらいます。
前回のファイルに、書き加えていきます。
まず、ヘッダファイル(AccessibleFromVBA.h)
#pragma once extern "C" { #define ACCESSIBLEFROMVBA_API __declspec(dllexport) ACCESSIBLEFROMVBA_API void WINAPI DoNothing(); ACCESSIBLEFROMVBA_API int WINAPI GetNumberI(int i); ACCESSIBLEFROMVBA_API void WINAPI GetNumberI2(int* pi); }
次に、ソースファイル(AccessibleFromVBA.cpp)
#include "stdafx.h" #include "AccessibleFromVBA.h" ACCESSIBLEFROMVBA_API void WINAPI DoNothing() { return; } //int型の値を受け取って、int型の復帰値を返す(VBAのファンクションプロシージャ相当) ACCESSIBLEFROMVBA_API int WINAPI GetNumberI(int i) { return i * 2; } //int型の値を受け取って、内部で値を変更して返す(VBAでパラメータをByRef で受け渡しするイメージ) ACCESSIBLEFROMVBA_API void WINAPI GetNumberI2(int* pi) { *pi *= 300; return; }
最後に、モジュール定義ファイル(AccessibleFromVBA.def)
LIBRARY AccessibleFromVba EXPORTS DoNothing GetNumberI GetNumberI2
全て追加したら、プロジェクトをビルドします。
========== ビルド: 1 正常終了、0 失敗、0 更新不要、0 スキップ ==========
と表示されればOK。
呼び出すExcel側は、
Private Declare Function GetNumberI Lib "C:\Datas\MyDatas\Developer\VisualStudioComunity2017\DllForVBA\ForTest\AccessibleFromVBA.dll" (ByVal l As Long) As Long Private Declare Sub GetNumberI2 Lib "C:\Datas\MyDatas\Developer\VisualStudioComunity2017\DllForVBA\ForTest\AccessibleFromVBA.dll" (ByRef l As Long)
呼び出してみる
Public Sub DllCallTest2() Dim lValue As Long Dim lResult As Long lValue = 1000 lResult = GetNumberI(lValue) Debug.Print "GetNumberI:", lValue, lResult Debug.Print "" Debug.Print "GetNumberI2(Before):", lValue Call GetNumberI2(lValue) Debug.Print "GetNumberI2(After ):", lValue End Sub
実行結果は、
call DllCallTest2
GetNumberI: 1000 2000
GetNumberI2(Before): 1000
GetNumberI2(After ): 300000
当てにならない次回予告
とりあえず、数値の受け渡しは出来たので、次は、- 文字列
- 配列
- 構造体
の受け渡しあたりをまとめられたらいいなぁ~
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
z1000s.hatenablog.com
VBAによる祝日判定および祝日取得(改正東京五輪・パラリンピック特別措置法 対応)
2018年6月13日、参院本会議で可決、成立しました。
これに伴って、2020年の祝日が移動するものが出てきたようなので、先日公開した祝日判定処理を更新しました。
また、2020年からは「体育の日」が「スポーツの日」になるそうなので、合わせて対応済みです。
まぁ、祝日の定義の部分を変えただけなんですけどね。
処理内容は全く変えないで、元になる祝日データだけいじればいいので、祝日の追加、廃止、変更等のメンテナンスは楽ですよ。
ソース
実行例
2019年から2021年の7月から10月の祝日を出力した結果が以下の通り。
----- 2019 -----
2019/07/15 海の日
2019/08/11 山の日
2019/08/12 振替休日
2019/09/16 敬老の日
2019/09/23 秋分の日
2019/10/14 体育の日----- 2020 -----
2020/07/23 海の日
2020/07/24 スポーツの日
2020/08/10 山の日
2020/09/21 敬老の日
2020/09/22 秋分の日----- 2021 -----
2021/07/19 海の日
2021/08/11 山の日
2021/09/20 敬老の日
2021/09/23 秋分の日
2021/10/11 スポーツの日
【VBA / Excel】祝日休日の判定、取得用データを生成し、実際に判定、取得してみる
この記事では、予め用意された祝日テーブルを使用するのではなく、
クラスモジュール内で、祝日テーブルを自前で生成し、Dictionary に格納して判定処理を行っています。
祝日の定義は、クラスモジュール内に、汎用性を考慮した状態でハードコーディングしてあります。
このため、法の変更等により、祝日が変わらなければ、このまま使用できます。
別途、祝日テーブルを用意する必要はありません。
2018/9/10 追記
この記事は、祝日(振替休日等を含む)を判定の対象にしていますが、日曜日や年末年始の休み等は判定対象外です。
これらの休日も判定対象に含めたい場合には、以下の記事をあわせて参照してみて下さい。
z1000s.hatenablog.com
2018/6/14 追記
この記事には、2018/6/13の改正東京五輪・パラリンピック特別措置法が参院本会議での可決に伴って、更新された記事があります。
z1000s.hatenablog.com
2019/5/23 追記
Googleで検索して来る方が多いようですが、中にはこのページが本来の目的ではない方もいるかもしれないので、とりあえず、「こんなページもあるよ」ということで載せておきます。
z1000s.hatenablog.com
z1000s.hatenablog.com
目次みたいなもの
- 1.月、日が固定のもの
- 2.月、第N ○曜日が固定のもの
- 3.月は固定だが、日が可変のもの
- 4.月日が定まっておらず、他の祝日などに依存するもの
- 最終的なコード
- パブリックメソッド一覧
- isNationalHoliday(ByVal dtDate As Date) As Boolean
- isNationalHoliday2(ByVal dtDate As Date, ByRef sHolidayName As String) As Boolean
- getNationalHolidays(ByVal lYear As Long, ByRef dtHolidays() As Date) As Long
- getNationalHolidayName(ByVal dtHoliday As Date) As String
- reInitialize(ByVal lLastYear As Long)
- パブリックプロパティ
- 使い方と実行例
- お約束
「Excelで祝日判定をしたい」というと、「祝日データを用意して、それを・・・」とお決まりの文言が出てくるのが一般的です。
では、その祝日データはどうすれば用意できるのでしょうか?
ここのように、期間を指定して生成してくれるようなところもあるようです。
が、必要なデータが変わるたびにサイトにアクセスしてというのも面倒です。
となれば、「自分で作ってしまえ作れるようにしてしまえ」となりました。
以上
???普通ならそうはならない???
まぁ、いいじゃないですか。私がそう思ったんだから。
それに、ここで終わったら、ただの落書きで終わってしましますよ。
そろそろ本題に。
まず、処理するに当たって祝日を分類してみる。カッコ内は例。
- 月、日が固定のもの(元日:1月1日)
- 月、第N ○曜日が固定のもの(成人の日:1月第2月曜日)
- 月は固定だが、日が可変のもの(春分の日:3月19〜22日)
- 月、日が定まっておらず、他の祝日などに依存するもの(振替休日、国民の休日)
1、2項は簡単に求められる。
3項は、計算式があるのでこれも問題なし。
4項は、他の祝日に依存するため、単独では求められない。予め1〜3項の祝日がわかっている必要がある。(或いは、その都度依存する日の状態を確認するか。)
では具体的にどうすれば求められるか、簡単(?)かもしれない解説。
前提条件として、データはDate型で保持するものとします。
1.月、日が固定のもの
VBA では、DateSerial 関数を使用すると、年、月、日を指定してDate 型の値を取得できます。
DateSerial( year, month, day )
DateSerial 関数については、こちら
2.月、第N ○曜日が固定のもの
指定された月の1日の曜日がわかれば、1日以降の最初の指定された曜日(第1○曜日)へのオフセット値(何日補正すればよいか)がわかります。
それがわかれば、7の倍数を加算することで求められます。
まず、指定月の1日を求めるのは、1項で登場したDateSerial 関数。
DateSerial( year, month, 1 )
と日に「1」を指定すればOK。
次にその曜日
曜日は、Weekday 関数を使用します。
Weekday( date , [ firstdayofweek ] )
dateに、上で求めた1日の日付データを渡します。
firstdayofweekは省略可能です。今回は使用しません。
そうすると、指定した日付によって1〜7の値が返ってきます。
定数 | 値 | 説明 |
---|---|---|
vbSunday | 1 | 日曜日 |
vbMonday | 2 | 月曜日 |
vbTuesday | 3 | 火曜日 |
vbWednesday | 4 | 水曜日 |
vbThursday | 5 | 木曜日 |
vbFriday | 6 | 金曜日 |
vbSaturday | 7 | 土曜日 |
これを書いている2018年5月の場合を考えてみます。
第2週までのカレンダーは、以下の通りです。
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
最初の曜日までの日数は、以下のようになります。
曜日 | 指定曜日の日付 | 1日から指定曜日までの日数 |
---|---|---|
日曜日 | 6 | 5 |
月曜日 | 7 | 6 |
火曜日 | 1 | 0 |
水曜日 | 2 | 1 |
木曜日 | 3 | 2 |
金曜日 | 4 | 3 |
土曜日 | 5 | 4 |
で、曜日を使って「指定曜日までの日数」をどうやって求めるかというと
前述の曜日の定数を使って「指定曜日」 から 「1日の曜日」を引きます。
ただし、「指定曜日」が「1日の曜日」より前の場合、結果が負の値となるのでその場合には7を加えます。
5月1日は火曜日なので、vbTuesday ===> 3
例えば、第1金曜日なら、vbFriday ===> 6
なので、6 - 3 = 3 となります。
第1日曜日なら、vbSunday ===> 1
なので、1 - 3 = -2 となりますが、結果が負なので、+7 して
-2 + 7 = 5 となります。
この値が求められれば、次は1日の日付に加算を行います。
日付の加減算は、DateAdd 関数を使用します。
DateAdd(interval, number, date)
intervalには、日数の計算を行うので、"d" を指定します。
numberには、上記の計算で求めた値を指定します。
dateには、1日の日付データを指定します。
最初の曜日であれば、これで良いのですが2回め以降の曜日の場合、更に7の倍数を加算する必要があります。
第2曜日であれば、+7
第3曜日であれば、+14
第4曜日であれば、+21
第5曜日であれば、+28
となるので、第N曜日の場合
(N ー 1)*7
を加算すれば良いことになります。
最終的には、以下のようなプロシージャにより求められます。
Public Function getNthWeeksDayOfWeek(ByVal lYear As Long, _ ByVal lMonth As Long, _ ByVal lNth As Long, _ ByVal lDayOfWeek As VbDayOfWeek) As Date Dim dt1stDate As Date Dim lDayOfWeek1st As Long Dim lOffset As Long '指定年月の1日を取得 dt1stDate = DateSerial(lYear, lMonth, 1) '1日の曜日を取得 lDayOfWeek1st = Weekday(dt1stDate) '指定日へのオフセットを取得 lOffset = lDayOfWeek - lDayOfWeek1st If lDayOfWeek1st > lDayOfWeek Then lOffset = lOffset + 7 End If lOffset = lOffset + 7 * (lNth - 1) getNthWeeksDayOfWeek = DateAdd("d", lOffset, dt1stDate) End Function
ただし、N >= 5を指定した場合、日付が翌月になる場合がありますので、注意が必要です。
3.月は固定だが、日が可変のもの
これは、春分の日と秋分の日が該当。
Wikipediaによると
春分の日は、3月19~22日のいずれか。
秋分の日は、22~24日のいずれか。
となっている。
一般に、
'春分の日 Int(20.8431 + 0.242194 * (年 - 1980)) - Int((年 - 1980) / 4) '秋分の日 Int(23.2488 + 0.242194 * (年 - 1980)) - Int((年 - 1980) / 4)
という計算で求められるようです。
1980年以降に限定すれば、以下の内容でもOK。
というか、こうしたかったんだけれど、1979年以前の場合、
¥演算子の結果が、Fix 関数と同等になり、Int 関数とは結果が変わってしまうため
If で振り分ける等の必要が生じてしまうので、上記の式をそのまま使用することにしました。
'春分の日 Int(20.8431 + 0.242194 * (年 - 1980)) - (年 - 1980) \ 4 '秋分の日 Int(23.2488 + 0.242194 * (年 - 1980)) - (年 - 1980) \ 4
ちなみに、¥演算子を使用した場合は、内部で整数演算が行われるが、
/演算子の場合、整数同士の演算であっても、内部では浮動小数点数で演算が行われます。
4.月日が定まっておらず、他の祝日などに依存するもの
4.1 振替休日
1973年の国民の祝日に関する法律が改正されたことにより制定された。これにより祝日(国民の祝日)が日曜日の場合、その翌日となる月曜日が振替休日となった(ハッピーマンデー制度の法律と同じ)。1973年の天皇誕生日(4月29日)が日曜日で、同年4月30日が最初の適用日となった。
当初は祝日が2日以上連続することがなかったため、「国民の祝日の日曜日の翌日の月曜日」としていた。しかし2005年の国民の祝日に関する法律の改正(2007年施行)で、4月29日が「昭和の日」となり、「みどりの日」が4月29日から5月4日へ変更。5月3日から5月5日まで祝日が3日連続することになり、その直後の「国民の祝日の日曜日の翌日の月曜日以降の国民の祝日でない祝日の翌日」を休日とすることと改められ振替先が月曜日固定ではなくなった。
振替休日 - Wikipedia
ということなので、
祝日が日曜日であった場合、適用された年から判断し、その翌日または、それ以降で最初の祝日でない日を求めればOK。
2007年以降では、当然ながらこの日が月曜日とは限りません。
2015年5月3日の憲法記念日の振替休日は、6日の水曜日でした。
5月3日(日):憲法記念日
5月4日(月):みどりの日
5月5日(火):こどもの日
5月6日(水):5月3日の振替休日
4.2 国民の休日
前後が祝日である平日は、国民の休日となり、休日となる。
国民の休日 - Wikipedia
こちらは、翌々日まで確認が必要なので、要件は振替休日より複雑。
- 最初の祝日が月曜日から金曜日の間にあること。
- 最初の祝日が日曜日の場合、翌日は振替休日となるので、2項を満足しない。
- 最初の祝日が土曜日の場合、翌日は日曜日となるので、平日ではない。
- 最初の祝日の翌日が、平日であること。(祝日でなく、休日でもないこと。)
- 最初の祝日の翌々日が、祝日であること。
これらの条件を満たした時、最初の祝日の翌日が「国民の休日」となる。
2項、3項の祝日は、年によって日付が変動する祝日であり、「移動祝日」または「移動祝祭日」と呼ぶそうです。
最終的なコード
VBAのクラスとして作成しました。
▶クリックでソースを展開/縮小
2021/4/11
都合により、
VBAによる「祝日判定処理」を「休日判定処理」に拡張してみた - 空腹おやじのログと備忘録
と、コードを統合した上で、
こちら に移動しました。
CCompanyHoliday.clsのみ
https://github.com/Z1000R/determining-and-retrieving-holidays/blob/main/Source/CCompanyHoliday.cls
その他諸々を含めて一式
github.com
パブリックメソッド一覧
isNationalHoliday(ByVal dtDate As Date) As Boolean
指定日が国民の祝日(休日)か?
引 数 | IN/OUT | 内容 |
---|---|---|
dtDate As Date | IN | 判定したい日付 |
復帰値 | 内容 |
---|---|
True | 国民の祝日(休日)である。 |
False | 国民の祝日(休日)ではない。 |
isNationalHoliday2(ByVal dtDate As Date, ByRef sHolidayName As String) As Boolean
指定日が国民の祝日(休日)か?そうであれば、その祝日名を合わせて返す
引 数 | IN/OUT | 内容 |
---|---|---|
dtDate As Date | IN | 判定したい日付 |
sHolidayName As String | OUT | 祝日名 |
復帰値 | 内容 |
---|---|
True | 国民の祝日(休日)である。 |
False | 国民の祝日(休日)ではない。 |
getNationalHolidays(ByVal lYear As Long, ByRef dtHolidays() As Date) As Long
指定年の国民の祝日を配列に格納して返す
引 数 | IN/OUT | 内容 |
---|---|---|
lYear As Long | IN | 取得したい年 |
dtHolidays() As Date | OUT | 指定された年の国民の祝日 |
復帰値 |
---|
国民の祝日の件数 |
getNationalHolidayName(ByVal dtHoliday As Date) As String
指定日の国民の祝日名を返す
引 数 | IN/OUT | 内容 |
---|---|---|
dtHoliday As Date | IN | 日付 |
復帰値 |
---|
指定された日付の国民の祝日名 |
指定された日付が国民の祝日でない場合、復帰値は長さ0の文字列となる。
reInitialize(ByVal lLastYear As Long)
指定年までの祝日データが生成されていなければ、追加生成する。
引 数 | IN/OUT | 内容 |
---|---|---|
lLastYear As Long | IN | 生成したい最終年 |
現在の何年までのデータが生成されているかは、後述のInitializedLastYearプロパティで取得できる。
パブリックプロパティ
InitializedLastYear() As Long
何年までの国民の祝日データが生成されているか
(読み取り専用)
使い方と実行例
クラス名は、CNationalHoliday としています。
使用する前に、Newを付けて、インスタンスを生成して下さい。
(そのタイミングで、デフォルトで現在の年から5年後の年末までの祝日を内部で生成します。)
その後に、必要なメソッドを呼び出します。
日付を指定して、祝日判定をして、祝日名を取得する
Public Sub Test1() Dim cnh As New CNationalHoliday Dim dt As Date Dim sHolidayName As String dt = #8/11/2015# Debug.Print Format$(dt, "yyyy/mm/dd"), cnh.getNationalHolidayName(dt), cnh.isNationalHoliday2(dt, sHolidayName) dt = #8/11/2016# Debug.Print Format$(dt, "yyyy/mm/dd"), cnh.getNationalHolidayName(dt), cnh.isNationalHoliday2(dt, sHolidayName) dt = #8/12/2018# Debug.Print Format$(dt, "yyyy/mm/dd"), cnh.getNationalHolidayName(dt), cnh.isNationalHoliday2(dt, sHolidayName) dt = #8/12/2019# Debug.Print Format$(dt, "yyyy/mm/dd"), cnh.getNationalHolidayName(dt), cnh.isNationalHoliday2(dt, sHolidayName) dt = #2/23/2020# Debug.Print Format$(dt, "yyyy/mm/dd"), cnh.getNationalHolidayName(dt), cnh.isNationalHoliday2(dt, sHolidayName) Set cnh = Nothing End Sub
実行結果
call Test1 2015/08/11 False 2016/08/11 山の日 True 2018/08/12 False 2019/08/12 振替休日 True 2020/02/23 天皇誕生日 True
年を指定して、祝日および祝日名を取得する
Public Sub Test2(ByVal lYear As Long) Dim cnh As New CNationalHoliday Dim dt() As Date Dim i As Long Call cnh.getNationalHolidays(lYear, dt) For i = 0 To UBound(dt) Debug.Print dt(i), cnh.getNationalHolidayName(dt(i)) Next i Set cnh = Nothing End Sub
実行結果
call Test2(2018) 2018/01/01 元日 2018/01/08 成人の日 2018/02/11 建国記念の日 2018/02/12 振替休日 2018/03/21 春分の日 2018/04/29 昭和の日 2018/04/30 振替休日 2018/05/03 憲法記念日 2018/05/04 みどりの日 2018/05/05 こどもの日 2018/07/16 海の日 2018/08/11 山の日 2018/09/17 敬老の日 2018/09/23 秋分の日 2018/09/24 振替休日 2018/10/08 体育の日 2018/11/03 文化の日 2018/11/23 勤労感謝の日 2018/12/23 天皇誕生日 2018/12/24 振替休日
祝日データを、デフォルトの5年後より後の分も生成する
Public Sub Test3() Dim cnh As CNationalHoliday Dim dt As Date Dim i As Long Set cnh = New CNationalHoliday Debug.Print "Before reInitialize", cnh.InitializedLastYear cnh.reInitialize 2030 Debug.Print "After reInitialize", cnh.InitializedLastYear Set cnh = Nothing End Sub
実行結果
call Test3 Before reInitialize 2023 After reInitialize 2030
秀丸で、選択行の行頭に文字を挿入するマクロ
先日、人力検索はてなで、
q.hatena.ne.jp
に回答して、ベストアンサーを頂いたのですが
ベースになった物があって、C++のソースを編集する際に、
- 行コメントの追加
- 既存の行を改修してコメント化
するために以前作ったもの(下記)でした。
(今回の件で、ちょっとしたバグを発見して修正したけど・・・)
あちらにアップしたものは、コメント削除したものなので、わかりにくかったかも。
$comment_prefix = "//"; //範囲選択されているか if ( selecting == 1 ) { //範囲選択されていたら解除 escape; //選択開始行 #TargetLine = seltopy + 1; //選択終了行 if ( selendx == 0 ) { //選択終了行のカーソル位置が行先頭なら、その行はコメント化しない #EndLine = selendy; } else { #EndLine = selendy + 1; } //選択開始行に移動 movetolineno 1, seltopy + 1; } else { //選択開始行 #TargetLine = lineno; //選択終了行 #EndLine = lineno; } //最終行に移動 gofileend; //最終行取得 #FileEndLine = lineno; //元の行に戻る movetolineno 1, #TargetLine; while ( #TargetLine != #EndLine + 1 ) { //行先頭に移動 golinetop; //コメント化 insert $comment_prefix; //処理行をインクリメント #TargetLine = #TargetLine + 1; if ( #TargetLine > #FileEndLine ) { break; } //1行下へ移動 movetolineno 1, #TargetLine; }