PHPのstrtotime関数でバグが発生するかも!翌月「+1 month」にする場合は要注意
2021年08月18日2023年10月10日
PHPのstrtotime関数は日付を求めるときによく使いますが、思わぬバグを生む可能性があります。
というのも、翌月を求めたい時「+1 month」とすると思いますが、翌月のデータが取得できないことがあります。
テストコードを書いてみる
とりあえずテストコードを書いてみます。
問題が無いパターン
現在(1月1日)とした場合、翌月のデータを取得してみます。
<?php
// 今日の日付
$date = "2018-01-01";
// 翌月
$ym1 = date('Y-m', strtotime('+1 month ' . $date));
// 2ヶ月後
$ym2 = date('Y-m', strtotime('+2 month ' . $date));
// 3ヶ月後
$ym3 = date('Y-m', strtotime('+3 month ' . $date));
// 4ヶ月後
$ym4 = date('Y-m', strtotime('+4 month ' . $date));
var_dump($date);
var_dump($ym1);
var_dump($ym2);
var_dump($ym3);
実行結果は下記になります。
string(10) “2018-01-01”
string(7) “2018-02”
string(7) “2018-03”
string(7) “2018-04”
string(7) “2018-02”
string(7) “2018-03”
string(7) “2018-04”
この場合は問題ありません。
不具合が発生するパターン
月によっては存在しない日である「29日」「30日」「31日」でやってみるとどうなるのか。
今回は「31日」でテストしてみます。
<?php
// 今日の日付
$date = "2018-01-31";
// 翌月
$ym1 = date('Y-m', strtotime('+1 month ' . $date));
// 2ヶ月後
$ym2 = date('Y-m', strtotime('+2 month ' . $date));
// 3ヶ月後
$ym3 = date('Y-m', strtotime('+3 month ' . $date));
// 4ヶ月後
$ym4 = date('Y-m', strtotime('+4 month ' . $date));
var_dump($date);
var_dump($ym1);
var_dump($ym2);
var_dump($ym3);
実行結果は下記になります。
string(10) “2018-01-31”
string(7) “2018-03”
string(7) “2018-03”
string(7) “2018-05”
string(7) “2018-03”
string(7) “2018-03”
string(7) “2018-05”
最初と比較すると、月が連番になっていないことがわかります。
理由として、strtotime関数は1月31日の1ヶ月後は2月31日と認識し、2月は31日が存在しないので次月である3月31日という認識するようです。
テストする日によっては、テストケースを問題なくクリアしていても、日によって不具合が起こる可能性があります。
解決策
解決策として、月によって存在しない日で次月を求めてしまうと不具合になってしまうので、すべての月で存在する日で強制指定することで解決します。
コードでは、日のところを「01」にしています。
<?php
// 今日の日付
$date = date("Y-m-01");
// 翌月
$ym1 = date('Y-m', strtotime('+1 month ' . $date));
// 2ヶ月後
$ym2 = date('Y-m', strtotime('+2 month ' . $date));
// 3ヶ月後
$ym3 = date('Y-m', strtotime('+3 month ' . $date));
// 4ヶ月後
$ym4 = date('Y-m', strtotime('+4 month ' . $date));
var_dump($date);
var_dump($ym1);
var_dump($ym2);
var_dump($ym3);
そうすることで、次月を正確に取得することができます。
string(10) “2018-01-01”
string(7) “2018-02”
string(7) “2018-03”
string(7) “2018-04”
string(7) “2018-02”
string(7) “2018-03”
string(7) “2018-04”
最後に
関数は呼び出すだけで、簡単に求めているデータを取得することができ便利ですが、逆にブラックボックスになってしまう欠点もあります。
今回のような現象もテストで気づかずに、後々不具合をおこす可能性があり、日付け系は色々なパターンのテストケースを準備してテストしたほうが良いですね。
コメント
テストでは見落としがちですよね、PHPってこうゆういい加減なところが嫌い。
コメントを残す