開始日〜終了日の期間( y年mヶ月d日 )を計算します。
『y年mヶ月d日』という期間のカウント方法は、事務手続きによって異なります。先ず、期間計算を
必要とする【事務手続き】では、どのようなカウント方法が採られているのかを確認してください。
その上で、それに適合するパラメータの組み合わせ(計算方法・初日算入・末日算入)を選んで
ください。適合する計算方法がktPeriodYMDに無い場合は利用しないでください(その事務
処理にとっては誤った計算という事になります)。DATEDIFでは大変な式が簡単に済みます。
『#VALUE!』
ktPeriodYMD ( 計算方法, 開始日, 終了日,
[初日算入], [末日算入], [算出対象], [ゼロ抑制] )
返却値 ‥‥‥ Variant(String/Integer) 型 計算方法 ‥‥‥ String 型 開始日 ‥‥‥ Variant ( Date ) 型 終了日 ‥‥‥ Variant ( Date ) 型 初日算入 ‥‥‥ Boolean 型 省略可(省略時 False ) 末日算入 ‥‥‥ Boolean 型 省略可(省略時 True ) 算出対象 ‥‥‥ String 型 省略可(省略時 "ymd") ゼロ抑制 ‥‥‥ Boolean 型 省略可(省略時 False )
算出対象="ymd" → 『 y年mヶ月d日』形式の文字列(計算方法4/4-xxでは『 y年mヶ月』形式)。
〃 ="y" → 上記結果の『年数』部分を数値で返します。
〃 ="m" → 〃 『月数』部分 〃
〃 ="d" → 〃 『日数』部分 〃
〃 ="mm" → 〃 総月数(12*年数+月数)を返します。
〃 ="ym" → 【日】部分を切り捨てて『y年mヶ月』形式の文字列。
※ 計算方法"5"(学齢)の場合は、算出対象に拠らず、常に年数を数値で返します。
※ ゼロ抑制=True & 算出対象=ymd/ym の場合は下記のように編集されます。
『算出対象=ymd』
1年1ヶ月1日 → 1年1ヶ月1日
0年1ヶ月1日 → 1ヶ月1日
0年0ヶ月1日 → 1日
0年0ヶ月0日 → 0日
1年0ヶ月1日 → 1年0ヶ月1日
1年0ヶ月0日 → 1年0ヶ月0日
『算出対象=ym』
1年1ヶ月 → 1年1ヶ月
0年1ヶ月 → 1ヶ月
0年0ヶ月 → 0ヶ月
1年0ヶ月 → 1年0ヶ月
算出対象 ‥‥‥ 上記【返却値】解説を参照(アルファベットは大文字/小文字のどちらでもOKです)
開始日
/ 終了日‥‥‥ 期間の 開始日 と 終了日 を シリアル値または日付文字列で指定します。
※ 新元号「令和」対応になっていない環境でも
「令和 日付文字列(元年 表記を含む)」を指定可能です。
計算方法 [ 5 ] を除き、『開始日 > 終了日』 の指定も可能です。
初日算入 ‥‥‥ 期間計算に『開始日』自体を含めるかをTrue/False で指定します。
末日算入 ‥‥‥ 期間計算に『終了日』自体を含めるかをTrue/False で指定します。 (※計算方法"5"(学齢)の場合には、初日算入/末日算入の指定は不要です)
ゼロ抑制 ‥‥‥ 「0年」などを表示しないようにする場合には[True]にします。
既定は[False]で「0年も表示」です。
計算方法 ‥‥‥ 下記の種類があります(アルファベットは大文字/小文字のどちらでもOKです) ※下記で云う「起算日/満了日 / 応当日」について
起算日:初日算入=Trueの場合は『開始日』
〃 =Falseの場合は『開始日の翌日』 (既定値)
満了日:末日算入=Trueの場合は『終了日』 (既定値)
〃 =Falseの場合は『終了日の前日』
xxx の応当日 : 個々の 『月』 で xxx の 『日』 と 同じ日付
( 2015/9/5 の場合、 10/5 , 11/5 , 12/5 … が 応当日 )
----- 応当日を基にして算出 -----
--+--------+--------+--------+--------+--------+--- 暦 ( + 〜 + が 1ヶ月 )
<----------------------------------------> 計算期間
#========#--------#========#--------#***** 応当日単位で 1ヶ月カウント
この例では [ 4ヶ月 + 端数日数 ]
1a: 民法(第139〜143条)で定められている『期間』の定義に沿った結果が得られ
ます。起算日の応当日を基準にして月数をカウントします。
民法での定義内容についてはこちらを参照して下さい。
(補) [ 1a ] での算出は ktDATEDIF 関数でも行なえます。
1b: 上記[1a]では『起算日=月末日 』の場合に、起算月と満了月の組み合わせに
よっては「見た目と異なる」結果が得られます。
[1b]では、【月末日の応当日は月末日とする】という条項を加える事によって、
このケースを矯正してカウントします。詳細はこちらを参照して下さい。
(注)民法では[1b]で問題にしているケースについて言及していません。
したがって、この部分の扱いは、当事者間での取り決めに拠る事となります。
----- 暦上の『月』と両端の『日』に分けて算出 -----
--+--------+--------+--------+--------+--------+--- 暦 ( + 〜 + が 1ヶ月 )
<----------------------------------------> 計算期間
*******#========#--------#========#****** 暦の月で 1ヶ月カウント
この例では [ 3ヶ月 + ( 端数日数1 + 端数日数2 ) ]
2a: 起算日〜満了日の間に挟まる暦上の【月】から年月数をカウントし、日数は
「起算日〜起算月の月末」+「1日〜満了日」でカウントします。ただし、
『起算の"日" ≦ 満了の"日"』の場合には「日数合計で1ヶ月を満たす」とし、
月数1ヶ月を加え、日数は『起算日の応当日 〜 満了日』で求めます。
(補) [ 2a ] での算出は ktDATEDIF_SPLIT 関数でも行なえます。
2b: 起算日〜満了日の間に挟まる暦上の【月】から年月数をカウントし、日数は
「起算日〜起算月の月末」+「1日〜満了日」でカウントします。
[2a]と違って、「日数合計」の月数への調整はしません。したがって、この計算
法では「日数合計」が【31】を超える場合もあります。
----- 1ヶ月を『30日間』として算出 -----
この計算方法の詳細は 『ねぇ、DAYS360 でホントに良いの?』 を参照して下さい。
3a: ワークシート関数の「DAYS360(NASD方式)」で求めています。
開始日(起算日前日)が月末日の場合には[30日扱い]とします。
(31日,2000/2/29,2001/2/28→[30日扱い],2000/2/28は28日のまま)
終了日(満了日)が『31日』の場合は[翌1日扱い](*1)とします。ただし、開始
日(起算日前日)が『30/31日』の場合には終了日『31日』は[30日扱い]です。
終了日で、2000/2/29は29日、2001/2/28は28日のままです。
(注) ↑はDAYS360関数の実際の動作によるものです。DAYS360 (NASD) は実際の NASD仕様
に一致していません。詳しくは、こちらを参照。
(*1)[〜3/31]の場合は[3月31日]という日付をカウントに加えるのと同じ効果になる
[〜4/ 1]の場合は 〃 をカウントに加えない
3b: ワークシート関数の「DAYS360(ヨーロッパ方式)」で求めています。
開始日(起算日前日)・終了日(満了日)ともに『31日』は[30日扱い](*2)です。
開始/終了日で、2000/2/29は29日、2001/2/28は28日のままです。
(*2)[〜3/31]でも[〜4/1]でも[3月31日]という日付はカウントに加えません。
3c: [3a,3b](DAYS360関数)と異なり、起算/満了日ともに月末日は1律[30日扱い] (*3)
として算出します。
31日,2000/2/29(閏年),2001/2/28(平年)→[30日扱い]
2000/2/28(閏年)は28日のまま
(*3)[〜3/31]でも[〜4/1]でも[3月31日]という日付はカウントに加えません。
----- 在籍期間 ----- 4: "1日"でも在籍した月は『丸1ヶ月』在籍と見故して算出します。
この計算方法の場合は【 y年mヶ月 】という編集で返します。
算出対象="D"の場合には常にゼロが返ります。
----- 日数切り上げ ----- 4-1a, 4-1b, 4-2a, 4-3a, 4-3b, 4-3c : [1a, 1b, 2a, 3a, 3b, 3c]の各計算方法で求めた結果に対して、
日数( >0 )を『月数』へ切り上げます。
[2b]に対するものはありません。
算出対象="D"の場合には常にゼロが返ります。
----- 学 齢 ----- 5: 開始日に「誕生日」を指定し、終了日時点での学齢を求めます。
算出対象に拠らず、常に年数が数値で返ります。初日算入/末日算入の指定は
不要です。開始日>終了日の場合は[#VALUE!]が返ります。
【学齢の算出条件】
・『4月2日〜翌年4月1日』誕生日で同一学齢
・『4月1日』から1Up
・誕生年度では1、小学校入学年度では7。
『どの計算方法を採用したら良いのか』という事を、私の方から推薦する事は出来ません。
対象とする事務手続きが、どのような計算方法を採っているのかを調べ、それに合ったもの
を利用して下さい。適合する計算方法が無い場合には利用しないで下さい(その事務処理
にとっては、間違った結果を返すという事ですから)。
[2a/2b]については、法的な根拠のある計算方法ではありません。「こういう計算方法
も有り得るかな」という程度の意味で加えたものです。
[1b]についても、「月末絡みの問題」を見た目に合うように、[1a]に対して補正処理を加えた
ものであり、法的な根拠はありません。
(1) 民法の定義に従った期間計算は『"1a",初日算入=False,末日算入=True 』です。
(2) 年齢計算で、法律上の加齢される日(24時と読む側の日なので生年月日の前日)に
加齢する計算は『"1a",初日算入=True,末日算入=True 』です。
(3) 年齢計算で、慣例として呼ばれている【今日からXX歳】という場合、つまり生年月日の
当日に加齢する計算は『"1a",初日算入=True,末日算入=False 』です。
(4) 年齢計算では『算出対象="y"』とすれば、年数部分だけが数値として返ります。
(5) 『日』部分を切り上げ/切り捨てて『y年mヶ月』という形式にするには、
切り上げ → 算出対象:ymd 計算方法:4-xx
切り捨て → 算出対象:ym 計算方法:xx (xx は 1a 〜 3c )
[ この場所へのリンク ]
ワークシート関数のDATEDIF(YM/MD)を使用すると同種の計算が出来ます。
=DATEDIF(開始日,終了日,"Y") & "年"
& DATEDIF(開始日,終了日,"YM") & "ヶ月"
& DATEDIF(開始日,終了日,"MD") & "日" (DATEDIFは初日不算入)
上記の式は『=ktPeriodYMD("1a",開始日,終了日,FALSE,TRUE, "ymd")』とほぼ同様の
結果が得られますが、月末の扱いで違う結果となったり、間違った結果を返す場合があります。
(DATEDIFが間違った結果を返す例)
1998/10/31〜2000/3/21 → 1年4ヶ月19日(初日不算入なので実際は11/1〜3/21で、1年4ヶ月21日が正解)
1998/10/31〜2000/3/ 1 → 1年4ヶ月-1日(初日不算入なので実際は11/1〜3/1で、1年4ヶ月1日が正解)
芳坂さんのHPから、エクセル奇譚「DATEDIF疑惑/DATEDIFの謎に迫る」で、何故このような
結果が出るのかの謎を、芳坂さんと私とで分析しています。
( 芳坂さんのHP/エクセル奇譚 より )
分析の結果、判明した Excel の中の 実装ロジック ( Excel 97 〜 2003 及び 2010 以降 )
--- DATEDIF ( MD ) ----
Function DateDif_MD(Date1 As Date, Date2 As Date) As Long
If (Day(Date2) >= Day(Date1)) Then
DateDif_MD = Day(Date2) - Day(Date1)
Else
DateDif_MD = Date2 - DateSerial(Year(Date2), Month(Date2) - 1, Day(Date1))
End If
End Function
--- DATEDIF ( YD ) ---
Function DateDif_YD(Date1 As Date, Date2 As Date) As Long
Dim d1 As Date
Dim d2 As Date
'開始日の「日」の分だけ開始日/終了日を前シフトさせる事により、
'『1日〜』というパターンに置き換えている
d1 = Date1 - Day(Date1) + 1
d2 = Date2 - Day(Date1) + 1
If (Month(d1) <= Month(d2)) Then
DateDif_YD = DateSerial(Year(d1), Month(d2), Day(d2)) - d1
Else
DateDif_YD = DateSerial(Year(d1) + 1, Month(d2), Day(d2)) - d1
End If
End Function
Excel2003以前と、Excel2007( SP無,SP1 )では DATEDIF 関数の算出結果が異なります。
[ SP2 ] 及び [ 最新の修正プログラムを施した SP1 ] では、2003以前に、ほぼ戻りました(2009/7/13)。
詳細は こちら を参照。
エクセル関数だけで、この不具合を回避する式を組む場合、下記のように、かなり複雑になります。
(Step1)
分析ツールの[EDATE]関数を使って代替式を組む事が出来ます。 (2002/7/19 追記)
A1: =DATEDIF(開始日, 終了日, "M")
A2: =INT(A1/12)&"年" & MOD(A1, 12)&"ヶ月"
& (終了日-EDATE(開始日, A1))&"日"
[Y][YM]は問題ないので、日の部分だけを置き換えるならば
=DATEDIF(開始日,終了日,"Y") & "年"
& DATEDIF(開始日,終了日,"YM") & "ヶ月"
& (終了日 - EDATE(開始日, DATEDIF(開始日, 終了日, "M")) )&"日"
↑開始日から[y年mヶ月]後の日付
この式で上記の間違いは修正されますが、未だ充分ではありません。 (2002/7/22 追記)
下記のように、[小の月の月末]から[大の月の月末]というパターンは、本来なら初日不算入
という点から、『1日〜月末』で丁度[丸Nヶ月]という結果になるはずですが、
2000/2/29〜2002/3/31 → 2年 1ヶ月2日
2000/4/30〜2002/3/31 → 1年11ヶ月1日
という風に、日数が残ってしまいます。
これは、[小の月の月末日]の応当日が[大の月]を相手にした時に月末日にならない為です。
逆に[大の月の月末日]から[小の月]の場合には、EDATE関数の仕様により、月末日が応当日
として得られる為、正しく[丸Nヶ月]になります。
(Step2)
これを回避する為には、開始日が月末日(=起算日が1日)の場合に、開始日/終了日を一緒
に1日だけ後ろにズラしてやります。そうすると、上のパターンは1日〜1日(初日不算入なので、
実際は2日〜1日)というパターンになり、正しく[丸Nヶ月]として求まります。終了日≠月末日の
場合も、これで一応正しく求まります。
=IF(DAY(開始日+1)=1,
DATEDIF(開始日+1, 終了日+1, "Y")&"年"
&DATEDIF(開始日+1, 終了日+1, "YM")&"ヶ月"
&((終了日+1)-EDATE(開始日+1,
DATEDIF(開始日+1, 終了日+1, "M")))&"日" ,
DATEDIF(開始日, 終了日, "Y")&"年"
&DATEDIF(開始日, 終了日, "YM")&"ヶ月"
&(終了日-EDATE(開始日, DATEDIF(開始日, 終了日, "M")))&"日" )
但し、この式でも、下記のケース(初日不算入)だけは正しく求まりません。
2000/3/27〜2001/2/28 → 0年11ヶ月 1日(正解:0年11ヶ月 1日)
2000/3/28〜2001/2/28 → 0年11ヶ月 0日(正解:0年11ヶ月 0日)
2000/3/29〜2001/2/28 → 0年10ヶ月30日(正解:0年11ヶ月 0日)
2000/3/30〜2001/2/28 → 0年10ヶ月29日(正解:0年11ヶ月 0日)
2000/3/31〜2001/2/28 → 0年11ヶ月 0日(正解:0年11ヶ月 0日)
2003/3/28〜2004/2/29 → 0年11ヶ月 1日(正解:0年11ヶ月 1日)
2003/3/29〜2004/2/29 → 0年11ヶ月 0日(正解:0年11ヶ月 0日)
2003/3/30〜2004/2/29 → 0年10ヶ月30日(正解:0年11ヶ月 0日)
2003/3/31〜2004/2/29 → 0年11ヶ月 0日(正解:0年11ヶ月 0日)
(Step3)
これを補正する式を組み込むと下記のようになります。 (2002/9/11 追記)
=IF(DAY(開始日+1)=1,
DATEDIF(開始日+1,終了日+1,"y")&"年"
&DATEDIF(開始日+1,終了日+1,"ym")&"ヶ月"
&(終了日+1-EDATE(開始日+1,DATEDIF(開始日+1,終了日+1,"m")))&"日" ,
IF(AND(DAY(終了日+1)=1,DAY(開始日)>DAY(終了日)),
DATEDIF(開始日,終了日,"y")&"年"
&(DATEDIF(開始日,終了日,"ym")+1)&"ヶ月0日" ,
DATEDIF(開始日,終了日,"y")&"年"
&DATEDIF(開始日,終了日,"ym")&"ヶ月"
&(終了日-EDATE(開始日,DATEDIF(開始日,終了日,"m")))&"日"))
もうひとつ別解として下記の式でも同じ様に修正されます。
=IF(DAY(開始日+1)=1,
DATEDIF(開始日+1,終了日+1,"y")&"年"
&DATEDIF(開始日+1,終了日+1,"ym")&"ヶ月"
&(終了日+1-EDATE(開始日+1,DATEDIF(開始日+1,終了日+1,"m")))&"日" ,
IF(終了日=EDATE(開始日,DATEDIF(開始日,終了日,"m")+1),
DATEDIF(開始日,終了日,"y")&"年"
&(DATEDIF(開始日,終了日,"ym")+1)&"ヶ月0日" ,
DATEDIF(開始日,終了日,"y")&"年"
&DATEDIF(開始日,終了日,"ym")&"ヶ月"
&(終了日-EDATE(開始日,DATEDIF(開始日,終了日,"m")))&"日"))
(補) 赤字の条件の場合では、[DATEDIF(開始日,終了日,"ym")]に1を加えても、
12になる(年に繰り上がる)ことはありません。
(補 2) EDATE関数を一般関数による代替式に置き換えると「分析ツール」は必要ありません。
=EDATE(日付,月)
↓
=MIN(DATE(YEAR(日付), MONTH(日付)+月, DAY(日付)),
DATE(YEAR(日付), MONTH(日付)+月+1, 0))
『ktPeriodYMD』を使えば、これだけ大変(これ全てDATEDIFにバグが有る為)な式が
=ktPeriodYMD("1a",A1,B1) または =ktPeriodYMD("1a",A1,B1,FALSE,TRUE,"ymd")
という記述だけで済みます(ktPeriodYMD[1a]のマクロを公開しています)。
-----------------------------------------------------------------------
(2010/ 5/28 追記)
YD,MD だけではなく 『[ Y ] [ M ] にもバグがある』という話をされているHPもあります。
これは、以下のようなケースの事を指しています。
[Y] [YM] [MD] [M]
2000/3/29〜2001/2/28 → 0年10ヶ月30日(正解:0年11ヶ月 0日) , 10ヶ月(正解:11ヶ月)
2000/3/30〜2001/2/28 → 0年10ヶ月29日(正解:0年11ヶ月 0日) , 10ヶ月(正解:11ヶ月)
2003/3/30〜2004/2/29 → 0年10ヶ月30日(正解:0年11ヶ月 0日) , 10ヶ月(正解:11ヶ月)
2004/2/29〜2006/2/28 → 1年11ヶ月30日(正解:2年 0ヶ月 0日) , 23ヶ月(正解:24ヶ月)
[ Y ] [ M ] だけを見れば(使えば)、確かに
「2年になる筈が 1年」 「11ヶ月/24ヶ月になる筈が 10ヶ月/23ヶ月」
と「1年(ヶ月)足りない」結果になります。
ただし、DATEDIF 内部からすると
「y年 mヶ月 d日 の計算で月の繰り上がりに1日足りない」
という事で、『DATEDIF 内部からすると辻褄は合っている』というのが扱いに困りますね。
「月の繰り上がり」境界付近の日付の組み合わせの場合には注意が必要です。
-----------------------------------------------------------------------
ワークシート関数のDATEDIFを使って『学齢』を求める式は、A1セルに誕生日が
入っているとして、下記のようになります。
=IF(TEXT(A1,"mmdd") < "0402"
,DATEDIF(DATE(YEAR(A1)-1,4,1),TODAY(),"y")
,DATEDIF(DATE(YEAR(A1),4,1),TODAY(),"y"))