【PHP】0埋めされていない1桁の分をDateTime::createFromFormat()に入れたらfalseを返してきた

とあるCSVファイルをインポートしてDBに登録するシステムの中でエラーが発生したので中を覗いてみた。すると日付変換のところでDateTime::createFromFormat()がfalseを返していた。

エラーが起きたコード

$importTime = '2021年08月01日 11時5分';

$date = DateTime::createFromFormat('Y年m月d日 H時i分', $importTime);
$formated = $date->format('Y-m-d H:i');

$dateにfalseが返っているので、$date->format()が以下のエラーを吐いていた。

Call to a member function format() on bool

原因

どうやら入力ファイル内の日時の仕様が変更されていて日時を読み取れなくなっていたのが原因。具体的には、ヤフオクの売上CSVファイルなのだが、取扱日の欄で今までは「○年○月○日 ○時○分」の「時」「分」が0埋め2桁だったのに、ここひと月以内に仕様が変更されて、1桁の時は0埋めせず1桁のままで出力されていた。

DateTime::createFromFormat()の仕様

DateTime::createFromFormat()の「時」は「H」で拾えて、0埋めされていてもされていなくてもどちらでもOK。なのに対して、「分」は「i」で拾えるが、必ず0埋めされた2桁じゃないと拾ってくれないという仕様。「秒」も同様。

解決方法

そこで、DateTime::createFromFormat()にかける前に、0埋めされていない「分」に0を付けて対処。

$importTime = '2021年08月01日 11時5分';

// 0埋めされていない分に0を付ける
$replaced = preg_replace('/時([0-9])分/', "時0\${1}分", $importTime);

echo $replaced; // 2021年08月01日 11時05分

上記のコードは、入力する日時が’2021年08月01日 11時5分’のような場合で、単純に「分」をピンポイントで抽出して置き換えているだけなので、入力形式が違えばそれに合わせた置き換えコードが必要です。

ちなみに、秒も一緒に0埋めしたい場合

$importTime = '2021年08月01日 11時5分2秒';

// 0埋めされていない分と秒に0を付ける
$replaced = preg_replace(['/時([0-9])分/', '/分([0-9])秒/'], ["時0\${1}分", "分0\${1}秒"], $importTime);

echo $replaced; // 2021年08月01日 11時05分02秒

違ったアプローチから(結果は同じです)

$importTime = '2021年08月01日 11時5分2秒';

// 0埋めされていない分と秒に0を付ける
$replaced = preg_replace_callback('/([0-9]*)分([0-9]*)秒/', function ($time) {
        return str_pad($time[1], 2, 0, STR_PAD_LEFT) . '分' . str_pad($time[2], 2, 0, STR_PAD_LEFT) . '秒';
    }, $importTime);

echo $replaced; // 2021年08月01日 11時05分02秒