やりたいこと
- Carbon で「2016-07-31」を指定
- この日付から同じ年の「4月1日」を取得したい
発生した問題
下記のように、month()
とday()
で、4月1日を指定すると、なぜか2016-05-01
となる。
<?php
namespace Acme;
use Carbon\Carbon;
require_once __DIR__ . '/vendor/autoload.php';
$date = Carbon::createFromDate(2016, 7, 31);
$date->month(4);
$date->day(1);
echo $date->format('Y-m-d'), PHP_EOL;
// 2016-05-01
これは、month(4)
を実行した時点で、2016-04-31
となってしまうため、これが2016-05-01
に変換されてしまう。(これは、基底クラスの DateTime の挙動)
ここに、day(1)
を実行しても日は変わらないので、結果として、2016-05-01
となる。
対応1
month()
を実行した時点で、存在しない日となるのが原因なので、day()
を先に実行すれば、動作としては正常になります。
<?php
namespace Acme;
use Carbon\Carbon;
require_once __DIR__ . '/vendor/autoload.php';
$date = Carbon::createFromDate(2016, 7, 31);
// 日を先に変えておけば、挙動としては正しい
$date->day(1);
$date->month(4);
echo $date->format('Y-m-d'), PHP_EOL;
// 2016-04-01
対応2
setDate()
で、アトミックに月と日を指定すれば、ok。
<?php
namespace Acme;
use Carbon\Carbon;
require_once __DIR__ . '/vendor/autoload.php';
$date = Carbon::createFromDate(2016, 7, 31);
$date->setDate($date->year, 4, 1);
echo $date->format('Y-m-d'), PHP_EOL;
// 2016-04-01
どちらで対応するか
どちらの対応でも動作しますが、対応1は、実行順を意識しておく必要があるのが難点です。「4月1日にする」という仕様だと、直感的にはmonth()
から実行したくなるのに、その反対の実装が必要となります。
その点、対応2の場合、単にsetDate()
を呼ぶだけなので、特段意識する必要はありません。
特別な理由が無い限りは、素直に対応2で実装するのが良いでしょう。