PHP 如何将带 GMT 的字符串转换为 DateTime 格式
支付小周
结论
可以使用 DateTime::createFromFormat() 按照 bKash 返回值的完整格式解析,不需要使用 substr():
$input = '2020-01-07T11:55:34:438 GMT+0600';
$dateTime = DateTime::createFromFormat(
'Y-m-d\TH:i:s:v \G\M\TO',
$input
);
if ($dateTime === false) {
throw new RuntimeException('paymentCreateTime 格式无效');
}
$mysql = $dateTime->format('Y-m-d H:i:s');
echo $mysql;
// 2020-01-07 11:55:34
格式说明
输入字符串为:
2020-01-07T11:55:34:438 GMT+0600
对应的 PHP 日期格式是:
Y-m-d\TH:i:s:v \G\M\TO
其中:
Y-m-d:年、月、日\T:固定字符TH:i:s:时、分、秒v:三位毫秒,即438\G\M\T:固定字符串GMTO:不带冒号的时区偏移量,即+0600
反斜杠用于匹配普通字符,避免 PHP 将 T、G、M 等字符解释为日期格式标记。
检查解析错误
如果数据来自外部 API,最好同时检查解析产生的警告和错误,防止异常值直接写入数据库:
$input = '2020-01-07T11:55:34:438 GMT+0600';
$format = 'Y-m-d\TH:i:s:v \G\M\TO';
$dateTime = DateTimeImmutable::createFromFormat($format, $input);
$errors = DateTimeImmutable::getLastErrors();
if (
$dateTime === false ||
($errors !== false &&
($errors['warning_count'] > 0 || $errors['error_count'] > 0))
) {
throw new InvalidArgumentException(
'无效的 paymentCreateTime:' . $input
);
}
$mysql = $dateTime->format('Y-m-d H:i:s');
这里使用 DateTimeImmutable,可以防止后续的时区转换意外修改原对象。
注意时区
直接调用 format() 会保留输入中的 GMT+0600 时区,因此结果是:
2020-01-07 11:55:34
如果数据库统一保存 UTC 时间,需要先转换时区:
$utcDateTime = $dateTime->setTimezone(new DateTimeZone('UTC'));
$mysql = $utcDateTime->format('Y-m-d H:i:s');
echo $mysql;
// 2020-01-07 05:55:34
MySQL 的 DATETIME 类型不记录时区。实际项目中通常会统一保存 UTC 时间,展示时再转换为用户所在的时区。
格式化为 Y-m-d H:i:s 时,毫秒 438 会被舍弃。如果业务需要保留毫秒,可以使用支持小数秒精度的字段,例如 DATETIME(3),并采用以下格式:
$mysql = $dateTime->format('Y-m-d H:i:s.v');
// 2020-01-07 11:55:34.438
备注:内容仅供参考。
