日付の間隔を○年○ヶ月○日と表示するには?: Notion Formula 解説 (32)

はじめに

Notion Formula の第32回目は、「日付の間隔を○年○ヶ月○日と表示するには?」を解説します。「○年○ヶ月」までは簡単なのですが、意外と最後の○日まで出すのは大変です。この例については、こちらの逆引きで紹介しています。

数式

数式は以下のようになります。こちらも条件判断が多いので、順に式を確認していきます。

lets(
 /* 二つの日付の配列を作り、日付で並び替えて変数 ds に代入 */
 ds, [prop("Start"), prop("End")].sort(),
 /* 配列の先頭(過去の日付)を取り出し、変数 st に代入*/
 st, ds.first(),
 /* 配列の末尾(未来の日付)を取り出し、変数 et に代入*/
 et, ds.last(),
 /* et と st の間の年数を取得し、変数 yd に代入 */
 yd, et.dateBetween(st, "years"),
 /* et と st の間の月数を取得し、変数 md に代入 */
 md, et.dateBetween(st, "months") % 12,
 /* yd の年数を st に追加、md の月数をさらに追加 */
 yma, st.dateAdd(yd, "years").dateAdd(md, "months"),
 /* et と yma の間の日数を取得 */
 dd, et.dateBetween(yma, "days"),
  (yd.empty() ? "" : yd + "年")
  + (md.empty() ? "" : md + "ヶ月")
  + (dd.empty() ? "" : dd + "日")
)

境界値で場合分けしてテストするため、以下のようなデータセットを用意しました。

種別 開始時刻 終了時刻
1日 2022/8/20 2022/8/21
1日(月またぎ) 2022/7/31 2022/8/1
ちょうど一月(7/31→8/31) 2022/7/31 2022/8/31
ちょうど一月(6/30→7/30) 2022/6/30 2022/7/30
1月1日(6/30→7/31) 2022/6/30 2022/7/31
ちょうど一月(5/31→6/30) 2022/5/31 2022/6/30
1月1日(5/31→7/1) 2022/5/31 2022/7/1
ちょうど一月(1/31→2/28) 2022/1/31 2022/2/28
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1
1年1月1日(1/31→3/1) 2022/3/1 2021/1/31
11ヶ月30日 2021/2/1 2022/1/31
11ヶ月30日 2022/1/31 2021/2/1
空白

最初は、日付範囲の開始時刻と終了時刻をそれぞれ st と et に代入します。最初にソートすることで、必ず過去の時刻が st に、未来の時刻が et に入るようになっています。

lets(
 /* 二つの日付の配列を作り、日付で並び替えて変数 ds に代入 */
 ds, [prop("Start"), prop("End")].sort(),
 /* 配列の先頭(過去の日付)を取り出し、変数 st に代入*/
 st, ds.first(),
 /* 配列の末尾(未来の日付)を取り出し、変数 et に代入*/
 et, ds.last(),
種別 ds st et
1日 [2022/8/20, 2022/8/21] 2022/8/20 2022/8/21
1日(月またぎ) [2022/7/31, 2022/8/1] 2022/7/31 2022/8/1
ちょうど一月(7/31→8/31) [2022/7/31, 2022/8/31] 2022/7/31 2022/8/31
ちょうど一月(6/30→7/30) [2022/6/30, 2022/7/30] 2022/6/30 2022/7/30
1月1日(6/30→7/31) [2022/6/30, 2022/7/31] 2022/6/30 2022/7/31
ちょうど一月(5/31→6/30) [2022/5/31, 2022/6/30] 2022/5/31 2022/6/30
1月1日(5/31→7/1) [2022/5/31, 2022/7/1] 2022/5/31 2022/7/1
ちょうど一月(1/31→2/28) [2022/1/31, 2022/2/28] 2022/1/31 2022/2/28
1年1月1日(1/31→3/1) [2021/1/31, 2022/3/1] 2022/1/31 2022/3/1
1年1月1日(1/31→3/1) [2021/1/31, 2022/3/1] 2022/1/31 2022/3/1
11ヶ月30日 [2022/2/1, 2022/1/31] 2021/2/1 2022/1/31
11ヶ月30日 [2022/2/1, 2022/1/31] 2021/2/1 2022/1/31
空白

yd は st と et の間の年数を取得します。これはすでに解説済みの dateBetween を使っているだけです。

 /* et と st の間の年数を取得し、変数 yd に代入 */
 yd, et.dateBetween(st, "years"),
種別 st et yd
1日 2022/8/20 2022/8/21 0
1日(月またぎ) 2022/7/31 2022/8/1 0
ちょうど一月(7/31→8/31) 2022/7/31 2022/8/31 0
ちょうど一月(6/30→7/30) 2022/6/30 2022/7/30 0
1月1日(6/30→7/31) 2022/6/30 2022/7/31 0
ちょうど一月(5/31→6/30) 2022/5/31 2022/6/30 0
1月1日(5/31→7/1) 2022/5/31 2022/7/1 0
ちょうど一月(1/31→2/28) 2022/1/31 2022/2/28 0
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1 1
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1 1
11ヶ月30日 2021/2/1 2022/1/31 0
11ヶ月30日 2021/2/1 2022/1/31 0
空白

md は st と et の間の月数を取得します。こちらもすでに dateBetween を使っていますが、1年を超えた場合 12 を超える値になります。月だけが欲しいので 12 の剰余を取得しています。

 /* et と st の間の月数を取得し、変数 md に代入 */
 md, et.dateBetween(st, "months") % 12,
種別 st et dateBetween md
1日 2022/8/20 2022/8/21 0 0
1日(月またぎ) 2022/7/31 2022/8/1 0 0
ちょうど一月(7/31→8/31) 2022/7/31 2022/8/31 0 1
ちょうど一月(6/30→7/30) 2022/6/30 2022/7/30 0 1
1月1日(6/30→7/31) 2022/6/30 2022/7/31 0 1
ちょうど一月(5/31→6/30) 2022/5/31 2022/6/30 0 1
1月1日(5/31→7/1) 2022/5/31 2022/7/1 0 1
ちょうど一月(1/31→2/28) 2022/1/31 2022/2/28 0 1
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1 13 1
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1 13 1
11ヶ月30日 2021/2/1 2022/1/31 0 11
11ヶ月30日 2021/2/1 2022/1/31 0 11
空白

yma は st に yd 分の年と md 分の月を加算します。こちらは dateAdd を2回続けて実行するだけです。

 /* yd の年数を st に追加、md の月数をさらに追加 */
 yma, st.dateAdd(yd, "years").dateAdd(md, "months"),
種別 st et dateAdd(year) yma
1日 2022/8/20 2022/8/21 2022/8/20 2022/8/20
1日(月またぎ) 2022/7/31 2022/8/1 2022/7/31 2022/7/31
ちょうど一月(7/31→8/31) 2022/7/31 2022/8/31 2022/7/31 2022/8/31
ちょうど一月(6/30→7/30) 2022/6/30 2022/7/30 2022/6/30 2022/7/30
1月1日(6/30→7/31) 2022/6/30 2022/7/31 2022/6/30 2022/7/30
ちょうど一月(5/31→6/30) 2022/5/31 2022/6/30 2022/5/31 2022/6/30
1月1日(5/31→7/1) 2022/5/31 2022/7/1 2022/5/31 2022/6/30
ちょうど一月(1/31→2/28) 2022/1/31 2022/2/28 2022/1/31 2022/2/28
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1 2022/1/31 2022/2/28
1年1月1日(1/31→3/1) 2021/1/31 2022/3/1 2022/1/31 2022/2/28
11ヶ月30日 2021/2/1 2022/1/31 2021/2/1 2022/1/1
11ヶ月30日 2021/2/1 2022/1/31 2021/2/1 2022/1/1
空白

yma は st に yd 分の年と md 分の月を加算します。こちらは dateAdd を2回続けて実行するだけです。

 /* et と yma の間の日数を取得 */
 dd, et.dateBetween(yma, "days"),
種別 yma et dd
1日 2022/8/20 2022/8/21 1
1日(月またぎ) 2022/7/31 2022/8/1 1
ちょうど一月(7/31→8/31) 2022/8/31 2022/8/31 0
ちょうど一月(6/30→7/30) 2022/7/30 2022/7/30 0
1月1日(6/30→7/31) 2022/7/30 2022/7/31 1
ちょうど一月(5/31→6/30) 2022/6/30 2022/6/30 0
1月1日(5/31→7/1) 2022/6/30 2022/7/1 1
ちょうど一月(1/31→2/28) 2022/2/28 2022/2/28 0
1年1月1日(1/31→3/1) 2022/2/28 2022/3/1 1
1年1月1日(1/31→3/1) 2022/2/28 2022/3/1 1
11ヶ月30日 2022/1/1 2022/1/31 30
11ヶ月30日 2022/1/1 2022/1/31 30
空白

最後に yd, md, dd から文字列を作成します。空か 0 の時には単位も表示したくないので、empty() で確認し、空白に置き換えます。

  (yd.empty() ? "" : yd + "年")
  + (md.empty() ? "" : md + "ヶ月")
  + (dd.empty() ? "" : dd + "日")
)
種別 yd md dd 関数の結果
1日 0 0 1 1日
1日(月またぎ) 0 0 1 1日
ちょうど一月(7/31→8/31) 0 1 0 1ヶ月
ちょうど一月(6/30→7/30) 0 1 0 1ヶ月
1月1日(6/30→7/31) 0 1 1 1ヶ月1日
ちょうど一月(5/31→6/30) 0 1 0 1ヶ月
1月1日(5/31→7/1) 0 1 1 1ヶ月1日
ちょうど一月(1/31→2/28) 0 1 0 1ヶ月
1年1月1日(1/31→3/1) 1 1 1 1年1ヶ月1日
1年1月1日(1/31→3/1) 1 1 1 1年1ヶ月1日
11ヶ月30日 0 11 30 11ヶ月30日
11ヶ月30日 0 11 30 11ヶ月30日
空白

おわりに

今回は、「日付の間隔を○年○ヶ月○日と表示するには?」を解説しました。日付の差分を求めるために、開始時刻の○年○ヶ月後の日付を計算することで、最後の○日を計算しています。