PHP
Laravel
日付計算
Carbon

【php/Laravel】Carbon使って「来月の今日」を求めたいのです


Carbonとは

まず公式をお読みください。

https://carbon.nesbot.com/

よくわからん、という場合はこの記事自体読む必要ないと思いますけど、ググって実際の利用ケースを参照してもらうと良いと思います。


何が便利なのか

phpの組み込み関数だけで日付計算しようとすると、結構煩雑な記述になって、後から見た時何しようとしてんのかわからん、っていうのが一番の問題ですよね。

たとえば、1836年1月3日に生まれた坂本龍馬が今生きてたら何歳かな?とか計算しようとしたら・・・


誕生日の計算

$birthday = "1836-01-03";    // なんかデータベースにでも入ってると仮定して

:
:
$now = date('Ymd');
$bd = date('Ymd', strtotime($birthday));
$age = floor(($now-$bd)/10000);

# [結果]
# => 183.0


のようになります。

特にこの、floor(($now-$bd)/10000) とかの計算部分がよくわからんですね。

こんなもん標準の関数で用意しとけ、て思うくらいの定型文。

なので、これをCarbon使ってやると


Carbonで誕生日から年齢計算

use Carbon\Carbon;

:
$birthday = "1836-01-03";
:
$age = Carbon::parse($birthday)->age;

# [結果]
# => 183


なんと、日付から生成したオブジェクトのageプロパティにアクセスしたら、自動的に年齢が取れてしまいました。

こういう細かく行き届いた機能が山のように用意されています。

便利ですね!


これまで(個人的に)問題だったこと

実務でlaravelを使ってシステム組んでいて、日付計算にcarbonを使うようになってずいぶんコード量もバグも減ったと思うのですが、1個だけどうしても解決できない面倒な記述があったのです。

それは 来月 の日付取得。

普通に考えれば、「来月の今日」をもってこようと思ったら


来月の今日


$raigetu = Carbon::addMonth()->toDateString();

みたいな感じでいい気がしますか?

では1月31日にこうやって日付取り出したらどうなりますか?

やってみてください。

うるう年かどうかによりますが、3月2日か3日が返ってきますね。

全然来月じゃない。

こんなロジックで請求書なんか出すシステム組んだ日にはえらいこっちゃですね。

通常、運用的にはこういう場合は翌月の最終日を返してほしいのです。

私は通常


来月の今日(2)


raigetu = date('Y-m-', strtotime('+1 month', strtotime(date('Y-m-01')))) . date('d');

みたいなややこしい書き方をしてました。Carbonのソースコード読むまでは。

このへんは人によっていろんなやり方があると思いますけど、月末で計算するから日数によって月またいだりいろいろしちゃうのであって、1日で計算したら問題ないでしょ?っていうやりかたです。

自分的には定型文ですが、他の人が見たら「なんじゃこりゃ」ってなりかねません。

でもCarbonのドキュメント読んでいてもそれっぽい機能が見当たらない。

で、仕方ないのでソースコード詠みこんでみたんですが、なんだかあっさりそれっぽいのが見つかりました。

その名も

addMonthNoOverflow


addMonthNoOverflow の使い方

って程の事はないです。


Carbon


// 今日が 1/31だと仮定して、翌月の今日を求めたい場合
$raigetu = Carbon::now()->addMonthNoOverflow()->toDateString();
# [結果]
# => "2019-02-28"

$raigetu = Carbon::parse("2019-03-31")->addMonthNoOverflow()->toDateString();
#[結果]
# => "2019-04-30"

// 1か月以上加算したいときは
// Carbon::addMonthsNoOverflow(int)
// をつかいます。
$hantosi = Carbon::parse("2019-03-31")->addMonthsNoOverflow(6)->toDateString();
#[結果]
# => "2019-09-30"


あらまぁ・・・便利ですわね、奥さん!

ロジック的には、普通にaddMonthしてから、元の「日」と計算後の「日」が違ったら計算後の日付の「前月の最終日」を返す、っていう組み方みたいです。

速度的なロスを考えると、なんとか1行で書いてしまおうとしている私のやり方よりも賢いかもしれません。何より読みやすいし。

というわけで、ますます Carbon万歳 っていう流れになりましたね。

というところで、また次回!