日本企業でエクセルを使う場合、「年度」で日付を管理したい場面が非常に多いです。
ですが、残念ながらタイトルの関数はデフォルトでは用意されていません。
と言うことで作りましょう。
ソースコード
Function DateSerial年度版(ByVal 年度y As Long, ByVal m As Long, ByVal d As Long) As Date ' 1~3月はyを1増やす If m <= 3 Then DateSerial年度版 = DateSerial(年度y + 1, m, d) Else DateSerial年度版 = DateSerial(年度y, m, d) End If ' m,dがカレンダーの日付にない組ならば、エラーとする If Month(DateSerial年度版) <> m Or Day(DateSerial年度版) <> d Then Call Err.Raise(1000, , "年月日の指定がカレンダーにない日付です。") End If End Function ' 実行例 ?DateSerial年度版(2019, 1, 1) ' = 2020/01/01
使い方
「月」と「日」のセルが別々になっているデータに対し、
日付の取得を1行にまとめるときなどに、よく使用します。
For R = 2 To 100 売上日 = DateSerial年度版(2019, Cells(R, CNo売上データ.月) , Cells(R, CNo売上データ.日)) Next
このように、「2019.4.1~2020.3.31」のデータを1行で表現できます。
コードの解説
メインの処理は、ほとんど解説不要ですね。
1~3月のときに、Yearに1を足して、DateSerialを使っているだけです。
※ エラー処理については後述するので読み飛ばしてOKです。
関数名がちょっと不自然な名前ですが、無理やり「DateSerial」から始めているのは、→がやりたいからです。
detes まで打ってCtrl + Spaceで、どっちを使うか選べます。
作りは非常に単純ですが、とても便利な関数です。
簡単な計算なので、計算を楽にしてくれるような利便性はないですが、
真価を発揮するのは「コードの可読性(読みやすさ)」です。
年度の調整をメインのSubプロシージャ内に書こうとすると、大した処理じゃないのに5行くらいになったり、yとy+1の違いだけで同じコードを複製するはめになったりします。
日付なんて、数ある項目のひとつでしかないのに、その部分のコードが肥大してしまうと、本当にやりたい処理が、どうでもいい処理に埋もれてしまいます。
こういった、「たいして意味はないが、システム上必要なIf文」を関数に隠し、
「メインコードを意味のある処理だけに保つ」ことが、関数の大きな役割です。
「年度」関連の処理は、こういった邪魔なIfがとても多いので、積極的に関数にしていきましょう。
・関連関数:日付から「年度」を求める関数
・関連関数:4月から3月を連番に変換してコードを整理する
DateSerialのちょっと特殊な仕様
本家DateSerialには、少し特殊な仕様があります。
DateSerial(2020, 1, 32) ' = 2020/02/01
このように、日付に32を代入すると、月が繰り上がります。
同じく月に13でも、念が繰り上がります。
DateSerial(2020, 1, 0) ' = 2019/12/31 DateSerial(2020, -1, 1) ' = 2019/11/01
逆に日付に0や、さらには-1を入れると、月や年が繰り下がったりもします。
この仕様、本記事の「年度版」ではどうしましょう?
「If m <= 3 Then」と書いていますが、dが32だと、(2019, 3,32)は、4月です。
「If m <= DateSerial(年度y, m, d) Then」 と書き替えた方がいいでしょうか?
しかし、これはこれで、「うるう年」の問題に引っ掛かります。
ってことはまたIfを使って…
いやちょっと待ってください。
本家DateSerialがこの仕様で大丈夫なのは、「連続関数」だからです。
年度版は連続関数ではありませんから、足し算は想定していません。
そもそも(2019, 3,32) に、2019と2020の、どっちの4月1日を返すべきかなんて、
それこそ関数の製作者の決めの問題でしかありません。
原点に立ち返ってこの関数の目的を考えましょう。
「○○年度の○月○日が欲しい」でした。
じゃあ、○に「0」やら「32」やらは、そもそもおかしい話ですよね?
mやdには、変な数字は入れられないようにしましょう。
ということで、mとdの組がカレンダーにない場合は、
エラーで止める関数にしました。
「カレンダーに存在する年月日の組かどうか」は、
「最終的に出来上がった日付のmとdが、元のmとdから変わっていないか」
で判定しています。
ちなみに、正しい日付かどうかは、
「DateValeu(y/m/d)がエラーかどうか」を調べる方が楽です。
こっちは↑の仕様を持つDateSerialと違い、
DateValue("2020/3/32")がエラーとなります。
ただし、今回のように「年の値が条件で切り替わる」場合は、
うるう年の判定をするために、完成後にm,dを判定しなければいけませんので気をつけましょう。