とりあえず書いてみる
突然ですが、与えられた日付のnヶ月後を表示する必要が発生しました。
とりあえず書いてみます。
function addSomeMonthsToTargetDate($targetDate, $months)
{
return date('Y-m-d', strtotime("+{$months} month", strtotime($targetDate)));
}
$result = addSomeMonthsToTargetDate('2022-10-25', 2);
var_dump($result); // 2022-12-25
一回strtotimeでUNIXタイムスタンプに変換して、計算してから戻せば良さそうです。
ただ、strtotimeは2038年問題が発生するので避けたほうが良いと聞いたことがあります。
とりあえず確認します。
$result = addSomeMonthsToTargetDate('2037-10-25', 3);
var_dump($result); // 1970-01-01
ダメでした。
改めて、とりあえず書いてみる
DateTime系統なら2038年問題は発生しないようなので、とりあえず書いてみます。
function addSomeMonthsToTargetDate($targetDate, $months)
{
$dt = new DateTime($targetDate);
return $dt->modify("+{$months} month")->format('Y-m-d');
}
$result1 = addSomeMonthsToTargetDate('2022-10-25', 2);
$result2 = addSomeMonthsToTargetDate('2037-10-25', 3);
$result3 = addSomeMonthsToTargetDate('2022-05-03', 50);
var_dump($result1); // 2022-12-25
var_dump($result2); // 2038-01-25
var_dump($result3); // 2026-07-03
良い感じです。もう少し続けてみます。
$result4 = addSomeMonthsToTargetDate('2022-10-31', 1);
$result5 = addSomeMonthsToTargetDate('2022-10-30', 4);
var_dump($result4); // 2022-12-01
var_dump($result5); // 2023-03-02
かなり怪しい結果が出てしまいました。
計算後の日付がその月の最後の日を過ぎた場合は、翌月に繰り越して計算しているようです。
日常生活で10/31の一か月後は12/1だと思わないので、人間の直感には反します。
もう一度、とりあえず書いてみる
計算後の日付が月の最終日を超えている場合は、その月の最終日を返すようにしたいです。
とりあえず書いてみます。
function addSomeMonthsToTargetDate($targetDate, $months)
{
// シンプルにmodify()を行った場合
$dt1 = new DateTime($targetDate);
$simplyModifiedTime = $dt1->modify("+{$months} month")->format('Y-m-d');
$dt1->modify('first day of this month');
// その月の月初からmodify()を行った場合
$dt2 = new DateTime($targetDate);
$dt2->modify('first day of this month')->modify("+{$months} month");
// 上記の結果が一致しなければ、月だけで計算して月末を取得。
if ($dt1->format('Y-m-d') === $dt2->format('Y-m-d')) {
return $simplyModifiedTime;
} else {
return $dt2->modify('last day of this month')->format('Y-m-d');
}
}
$result1 = addSomeMonthsToTargetDate('2022-10-25', 2);
$result2 = addSomeMonthsToTargetDate('2037-10-25', 3);
$result3 = addSomeMonthsToTargetDate('2022-05-03', 50);
$result4 = addSomeMonthsToTargetDate('2022-10-31', 1);
$result5 = addSomeMonthsToTargetDate('2022-10-30', 4);
var_dump($result1); // 2022-12-25
var_dump($result2); // 2038-01-25
var_dump($result3); // 2026-07-03
var_dump($result4); // 2022-11-30
var_dump($result5); // 2023-02-28
$dt2
で必ず求めたい月にたどり着くようにして、$dt1
の行き着いた月とずれがある場合は$dt2
の月の月末を返すようにしました。
かなり無理やりな感じはしますが、挙動は問題なさそうです。
もしより良い実装があればぜひ教えてください。