PAYATHON 2026

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:固定字符 T
  • H:i:s:时、分、秒
  • v:三位毫秒,即 438
  • \G\M\T:固定字符串 GMT
  • O:不带冒号的时区偏移量,即 +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

备注:内容仅供参考。