📅

2月29日の1ヶ月後、1年後は言語によって違う

2022/11/18に公開

ある基準日から1ヶ月後、1年後を求めたい時ってよくありますよね。
そんな時に問題になるのがうるう年の2月29日です。
実は各言語の標準機能ごとに微妙に結果が異なることをご存知ですか?

早速例を見ていきましょう。

Javaの場合

1ヶ月後

Calendar c = Calendar.getInstance();
c.set(2020, 1, 29); // 2020/2/29(月は0始まり)
c.add(Calendar.MONTH, 1);
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(c.getTime()));

結果は、

2020/03/29

シンプルですね。単純に1ヶ月後の同日が設定されます。

1年後

Calendar c = Calendar.getInstance();
c.set(2020, 1, 29); // 2020/2/29(月は0始まり)
c.add(Calendar.YEAR, 1);
System.out.println(new SimpleDateFormat("yyyy/MM/dd").format(c.getTime()));

結果は、

2021/02/28

存在しない日付になる場合はその前日が設定されます。
3月31日の1ヶ月後も同様に4月30日になります。

JavaScriptの場合

1ヶ月後

var dt = new Date(2020, 1, 29); // 2020/2/29(月は0始まり)
dt.setMonth(dt.getMonth() + 1);
console.log(dt);

結果は、

Sun Mar 29 2020 00:00:00 GMT+0900 (日本標準時)
= 2020/03/29

Javaと同じく、1ヶ月後の同日が設定されます。

1年後

var dt = new Date(2020, 1, 29); // 2020/2/29(月は0始まり)
dt.setFullYear(dt.getFullYear() + 1);
console.log(dt);

結果は、

Mon Mar 01 2021 00:00:00 GMT+0900 (日本標準時)
= 2021/03/01

!?
存在しない日付の場合はその翌日が設定されます。
3月31日の1ヶ月後も同様に5月1日になります。

Oracle (SQL)の場合

1ヶ月後

SELECT
  ADD_MONTHS(TO_DATE('20200229', 'yyyymmdd'), 1)
FROM
  DUAL

結果は、

2020/03/31 0:00:00

!?
月末が指定された場合は必ず月末になるように設定されます。
(どういう仕組みだ・・・)

1年後

SELECT
  ADD_MONTHS(TO_DATE('20200229', 'yyyymmdd'), 12)
FROM
  DUAL

結果は、

2021/02/28 0:00:00

1ヶ月後の場合にあるように月末が指定されたら必ず月末になるように設定されます。

まとめ

比較的メジャーな言語でもこれだけ差異があるというのは留意しないといけませんね。
日付を扱うプログラムを書くときはよく確認するようにしましょう。

余談

ちなみに、年齢はいつ加算されるかご存知ですか?

正解は、「誕生日の0時0分0秒の直前」つまり前日です。(前日の24時と訳される事もあります)
2月29日生まれの人は2月28日にちゃんと歳をとるので、4年に1度しか歳をとらないということにはなりません(笑)

GitHubで編集を提案

Discussion