date_create_immutable() 函数创建一个新的不可变 DateTime 对象,返回 DateTimeImmutable 对象。与普通的 DateTime 对象不同,DateTimeImmutable 对象是不可变的,任何对它的修改都会返回一个新的对象,而原对象保持不变。
这个函数是 new DateTimeImmutable() 构造函数的过程化别名,推荐在函数式编程中使用。
date_create_immutable([string $datetime = "now" [, DateTimeZone $timezone = null]]) : DateTimeImmutable|false
| 参数 | 默认值 | 描述 |
|---|---|---|
| datetime | "now" |
可选。 日期/时间字符串,任何能被 例如:"2023-12-25 14:30:00", "next Monday", "+1 week" 等。 |
| timezone | null |
可选。 DateTimeZone 对象,表示时区。 如果为 null,则使用当前默认时区。 |
成功时返回一个新的 DateTimeImmutable 对象,失败时返回 false。
<?php
// 创建当前时间的不可变对象
$date = date_create_immutable();
if ($date !== false) {
echo "当前时间: " . $date->format('Y-m-d H:i:s') . "<br>";
echo "时区: " . $date->getTimezone()->getName() . "<br>";
echo "对象类型: " . get_class($date);
}
// 输出类似:
// 当前时间: 2023-08-15 10:30:45
// 时区: Asia/Shanghai
// 对象类型: DateTimeImmutable
?>
<?php
// 创建指定时间的不可变对象
$date1 = date_create_immutable('2023-12-25 14:30:00');
$date2 = date_create_immutable('next Monday');
$date3 = date_create_immutable('+1 week 2 days');
echo "指定时间: " . $date1->format('Y-m-d H:i:s') . "<br>";
echo "下周一: " . $date2->format('Y-m-d') . "<br>";
echo "1周2天后: " . $date3->format('Y-m-d') . "<br>";
// 使用相对时间
$date4 = date_create_immutable('first day of next month');
echo "下个月第一天: " . $date4->format('Y-m-d') . "<br>";
// 输出类似:
// 指定时间: 2023-12-25 14:30:00
// 下周一: 2023-08-21
// 1周2天后: 2023-08-24
// 下个月第一天: 2023-09-01
?>
<?php
// 使用时区创建不可变对象
$timezone_ny = new DateTimeZone('America/New_York');
$timezone_tokyo = new DateTimeZone('Asia/Tokyo');
$date_ny = date_create_immutable('2023-12-25 12:00:00', $timezone_ny);
$date_tokyo = date_create_immutable('2023-12-25 12:00:00', $timezone_tokyo);
echo "纽约时间: " . $date_ny->format('Y-m-d H:i:s T') . "<br>";
echo "东京时间: " . $date_tokyo->format('Y-m-d H:i:s T') . "<br><br>";
// 比较时间戳(应该相同,因为指定了不同的时区)
echo "时间戳比较:<br>";
echo "纽约时间戳: " . $date_ny->getTimestamp() . "<br>";
echo "东京时间戳: " . $date_tokyo->getTimestamp() . "<br>";
echo "是否相同: " . ($date_ny->getTimestamp() === $date_tokyo->getTimestamp() ? '是' : '否');
// 输出类似:
// 纽约时间: 2023-12-25 12:00:00 EST
// 东京时间: 2023-12-25 12:00:00 JST
//
// 时间戳比较:
// 纽约时间戳: 1703520000
// 东京时间戳: 1703520000
// 是否相同: 是
?>
<?php
// 不可变对象与可变对象的对比
echo "<h4>可变 DateTime 对象:</h4>";
$mutable_date = date_create('2023-01-01');
echo "原始日期: " . $mutable_date->format('Y-m-d') . "<br>";
// 修改可变对象
$modified_mutable = $mutable_date->modify('+1 month');
echo "修改后日期: " . $mutable_date->format('Y-m-d') . "<br>";
echo "返回的新对象: " . ($modified_mutable === $mutable_date ? '是同一个对象' : '新对象') . "<br><br>";
echo "<h4>不可变 DateTimeImmutable 对象:</h4>";
$immutable_date = date_create_immutable('2023-01-01');
echo "原始日期: " . $immutable_date->format('Y-m-d') . "<br>";
// 尝试修改不可变对象
$modified_immutable = $immutable_date->modify('+1 month');
echo "原对象日期: " . $immutable_date->format('Y-m-d') . "<br>";
echo "修改后返回的新对象日期: " . $modified_immutable->format('Y-m-d') . "<br>";
echo "原对象与新对象是否相同: " . ($immutable_date === $modified_immutable ? '是' : '否') . "<br>";
// 输出类似:
// 可变 DateTime 对象:
// 原始日期: 2023-01-01
// 修改后日期: 2023-02-01
// 返回的新对象: 是同一个对象
//
// 不可变 DateTimeImmutable 对象:
// 原始日期: 2023-01-01
// 原对象日期: 2023-01-01
// 修改后返回的新对象日期: 2023-02-01
// 原对象与新对象是否相同: 否
?>
<?php
// 日期运算和比较
$start_date = date_create_immutable('2023-01-01');
// 创建新的不可变对象进行运算
$one_month_later = $start_date->modify('+1 month');
$two_months_later = $one_month_later->modify('+1 month');
$one_year_later = $start_date->modify('+1 year');
echo "开始日期: " . $start_date->format('Y-m-d') . "<br>";
echo "一个月后: " . $one_month_later->format('Y-m-d') . "<br>";
echo "两个月后: " . $two_months_later->format('Y-m-d') . "<br>";
echo "一年后: " . $one_year_later->format('Y-m-d') . "<br><br>";
// 日期比较
echo "日期比较:<br>";
echo "开始日期 < 一个月后: " . ($start_date < $one_month_later ? '是' : '否') . "<br>";
echo "一个月后 < 两个月后: " . ($one_month_later < $two_months_later ? '是' : '否') . "<br>";
echo "一年后 > 开始日期: " . ($one_year_later > $start_date ? '是' : '否') . "<br><br>";
// 日期差异
$diff = $start_date->diff($one_year_later);
echo "日期差异: " . $diff->format('%y 年 %m 月 %d 天') . "<br>";
// 输出类似:
// 开始日期: 2023-01-01
// 一个月后: 2023-02-01
// 两个月后: 2023-03-01
// 一年后: 2024-01-01
//
// 日期比较:
// 开始日期 < 一个月后: 是
// 一个月后 < 两个月后: 是
// 一年后 > 开始日期: 是
//
// 日期差异: 1 年 0 月 0 天
?>
<?php
// 错误处理
function safeDateCreateImmutable($datetime, $timezone = null) {
$date = date_create_immutable($datetime, $timezone);
if ($date === false) {
return "错误: 无法解析日期时间字符串 '{$datetime}'";
}
return $date;
}
// 测试各种情况
$test_cases = [
'2023-12-25 14:30:00',
'invalid date string',
'next Monday',
'2023-13-45', // 无效日期
'',
'2023-02-28 +1 day' // 有效
];
foreach ($test_cases as $datetime) {
$result = safeDateCreateImmutable($datetime);
if (is_string($result)) {
echo "'{$datetime}': {$result}<br>";
} else {
echo "'{$datetime}': 成功创建 - " . $result->format('Y-m-d H:i:s') . "<br>";
}
}
// 输出类似:
// '2023-12-25 14:30:00': 成功创建 - 2023-12-25 14:30:00
// 'invalid date string': 错误: 无法解析日期时间字符串 'invalid date string'
// 'next Monday': 成功创建 - 2023-08-21 00:00:00
// '2023-13-45': 错误: 无法解析日期时间字符串 '2023-13-45'
// '': 成功创建 - 2023-08-15 10:30:45
// '2023-02-28 +1 day': 成功创建 - 2023-03-01 00:00:00
?>
<?php
// 比较 date_create_immutable() 和 date_create()
echo "<h4>date_create_immutable() 与 date_create() 比较:</h4>";
// 创建可变对象
$mutable = date_create('2023-01-01');
echo "可变对象类型: " . get_class($mutable) . "<br>";
// 创建不可变对象
$immutable = date_create_immutable('2023-01-01');
echo "不可变对象类型: " . get_class($immutable) . "<br><br>";
// 测试修改操作
echo "<h5>修改操作对比:</h5>";
// 修改可变对象
$mutable_original = $mutable->format('Y-m-d');
$mutable_modified = $mutable->modify('+1 day');
$mutable_after = $mutable->format('Y-m-d');
echo "可变对象 - 修改前: {$mutable_original}<br>";
echo "可变对象 - 修改后: {$mutable_after}<br>";
echo "可变对象 - 是否相同对象: " . ($mutable === $mutable_modified ? '是' : '否') . "<br><br>";
// 修改不可变对象
$immutable_original = $immutable->format('Y-m-d');
$immutable_modified = $immutable->modify('+1 day');
$immutable_after = $immutable->format('Y-m-d');
echo "不可变对象 - 修改前: {$immutable_original}<br>";
echo "不可变对象 - 原对象修改后: {$immutable_after}<br>";
echo "不可变对象 - 新对象日期: " . $immutable_modified->format('Y-m-d') . "<br>";
echo "不可变对象 - 是否相同对象: " . ($immutable === $immutable_modified ? '是' : '否') . "<br><br>";
// 序列化测试
echo "<h5>序列化对比:</h5>";
$serialized_mutable = serialize($mutable);
$serialized_immutable = serialize($immutable);
echo "可变对象序列化长度: " . strlen($serialized_mutable) . "<br>";
echo "不可变对象序列化长度: " . strlen($serialized_immutable) . "<br>";
// 输出类似:
// date_create_immutable() 与 date_create() 比较:
// 可变对象类型: DateTime
// 不可变对象类型: DateTimeImmutable
//
// 修改操作对比:
// 可变对象 - 修改前: 2023-01-01
// 可变对象 - 修改后: 2023-01-02
// 可变对象 - 是否相同对象: 是
//
// 不可变对象 - 修改前: 2023-01-01
// 不可变对象 - 原对象修改后: 2023-01-01
// 不可变对象 - 新对象日期: 2023-01-02
// 不可变对象 - 是否相同对象: 否
//
// 序列化对比:
// 可变对象序列化长度: 79
// 不可变对象序列化长度: 90
?>
<?php
// 在实际应用中使用不可变对象
class EventScheduler {
private $events = [];
public function addEvent($name, $start_date) {
// 确保使用不可变对象
if (!$start_date instanceof DateTimeImmutable) {
$start_date = date_create_immutable($start_date->format('Y-m-d H:i:s'), $start_date->getTimezone());
}
$this->events[] = [
'name' => $name,
'start' => $start_date,
'end' => $start_date->modify('+2 hours') // 返回新对象,不影响原对象
];
return $this;
}
public function getEventsAfter($date) {
$result = [];
foreach ($this->events as $event) {
if ($event['start'] > $date) {
$result[] = $event;
}
}
return $result;
}
public function displayEvents() {
foreach ($this->events as $event) {
echo "活动: " . $event['name'] . "<br>";
echo "开始: " . $event['start']->format('Y-m-d H:i:s') . "<br>";
echo "结束: " . $event['end']->format('Y-m-d H:i:s') . "<br><br>";
}
}
}
// 使用示例
$scheduler = new EventScheduler();
// 添加活动
$scheduler->addEvent('会议', date_create_immutable('2023-12-25 09:00:00'))
->addEvent('培训', date_create_immutable('2023-12-26 14:00:00'))
->addEvent('派对', date_create_immutable('2023-12-31 20:00:00'));
echo "<h4>所有活动:</h4>";
$scheduler->displayEvents();
// 获取特定日期之后的活动
$cutoff_date = date_create_immutable('2023-12-26 00:00:00');
$future_events = $scheduler->getEventsAfter($cutoff_date);
echo "<h4>2023-12-26 之后的活动:</h4>";
if (empty($future_events)) {
echo "没有找到活动";
} else {
foreach ($future_events as $event) {
echo "活动: " . $event['name'] . " (" . $event['start']->format('Y-m-d') . ")<br>";
}
}
// 输出类似:
// 所有活动:
// 活动: 会议
// 开始: 2023-12-25 09:00:00
// 结束: 2023-12-25 11:00:00
//
// 活动: 培训
// 开始: 2023-12-26 14:00:00
// 结束: 2023-12-26 16:00:00
//
// 活动: 派对
// 开始: 2023-12-31 20:00:00
// 结束: 2024-01-01 22:00:00
//
// 2023-12-26 之后的活动:
// 活动: 派对 (2023-12-31)
?>
| 特性 | DateTime(可变) | DateTimeImmutable(不可变) |
|---|---|---|
| 创建函数 | date_create() 或 new DateTime() |
date_create_immutable() 或 new DateTimeImmutable() |
| 修改行为 | 修改原对象,返回自身($this) | 返回新对象,原对象不变 |
| 线程安全 | 不安全(可能被意外修改) | 安全(不可修改) |
| 函数式编程 | 不适用 | 适用 |
| 内存使用 | 较低(原地修改) | 较高(创建新对象) |
| 调试难度 | 较难(值可能变化) | 较易(值不变) |
| 推荐场景 | 简单脚本,性能敏感场景 | 复杂应用,多线程,函数式编程 |
| 方法 | 描述 | 是否返回新对象 |
|---|---|---|
modify() |
修改日期时间 | 是 |
add() |
添加时间间隔 | 是 |
sub() |
减去时间间隔 | 是 |
setDate() |
设置日期 | 是 |
setTime() |
设置时间 | 是 |
setTimestamp() |
设置时间戳 | 是 |
setTimezone() |
设置时区 | 是 |
format() |
格式化日期时间 | 否(返回字符串) |
getTimestamp() |
获取时间戳 | 否(返回整数) |
getTimezone() |
获取时区 | 否(返回DateTimeZone) |
diff() |
计算时间差 | 否(返回DateInterval) |
| 场景 | 建议 |
|---|---|
| 需要保留原始日期 | 使用 DateTimeImmutable |
| 函数式编程 | 使用 DateTimeImmutable |
| 性能敏感场景 | 使用 DateTime(如果不需要不可变性) |
| API 设计 | 接受和返回 DateTimeImmutable |
| 数据库操作 | 使用 DateTimeImmutable 避免意外修改 |
| 缓存日期对象 | 使用 DateTimeImmutable 保证值不变 |