0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel】Carbonで年月指定時の日付ズレを防ぐ「!」修飾子の活用

Posted at

はじめに

Laravel(Carbon)で、検索フォームなどで、2025-02のような「年月」形式の文字列を受け取り、その月の範囲(月初から月末まで)をテーブルなどから取得するケースがある。

これまでは、「日付オーバーフロー問題(いわゆる31日問題)」の回避策として、文字列操作を用いた実装を行っていた。しかし、フォーマット指定子を工夫するだけでよりスマートに記述できることを最近知ったので紹介する。

従来の方法

年月指定の文字列からCarbonインスタンスを生成する場合、以下のように文字列連結で明示的に「1日」を付与してパースしていた。

$date = '2025-02'; // 入力値

// 文字列結合で '-01' を付与して Y-m-d 形式にする
$start = Carbon::createFromFormat('Y-m-d', $date . '-01')->startOfMonth();
$end   = Carbon::createFromFormat('Y-m-d', $date . '-01')->endOfMonth();

なぜこうしていたか

簡単に説明すると、
現在日が「1月31日」の場合、内部的に2025-02-31が生成され、2月には31日がないためオーバーフローして2025-03-03になってしまう。

詳細は31日問題とかで調べてください

スマートな方法

フォーマット文字列の先頭に!を付与することで、文字列連結を行わずにこの問題を解決できる。

$date = '2025-02';

// ! を付けることでパース可能
$start = Carbon::createFromFormat('!Y-m', $date); 
$end   = Carbon::createFromFormat('!Y-m', $date)->endOfMonth();

これだけで、現在日に依存せず2025-02-01 00:00:00が取得できる。

仕組み

実際は、これはLaravelやCarbon独自の機能ではなく、PHPのDateTimeクラスの仕様である。

全てのフィールドは現在の日付/時刻で初期化されます。 ほとんどの場合、これらの値を "ゼロ" (Unix タイムのエポック 1970-01-01 00:00:00 UTC) でリセットしたいはずです。 これは、format の最初の文字に ! を入れるか、 最後の文字に | を入れることで実現できます
DateTimeImmutable::createFromFormat

今回は!で説明したが、|でも似た挙動になる。
ただし、リセットされるタイミングが異なるため注意が必要。

!の定義

すべてのフィールド (年、月、日、時、分、秒、マイクロ秒およびタイムゾーン情報) を ゼロ相当の値 (時、分、秒、マイクロ秒については 0。 月、日については 1。 年については 1970。 タイムゾーンについては UTC) にリセットします。

|の定義

まだパースされていないすべてのフィールド (年、月、日、時、分、秒、マイクロ秒およびタイムゾーン情報) を ゼロ相当の値にリセットします。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?