Laravel PHP

【php】Carbonまとめ【日時操作ライブラリ】

日時操作基礎知識(Carbonまとめの前に...)

タイムゾーンは各国の時刻を表すものになります。
イギリスを中心(±0時間)として各地域がどれだけ差異があるかを表します。
日本の時刻はJSTと呼び、+9時間になります。タイムゾーンとしてはAsia/Tokyoなどと表記します。
+9時間なのでイギリスが10時のときは日本は19時になります。
UTC/GMT、サマータイムなどについての説明はここでは省きます。

Laravelで使えるCarbonには
・元々のCarbon\Carbon
・少し機能が追加(後述)されている Illuminate\Support\Carbon
・加減処理の際にcopy()不要で直感的な Carbon\CarbonImmutable
の3種類があります。(参考

Laravelでは、Modelのデフォルトの日時フォーマットのキャストがCarbonなので
機能が追加されているIlluminate\Support\Carbon を基本的に使い、
必要に応じて加減処理が直感的なCarbon\CarbonImmutable を使うとよいです。
Modelの$castを全て設定するのであればCarbonImmutableに統一してしまえれば楽だと思いますが混ざると不具合になりやすいのでこの辺りはお好みで。

Illuminate\Support\Carbon はLaravel8.x、9.xと地味な機能が追加されてきているので今後も機能が増えていくかもしれません。

入力

Carbonにデータを入れるパターンです。
基本的に第2引数にタイムゾーンを入れることができます。
どこのタイムゾーンでも同じ日時が入る場合(タイムゾーンが入っているISOフォーマットや秒単位で現在日時が入るnow)以外は
タイムゾーンを入れる癖をつけておくとよいです。

# 現在日時系
$carbon = Carbon::now(); // 現在の日時が入る

$carbon = Carbon::today(); // (今が何時でも)今日の0時0分0秒が入る
$carbon = Carbon::today('Asia/Tokyo'); // 引数にTimeZoneを入れられる 例の場合は日本時間(JST)で今日の0時0分0秒が入る

# 現在日時基準系
$carbon = Carbon::yesterday(); // (今が何時でも)昨日の0時0分0秒が入る
$carbon = Carbon::tomorrow(); // (今が何時でも)明日の0時0分0秒が入る


# 日時指定系
$carbon = Carbon::parse('2023/04/05 06:07:08'); // 指定の日時で入る parse内の形式はわりとよしなに解釈してくれる
$carbon = Carbon::parse('2023/04/05 06:07:08', 'Asia/Tokyo'); // 第2引数にTimeZoneを入れられる 例の場合JSTの指定日時として入る
$carbon = new Carbon('2023/04/05 06:07:08'); // Carbon::parse() と同様

$carbon = Carbon::parse('2023-12'); // 2023-12-01 00:00:00 日付指定の場合は指定されていないところは最小値(0か1)が自動的に入る
$carbon = Carbon::parse('01:23'); // 2022-10-20 01:23:00 時間指定の場合は日付は今日になる

# timestamp
$carbon = Carbon::createFromTimestamp(1680674828); // unixtimeで指定の日時で入る
$carbon = Carbon::createFromTimestampMs(1680674828000); // unixtime(ミリ秒)で指定の日時で入る

# 複製
$carbon2 = $carbon->copy(); // 複製する

日時の加算 / 減算

上記のように$carbonを生成したら、それを元に日時の加算や減算ができます。
なお、$carbonに格納せずにいきなり $example_at = Carbon::now()->addMinutes(5); などとすることも可能です。
冒頭でも書きましたがCarbonとCarbonImmutableで動作に差が出るところになります。
Carbonインスタンスに加算や減算したあとにも同インスタンスを使い回す場合はよく確認しておいてください。

# 加算
$carbon->addSeconds(3); // +3秒
$carbon->addMinutes(3); // +3分
$carbon->addHours(3); // +3時間
$carbon->addDays(3); // +3日
$carbon->addWeekdays(3); // 土日をカウントせずに+3日 e.g. 金曜でaddWeekdays(2)すると翌週の火曜になる
$carbon->addWeek(3); // +3週間(= +21日)
$carbon->addMonths(3); // +3ヶ月 2月31日など存在しない日になる場合翌月にはみ出す e.g.2月31日なら3日はみ出しているので3月3日
$carbon->addMonthsNoOverflow(3); // +3ヶ月 2月31日など存在しない日になる場合は月末の日になる e.g. 2月31日なら2月28日
$carbon->addYears(3); // +3年 うるう年の2月29日から他の年になったときは3月1日になる
$carbon->addYearsNoOverflow(3); // +3年 うるう年の2月29日から他の年になったときは2月28日になる
$carbon->addDecade(3); // +30年
$carbon->addCenturies(3); // +300年
$carbon->addMillennia(3); // +3000年

# 減算
$carbon->subSeconds(5); // -5秒
$carbon->subMinutes(5); // -5分
$carbon->subHours(5); // -5時間
$carbon->subDays(5); // -5日
$carbon->subWeekdays(5); // 土日をカウントせずに-5日 e.g. 月曜でsubWeekdays(2)すると前の週の木曜になる
$carbon->subWeek(5); // -5週間(= -35日)
$carbon->subMonths(5); // -5ヶ月 2月31日など存在しない日になる場合翌月にはみ出す e.g.2月31日なら3日はみ出しているので3月3日
$carbon->subMonthsNoOverflow(5); // -5ヶ月 2月31日など存在しない日になる場合は月末の日になる e.g. 2月31日なら2月28日
$carbon->subYears(5); // -5年 うるう年の2月29日から他の年になったときは3月1日になる
$carbon->subYearsNoOverflow(5); // -5年 うるう年の2月29日から他の年になったときは2月28日になる
$carbon->subDecade(5); // -50年
$carbon->subCenturies(5); // -500年
$carbon->subMillennia(5); // -5000年

加算や減算後の状態について

日時を加算や減算した際のインスタンスの状態にCarbonとCarbonImmutableで差が出ます。
通常のCarbonの場合、 $today->addDays(7)->format('Y-m-d H:i:s'); などと実行してしまうと$todayの中身自体が7日後になってしまうので注意が必要です。

# use Illuminate\Support\Carbon; もしくは use Carbon\Carbon; なら
$today = Carbon::createFromFormat('Y-m-d H:i:s', '2023-01-23 01:23:45'); // 今日は23日
$today->addDays(1)->format('Y-m-d H:i:s'); // 2023-01-24 01:23:45 1日進んで24日
$today->format('Y-m-d H:i:s'); // 2023-01-24 01:23:45 24日になっていて$today自体が1日進んでいる!

# $todayを維持するなら下記のように操作する
$today = Carbon::createFromFormat('Y-m-d H:i:s', '2023-01-23 01:23:45'); // 今日は23日
$tomorrow = $today->copy()->addDays(1); // コピーしてから1日足したものを$tomorrowに格納
$tomorrow->format('Y-m-d H:i:s'); // 2023-01-24 01:23:45 こちらは24日
$today->format('Y-m-d H:i:s'); // 2023-01-23 01:23:45 $todayは23日のまま

# $tomorrowを作りたくなければ
$today->copy()->addDays(1)->format('Y-m-d H:i:s'); // これでもOK
# use Carbon\CarbonImmutable; なら
$today = CarbonImmutable::createFromFormat('Y-m-d H:i:s', '2023-01-23 01:23:45'); // 今日は23日
$today->addDays(1)->format('Y-m-d H:i:s'); // 2023-01-24 01:23:45 1日進んで24日
$today->format('Y-m-d H:i:s'); // 2023-01-24 01:23:45 $todayは23日のまま

出力

$carbonが保持している時間などの情報を出力するものです。

$carbon->getTimestamp(); // unixtimestampで出力 e.g. 1674444896
$carbon->toISOString(); // ISO-8601形式で出力(Laravel7以降のEloquentでtoArrayやtoJsonをするとデフォルトはこの形式)e.g. 2023-01-23T03:34:56.000000Z
$carbon->format('Y-m-d H:i:s'); // 指定した形式で出力 e.g. 例の場合 2023-01-23 12:34:56
$carbon->format('Y-m-d\TH:i:sP'); // YYYY-MM-DDThh:mm:ssTZD 形式の指定出力 e.g. 2023-01-23T12:34:56+09:00

# 曜日など
$carbon->dayOfWeek; // 数字が返る 月:1 火:2 水:3 木:4 金:5 土:6 日:0 
$carbon->dayOfWeekIso; // 数字が返る 月:1 火:2 水:3 木:4 金:5 土:6 日:7

# 日本語で曜日を出す ※localeを日本にする必要有 Carbon::setLocale('ja');
$carbon->isoFormat('Y年M月D日(ddd) H:m:s'); // e.g. 2023年1月23日(月) 1:23:45
$carbon->isoFormat('YYYY年MM月DD日(dddd) HH:mm:ss') // e.g. 2023年01月23日(月曜日) 01:23:45

比較(判定)

真偽判定など、$carbonが保持している時間以外の情報を返すものです。

$carbon->max($carbon2); // 大きいもの(より遅い:未来)を返す 引数を省略すると現在時刻との比較
$carbon->min($carbon2); // 小さいもの(より早い:過去)を返す 引数を省略すると現在時刻との比較

# 差分
$carbon->diffInMicroseconds($carbon2); // 何マイクロ秒違うかを数字で返す e.g. int(1000013)
$carbon->diffInMilliseconds($carbon2); // 何ミリ秒違うかを数字で返す e.g. int(1000)
$carbon->diffInSeconds($carbon2); // 何秒違うかを数字で返す e.g. int(1234)
$carbon->diffInMinutes($carbon2); // 何分違うかを数字で返す 秒単位は切り捨て e.g. int(1234)
$carbon->diffInHours($carbon2); // 何時間違うかを数字で返す 分単位以下は切り捨て e.g. int(1234)
$carbon->diffInDays($carbon2); // 何日違うかを数字で返す 時間単位以下は切り捨て e.g. int(1234)
$carbon->diffInWeekdays($carbon2); // 平日(月〜金)のみカウントで何日違うかを数字で返す 祝日は考慮されない e.g. int(1234)
$carbon->diffInWeeks($carbon2); // 何週間違うかを数字で返す 日単位以下は切り捨て e.g. int(1234)
$carbon->diffInMonths($carbon2); // 何ヶ月違うかを数字で返す e.g. 1/10と2/9は30日差だが0ヶ月 2/10と3/12は30日差で1ヶ月 e.g. int(1234)
$carbon->diffInQuarters($carbon2); // 何四半期違うかを数字で返す ( = diffInMonths / 3 の切り捨て)
$carbon->diffInYears($carbon2); // 何年違うかを数字で返す 月単位以下は切り捨て e.g. int(1234)
$carbon->diff($carbon2); // 何年何ヶ月...何分何秒違うかを分解して出してくれる e.g. 2000分の差ならd=>1,h=>9,i=>20

# 差分亜種
$carbon->floatDiffInHours($carbon2); // 上記diffIn系にfloatをつけると切り捨てずに小数で返す e.g. float(1.9833333369444444)

# その他
$carbon->isBirthday(); // $carbon生まれの人が今日誕生日か(年を無視して、月と日が一致しているか)引数で今日以外の指定も可

テスト用

テストをするとき、現在時刻の影響を受けることは多いと思います。
その際、まるで今が指定した時間であるかのように振る舞ってテストをするための機能があります。

なお、ここで Illuminate\Support\Carbon のCarbonを使っている場合、CarbonとCarbonImmutableの両方に一括でsetTestNowされます。(Laravel 8.x以降)
setTestNow()は基本的にIlluminate\Support\Carbonで行うのがよいでしょう。

# set1
$carbon = Carbon::parse('2023/04/05 06:07:08');
Carbon::setTestNow($carbon); // $carbonの時間を"今"扱いにする Carbon::now()がこの時間になる

# set2、3
Carbon::setTestNow('2023-04-05 06:07:08'); // これでもよい
Carbon::setTestNow('2023-01-23T03:34:56.000000Z'); // これでもよい テストとしてはタイムゾーンも指定しているこの形式がおすすめ

# set状況確認
Carbon::hasTestNow(); // テスト時間がセットされているか この例では前の行でセットされているのでtrueになる
Carbon::getTestNow(); // セットしたテスト時間が返る セットされていなければnull nullでなければ->format()などを繋げることもできる

Carbon::setTestNow(); // 空だと初期化 nowが実際の現在時刻の状態に戻る
Carbon::hasTestNow(); // テスト時間がセットされているか 前の行で外されているのでfalseになる

Illuminate\Support\Carbon の機能

おまけです。
Illuminate\Support\Carbon はLaravel8.xでCarbonImmutableも一緒にsetTestNow()できる機能、
Laravel9.xでwhen()とunless()が追加されています。
when()の使い方はStrのヘルパのwhenと同じで、falseの場合に実行するcallbackを第三引数に渡すこともできます。
休日が指定されたら会社の営業日にズラすなど作れそうですね。

$carbon = Carbon::now()
			->when(true, function (Carbon $carbon) {
				return $carbon->addDay();
			});

まとめ

とりあえず(自分が)Carbon関係を使うときに見るページを目指して作成しました。
重複した分かりにくい機能は省きつつ情報をまとめて「困ったら見る」「どんな事ができるか把握したいときに見る」という感じにしてあります。
ちなみに全機能知りたいときはCarbonの公式ドキュメントがわかりやすくまとまっています。
必要に応じて適宜更新していきます。

-Laravel, PHP