Help us understand the problem. What is going on with this article?

【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万歳 っていう流れになりましたね。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした