phpの日付操作の定番ライブラリであるCarbonについてです。
問題点
「10月31日」に1ヶ月足すと何日が正解でしょうか?
感覚的には「11月30日」になってほしいのではないでしょうか?
CarbonではaddMonth()やaddMonths()で1ヶ月足すと
Carbon::parse('2018-10-31')->addMonth()->toDateString(); // 2018-12-01
2018-12-01になってしまいます。
こうなることを知ってないとプログラム中で意図しないことになる場合があります。
やっかいなのは月末くらいしかこのパターンにならないためテスト中に問題に気付きにくいことです。
この問題を解決する方法はいくつかあります。
addMonthNoOverflow()
addMonth()のかわりにaddMonthNoOverflow()を使います。
これを使うと
Carbon::parse('2018-10-31')->addMonthNoOverflow()->toDateString(); // 2018-11-30
2018-11-30となります。
デフォルトのaddMonth()でこの動きをしてほしい気がしますが・・・
useMonthsOverflow(false)
いろんなところでaddMonth()が必要な場合、いちいちaddMonthNoOverflow()を使うのは面倒な場合があります。
そんなときはCarbon::useMonthsOverflow(false)をしてやると、addMonth()のメソッドを使ったときの動きがaddMonthNoOverflow()の動きになります。
Carbonはシングルトンでこの設定値は保持されるので、一度設定してやるとどこでaddMonth()使ってもaddMonthNoOverflow()の動きになります。
もしデフォルトの動きをしたい場合はaddMonthWithOverflow()を使ってやるともとの動きになります。
settings(['monthOverflow' => false])
これはCarbonのバージョン2から利用できます。
settingsは全体に設定するものではなく、個々に設定する場合に使うイメージです。
Carbon::parse('2018-10-31')->settings(['monthOverflow' => false])->addMonth()->toDateString(); // 2018-11-31
ちなみに
subMonth()でもこの問題が起こります。
例えば「10月31日」にsubMonth()すると「9月30日」ではなく、「10月1日」となります。
addYear()もうるう年でずれるようで、useYearsOverflow()なんてものが用意されてます。
Carbon::parse('2016-02-29')->addYear()->toDateString(); // 2017-03-01
Carbon::parse('2016-02-29')->settings(['yearOverflow' => false])->addYear()->toDateString(); // 2017-02-28
まとめ
phpのCarbonの注意を書きました。
知ってないとバグが起きてしまう可能性は大いにあるので抑えておきましょう。
他の言語でも日付の計算は最初に動きを確かめておいたほうが個人的には良いかなと思います。