前回、「起算日から6月経過しているか調べる」ための関数を作成しましたが、どのようにして求めたか解説したいと思います。
Function hantei(KISAN_YMD As Date, KIZYUN_YMD As Date, tukisu As Long) As Boolean
Function で記述することで、関数を自作することができます。関数に入力したい数値など(引数といいます。)は、カッコ内に変数を宣言することで渡すことができます。
手順1
'手順1 日付A(起算日)、日付B(基準日)、判定する月数(今回の例では6月)を指定する
今回の例で、計算に必要な数値(引数)は起算日、基準日、月数の3つです。
- 起算日… KISAN_YMD (日付型で定義)
- 基準日… KIZYUN_YMD (日付型で定義)
- 月数… tukisu (長整数型で定義)
カッコの次の「As Boolean」ですが、関数は入力したものを計算し、結果を出力するという特徴があります。今回、求めたい答えは「〇」か「×」かの判定ですので、テーブルデザインで使用するYes/No型、VBAの型でいうと「Boolean型」で出力するように設定しました。
手順2
'手順2 起算日からn月後の満了日を民法第143条第2項に沿って計算する
今回の処理のキモとなる部分です。
応当日と満了日を求める
民法第143条は、このように定めています。
(暦による期間の計算)
e-Gov 法令データ提供システム から引用しました
第143条 週、月又は年によって期間を定めたときは、その期間は、暦に従って計算する。
2 週、月又は年の初めから期間を起算しないときは、その期間は、最後の週、月又は年においてその起算日に応当する日の前日に満了する。ただし、月又は年によって期間を定めた場合において、最後の月に応当する日がないときは、その月の末日に満了する。
- n月後に起算日に応当する日付があれば、その日の前日に期間が満了する。
- n月後に起算日に応当する日付がなければ、その月の末日に期間が満了する。
「応当する日」とは、例えば2019/7/11を起算日として1週間後という期間を定めたとき、1週間後に当たる日は2019/7/18です。この1週間後に当たる日を「応当日」といいます。
同様に、2019/7/11を起算日として1月後という期間を定めたとき、1月後に当たる日は2019/8/11です。
ところが難しいのが、第2項のただし書き「最後の月に応当する日がないときは、その月の末日に満了する。」という一文。
例えば2019/8/31を起算日として6月後という期間を定めたとき、6月後に当たる日は、2020/2/31となりますが、2月には31日なんて日付はありませんので、2月末日が満了日となります。
さらに、2020年はうるう年なので、29日まであります。
そう、月によって、場合によっては年によって満了する日が変動するのです。
今回、和休はこれを実現するためにこんな条件を設定しました。
- n月後の月の日数を調べる
- 起算日の日付がその日数より大きいかどうか
- 大きければ起算日からn月後の応当日を求め、その前日を満了日とし、そうでなければn月後の最終日を満了日とする
n月後の月の日数を調べる
では、n月後の月の日数を調べていきましょう。
起算日からn月後の翌日1日の日付を求め、その前日を求めれば、n月後の月の最終日の日付を取得できますね。
その日付を数値に変換すれば、n月後の月に含まれている日数が分かりますね。
変数を宣言しました。n月後の月に含まれる日数を格納する変数「NISSU」、満了日を格納する「MANRYO_YMD」です。
Dim NISSU As Long '日数を格納する Dim MANRYO_YMD As Date '満了日を格納する
n月後の月に含まれる日数を求めます。
NISSU = Day(DateSerial(Year(KISAN_YMD), Month(KISAN_YMD) + tukisu + 1, 1) - 1)
今回、起算日は2019/7/11、月数は6月と指定します。
この式の変数を、入力された数値に置き換えます。
NISSU = Day(DateSerial(Year(#2019/7/11#), Month(#2019/7/11#) + 6 + 1, 1) - 1)
NISSU = Day(DateSerial(2019, 7 + 6 + 1, 1) - 1)
NISSU = Day(DateSerial(2019,14, 1) - 1)
NISSU = Day(#2020/2/1# - 1)
NISSU = Day(#2020/1/31#)
NISSU = 31
7月の6月後は翌年の1月なので、「31」という答えが返ってきました。
式をバラしていきます。一番外側の関数は、Day関数です。指定した日付から日を取り出します。
例:2019/7/11 → Day(#2019/7/11#)→ 11
Accessでは、日付を指定するときは#で囲むことがお約束です(日付型の変数を使う場合、あらかじめ日付型であると宣言しているので#は不要です。)。
#で囲まないと、場合によっては 2019 ÷ 7 ÷ 11 の答えを指定したように判断されます。
DAY関数の中に入っている関数は、DateSerial関数です。カッコの中をカンマで区切り、年,月,日の数値を入れることで、日付型に変換する関数です。
例:DateSerial(2019,7,11) → 2019/7/11(日付型)
DateSerial関数の中にはさらにYear関数と、Month関数が入っています。それぞれ指定した日付からYear関数は年、Month関数は月を取り出します。
例:2019/7/11 → Year(#2019/7/11#)→ 2019
例:2019/7/11 → Month(#2019/7/11#)→ 7
DateSerial(Year(KISAN_YMD), Month(KISAN_YMD) + tukisu + 1, 1)
DateSerial関数に指定している数値を解説します。
まず、「年」を指定する部分には、Year関数で起算日の属する年を指定しています。
例:Year(KISAN_YMD) → Year(#2019/7/11#) → 2019
次に、「月」を指定する部分には、Month関数で起算日の属する月を指定しています。
例:Month(KISAN_YMD) → Month(#2019/7/11#) → 7
さらに変数「tukisu」で指定した「6」を加算し、DateSerial関数の「月」に入力された数値は「13」になります。
さらに「+1」することで7月後の月を取得しています。結果は「14」です。
NISSU = Day(DateSerial(2019,14, 1) - 1)
でも、2019年14月なんて日付、ありませんよね。でも大丈夫。DateSerial関数は、実際に存在する月に自動的に変換してくれるのです。
月に「14」を入力した場合、1年と2月に変換してくれるので、2020年2月になるのです。
最後に「日」を指定する部分には、「1」を入力しました。つまり、このDateSerial関数の結果は、2020/2/1となります。
NISSU = Day(#2020/2/1# - 1)
2020/2/1 から「-1」することで、1日前の日付を求めることができます。この結果、Day関数のカッコ内は、2020/1/31になっているのです。
NISSU = Day(#2020/1/31#)
結果、変数「NISSU」には「31」が入力されることになるのです。
起算日の日付がその月にある日数より大きいかどうか
ここまでの計算で、起算日は2019/7/11、6月後の月には31日まであることが分かりました。
起算日は「11日」なので6月後の月にも存在します。ですので応当日は2020/1/11となります。
もし、起算日が2019/8/31としたら、6月後の応当日はどうなるでしょう。
6月後は2020年2月なので、29日までしかありません。つまり応当日は存在しない、ということになります。
なお、この場合、民法第143条第2項のただし書き「最後の月に応当する日がないときは、その月の末日に満了する。」が適用されて、満了日は2020/2/29となります。
では、IF構文に当てはめてみましょう。
'起算日の日付とn月後の月に属する最後の日付とを比較 If Day(KISAN_YMD) <= NISSU Then
この式の変数を、入力された数値に置き換えます。
If Day(#2019/7/11#) <= 31 Then
If 11 <= 31 Then
11と31を比較した結果、条件を満たしているので、次の式へ飛びます。
'起算日からn月後の応当日の前日を満了日とする(応当日がその月に存在するとき) MANRYO_YMD = DateSerial(Year(KISAN_YMD), Month(KISAN_YMD) + tukisu, Day(KISAN_YMD)) - 1
DateSerial(Year(KISAN_YMD), Month(KISAN_YMD) + tukisu, Day(KISAN_YMD))
DateSerial関数に指定している数値を解説します。
起算日は2019/7/11、6月後の応当日とします。
DateSerial(Year(#2019/7/11#),month(#2019/7/11#)+6,day(#2019/7/11#)
DateSerial(2019,7+6,11)
DateSerial(2019,13,11)
#2020/1/11#
この結果、2020/1/11となります。これが応当日です。
ここから「-1」することで、2020/1/10となります。これが満了日になります。
MANRYO_YMD = #2020/1/10#
変数「MANRYO_YMD」に2020/1/10が格納されました。
では、条件を満たさなかったときはどうでしょうか。
起算日を2019/8/31、6月後の応当日を求めます。
この場合、6月後は2020/2となり、変数「NISSU」には、2020/2に含まれる日数である「29」が格納されます。
起算日は「31」、2月に含まれる日数は「29」なので、31と29を比較すると、条件を満たしません。ですので、「Else」の次に記述した式へ飛ぶのです。
MANRYO_YMD = DateSerial(Year(KISAN_YMD), Month(KISAN_YMD) + tukisu, NISSU)
DateSerial関数に指定している数値を解説します。
DateSerial(Year(KISAN_YMD), Month(KISAN_YMD) + tukisu, NISSU)
DateSerial(Year(#2019/8/31#), Month(#2019/8/31#) + 6, 29)
DateSerial(2019, 8 + 6, 29)
DateSerial(2019, 14, 29)
#2020/2/29#
この結果、2020/2/29となります。これは「応当日」ではなく、「満了日」ですので、「-1」する必要はありません。
MANRYO_YMD = #2020/2/29#
変数「MANRYO_YMD」に2020/2/29が格納されました。
手順3
'手順3 基準日と満了日を比較し、基準日>満了日 であればTRUE、そうでないときはFalseを返す
最後の手順です。あとは簡単です。基準日と満了日をIF構文で比較し、関数「Hantei」に計算の結果が「True」であるか「False」であるかを入力してやればよいのです。
If KIZYUN_YMD > MANRYO_YMD Then hantei = True Else hantei = False End If
計算の結果を入力したところです。関数「Hantei」は「Boolean型」なので、必ず「True」又は「False」になるように記述する必要があります。
コメントをどうぞ!