1
0

More than 1 year has passed since last update.

Carbon::parse で不正な日時も取得する

Last updated at Posted at 2022-05-19

24時超えの終業時刻ってw

担当した勤怠管理プロジェクトにて、24時超えの終業時刻(どころか始業すら)が当たり前のようにあった。
フォームからの入力値を正しい日時オブジェクトに変換するためにも Carbon::parse を用いたかったが、残念ながらエラーで取得できない。

24時超えや31日超えの不正な日時でも取得可能なのは、Carbon::create だけのようだ。

ということで、Carbon::parse を拡張してみた。

Carbon::parse の拡張

前の記事App\Helpers\Carbon を作成しているので、そこにメソッドを加える。

app/Helpers/Carbon.php

<?php

namespace App\Helpers;

use Carbon\Carbon as BaseCarbon;
use Carbon\CarbonImmutable as BaseCarbonImmutable;

class Carbon extends BaseCarbonImmutable
{
    /**
     * {@inheritdoc}
     */
    public static function setTestNow($testNow = null)
    {
        BaseCarbon::setTestNow($testNow);
        BaseCarbonImmutable::setTestNow($testNow);
    }

    /**
     * Create a carbon instance from a string.
     *
     * @param string|DateTimeInterface|null $time
     * @return static
     */
    public static function parse($time = null, $tz = null)
    {
        try {
            // '-1 month' などが扱えるよう、まずは parse してみる
            return parent::parse($time, $tz);
        } catch (\Exception $e) {
            // 24時超えや31日超えでエラーとなった場合
            if (preg_match('/^(\\d+?)[\\/-](\\d+?)[\\/-](\\d+) (\\d+):(\\d+):?(\\d*)$/', $time, $m)) {
                // 2021-10-10 10:10:00 または 2021/10/10 10:10:00
                return parent::create((int) $m[1], (int) $m[2], (int) $m[3], (int) $m[4], (int) $m[5], $m[6] ?? 0, $tz);
            } elseif (preg_match('/^(\\d+?)[\\/-](\\d+?)[\\/-](\\d+)$/', $time, $m)) {
                // 2021-10-10 または 2021/10/10
                return parent::create((int) $m[1], (int) $m[2], (int) $m[3], 0, 0, 0, $tz);
            } elseif (preg_match('/^(\\d+):(\\d+):?(\\d*)$/', $time, $m)) {
                // 10:10:00
                $now = now();
                return parent::create($now->year, $now->month, $now->day, (int) $m[1], (int) $m[2], (int) $m[3] ?? 0, $tz);
            }
        }
        return null;
    }
}

動作確認

比較のため strtotime の結果も記す。
オリジナルカーボンである Carbon\Carbon と、今回置き換えた App\Helpers\Carbon との結果の違いを見てほしい。

「2月30日」はすべて3月に繰り越せる。

>>> date('Y-m-d', strtotime('2022-02-30'))
=> "2022-03-02"
>>> Carbon\Carbon::parse('2022-02-30')
=> Carbon\Carbon @1646146800 {#4429
     date: 2022-03-02 00:00:00.0 Asia/Tokyo (+09:00),
   }
>>> Carbon::parse('2022-02-30')
=> App\Helpers\Carbon @1646146800 {#4416
     date: 2022-03-02 00:00:00.0 Asia/Tokyo (+09:00),
   }
>>> 

31日超えがエラーにならないのは App\Helpers\Carbon だけ。

>>> date('Y-m-d', strtotime('2022-02-32'))
=> "1970-01-01"
>>> Carbon\Carbon::parse('2022-02-32')
Carbon\Exceptions\InvalidFormatException with message 'Could not parse '2022-02-32': DateTime::__construct(): Failed to parse time string (2022-02-32) at position 9 (2): Unexpected character'
>>> Carbon::parse('2022-02-32')
=> App\Helpers\Carbon @1646319600 {#4323
     date: 2022-03-04 00:00:00.0 Asia/Tokyo (+09:00),
   }

時刻として「10:10」はすべて読み取れる。日付部分は「今日」が基準となる。

>>> date('Y-m-d H:i:s', strtotime('10:10'))
=> "2022-05-19 10:10:00"
>>> Carbon\Carbon::parse('10:10')
=> Carbon\Carbon @1652922600 {#4440
     date: 2022-05-19 10:10:00.0 Asia/Tokyo (+09:00),
   }
>>> Carbon::parse('10:10')
=> App\Helpers\Carbon @1652922600 {#4323
     date: 2022-05-19 10:10:00.0 Asia/Tokyo (+09:00),
   }

24時超えの時刻が読み取れるのは App\Helpers\Carbon だけ。

>>> date('Y-m-d H:i:s', strtotime('25:10'))
=> "1970-01-01 09:00:00"
>>> Carbon\Carbon::parse('25:10')
Carbon\Exceptions\InvalidFormatException with message 'Could not parse '25:10': DateTime::__construct(): Failed to parse time string (25:10) at position 0 (2): Unexpected character'
>>> Carbon::parse('25:10')
=> App\Helpers\Carbon @1652976600 {#4290
     date: 2022-05-20 01:10:00.0 Asia/Tokyo (+09:00),
   }

Eloquent の日時はすでに Carbon だが、再パースしてもOK。

>>> $date = User::find(1)->created_at
=> App\Helpers\Carbon @1652765323 {#4319
     date: 2022-05-17 14:28:43.0 Asia/Tokyo (+09:00),
   }
>>> Carbon::parse($date)
=> App\Helpers\Carbon @1652765323 {#4407
     date: 2022-05-17 14:28:43.0 Asia/Tokyo (+09:00),
   }
1
0
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
1
0