date_create_immutable_from_format() 函数根据指定的格式从日期字符串创建一个新的不可变 DateTime 对象,返回 DateTimeImmutable 对象。与 date_create_immutable() 不同,这个函数允许你指定日期字符串的确切格式,而不是依赖于 strtotime() 的自动解析。
这个函数是 DateTimeImmutable::createFromFormat() 静态方法的过程化别名,推荐在需要精确控制日期格式解析的场景中使用。
date_create_immutable_from_format(string $format, string $datetime [, DateTimeZone $timezone = null]) : DateTimeImmutable|false
| 参数 | 默认值 | 描述 |
|---|---|---|
| format | 无 |
必需。 日期/时间格式字符串,指定 格式字符与 |
| datetime | 无 |
必需。 日期/时间字符串,必须符合 |
| timezone | null |
可选。 DateTimeZone 对象,表示时区。 如果为 null,则使用当前默认时区。 |
成功时返回一个新的 DateTimeImmutable 对象,失败时返回 false。
| 字符 | 描述 | 示例 |
|---|---|---|
| d | 月份中的第几天,有前导零的 2 位数字 | 01 到 31 |
| m | 数字表示的月份,有前导零 | 01 到 12 |
| Y | 4 位数字完整表示的年份 | 例如:1999 或 2023 |
| y | 2 位数字表示的年份 | 例如:99 或 23 |
| H | 24 小时格式,有前导零 | 00 到 23 |
| i | 有前导零的分钟数 | 00 到 59 |
| s | 有前导零的秒数 | 00 到 59 |
| U | Unix 时间戳 | 例如:1617180000 |
| c | ISO 8601 日期 | 2023-12-25T14:30:00+00:00 |
| r | RFC 2822 格式日期 | Mon, 25 Dec 2023 14:30:00 +0000 |
<?php
// 从标准格式创建不可变对象
$date = date_create_immutable_from_format('Y-m-d H:i:s', '2023-12-25 14:30:00');
if ($date !== false) {
echo "日期: " . $date->format('Y-m-d H:i:s') . "<br>";
echo "星期: " . $date->format('l') . "<br>";
echo "时间戳: " . $date->getTimestamp();
} else {
echo "日期解析失败";
}
// 输出:
// 日期: 2023-12-25 14:30:00
// 星期: Monday
// 时间戳: 1703500200
?>
<?php
// 解析不同格式的日期
$formats_and_dates = [
['d/m/Y', '25/12/2023'],
['Y年m月d日', '2023年12月25日'],
['m-d-Y', '12-25-2023'],
['Ymd His', '20231225 143000'],
['l, F j, Y', 'Monday, December 25, 2023']
];
foreach ($formats_and_dates as $item) {
list($format, $date_str) = $item;
$date = date_create_immutable_from_format($format, $date_str);
if ($date !== false) {
echo "格式: '{$format}', 字符串: '{$date_str}'<br>";
echo "解析结果: " . $date->format('Y-m-d H:i:s') . "<br><br>";
} else {
echo "格式: '{$format}', 字符串: '{$date_str}' - 解析失败<br><br>";
}
}
// 输出类似:
// 格式: 'd/m/Y', 字符串: '25/12/2023'
// 解析结果: 2023-12-25 00:00:00
//
// 格式: 'Y年m月d日', 字符串: '2023年12月25日'
// 解析结果: 2023-12-25 00:00:00
//
// 格式: 'm-d-Y', 字符串: '12-25-2023'
// 解析结果: 2023-12-25 00:00:00
//
// 格式: 'Ymd His', 字符串: '20231225 143000'
// 解析结果: 2023-12-25 14:30:00
//
// 格式: 'l, F j, Y', 字符串: 'Monday, December 25, 2023'
// 解析结果: 2023-12-25 00:00:00
?>
<?php
// 处理不完整的日期(自动填充缺失部分)
echo "<h4>自动填充缺失部分:</h4>";
// 只提供日期,没有时间
$date1 = date_create_immutable_from_format('Y-m-d', '2023-12-25');
echo "只提供日期: " . ($date1 !== false ? $date1->format('Y-m-d H:i:s') : '失败') . "<br>";
// 只提供年月
$date2 = date_create_immutable_from_format('Y-m', '2023-12');
echo "只提供年月: " . ($date2 !== false ? $date2->format('Y-m-d H:i:s') : '失败') . "<br>";
// 只提供年份
$date3 = date_create_immutable_from_format('Y', '2023');
echo "只提供年份: " . ($date3 !== false ? $date3->format('Y-m-d H:i:s') : '失败') . "<br>";
// 提供日期和小时分钟
$date4 = date_create_immutable_from_format('Y-m-d H:i', '2023-12-25 14:30');
echo "提供日期和小时分钟: " . ($date4 !== false ? $date4->format('Y-m-d H:i:s') : '失败') . "<br>";
echo "<h4>使用 ! 前缀避免自动填充:</h4>";
// 使用 ! 前缀表示重置所有字段
$date5 = date_create_immutable_from_format('!Y', '2023');
echo "只解析年份(使用!): " . ($date5 !== false ? $date5->format('Y-m-d H:i:s') : '失败') . "<br>";
// 输出类似:
// 自动填充缺失部分:
// 只提供日期: 2023-12-25 00:00:00
// 只提供年月: 2023-12-01 00:00:00
// 只提供年份: 2023-01-01 00:00:00
// 提供日期和小时分钟: 2023-12-25 14:30:00
//
// 使用 ! 前缀避免自动填充:
// 只解析年份(使用!): 2023-01-01 00:00:00
?>
<?php
// 错误处理和验证
function parseDateWithValidation($format, $date_str) {
$date = date_create_immutable_from_format($format, $date_str);
if ($date === false) {
$errors = DateTimeImmutable::getLastErrors();
return [
'success' => false,
'error' => '解析失败',
'warnings' => $errors['warnings'] ?? [],
'errors' => $errors['errors'] ?? []
];
}
// 验证解析后的日期是否与原始字符串匹配
$formatted_back = $date->format($format);
$is_valid = ($formatted_back === $date_str);
return [
'success' => true,
'date' => $date,
'is_valid' => $is_valid,
'formatted_back' => $formatted_back
];
}
// 测试
$test_cases = [
['Y-m-d', '2023-12-25'],
['Y-m-d', '2023-13-45'], // 无效日期
['d/m/Y', '25/12/2023'],
['d/m/Y', '32/12/2023'], // 无效日
['H:i:s', '14:30:00'],
['Y-m-d H:i:s', '2023-12-25 14:30:00']
];
foreach ($test_cases as $test) {
list($format, $date_str) = $test;
$result = parseDateWithValidation($format, $date_str);
echo "<strong>测试:</strong> 格式 '{$format}', 字符串 '{$date_str}'<br>";
if ($result['success']) {
echo "<span style='color: green;'>✓ 解析成功</span><br>";
echo "日期: " . $result['date']->format('Y-m-d H:i:s') . "<br>";
echo "验证: " . ($result['is_valid'] ? '通过' : '不匹配') . "<br>";
} else {
echo "<span style='color: red;'>✗ 解析失败</span><br>";
if (!empty($result['warnings'])) {
echo "警告: " . implode(', ', $result['warnings']) . "<br>";
}
if (!empty($result['errors'])) {
echo "错误: " . implode(', ', $result['errors']) . "<br>";
}
}
echo "<hr>";
}
// 输出类似:
// 测试: 格式 'Y-m-d', 字符串 '2023-12-25'
// ✓ 解析成功
// 日期: 2023-12-25 00:00:00
// 验证: 通过
//
// 测试: 格式 'Y-m-d', 字符串 '2023-13-45'
// ✗ 解析失败
// 错误: The parsed date was invalid
//
// 测试: 格式 'd/m/Y', 字符串 '25/12/2023'
// ✓ 解析成功
// 日期: 2023-12-25 00:00:00
// 验证: 通过
?>
<?php
// 时区处理
$timezone_ny = new DateTimeZone('America/New_York');
$timezone_tokyo = new DateTimeZone('Asia/Tokyo');
// 从带时区信息的字符串创建
$date1 = date_create_immutable_from_format('Y-m-d H:i:s P', '2023-12-25 14:30:00 +09:00');
echo "带时区偏移: " . ($date1 !== false ? $date1->format('Y-m-d H:i:s T P') : '失败') . "<br>";
// 指定时区创建
$date2 = date_create_immutable_from_format('Y-m-d H:i:s', '2023-12-25 14:30:00', $timezone_ny);
echo "纽约时区: " . ($date2 !== false ? $date2->format('Y-m-d H:i:s T') : '失败') . "<br>";
$date3 = date_create_immutable_from_format('Y-m-d H:i:s', '2023-12-25 14:30:00', $timezone_tokyo);
echo "东京时区: " . ($date3 !== false ? $date3->format('Y-m-d H:i:s T') : '失败') . "<br><br>";
// 比较时间戳
if ($date2 !== false && $date3 !== false) {
echo "时间戳比较:<br>";
echo "纽约时间戳: " . $date2->getTimestamp() . "<br>";
echo "东京时间戳: " . $date3->getTimestamp() . "<br>";
echo "时差: " . (($date3->getOffset() - $date2->getOffset()) / 3600) . " 小时<br>";
}
// 输出类似:
// 带时区偏移: 2023-12-25 14:30:00 +09:00 +09:00
// 纽约时区: 2023-12-25 14:30:00 EST
// 东京时区: 2023-12-25 14:30:00 JST
//
// 时间戳比较:
// 纽约时间戳: 1703529000
// 东京时间戳: 1703529000
// 时差: 14 小时
?>
<?php
// 与可变版本比较
echo "<h4>date_create_immutable_from_format() 与 date_create_from_format() 比较:</h4>";
// 创建可变对象
$mutable = date_create_from_format('Y-m-d H:i:s', '2023-12-25 14:30:00');
echo "可变对象类型: " . get_class($mutable) . "<br>";
// 创建不可变对象
$immutable = date_create_immutable_from_format('Y-m-d H:i:s', '2023-12-25 14:30:00');
echo "不可变对象类型: " . get_class($immutable) . "<br><br>";
// 测试修改操作
echo "<h5>修改操作对比:</h5>";
if ($mutable !== false && $immutable !== false) {
// 修改可变对象
$mutable_original = $mutable->format('Y-m-d H:i:s');
$mutable_modified = $mutable->modify('+1 day');
$mutable_after = $mutable->format('Y-m-d H:i:s');
echo "可变对象 - 修改前: {$mutable_original}<br>";
echo "可变对象 - 修改后: {$mutable_after}<br>";
echo "可变对象 - 是否相同对象: " . ($mutable === $mutable_modified ? '是' : '否') . "<br><br>";
// 修改不可变对象
$immutable_original = $immutable->format('Y-m-d H:i:s');
$immutable_modified = $immutable->modify('+1 day');
$immutable_after = $immutable->format('Y-m-d H:i:s');
echo "不可变对象 - 修改前: {$immutable_original}<br>";
echo "不可变对象 - 原对象修改后: {$immutable_after}<br>";
echo "不可变对象 - 新对象日期: " . $immutable_modified->format('Y-m-d H:i:s') . "<br>";
echo "不可变对象 - 是否相同对象: " . ($immutable === $immutable_modified ? '是' : '否') . "<br>";
}
// 输出类似:
// date_create_immutable_from_format() 与 date_create_from_format() 比较:
// 可变对象类型: DateTime
// 不可变对象类型: DateTimeImmutable
//
// 修改操作对比:
// 可变对象 - 修改前: 2023-12-25 14:30:00
// 可变对象 - 修改后: 2023-12-26 14:30:00
// 可变对象 - 是否相同对象: 是
//
// 不可变对象 - 修改前: 2023-12-25 14:30:00
// 不可变对象 - 原对象修改后: 2023-12-25 14:30:00
// 不可变对象 - 新对象日期: 2023-12-26 14:30:00
// 不可变对象 - 是否相同对象: 否
?>
<?php
// 处理各种日期格式的实用函数
class DateParser {
// 常见日期格式
private static $common_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日',
'Ymd His',
'Ymd',
'Y-m-d\TH:i:sP', // ISO 8601
'D, d M Y H:i:s O' // RFC 2822
];
public static function parseFlexible($date_str) {
// 首先尝试常见格式
foreach (self::$common_formats as $format) {
$date = date_create_immutable_from_format($format, $date_str);
if ($date !== false) {
$errors = DateTimeImmutable::getLastErrors();
if (empty($errors['warnings']) && empty($errors['errors'])) {
return [
'success' => true,
'date' => $date,
'format' => $format,
'method' => 'createFromFormat'
];
}
}
}
// 如果常见格式都失败,尝试 strtotime
$timestamp = strtotime($date_str);
if ($timestamp !== false) {
$date = date_create_immutable('@' . $timestamp);
return [
'success' => true,
'date' => $date,
'format' => null,
'method' => 'strtotime'
];
}
return [
'success' => false,
'error' => '无法解析日期字符串'
];
}
public static function detectFormat($date_str) {
foreach (self::$common_formats as $format) {
$date = date_create_immutable_from_format($format, $date_str);
if ($date !== false) {
$errors = DateTimeImmutable::getLastErrors();
if (empty($errors['warnings']) && empty($errors['errors'])) {
// 验证解析结果
$formatted = $date->format($format);
if ($formatted === $date_str) {
return $format;
}
}
}
}
return false;
}
}
// 测试
$test_dates = [
'2023-12-25 14:30:00',
'25/12/2023',
'12/25/2023 14:30:00',
'2023年12月25日',
'20231225 143000',
'2023-12-25T14:30:00+08:00',
'Mon, 25 Dec 2023 14:30:00 +0800',
'next Monday',
'invalid date'
];
echo "<h4>灵活日期解析:</h4>";
foreach ($test_dates as $date_str) {
$result = DateParser::parseFlexible($date_str);
echo "<strong>输入:</strong> '{$date_str}'<br>";
if ($result['success']) {
echo "<span style='color: green;'>✓ 解析成功</span><br>";
echo "日期: " . $result['date']->format('Y-m-d H:i:s') . "<br>";
echo "方法: " . $result['method'] . "<br>";
if ($result['format']) {
echo "格式: " . $result['format'] . "<br>";
}
} else {
echo "<span style='color: red;'>✗ 解析失败</span><br>";
}
echo "<hr>";
}
// 检测格式
echo "<h4>日期格式检测:</h4>";
$sample = '2023-12-25 14:30:00';
$format = DateParser::detectFormat($sample);
echo "示例: '{$sample}'<br>";
echo "检测到的格式: " . ($format ?: '未识别') . "<br>";
// 输出类似:
// 灵活日期解析:
// 输入: '2023-12-25 14:30:00'
// ✓ 解析成功
// 日期: 2023-12-25 14:30:00
// 方法: createFromFormat
// 格式: Y-m-d H:i:s
//
// 输入: '25/12/2023'
// ✓ 解析成功
// 日期: 2023-12-25 00:00:00
// 方法: createFromFormat
// 格式: d/m/Y
//
// 输入: 'next Monday'
// ✓ 解析成功
// 日期: 2023-08-21 00:00:00
// 方法: strtotime
//
// 输入: 'invalid date'
// ✗ 解析失败
?>
<?php
// 从时间戳创建不可变对象
echo "<h4>从 Unix 时间戳创建:</h4>";
$timestamp = 1703500200; // 2023-12-25 14:30:00 UTC
// 方法1: 使用 U 格式
$date1 = date_create_immutable_from_format('U', $timestamp);
echo "方法1 (U格式): " . ($date1 !== false ? $date1->format('Y-m-d H:i:s') : '失败') . "<br>";
// 方法2: 在时间戳前加 @
$date2 = date_create_immutable_from_format('U', (string)$timestamp);
echo "方法2 (字符串): " . ($date2 !== false ? $date2->format('Y-m-d H:i:s') : '失败') . "<br>";
// 方法3: 使用 date_create_immutable
$date3 = date_create_immutable('@' . $timestamp);
echo "方法3 (@前缀): " . $date3->format('Y-m-d H:i:s') . "<br><br>";
// 带时区的时间戳
echo "<h4>带时区的时间戳:</h4>";
$timezone = new DateTimeZone('America/New_York');
$date4 = date_create_immutable_from_format('U', $timestamp, $timezone);
echo "纽约时区: " . ($date4 !== false ? $date4->format('Y-m-d H:i:s T') : '失败') . "<br>";
// 验证
if ($date1 !== false && $date3 !== false) {
echo "<br><strong>验证:</strong><br>";
echo "date_create_immutable_from_format: " . $date1->getTimestamp() . "<br>";
echo "date_create_immutable: " . $date3->getTimestamp() . "<br>";
echo "是否相同: " . ($date1->getTimestamp() === $date3->getTimestamp() ? '是' : '否') . "<br>";
}
// 输出类似:
// 从 Unix 时间戳创建:
// 方法1 (U格式): 2023-12-25 14:30:00
// 方法2 (字符串): 2023-12-25 14:30:00
// 方法3 (@前缀): 2023-12-25 14:30:00
//
// 带时区的时间戳:
// 纽约时区: 2023-12-25 09:30:00 EST
//
// 验证:
// date_create_immutable_from_format: 1703500200
// date_create_immutable: 1703500200
// 是否相同: 是
?>
| 字符 | 描述 | 示例 |
|---|---|---|
| d | 月份中的第几天,有前导零的 2 位数字 | 01 到 31 |
| D | 星期中的第几天,文本表示,3 个字母 | Mon 到 Sun |
| j | 月份中的第几天,没有前导零 | 1 到 31 |
| l | 星期几,完整的文本格式 | Sunday 到 Saturday |
| F | 月份,完整的文本格式 | January 到 December |
| m | 数字表示的月份,有前导零 | 01 到 12 |
| M | 三个字母缩写表示的月份 | Jan 到 Dec |
| n | 数字表示的月份,没有前导零 | 1 到 12 |
| Y | 4 位数字完整表示的年份 | 例如:1999 或 2003 |
| y | 2 位数字表示的年份 | 例如:99 或 03 |
| a | 小写的上午和下午值 | am 或 pm |
| A | 大写的上午和下午值 | AM 或 PM |
| g | 小时,12 小时格式,没有前导零 | 1 到 12 |
| G | 小时,24 小时格式,没有前导零 | 0 到 23 |
| h | 小时,12 小时格式,有前导零 | 01 到 12 |
| H | 小时,24 小时格式,有前导零 | 00 到 23 |
| i | 有前导零的分钟数 | 00 到 59 |
| s | 有前导零的秒数 | 00 到 59 |
| u | 微秒 | 例如:654321 |
| v | 毫秒 | 例如:654 |
| O | 与格林威治时间相差的小时数 | 例如:+0200 |
| P | 与格林威治时间(GMT)的差别,小时和分钟之间有冒号分隔 | 例如:+02:00 |
| T | 时区缩写 | 例如:EST, MDT ... |
| U | 从 Unix 纪元(January 1 1970 00:00:00 GMT)开始至今的秒数 | 参见 time() |
date_create_immutable_from_format() 对格式要求严格,日期字符串必须完全匹配格式strtotime() 不同,这个函数不会尝试猜测或自动修正格式false,可以使用 DateTimeImmutable::getLastErrors() 获取错误信息\T 表示字面量 'T'! 前缀来重置所有字段到 Unix 纪元| 场景 | 建议 |
|---|---|
| 已知确切格式 | 使用 date_create_immutable_from_format() 确保精确解析 |
| 用户输入日期 | 先验证格式,再使用此函数解析 |
| 多格式支持 | 尝试多种格式,直到成功解析 |
| 错误处理 | 总是检查返回值,使用 getLastErrors() 获取详细错误 |
| 性能敏感 | 此函数比 strtotime() 更快,因为不需要猜测格式 |
| 时区处理 | 明确指定时区,避免依赖系统默认时区 |