PHP date_parse_from_format() 函数

date_parse_from_format() 函数根据指定的格式解析日期时间字符串,返回包含详细日期时间信息的数组。与 date_parse() 函数不同,此函数可以处理自定义格式的日期字符串。

提示: 这个函数特别适用于解析非标准格式的日期字符串,例如来自第三方API或用户自定义格式的日期数据。

语法

array date_parse_from_format ( string $format , string $datetime )

参数说明

参数 描述 必需
$format 日期时间字符串的格式,使用与 date() 相同的格式化字符
$datetime 要解析的日期时间字符串

返回值

  • 数组 - 包含解析后的日期时间信息,如果解析失败则返回 false
  • 返回数组包含年、月、日、时、分、秒等详细信息
  • 包含警告和错误信息,便于调试

返回数组结构

键名 描述 示例值
year 年份 2024
month 月份(1-12) 3
day 日期(1-31) 15
hour 小时(0-23) 14
minute 分钟(0-59) 30
second 秒(0-59) 45
fraction 微秒部分(秒的小数部分) 0.123456
warning_count 警告数量 2
warnings 警告信息数组 ['12' => 'The parsed date was invalid']
error_count 错误数量 1
errors 错误信息数组 ['0' => 'Unexpected data found.']
is_localtime 是否包含本地时间信息 true
zone_type 时区类型(1=UTC偏移,2=时区缩写,3=时区标识符) 1
zone 时区偏移(秒) 28800
is_dst 是否夏令时 false

示例代码

示例 1:基本用法

<?php
// 解析标准格式日期
$dateStr = "2024-03-15 14:30:45";
$format = "Y-m-d H:i:s";

$parsed = date_parse_from_format($format, $dateStr);

echo "解析日期: {$dateStr}<br>";
echo "使用格式: {$format}<br><br>";

echo "解析结果:<br>";
echo "年: " . ($parsed['year'] ?? 'N/A') . "<br>";
echo "月: " . ($parsed['month'] ?? 'N/A') . "<br>";
echo "日: " . ($parsed['day'] ?? 'N/A') . "<br>";
echo "时: " . ($parsed['hour'] ?? 'N/A') . "<br>";
echo "分: " . ($parsed['minute'] ?? 'N/A') . "<br>";
echo "秒: " . ($parsed['second'] ?? 'N/A') . "<br>";

if (isset($parsed['warning_count']) && $parsed['warning_count'] > 0) {
    echo "警告数量: " . $parsed['warning_count'] . "<br>";
}

if (isset($parsed['error_count']) && $parsed['error_count'] > 0) {
    echo "错误数量: " . $parsed['error_count'] . "<br>";
}

/*
输出:
解析日期: 2024-03-15 14:30:45
使用格式: Y-m-d H:i:s

解析结果:
年: 2024
月: 3
日: 15
时: 14
分: 30
秒: 45
*/
?>

示例 2:解析自定义格式

<?php
// 解析各种自定义格式
$testCases = [
    [
        'format' => 'd/m/Y',
        'date' => '15/03/2024',
        'desc' => '欧洲日期格式'
    ],
    [
        'format' => 'm/d/Y H:i A',
        'date' => '03/15/2024 02:30 PM',
        'desc' => '美国日期时间格式'
    ],
    [
        'format' => 'Y年m月d日 H时i分s秒',
        'date' => '2024年03月15日 14时30分45秒',
        'desc' => '中文日期时间格式'
    ],
    [
        'format' => 'D, d M Y H:i:s T',
        'date' => 'Fri, 15 Mar 2024 14:30:45 CST',
        'desc' => 'RFC 2822格式'
    ],
    [
        'format' => 'Y-m-d\TH:i:s.uP',
        'date' => '2024-03-15T14:30:45.123456+08:00',
        'desc' => 'ISO 8601带微秒和时区'
    ],
];

foreach ($testCases as $case) {
    echo "<strong>{$case['desc']}:</strong><br>";
    echo "格式: {$case['format']}<br>";
    echo "日期: {$case['date']}<br>";

    $parsed = date_parse_from_format($case['format'], $case['date']);

    if ($parsed === false) {
        echo "解析失败<br>";
    } else {
        echo "解析结果: " .
             "{$parsed['year']}-{$parsed['month']}-{$parsed['day']} " .
             "{$parsed['hour']}:{$parsed['minute']}:{$parsed['second']}<br>";

        if (isset($parsed['fraction']) && $parsed['fraction'] > 0) {
            echo "微秒: " . $parsed['fraction'] . "<br>";
        }

        if (isset($parsed['zone'])) {
            echo "时区偏移: " . ($parsed['zone'] / 3600) . "小时<br>";
        }
    }
    echo "<br>";
}
?>

示例 3:错误处理与验证

<?php
// 错误处理与验证
function validateDateWithFormat($dateString, $format) {
    $parsed = date_parse_from_format($format, $dateString);

    if ($parsed === false) {
        return [
            'valid' => false,
            'message' => '无法解析日期字符串',
            'errors' => [],
            'warnings' => []
        ];
    }

    // 检查错误和警告
    $hasErrors = isset($parsed['error_count']) && $parsed['error_count'] > 0;
    $hasWarnings = isset($parsed['warning_count']) && $parsed['warning_count'] > 0;

    // 验证日期是否有效
    $isValidDate = false;
    if (isset($parsed['year'], $parsed['month'], $parsed['day'])) {
        $isValidDate = checkdate($parsed['month'], $parsed['day'], $parsed['year']);
    }

    // 验证时间是否有效
    $isValidTime = true;
    if (isset($parsed['hour']) && ($parsed['hour'] < 0 || $parsed['hour'] > 23)) {
        $isValidTime = false;
    }
    if (isset($parsed['minute']) && ($parsed['minute'] < 0 || $parsed['minute'] > 59)) {
        $isValidTime = false;
    }
    if (isset($parsed['second']) && ($parsed['second'] < 0 || $parsed['second'] > 59)) {
        $isValidTime = false;
    }

    $messages = [];
    if (!$isValidDate) {
        $messages[] = '无效的日期';
    }
    if (!$isValidTime) {
        $messages[] = '无效的时间';
    }
    if ($hasErrors && isset($parsed['errors'])) {
        $messages[] = '解析错误: ' . implode(', ', $parsed['errors']);
    }
    if ($hasWarnings && isset($parsed['warnings'])) {
        $messages[] = '解析警告: ' . implode(', ', $parsed['warnings']);
    }

    return [
        'valid' => $isValidDate && $isValidTime && !$hasErrors,
        'message' => empty($messages) ? '日期有效' : implode('; ', $messages),
        'parsed' => $parsed,
        'errors' => $hasErrors ? $parsed['errors'] : [],
        'warnings' => $hasWarnings ? $parsed['warnings'] : [],
        'date_info' => [
            'year' => $parsed['year'] ?? null,
            'month' => $parsed['month'] ?? null,
            'day' => $parsed['day'] ?? null,
            'hour' => $parsed['hour'] ?? null,
            'minute' => $parsed['minute'] ?? null,
            'second' => $parsed['second'] ?? null,
        ]
    ];
}

// 测试各种日期验证
$testDates = [
    ['date' => '2024-03-15', 'format' => 'Y-m-d', 'desc' => '有效日期'],
    ['date' => '2024-02-30', 'format' => 'Y-m-d', 'desc' => '无效日期(2月30日)'],
    ['date' => '2024-03-15 25:61:61', 'format' => 'Y-m-d H:i:s', 'desc' => '无效时间'],
    ['date' => '15/03/2024', 'format' => 'Y-m-d', 'desc' => '格式不匹配'],
    ['date' => '2024年03月15日', 'format' => 'Y年m月d日', 'desc' => '中文格式有效'],
];

foreach ($testDates as $test) {
    echo "<strong>测试: {$test['desc']}</strong><br>";
    echo "日期: {$test['date']}, 格式: {$test['format']}<br>";

    $result = validateDateWithFormat($test['date'], $test['format']);

    echo "状态: " . ($result['valid'] ? '✅ 有效' : '❌ 无效') . "<br>";
    echo "消息: {$result['message']}<br>";

    if (!$result['valid'] && !empty($result['errors'])) {
        echo "错误详情:<br>";
        foreach ($result['errors'] as $pos => $msg) {
            echo "位置 {$pos}: {$msg}<br>";
        }
    }

    echo "<br>";
}
?>

示例 4:与相关函数比较

<?php
// 与相关函数的比较
echo "日期解析函数比较:<br><br>";

$dateString = "15/03/2024 14:30:45";
$format = "d/m/Y H:i:s";

echo "日期字符串: {$dateString}<br>";
echo "格式: {$format}<br><br>";

// 1. date_parse_from_format()
echo "<strong>1. date_parse_from_format():</strong><br>";
$parsed1 = date_parse_from_format($format, $dateString);
if ($parsed1 !== false) {
    echo "成功解析<br>";
    echo "结果: {$parsed1['year']}-{$parsed1['month']}-{$parsed1['day']} {$parsed1['hour']}:{$parsed1['minute']}:{$parsed1['second']}<br>";
} else {
    echo "解析失败<br>";
}

// 2. date_create_from_format() / DateTime::createFromFormat()
echo "<br><strong>2. date_create_from_format():</strong><br>";
$date2 = date_create_from_format($format, $dateString);
if ($date2 !== false) {
    echo "成功创建DateTime对象<br>";
    echo "格式化输出: " . date_format($date2, 'Y-m-d H:i:s') . "<br>";
} else {
    echo "创建失败<br>";
}

// 3. strtotime() + date()
echo "<br><strong>3. strtotime():</strong><br>";
$timestamp = strtotime($dateString);
if ($timestamp !== false) {
    echo "成功解析为时间戳<br>";
    echo "格式化输出: " . date('Y-m-d H:i:s', $timestamp) . "<br>";
    echo "注意: strtotime可能将15/03/2024解释为3月15日(美国格式)<br>";
} else {
    echo "解析失败<br>";
}

// 4. date_parse()(无格式)
echo "<br><strong>4. date_parse():</strong><br>";
$parsed4 = date_parse($dateString);
echo "解析结果: ";
if ($parsed4 !== false) {
    if ($parsed4['error_count'] > 0) {
        echo "有错误,无法正确解析此格式<br>";
    } else {
        echo "{$parsed4['year']}-{$parsed4['month']}-{$parsed4['day']} {$parsed4['hour']}:{$parsed4['minute']}:{$parsed4['second']}<br>";
    }
}

// 性能比较
echo "<br><strong>性能比较 (1000次迭代):</strong><br>";
$iterations = 1000;

// date_parse_from_format
$start1 = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
    $parsed = date_parse_from_format($format, $dateString);
}
$time1 = microtime(true) - $start1;

// date_create_from_format
$start2 = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
    $date = date_create_from_format($format, $dateString);
}
$time2 = microtime(true) - $start2;

// strtotime
$start3 = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
    $timestamp = strtotime($dateString);
}
$time3 = microtime(true) - $start3;

echo "date_parse_from_format: " . number_format($time1 * 1000, 2) . " ms<br>";
echo "date_create_from_format: " . number_format($time2 * 1000, 2) . " ms<br>";
echo "strtotime: " . number_format($time3 * 1000, 2) . " ms<br>";
?>

示例 5:实用工具函数

<?php
/**
 * 日期解析实用工具类
 */
class DateParserUtils {

    /**
     * 尝试多种格式解析日期
     */
    public static function parseDateMultipleFormats($dateString, $formats = null) {
        if ($formats === null) {
            // 常用日期格式
            $formats = [
                'Y-m-d H:i:s',
                'Y-m-d',
                'd/m/Y H:i:s',
                'd/m/Y',
                'm/d/Y H:i:s',
                'm/d/Y',
                'Y年m月d日 H时i分s秒',
                'Y年m月d日',
                'Y.m.d H:i:s',
                'Y.m.d',
                'd-m-Y H:i:s',
                'd-m-Y',
                'Y/m/d H:i:s',
                'Y/m/d',
            ];
        }

        $results = [];

        foreach ($formats as $format) {
            $parsed = date_parse_from_format($format, $dateString);

            if ($parsed !== false &&
                isset($parsed['year'], $parsed['month'], $parsed['day']) &&
                $parsed['error_count'] == 0) {

                // 验证日期有效性
                if (checkdate($parsed['month'], $parsed['day'], $parsed['year'])) {
                    $results[] = [
                        'format' => $format,
                        'parsed' => $parsed,
                        'date' => sprintf(
                            '%04d-%02d-%02d %02d:%02d:%02d',
                            $parsed['year'],
                            $parsed['month'],
                            $parsed['day'],
                            $parsed['hour'] ?? 0,
                            $parsed['minute'] ?? 0,
                            $parsed['second'] ?? 0
                        ),
                        'confidence' => self::calculateFormatConfidence($parsed, $format, $dateString)
                    ];
                }
            }
        }

        // 按置信度排序
        usort($results, function($a, $b) {
            return $b['confidence'] <=> $a['confidence'];
        });

        return $results;
    }

    /**
     * 计算格式匹配的置信度
     */
    private static function calculateFormatConfidence($parsed, $format, $dateString) {
        $confidence = 100;

        // 如果有警告,降低置信度
        if (isset($parsed['warning_count']) && $parsed['warning_count'] > 0) {
            $confidence -= $parsed['warning_count'] * 10;
        }

        // 检查是否有未使用的数据
        $expectedLength = strlen($dateString);
        $parsedLength = 0;
        if (isset($parsed['hour'])) $parsedLength += 2;
        if (isset($parsed['minute'])) $parsedLength += 2;
        if (isset($parsed['second'])) $parsedLength += 2;

        // 格式越完整,置信度越高
        $formatComplexity = strlen($format);
        $confidence += $formatComplexity * 0.5;

        return max(0, min(100, $confidence));
    }

    /**
     * 将解析结果转换为DateTime对象
     */
    public static function parsedToDateTime($parsed) {
        if (!isset($parsed['year'], $parsed['month'], $parsed['day'])) {
            return false;
        }

        $dateStr = sprintf(
            '%04d-%02d-%02d %02d:%02d:%02d',
            $parsed['year'],
            $parsed['month'],
            $parsed['day'],
            $parsed['hour'] ?? 0,
            $parsed['minute'] ?? 0,
            $parsed['second'] ?? 0
        );

        if (isset($parsed['fraction']) && $parsed['fraction'] > 0) {
            $dateStr .= '.' . $parsed['fraction'];
        }

        try {
            $date = new DateTime($dateStr);

            // 设置时区
            if (isset($parsed['zone']) && $parsed['is_localtime']) {
                $offset = $parsed['zone'];
                $timezone = timezone_name_from_abbr('', $offset, 0);
                if ($timezone) {
                    $date->setTimezone(new DateTimeZone($timezone));
                }
            }

            return $date;
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * 获取日期字符串的详细信息
     */
    public static function getDateDetails($dateString, $format = null) {
        if ($format === null) {
            // 尝试自动检测
            $parsedResults = self::parseDateMultipleFormats($dateString);
            if (empty($parsedResults)) {
                return false;
            }
            $bestMatch = $parsedResults[0];
            $parsed = $bestMatch['parsed'];
            $detectedFormat = $bestMatch['format'];
        } else {
            $parsed = date_parse_from_format($format, $dateString);
            if ($parsed === false) {
                return false;
            }
            $detectedFormat = $format;
        }

        $dateTime = self::parsedToDateTime($parsed);

        if ($dateTime === false) {
            return false;
        }

        return [
            'original_string' => $dateString,
            'detected_format' => $detectedFormat,
            'parsed_data' => $parsed,
            'datetime' => $dateTime,
            'formats' => [
                'iso' => $dateTime->format('c'),
                'rfc2822' => $dateTime->format('r'),
                'mysql' => $dateTime->format('Y-m-d H:i:s'),
                'human' => $dateTime->format('l, F j, Y g:i A'),
                'timestamp' => $dateTime->getTimestamp(),
            ],
            'validation' => [
                'is_valid_date' => checkdate(
                    $parsed['month'] ?? 1,
                    $parsed['day'] ?? 1,
                    $parsed['year'] ?? 1970
                ),
                'error_count' => $parsed['error_count'] ?? 0,
                'warning_count' => $parsed['warning_count'] ?? 0,
                'errors' => $parsed['errors'] ?? [],
                'warnings' => $parsed['warnings'] ?? [],
            ]
        ];
    }
}

echo "日期解析实用工具示例:<br><br>";

// 测试多格式解析
echo "<strong>1. 多格式日期解析:</strong><br>";
$dateStrings = [
    '2024-03-15 14:30:45',
    '15/03/2024',
    '03/15/2024 02:30 PM',
    '2024年03月15日',
    '15-03-2024 14:30',
];

foreach ($dateStrings as $dateStr) {
    echo "解析: {$dateStr}<br>";
    $results = DateParserUtils::parseDateMultipleFormats($dateStr);

    if (!empty($results)) {
        $best = $results[0];
        echo "最佳匹配格式: {$best['format']}<br>";
        echo "解析结果: {$best['date']}<br>";
        echo "置信度: {$best['confidence']}%<br>";
    } else {
        echo "无法解析<br>";
    }
    echo "<br>";
}

// 测试日期详情获取
echo "<strong>2. 日期详情获取:</strong><br>";
$testDate = '2024-03-15 14:30:45.123456';
$details = DateParserUtils::getDateDetails($testDate);

if ($details !== false) {
    echo "原始字符串: {$details['original_string']}<br>";
    echo "检测格式: {$details['detected_format']}<br>";
    echo "ISO格式: {$details['formats']['iso']}<br>";
    echo "时间戳: {$details['formats']['timestamp']}<br>";
    echo "可读格式: {$details['formats']['human']}<br>";

    if ($details['validation']['error_count'] > 0) {
        echo "错误: " . implode(', ', $details['validation']['errors']) . "<br>";
    }
} else {
    echo "无法获取日期详情<br>";
}
?>

注意事项

重要提示:
  • date_parse_from_format() 返回的是数组,而不是 DateTime 对象
  • 函数会尽可能解析日期,即使有错误或警告也会返回结果
  • 解析结果中的年月日可能为 false,表示解析失败
  • 时区信息只有在日期字符串中包含时区信息时才会被解析
  • 微秒部分(fraction)以小数形式返回,如 0.123456 表示 123456 微秒
  • 对于无效的日期(如2月30日),函数会返回解析结果但不验证有效性
  • 使用 checkdate() 函数验证解析出的日期是否有效

常见错误和警告信息

错误/警告 描述 常见原因
Unexpected data found. 发现意外的数据 日期字符串比格式字符串长
Data missing 数据缺失 日期字符串比格式字符串短
The parsed date was invalid 解析的日期无效 如2月30日、13月等
Double time specification 重复的时间规范 格式中指定了两次小时/分钟/秒
Trailing data 尾部数据 日期字符串末尾有多余字符

相关函数

函数 描述
date_create_from_format() 根据格式创建DateTime对象
date_parse() 解析日期字符串(自动检测格式)
strtotime() 将英文文本日期时间解析为Unix时间戳
checkdate() 验证日期的有效性
DateTime::createFromFormat() 面向对象的格式化日期创建方法
date_get_last_errors() 获取上次日期解析的错误信息