Skip to content

日期格式化与解析

本节将全面讲解 PHP 中的日期时间格式化和解析,包括 DateTime::format()、DateTime::createFromFormat()、IntlDateFormatter(国际化格式化)以及自定义格式化方案。

前置知识

阅读本节前,建议先了解:DateTime 类DateTimeZone 时区处理

format() 格式化

完整格式字符参考

php
<?php
declare(strict_types=1);

$dt = new DateTimeImmutable('2024-01-15 14:30:45.123456');

// === 年 ===
echo $dt->format('Y') . PHP_EOL;  // 2024 (4位)
echo $dt->format('y') . PHP_EOL;  // 24 (2位)

// === 月 ===
echo $dt->format('m') . PHP_EOL;  // 01 (2位,前导零)
echo $dt->format('n') . PHP_EOL;  // 1 (无前导零)
echo $dt->format('F') . PHP_EOL;  // January (完整英文名)
echo $dt->format('M') . PHP_EOL;  // Jan (缩写)

// === 日 ===
echo $dt->format('d') . PHP_EOL;  // 15 (2位)
echo $dt->format('j') . PHP_EOL;  // 15 (无前导零)
echo $dt->format('S') . PHP_EOL;  // th (英文序数后缀)
echo $dt->format('z') . PHP_EOL;  // 14 (年中第几天)

// === 时间 ===
echo $dt->format('H') . PHP_EOL;  // 14 (24小时制)
echo $dt->format('h') . PHP_EOL;  // 02 (12小时制)
echo $dt->format('g') . PHP_EOL;  // 2 (12小时制,无零)
echo $dt->format('i') . PHP_EOL;  // 30 (分钟)
echo $dt->format('s') . PHP_EOL;  // 45 (秒)
echo $dt->format('u') . PHP_EOL;  // 123456 (微秒)
echo $dt->format('v') . PHP_EOL;  // 123 (毫秒, PHP 7.1+)

// === AM/PM ===
echo $dt->format('A') . PHP_EOL;  // PM
echo $dt->format('a') . PHP_EOL;  // pm

// === 时区 ===
echo $dt->format('e') . PHP_EOL;  // Asia/Shanghai (时区标识符)
echo $dt->format('T') . PHP_EOL;  // CST (时区缩写)
echo $dt->format('Z') . PHP_EOL;  // 28800 (UTC 偏移秒)
echo $dt->format('P') . PHP_EOL;  // +08:00 (UTC 偏移)

// === 周和日 ===
echo $dt->format('w') . PHP_EOL;  // 1 (周几,0=周日)
echo $dt->format('N') . PHP_EOL;  // 1 (ISO-8601,1=周一)
echo $dt->format('l') . PHP_EOL;  // Monday (完整周几名)
echo $dt->format('D') . PHP_EOL;  // Mon (缩写)
echo $dt->format('W') . PHP_EOL;  // 03 (ISO 周数)
echo $dt->format('t') . PHP_EOL;  // 31 (月份天数)
echo $dt->format('L') . PHP_EOL;  // 0 (是否闰年)

// === 预定义格式 ===
echo $dt->format('c') . PHP_EOL;  // ISO 8601
echo $dt->format('r') . PHP_EOL;  // RFC 2822
echo $dt->format('U') . PHP_EOL;  // Unix 时间戳

日期函数

php
<?php
declare(strict_types=1);

// date() - 格式化时间戳
echo date('Y-m-d H:i:s') . PHP_EOL;

// strftime() - 已弃用(PHP 8.1+),使用 IntlDateFormatter 替代
// gmdate() - UTC 时间格式化
echo gmdate('Y-m-d H:i:s') . PHP_EOL;

// checkdate() - 验证日期有效性
var_dump(checkdate(2024, 2, 29)); // bool(false) 2024是闰年?不是
var_dump(checkdate(2024, 2, 29)); // 实际 2024 非闰年
var_dump(checkdate(2023, 2, 29)); // bool(false)

// getdate() - 获取日期详细信息
$info = getdate();
print_r($info);

createFromFormat 解析

常见日期格式解析

php
<?php
declare(strict_types=1);

$formats = [
    // 数据库日期时间格式
    ['Y-m-d H:i:s', '2024-01-15 14:30:00'],
    ['Y-m-d', '2024-01-15'],

    // 中文格式
    ['Y年m月d日', '2024年1月15日'],
    ['Y年m月d日 H时i分', '2024年1月15日 14时30分'],

    // 紧凑格式
    ['Ymd', '20240115'],
    ['YmdHis', '20240115143000'],

    // 英文格式
    ['d/m/Y', '15/01/2024'],
    ['m/d/Y', '01/15/2024'],
    ['d M Y', '15 Jan 2024'],
    ['M j, Y', 'Jan 15, 2024'],

    // ISO 格式
    ['Y-m-d\TH:i:sP', '2024-01-15T14:30:00+08:00'],
    ['Y-m-d\TH:i:s\Z', '2024-01-15T06:30:00Z'],

    // 自定义格式
    ['Y.m.d', '2024.01.15'],
];

foreach ($formats as [$format, $string]) {
    $dt = DateTimeImmutable::createFromFormat($format, $string);
    if ($dt !== false) {
        echo "'{$string}' => " . $dt->format('Y-m-d H:i:s') . PHP_EOL;
    } else {
        echo "'{$string}' => 解析失败" . PHP_EOL;
    }
}

带时区的解析

php
<?php
declare(strict_types=1);

// 解析时指定时区
$dt = DateTimeImmutable::createFromFormat(
    'Y-m-d H:i:s',
    '2024-01-15 14:30:00',
    new DateTimeZone('America/New_York')
);

echo "纽约时间: " . $dt->format('Y-m-d H:i:s e') . PHP_EOL;
echo "UTC 时间: " . $dt->setTimezone(new DateTimeZone('UTC'))->format('Y-m-d H:i:s e') . PHP_EOL;

解析错误处理

php
<?php
declare(strict_types=1);

// 获取解析错误
$result = DateTime::createFromFormat('Y-m-d', '2024-13-45');

if ($result === false) {
    $errors = DateTime::getLastErrors();
    echo "警告数量: " . $errors['warning_count'] . PHP_EOL;
    echo "错误数量: " . $errors['error_count'] . PHP_EOL;
    print_r($errors['warnings']);
    print_r($errors['errors']);
}

实战示例

日期工具类

php
<?php
declare(strict_types=1);

class DateFormatter
{
    /**
     * 格式化为中文日期
     */
    public static function toChinese(DateTimeInterface $dt): string
    {
        $weekdays = ['日', '一', '二', '三', '四', '五', '六'];
        $w = $weekdays[(int) $dt->format('w')];

        return sprintf(
            '%d年%d月%d日 星期%s %02d:%02d',
            (int) $dt->format('Y'),
            (int) $dt->format('m'),
            (int) $dt->format('d'),
            $w,
            (int) $dt->format('H'),
            (int) $dt->format('i')
        );
    }

    /**
     * 友好的时间显示
     */
    public static function friendly(DateTimeInterface $dt): string
    {
        $now = new DateTimeImmutable();
        $diff = $dt->diff($now);

        if ($diff->days === 0) {
            if ($diff->h === 0) {
                if ($diff->i === 0) {
                    return '刚刚';
                }
                return $diff->i . ' 分钟前';
            }
            return $diff->h . ' 小时前';
        }

        if ($diff->days === 1) {
            return '昨天 ' . $dt->format('H:i');
        }

        if ($diff->days < 7) {
            return $diff->days . ' 天前';
        }

        if ($diff->days < 30) {
            return (int) ($diff->days / 7) . ' 周前';
        }

        return $dt->format('Y-m-d');
    }

    /**
     * 计算过期时间
     */
    public static function expiresAt(int $minutes): string
    {
        $dt = new DateTimeImmutable();
        $dt = $dt->add(new DateInterval("PT{$minutes}M"));
        return $dt->format(DateTimeInterface::ATOM);
    }
}

echo DateFormatter::toChinese(new DateTimeImmutable('2024-01-15 14:30:00'));
echo DateFormatter::friendly(new DateTimeImmutable('-2 hours'));
echo DateFormatter::friendly(new DateTimeImmutable('-3 days'));

注意事项

  • format() 区分大小写,注意 Y/ym/M 的区别
  • 使用 createFromFormat 解析不明确日期时注意时区
  • strftime() 在 PHP 8.1 中已弃用

下一节

继续学习:HRTime 高精度计时

参考链接