ktPeriodYMD

開始日〜終了日の期間( 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ヶ月』形式の文字列。

 計算方法""(学齢)の場合は、算出対象に拠らず、常に年数を数値で返します。

 ゼロ抑制=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 で指定します。 
  計算方法""(学齢)の場合には、初日算入/末日算入の指定は不要です)
 
ゼロ抑制 ‥‥‥ 「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
  ・誕生年度では、小学校入学年度では
 

【 解 説 】

『どの計算方法を採用したら良いのか』という事を、私の方から推薦する事は出来ません。
対象とする事務手続きが、どのような計算方法を採っているのかを調べ、それに合ったもの
を利用して下さい。適合する計算方法が無い場合には利用しないで下さい(その事務処理
にとっては、間違った結果を返すという事ですから)。

[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による期間計算 】

ワークシート関数の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ヶ月日が正解)

芳坂さんの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ヶ月
      2000/4/30〜2002/3/31  →  1年11ヶ月
という風に、日数が残ってしまいます。
  これは、[小の月の月末日]の応当日が[大の月]を相手にした時に月末日にならない為です。
逆に[大の月の月末日]から[小の月]の場合には、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"))


Home  kt関数 Top  kt関数 Ref  Back Page  Next Page


角田 桂一 Mail:addinbox@h4.dion.ne.jp CopyRight(C) 2001 Allrights Reserved.