1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

いえらぶAdvent Calendar 2022

Day 21

【PHP】nヶ月後の日付を計算したい!

Last updated at Posted at 2022-12-25

とりあえず書いてみる

突然ですが、与えられた日付の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の月の月末を返すようにしました。
かなり無理やりな感じはしますが、挙動は問題なさそうです。

もしより良い実装があればぜひ教えてください。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?